0

I am asked to implement localization in a net.core solution as following:

  • Resource files must be in a separated project (like "MyProject.Common") to be used in all other projects in the solution,

  • Resources must be split by "sections", for example, we have 3 areas in the Web project like following :

  • Users,

  • Content,

  • Administration,

So I'm asked to have something like:

  • UsersResources.fr-CA.resx,
  • UsersResources.en-CA.resx,
  • ContentResources.fr-Ca.resx,
  • ...

I started to read documentation for Localization is AP-NET core and I'm a bit confused on how itr works. Doesn't seem like what i'm told to do is possible.

Thing is, I may need to use resources in Business, Views and Controllers so I'm searching for a way to implement it so the team could use the old way, by calling ContentResources.MyCustomResource.

Is there a way to get close from that?

I found a post where someone was mentioning https://www.nuget.org/packages/ResXResourceReader.NetStandard.

But I don't know if it will fit my needs...

#EDIT : So, trying to implement Laz's solution for shared resources.

So far, in startup I have this : in ConfigureServices :

services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
            .AddDataAnnotationsLocalization(options => {
                            options.DataAnnotationLocalizerProvider = (type, factory) =>
                            factory.Create(typeof(SharedResources));
                        
services.AddLocalization();

services.Configure<RequestLocalizationOptions>(
            opts =>
            {
                    /* your configurations*/
                    var supportedCultures = new List<CultureInfo>
                    {
                        new CultureInfo("en"),
                        new CultureInfo("fr")
                    };
                opts.DefaultRequestCulture = new RequestCulture("fr", "fr");
                opts.SupportedCultures = supportedCultures;
                opts.SupportedUICultures = supportedCultures;
            }
        );
  

and in Configure :

app.UseRequestLocalization();
// used to force culture to fr but doen't seem to work
var cultureInfo = new CultureInfo("fr");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;

in MyProject.Common, I have this structure:

MyProject.Common
-Resources
 --SharedResources.cs
  ---SharedResources.fr.resx
  ---SharedResources.en.resx
 --UsersResources.cs
  ---UsersResources.fr.resx
  ---UsersResources.en.resx

Let's say I want to use SharedResources.

In SharedResources.en.resx I added resources:
enter image description here

In SharedResources.fr.resx I added resources:
enter image description here

Now in my UserService, in Business layer, I did this:

 private readonly IStringLocalizer Localizer;

    public UserService(IStringLocalizerFactory factory)
    {
        var type = typeof(SharedResources);
        var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
        _localizer = factory.Create(type);
    }       

    public void Test()
    {
        var test = Localizer["Test"]; //using the key of resources file i want
    }

but all I get as a result in test variable is "Test", which is the key of the resource, and not the value.

2
  • 1
    You can refer to this SO thread that discussed similar requirement : stackoverflow.com/a/52775064/6751634 Commented Jun 3, 2020 at 7:27
  • @FeiHan Thanks for your time. Also tried the solution you linked but ended as the same result as Laz Ziya's solution below : all i get is the key of the resource Commented Jun 3, 2020 at 18:21

1 Answer 1

1

The default localization setup of .net core can be based on one shared resource file or based on the class name.

In your case, you can use the shared resource approach, but you have to create the cutomized localizer in each controller/class by providing the factory with the desired resource type.

First create a class library with the desired resources, create a public dummy class for each resource type you want, so the class library structure can be like below:

// SharedResourcesLibrary.csproj

- UsersResources.cs
 - UsersResources.fr-ca.resx
 - UsersResources.en-ca.resx

- ContentResources.cs
  - ContentResources.fr-ca.resx
  - ContentResources.en-ca.resx

...

The dummy classes are empty, they are used just as a type to call the relevant resx file.

// Dummy users resources class
public class UsersResources { }

Then after referencing the ResourcesLibrary project into other projects, you can use the resources by calling the relevant resource type (the dummy class).:

using SharedResourcesLibrary;

public class UsersController : Controller
{
    private readonly IStringLocalizer _localizer;

    public UsersController(IStringLocalizerFactory factory)
    {
        var type = typeof(UsersResources);
        _localizer = factory.Create(type);
    }       

    public IActionResult About()
    {
         ViewData["Message"] = _localizer["Welcome."];
    }
}

To use other resources just create the localizer using the relevant resource type.


Another approach can be done by creating custom multiple IStringLocalizers according to your areas, then inject them in the controllers.

// Create one localizer for each area
public class UsersLocalizer : IStringLocalizer
{
    private readonly IStringLocalizer _localizer;

    public UsersLocalizer(IStringLocalizerFactory factory)
    {
        var type = typeof(UsersResources);
        var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
        _localizer = factory.Create(type);
    }

    public LocalizedString this[string name] => _localizer[name];

    public LocalizedString this[string name, params object[] arguments] => _localizer[name, arguments];

    // ...
}

Similarly you can create localizers for other areas... then register in startup:

services.AddTransient<IStringLocalizer, UsersLocalizer>();
services.AddTransient<IStringLocalizer, AdminsLocalizer>();
services.AddTransient<IStringLocalizer, ContentLocalizer>();
// ...

This way all localizers will be registered, and if you simply inject IStringLocalizer it will get the last registered one, because all localizers are implementing the same IStringLocalizer interface.

So you have to do type selection for injecting the correct localizer:

public UsersController : Controller
{
    private readonly IStringLocalizer _localizer;

    public UsersController(IEnumerable<IStringLocalizer> localizers)
    {
        _localizer = localizers.FirstOrDefault(x => x.GetType() == typeof(UsersLocalizer));
    }

    public IActionResult About()
    {
        ViewData["Message"] = _localizer["Welcome."];
    }
}

You can refere to this article for different ways of Registering multiple implementation with the same interface in Asp.Net Core

Sign up to request clarification or add additional context in comments.

7 Comments

Thank you for the detailed answer. Currently trying the first approach (the factory.Create(type) one), and all i have as a result when I use Localizer["Test"] in my business layer is the key Test itself, and not the value Here is a test. Any idea what i could have missed here?
Do you get the same result when the resource files in the same project?
And how do you change the culture? See the output of @CultureInfo.CurrentCulture.Name in any view to ensure you are at the right culture
I just created a test sample with the first approach and your startup settings, everything worked fine can share if you want :) So, I assume your issue is with the culture setup and current culture.
@j0w glad to know it worked, and for the enums that would be another question, but this might help :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.