How to get localized string from ASP.NET Core Controller using IStringLocalizer?

dapug picture dapug · Jan 30, 2018 · Viewed 21.8k times · Source

Kinda confused here, super simple hello-world example of localization in ASP.Net Core 2.0. My About page is set up to render two localized strings:

  1. From the view (using IViewLocalizer)
  2. From code (using IStringLocalizer<HomeController> via the controller)

The code in the controller refuses to get the loc string appropriately. This is not complicated, what obvious things am I missing?

About.cshtml

@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer
@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>

<p>@Localizer["Use this area to provide additional information."]</p>

^ Note the two strings: "Message" will be localized from code using IStringLocalizer (see HomeController below), and the @Localizer will use the IViewLocalizer class.

HomeController.cs

public class HomeController : Controller
{
    private readonly IStringLocalizer _localizer;

    public HomeController(IStringLocalizer<HomeController> localizer)
    {
       _localizer = localizer;
    }

    public IActionResult Index()
    {
        return View();
    }

    public IActionResult About()
    {
        ViewData["Message"] = _localizer["Your application description page."];

        return View();
    }
}

Startup.cs (relevant parts)

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddLocalization(options => options.ResourcesPath = "Resources");

        services.AddMvc()
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
            .AddDataAnnotationsLocalization();

        services.Configure<RequestLocalizationOptions>(options =>
        {
            var supportedCultures = new[]
            {
                new CultureInfo("en-US"),
                new CultureInfo("fr-CH"),
            };

            options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
            options.SupportedCultures = supportedCultures;
            options.SupportedUICultures = supportedCultures;
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        var locOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
        app.UseRequestLocalization(locOptions.Value);

        if (env.IsDevelopment())
        {
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

Resources:

Views.Home.About.fr-CH.resx

^ with two values in it:

  • "Use this area to provide additional information." = "Use this area... success for fr-CH!"
  • "Your application description page." = "Your app descript... success for fr-CH!"

My Results:

localhost:56073/Home/About

^ This renders the strings as expected in en-US (default finds nothing, uses the strings actually hard coded)

localhost:56073/Home/About?culture=fr-CH

^ This renders ONLY the 2nd string: "Use this area... success for fr-CH!", which clearly means all the code wired up is working and finding the fr-CH.resx as expected.

BUT, the first string (set in code as ViewData["Message"]) does NOT get the fr-CH version! It's like the IStringLocalizer<HomeController> failed to realize there was a lang specified, or failed to find the fr-CH.resx that is clearly available.

Why???

Also BTW, I tried using the ShareResource example too (see link below), and passed in the factory to the HomeController ctor as IStringLocalizerFactory factory, also with no love, still not getting the fr-CH resource. Sigh.

Other notes:

Using this as my primary reference: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization

Using VS 2017, latest updates, with ASP.Net Core 2.0

Answer

Reza Aghaei picture Reza Aghaei · Feb 3, 2018

You are using IStringLocalizer<HomeController> as localizer in the controller to find the localized string. The localizer will look in Resources folder to find YouControllerNameSpace.HomeController resource file and since it doesn't find it, it will return the original key which you passed to the localizer.

To solve the problem, you can use either of following options:

  • Inject IStringLocalizer<T>
  • Inject IStringLocalizerFactory

For more information about resource file names, take a look at Resource file naming section in documentations.

Inject IStringLocalizer<T>

Using this option, you should have a resource file with the same name as full name of T, in your case, the controller code should be the same as it is:

IStringLocalizer _localizer;
public HomeController(IStringLocalizer<HomeController> localizer)
{
   _localizer = localizer;
}

For the resource file:

  • Make sure you have YouControllerNameSpace.HomeController resource file. (YouControllerNameSpace is just a placeholder, use your controller namespace.)
  • Make sure you have the specified string in resource file.
  • Make sure you have resource files for different cultures.

Inject IStringLocalizerFactory

Using this option you can use any file as resource file. For example if you want to read resources from Views.Home.About resource file, you should change the controller code to this:

IStringLocalizer _localizer;
public HomeController(IStringLocalizerFactory factory)
{
    _localizer = factory.Create("Views.Home.About", 
        System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
}

For the resource file:

  • Make sure you have Views.Home.About resource file.
  • Make sure you have the specified string in resource file.
  • Make sure you have resource files for different cultures.