Reputation: 1706
There are many answered questions out there covering this topic, but i cannot make it work here.
Our .NET Core 6 solution contains many projects - most of them RESTful APIs - which can share the resources for localization.
So the goal is now setting up a SharedLocResources
project containing all the resx-files with the translations which then can be accessed in all other project. The request locale is provided by the Accept-Language header. Getting this to work is not the problem. The setup of using the resources files is what I am struggling with.
RestfulAPI.csproj
| - Controllers/MyController.cs
SharedLocalizationResources.csproj
|
| - Properties
| - LocalizationService.cs
| - SharedResource.cs //Dummy class to group shared resources
| - Resources.resx
| - Resources.ar.resx
| - Resources.bn.resx
....
A custom service uses the IStringLocalizerFactory
. It creates an IStringLocalizer
instance together with a dummy class providing the type.
SharedResources.cs
resides within the SharedLocResources
project.
namespace SharedLocalizationResources;
public class LocalizationService
{
private readonly IStringLocalizer localizer;
public LocalizationService(IStringLocalizerFactory factory)
{
var type = typeof(SharedResource);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
localizer = factory.Create("SharedResource", assemblyName.Name);
}
}
namespace SharedLocalizationResources;
/// <summary>
/// Dummy class to group shared resources
/// </summary>
public class SharedResource{}
This service is added to the startup routine:
builder.Services.AddSingleton<LocalizationService>();
builder.Services.AddLocalization(options => options.ResourcesPath = "SharedLocalizationResources"); // Resource-containing project name
builder.Services.AddMvc().AddViewLocalization()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
var assemblyName = new AssemblyName(typeof(SharedResource).GetTypeInfo().Assembly.FullName);
return factory.Create("SharedResource", assemblyName.Name);
};
});
builder.Services.Configure<RequestLocalizationOptions>(
options =>
{
var supportedCultures = new List<CultureInfo>
{
new("ar"),
new("bn"),
new("bg"),
new("en"),
new("de")
};
options.DefaultRequestCulture = new RequestCulture(culture: "de", uiCulture: "de");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders.Insert(0, new QueryStringRequestCultureProvider());
});
But using this in a controller, I see only the default "Test" string, but not the localized resources.
public MyController( IStringLocalizer<SharedResource> sharedLocalizer)
{
sharedLocalizer = sharedLocalizer;
}
public ActionResult<string> Version()
{
var res = new ActionResult<string>(sharedLocalizer["Test"]);
return res;
}
How do I have to fix the setup to make the IStringLocalizer
make use of the resources from the SharedLocalizationResources
project?
Upvotes: 0
Views: 389
Reputation: 1133
Put a breakpoint in your constructor and inspect sharedLocalizer
for a private property called _resourceBaseName
. The underlying class (probably Microsoft.Extensions.Localization.ResourceManagerStringLocalizer
) often mangles the full type name by injecting .Resources.
after the first moniker.
They do this with the intention that your project's \Resources
folder can easily mirror the rest of your application and string localization will auto-magically work. For example, if you have a Razor page at WebApp\Pages\MyPageModel.cshtml
and your underlying code class injects IStringLocalizer<WebApp.Pages.MyPageModel> _localizer
(as in the plethora of online examples), then Microsoft's code tries to be helpful and look for an implementation of WebApp.Resources.Pages.MyPageModel
. Yay for magic string manipulation they don't tell you about!
This unfortunately doesn't help at all when you are trying access a resource file located somewhere else.
In your case, the class is likely looking for a resource class at SharedLocalizationResources.Resources.Properties.SharedResource
and not finding it obviously.
My recommendation would be to stop using the \Properties
folder to store your resource files, add a \Resources
folder and put all your resources in there, then add a dummy class at the project root that you use as your generic type when declaring your IStringLocalizer<T>
variables elsewhere.
So you would have a dummy class at SharedLocalizationResources.SharedResource
and all your resource files we be located at SharedLocalizationResources\Resources\SharedResource.resx
.
Hope this helps!
Upvotes: 0