【问题标题】:Blazor IStringLocalizer injection to servicesBlazor IStringLocalizer 向服务注入
【发布时间】:2021-05-15 05:08:41
【问题描述】:

我正在使用 IStringLocalizer 方法来本地化我的 Blazor 应用程序,如 here 所述。

在剃须刀页面上注入IStringLocalizer 效果很好。我还需要它来本地化一些服务 - 无论是范围服务还是单例服务。

使用构造函数注入将我的IStringLocalizer 服务注入到服务中。但是,当用户通过 UI 更改语言时,服务(无论是单例还是作用域)保持 initial IStringLocalizer - 即启动时使用 original 语言的服务应用程序,而不是用户选择的更新语言。

从代码中检索更新的IStringLocalizer 的建议方法是什么?

编辑 为了防止更多细节,这里有一些代码。 首先,我添加一个Resources 文件夹并在其中创建一个默认的LocaleResources.resx(带有公共修饰符)和一个LocaleResources.fr.resx 文件,其中包含每种语言的键值对。

支持的文化在appsettings.json 文件中定义为

"Cultures": {
        "en-US": "English",
        "fr": "Français (Suisse)",
        ...
    }

在启动时,我注册了Resources 文件夹和支持的文化:

public void ConfigureServices(IServiceCollection services {
    ...
    services.AddLocalization(options => options.ResourcesPath = "Resources");
    ...
    services.AddSingleton<MySingletonService>();
    services.AddScoped<MyScopedService>();
}

// --- helper method to retrieve the Cultures from appsettings.json
protected RequestLocalizationOptions GetLocalizationOptions() {
    var cultures = Configuration.GetSection("Cultures")
        .GetChildren().ToDictionary(x => x.Key, x => x.Value);

    var supportedCultures = cultures.Keys.ToArray();

    var localizationOptions = new RequestLocalizationOptions()
        .AddSupportedCultures(supportedCultures)
        .AddSupportedUICultures(supportedCultures);

    return localizationOptions;
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    ...
    app.UseRequestLocalization(GetLocalizationOptions());
    ...
    app.UseEndpoints(endpoints => {
        endpoints.MapControllers();
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
    });
}

我在项目的根目录下创建了一个空的LocaleResources.razor 控件(这是用于将单个资源文件注入所有组件的技巧)。

我包含了一个路由控制器来更改语言:

[Route("[controller]/[action]")]
public class CultureController : Controller {
    public IActionResult SetCulture(string culture, string redirectUri) {
        if (culture != null) {
            HttpContext.Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(
                new RequestCulture(culture)));
        }
        return LocalRedirect(redirectUri);
    }
}

语言 UI 切换器看起来像这样(我在这里使用 SyncFusion 控件,但实际上它可以是任何查找,这并不重要)

@inject NavigationManager NavigationManager
@inject IConfiguration Configuration
 
<SfComboBox TValue="string" TItem="Tuple<string, string>" Placeholder="Select language" DataSource="@Cultures"
            @bind-Value="selectedCulture" CssClass="lan-switch" Width="80%">
  <ComboBoxFieldSettings Text="Item2" Value="Item1"></ComboBoxFieldSettings>
</SfComboBox>

<style>
  .lan-switch {
    margin-left: 5%;
  }
</style>

@code {
  string _activeCulture = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
  private string selectedCulture {
    get => _activeCulture;
    set {
      _activeCulture = value;
      SelectionChanged(value);
    }
  }

  List<Tuple<string, string>> Cultures;

  protected override void OnInitialized() {
    var cultures = Configuration.GetSection("Cultures")
      .GetChildren().ToDictionary(x => x.Key, x => x.Value);
    Cultures = cultures.Select(p => Tuple.Create<string, string>(p.Key, p.Value)).ToList();
  }

  protected override void OnAfterRender(bool firstRender) {
    if (firstRender && selectedCulture != AgendaSettings.SelectedLanguage) {
      selectedCulture = AgendaSettings.SelectedLanguage;
    }
  }

  private void SelectionChanged(string culture) {
    if (string.IsNullOrWhiteSpace(culture)) {
      return;
    }
    AgendaSettings.SelectedLanguage = culture;
    var uri = new Uri(NavigationManager.Uri)
    .GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
    var query = $"?culture={Uri.EscapeDataString(culture)}&" +
        $"redirectUri={Uri.EscapeDataString(uri)}";

    NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true);
  }
}

最后,到了注入。我将IStringLocalizer 注入页面如下,它在剃须刀控件上运行良好:

@inject IStringLocalizer<LocaleResources> _loc

<h2>@_loc["hello world"]</h2>

以上,当我更改语言时,页面显示相应资源文件中的值。

现在,对于服务:MySingletonServiceMyScopedService 在启动时注册。他们都有一个像

这样的构造函数
protected IStringLocalizer<LocaleResources> _loc;
public MySingletonService(IStringLocalizer<LocaleResources> loc) {
    _loc = loc;
}

public void someMethod() {
    Console.WriteLine(_loc["hello world"])
}

我在计时器上运行someMethod。奇怪的是,当我打破上述行时,结果似乎摇摆不定:一旦它返回默认语言的值,一旦本地化......!

【问题讨论】:

  • 我认为问题在于语言更改逻辑,而不是 IStringLocalizer。但这很难说,你需要提供更多关于代码到底在做什么的信息。
  • 您没有提供足够的信息(至少对我而言)来了解 IStringLocalizer 如何基于用户选择的语言。所选语言是IStringLocalizer 实例的构造函数参数吗?如果是这样,则意味着您的服务可能超出了包含语言更改的范围。但是你真的需要在这里提供一个更具体(而且仍然很简短)的例子。
  • 好的,让我调整一下问题,让它足够清楚!

标签: dependency-injection localization blazor


【解决方案1】:

我的问题的答案是:你的代码是正确的!

我发现原因是我使用了在默认 App 的起始页上启动的 Scoped 服务:

protected async override Task OnAfterRenderAsync(bool firstRender) {
if (firstRender) {
  MyScopedService.StartTimer();
}
await base.OnAfterRenderAsync(firstRender);

}

当用户更改语言时,会刷新整个页面并创建范围服务的新实例并启动计时器。由于我的服务没有实现IDisposable,定时器实际上并没有停止。

这里有两个解决方案:

  1. 使用单例服务
  2. 将服务设为一次性,并确保在服务处理完毕后取消任务。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-21
    • 1970-01-01
    • 2020-01-29
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    相关资源
    最近更新 更多