Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization

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

like image 311
dapug Avatar asked Jan 30 '18 19:01

dapug


2 Answers

The issue is that ASP .NET Core creates wrong RESX namespace for localization using IStringLocalizer. If you have in the code

services.AddLocalization(options => options.ResourcesPath = "Resources");

then the instance of injected service IStringLocalizer has in the namespace twice "Resources", namespace looking "Resources.Resources" . This is the root cause why the RESX cannot be found.

like image 71
Ondrej Rozinek Avatar answered Oct 17 '22 13:10

Ondrej Rozinek


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.
like image 38
Reza Aghaei Avatar answered Oct 17 '22 15:10

Reza Aghaei