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

5

13

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

Fritillary answered 30/1, 2018 at 19:12 Comment(0)
C
14

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.

Cady answered 25/9, 2019 at 7:7 Comment(1)
This should be voted the correct answer. I strugeled with this for about an hour and it may be the easiest thing to test first.Ankus
A
12

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.
Actinomorphic answered 3/2, 2018 at 22:58 Comment(3)
The snag is, I have resources named properly, I see the strings for en-US (default), but when I switch to another culture, it fails to find the string. e.g. I have TWO resx files in the Resources folder: Controllers.HomeController.en-US.resx, and Controllers.HomeController.fr-CH.resx. When the app is set to en-US, it successfully gets the string, which tells me the naming and all else is set up and working correctly. But when set to fr-CH, it fails to find the string even though the fr-CH file is also named correctly and the appropriate key/value also is there.Fritillary
Are you sure what you see is strings for en-US and not the keys which are passed to localizer? Anyway, using the settings and options which I shared, surely you can setup localization.Actinomorphic
I purposely used keys that are not "display" strings so that I could verify that the resx (which contains the display values) is being accessed when requested. en-US is working, any other culture wont. I agree, surely this should work.Fritillary
I
0

Try the technique described by tmg in this answer.

Specifically, try adding the lines

options.RequestCultureProviders = new List<IRequestCultureProvider>
{
    new QueryStringRequestCultureProvider(),
    new CookieRequestCultureProvider()
};

to your ConfigureServices() function

Innes answered 23/2, 2018 at 18:57 Comment(0)
A
0

Just add HomeController to your field.

IStringLocalizer<HomeController> _localizer;

Anything answered 26/7, 2024 at 15:41 Comment(2)
IStringLocalizer<HomeController> _localizer;Anything
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Dibru
C
-1

The error occurs because it cannot find the path of the .resx file. It is solved by modifying the path in Startup.cs or Program.cs

builder.Services.AddLocalization(options => options.ResourcesPath = "");
Cryptozoic answered 11/6, 2024 at 1:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.