4

I'm trying to implement localization in my Blazor WebAssembly app, by following one of tutorials found on internet, such as: Localization in Blazor

I have set up everything described in tutorials, but I cannot get a localized value for a specific key, I get a KEY displayed as text I want to localize.

Here is my code: Program.cs - hardcoded culture, to make sure i choose the right one:

builder.Services.AddLocalization(opts => { opts.ResourcesPath = "Localization"; });
var culture = new CultureInfo("sr");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;

In my .razor file I injected a IStringLocalizer:

@inject IStringLocalizer<General> localizer

And I use a localizer like this: <MudTooltip Text="@localizer["Open"]">

My Class generated from .resx is called General. Here is my resources setup: Added a resx as Embedded resource and code gen tool PublicResXFileCodeGenerator Main resources file for 'en'

My localized resx for Serbian language is set as EmbeddedResource and with no code generation tool.

1 Answer 1

2

The IStringLocalizer<T> doesn't rely on a generated class from the .resx file. During the build process, the ResourceManagerembeds the resource in the main dll and creates satellite dlls. The default implementation of IStringLocalizer<T> rely on the ResourceManager to read the files inside the assembly or satellite assembly. So turn off the code generation should solve your problem.

Properties of a .resx files

Update:

The <T> is referring to any possible class. There is no constraint enforce. However, there should be a link between the namespace of the class and the file location.

Assume you have the file Pages/Users/AddUserPage.razor

@inject IStringLocalizer<AddUser> Localizer

If you don't specify a namespace manually, AddUserPage class will end up in the namespace <ProjectRootNamespace>.Pages.Users.

So, you need to create a .resx file reflecting the folder structure. Your root is specified as Localization so you need to create the file Localization/Pages/Users/AddUserPage.resx or and their locazied counterparts like Localization/Pages/Users/AddUserPage.sr.resx

In some cases, it makes sense to group certain localization, like the caption of buttons (Ok, Close, Yes, No, etc.). To avoid having these keys in each .resx file, you can create an empty class. I like to name it SharedRessources. However, the name is not important as long as you name your .resx file accordingly.


namespace <ProjectRootNamespace>
{
    public class SharedRessources
    {
    }
}

Because the class is in the root namespace of your project, the .resx file should be placed inside the root folder of your .resx file. In your case, Localization. So, the name should be Localization/SharedRessources.resx and Localization/SharedRessources.sr.resx

Now, you can this can be injected too.

@inject IStringLocalizer<AddUser> Localizer
@inject IStringLocalizer<SharedRessources> SharedLocalizer

<p> Hi from @Localizer["KeyLocalToAddUser"] </p>
<p> Do you want to delete the user? <span> @SharedLocalizer["No"] or SharedLocalizer["Yes"] 

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

4 Comments

So T is for what? I tried to put my page's class name for T, and I did like you proposed - no code generation, but still not working. I also read some articles that there should not be a default resource file - tried that - no luck...
Looks like I did not follow the naming rules for resource files: learn.microsoft.com/en-us/aspnet/core/fundamentals/… So if I had a page named Playlists, a resource file should be named "Pages.Playlists.xx.resx I am not sure if this should be an answer, I am curious if there is a way to group localized strings in something bigger than a page.
There is an answer for my previous comment in the same article. A dummy class should be used to group shared resources.
Thanks for your input @vpetrovic. I've updated the answer with more information about T, the naming convention and how to handle sharing of localization between multiple pages

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.