Reputation: 545
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:
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:
In SharedResources.fr.resx
I added resources:
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
.
Upvotes: 0
Views: 3553
Reputation: 5719
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 IStringLocalizer
s 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
Upvotes: 1