Blazor IStringLocalizer 向服务注入

Posted

技术标签:

【中文标题】Blazor IStringLocalizer 向服务注入【英文标题】:Blazor IStringLocalizer injection to services 【发布时间】: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 实例的构造函数参数吗?如果是这样,则意味着您的服务可能超出了包含语言更改的范围。但是你真的需要在这里提供一个更具体(而且仍然很简短)的例子。 好的,让我调整一下问题,让它足够清楚! 【参考方案1】:

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

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

protected async override Task OnAfterRenderAsync(bool firstRender) 
if (firstRender) 
  MyScopedService.StartTimer();

await base.OnAfterRenderAsync(firstRender);

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

这里有两个解决方案:

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

【讨论】:

以上是关于Blazor IStringLocalizer 向服务注入的主要内容,如果未能解决你的问题,请参考以下文章

如何将IStringLocalizer注入IApplicationModelConvention?

带有 IStringLocalizer 的 RazorPages 不起作用

使用 Blazor 向其他浏览器发送通知

除了 dbContext 之外,如何向 Blazor 中的服务添加参数?

如何向服务器端 Blazor 项目添加控制器(而非视图)支持

没有 Web 程序集的 blazor