简体   繁体   中英

ASP.NET Core 3.1 : Shared Localization not working for version 3.1

I may be not doing the correct configurations in the startup.cs file. I have created a demo application to make it working, but after trying various things it is not working. The demo repository is available at following link

https://github.com/gurpreet42/MyAppV3

Configurations of startup.cs files are

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

   services.Configure<RequestLocalizationOptions>(options =>
            {
                var supportedCultures = new List<CultureInfo>
                                            {
                                                new CultureInfo("en-US"),
                                                new CultureInfo("nl")
                                            };

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

   services.AddMvc()
           .AddViewLocalization()
           .AddDataAnnotationsLocalization(options =>
                {
                   options.DataAnnotationLocalizerProvider = (type, factory) =>
                   {
                       var assemblyName = new AssemblyName(typeof(SharedResource).GetTypeInfo().Assembly.FullName);
                       return factory.Create("SharedResource", assemblyName.Name);
                   };
               }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

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

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseAuthentication();
    app.UseSession();

    app.UseSession();
    app.UseCookiePolicy();
}

The code in the LocService class is

public class LocService
{
    private readonly IStringLocalizer _localizer;

    public LocService(IStringLocalizerFactory factory)
    {
        var type = typeof(SharedResource);
        var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
        _localizer = factory.Create("SharedResource", assemblyName.Name);
    }

    public LocalizedString GetLocalizedHtmlString(string key)
    {
        var value= _localizer[key];
        return value;
    }
}

Now on our controller, we can access the localized string as

localizerService.GetLocalizedHtmlString("my_string")

Under the "Resources" folder we have following files present

SharedResource.cs
SharedResource.en-US.resx
SharedResource.nl.resx

Please suggest where the configurations are wrong or do I need to add some extra package?

It turns out that in asp.net core 3.1, you need to place SharedResource.cs out of Resources folder(see this github issue )

If class SharedResource.cs and SharedResource.*.resx in same folder, the namespace will be error in compiled dll xxx.lang.dll .

So, just delete original SharedResource.cs create a new one under the project directly:

namespace MyAppV3
{
    public class SharedResource
    {
    }
}

And readd resource files to the Resources folder.

There is no need to create the LocService you can use IStringLocalizer.

"Resources" folder structure

SharedResource.cs
SharedResource.en-US.resx
SharedResource.nl.resx

At the class SharedResource, do not add "Resources" in the namespace. Like MyAppV3.Resources. Please keep it just MyAppV3.

namespace MyAppV3
{
    public class SharedResource
    {
    }
}

Add in your .csproj the following property

<PropertyGroup><EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention></PropertyGroup>

Add to startup.cs > ConfigureServices

    services.AddLocalization(options => options.ResourcesPath = "Resources");
    services.AddScoped<IStringLocalizer, StringLocalizer<SharedResource>>();

    services
        .Configure<RequestLocalizationOptions>(options =>
            {
                var cultures = new[]
                                   {
                                       new CultureInfo("en"),
                                       new CultureInfo("nl")
                                   };
                options.DefaultRequestCulture = new RequestCulture("en");
                options.SupportedCultures = cultures;
                options.SupportedUICultures = cultures;
            });

Startup.cs > Configure

   app.UseRequestLocalization(app.ApplicationServices
            .GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);

Add to the controller the IStringLocalizer parameter.

   public MyTestController(IStringLocalizer localizer)
   {
        this.localizer = localizer;
   }

   public IActionResult Get()
   {          
        var value = this.localizer.GetString("RessourceName");
        return this.Ok(value);
   }

There was a problem with the namespace in my case. Modifying it solved the problem for me.

I had similar issue during upgrade from 2.2 to 3.1.

In 2.2 my setup was:

        services.AddLocalization(options => options.ResourcesPath = "Resources");
        services.AddMvc(config =>
            {
               ... <omitted for brevity>
            })
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

All resources where in "Resources" folder. SharedResources.cs had recommended namespace of the project:

// *************************
// DO NOT CHANGE NAMESPACE
// *************************
// ReSharper disable once CheckNamespace
namespace MyProject
{
    // Dummy class to group shared resources
    public class SharedResources
    {
    }
}

And View Localizations were based on the path eg

Resources
   Pages
      Error.en-US.resx
      Error.ja-JP.resx

With 3.1, recommended setup was (with minimal changes):

        services.AddLocalization();
        services.AddRazorPages().AddRazorRuntimeCompilation();
        services.AddControllersWithViews(config =>
            {
               ... <omitted for brevity>
            })
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
            .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

At this point SharedResources was working fine but IViewLocalizer and view localizations were not working.

I found two ways to fix it based on above suggestions and this article :

  1. Opt out of the new naming convention, effectively going back to what was used in 2.2 by adding following to csproj file:

     <PropertyGroup> <EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention> </PropertyGroup>

And, adding options => options.ResourcesPath = "Resources" back to services.AddLocalization() .

  1. Keep 3.1 setup and fix each view localization by adding dummy .cs file with namespace that is outside of resources folder, for example:

     Resources Pages Error.cs Error.en-US.resx Error.ja-JP.resx

Where Error.cs is:

// *************************
// DO NOT CHANGE NAMESPACE
// *************************
// ReSharper disable once CheckNamespace
namespace MyProject.Pages
{
    // Dummy class to group shared resources
    public class Error
    {
    }
}

So if you have lots of View Localizations like me, #1 option would probably be better, as it is configuration change only, leaving all existing view localization as they were.

I have been struggling with Localization over the past week, and came across a much simpler solution that I have not yet seen a downside for, and I can't figure out why almost nobody does it this way.

  1. Create a resource file without a culture (eg Resource.resx) for your default strings, and one (or multiple) with a culture (eg Resource.nl.resx).

  2. Then on the Resource file set Access Modifier to 'Public': image where to set access modifier

  1. In your views and controllers you can then just use:

    @Resource.CurrentMarketValue

like in the good old days! As long as you are 'using project .Resources'.

No magic strings, IntelliSense, and language switching still works fine. Don't even need to change Startup.cs.

You can set the culture as follows:

Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en");
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM