dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.1k stars 9.91k forks source link

Blazor server localization not working correctly when using alternate folder for storing resource files #49550

Open htmlsplash opened 1 year ago

htmlsplash commented 1 year ago

Is there an existing issue for this?

Describe the bug

Blazor server localization using shared resource strings (as explained here: https://learn.microsoft.com/en-us/aspnet/core/blazor/globalization-localization?view=aspnetcore-7.0&pivots=server) via IStringLocalizer works fine if the resources are placed in the default location "Localization" folder. The Program.cs for this looks like this:

builder.Services.AddLocalization(); app.UseRequestLocalization("en-CA");

But, when changing the default location to another folder (Resources) the localized strings are not found by the IStringLocalizer object. The Program.cs for this looks like this:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); app.UseRequestLocalization("en-CA");

Other findings: 1) When reverting back to using the original "Localization" folder (using statement: builder.Services.AddLocalization()) my strings from my resource files located under "Resources" are magically picked up and translated (verified by restarting VS/application). This is strange since it should only look at the "Localization" folder.

2) When using a resource file named App.resx (placed in the "Resources" folder) that maps to the App component and accessing the strings via IStringLocalizer (App type) works as expected when configuring the default location for resource strings to be the alternate "Resources" folder. It just doesn't work if the resource file use an arbitrary class name as described in the link above under the Shared resources topic.

Expected Behavior

Sharing resources in Blazor Server: When re-mapping to a different resource folder using "builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");" the resource files from the "Resources" folder should be used.

Steps To Reproduce

Follow the instructions from: https://learn.microsoft.com/en-us/aspnet/core/blazor/globalization-localization?view=aspnetcore-7.0&pivots=server

Topic: Shared resources

Then change the default resource file folder to "Resources" folder with the following line

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

Move your resource files to the "Resources" folder and observe the bug (strings are not translated).

Exceptions (if any)

No response

.NET Version

.net 7 Blazor Server

Anything else?

No response

hishamco commented 1 year ago

Could you please provide a minimal repo to reproduce the issue?

htmlsplash commented 1 year ago

Thank you for your response. To reproduce the issue:

1) Just create a brand new Blazor Server project with default pages (counter/weather forecast pages). 2) Add these 2 lines for localization:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

// this line goes under routing middleware app.UseRequestLocalization("en-CA");

or Copy my Program.cs below

`using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Localization;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

// Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton();

var app = builder.Build();

// Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); }

app.UseStaticFiles();

app.UseRouting(); app.UseRequestLocalization("en-CA");

app.UseSystemWebAdapters();

app.MapBlazorHub();

app.MapFallbackToPage("/_Host");

app.Run();`

3) Add "Resources" folder 4) Add a resource file (call it "SharedStrings1") with a string "Test" that translates to a different string ("this is my test string"). 5) Edit index.razor and add the following lines:

@inject IStringLocalizer Loc

Test string=@Loc["Test"]

Run the project and you should observe "this is my test string" but instead you will see only "Test" string printed out as pointed out in the post.

hishamco commented 1 year ago

Do you have a repo just to save time?

htmlsplash commented 1 year ago

Sorry, I don't. I am not that familiar with github.

hishamco commented 1 year ago

Is the issue reproduced in one of the docs samples?

htmlsplash commented 1 year ago

"Is the issue reproduced in one of the docs samples?" - Not that I know of. I think it would be quicker just to follow my instructions to reproduce this issue. Creating a project, adding 2 lines of code, sample resource file will take you no more than 5mins.

hishamco commented 1 year ago

I'm sure the samples target .NET 7.0, If you can try it will take a minute :) then I can figure out the issue if it's initially a bug from the localization APIs or not. Because many of the issues I'm facing are setup issues from the devs themselves

I might try this but not this week, that's why I asked you :)

htmlsplash commented 1 year ago

Yes, I am using .net 7 (Blazor Server sample template).

"If you can try it will take a minute" - I am not sure what you mean by this? I already have a working sample on my end that's why I reported this issue. The only difference is that I have also YARP plugged in but I doubt YARP would interfere with Localization middleware.

hishamco commented 1 year ago

I am not sure what you mean by this?

Trying the localization sample :)

htmlsplash commented 1 year ago

Trying the localization sample - Do you have a link to that?

Thanks.

hishamco commented 1 year ago

I mean I will try the localization sample if I have a time

htmlsplash commented 1 year ago

FYI:

Accessing the resource strings located in "Resources" folder using ResourceManager works fine, just not when using "IStringLocalizer" api. I think the bug is in the IStringLocalizer api.

Also, as per debugging my Blazor server app, I do see the following paths added to resource search (when using ResourceManager.GetString() method):

"MyApp1.Localization.SharedStrings2.resources", "MyApp1.Properties.Resources.resources", "MyApp1.Resources.App.resources", "MyApp1.Resources.SharedStrings1.resources"

NOTE from the above that the "Resources" folder is there.

Hope this helps.

ghost commented 12 months ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

ghost commented 8 months ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

marzman95 commented 2 months ago

Documentation issue?

Reproduction

I tried following the documentation, as described in the issue, using the Shared Resources section in the Localization chapter. To prevent any mistakes I used a generated template from Visual Studio 2022.

First I created a new project using the Blazor Web App template in Visual Studio 2022. I then added the Microsoft.Extenstions.Localization NuGet package and the <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData> to the project file. I added using System.Globalization to the program.cs-file, despite Visual Studio marking it as unnecessary using. I added builder.services.AddLocalization(options => options.ResourcesPath = "Resources") in the builder section, and the supportedCultures-snippet, changing the second language to Dutch. It is very unclear where app.UseRequestLocalization(localizationOptions) needs to go in the middleware pipeline, as the template includes app.UseAntiorgery(), but that middleware is not mentioned in any middleware documentation. Using several StackOverflow-Answers it was clear that the localization had to go before, but after app.UseStaticFiles().

After adjusting the program.cs-file I created a "Resources" folder with the Languague.cs class, Languague.resx as a base (English) translation file, and a Languague.nl.resx as a secondary (Dutch) translation file. I also added @using Microsoft.Extensions.Localization in the Components_Imports.razor file, and created a new page Components\Pages\Translate.razor on the @page "/translate" route. Note that I used the Resources instead of Localization-directory and Language instead of SharedResource as resource files!

Then I added the @inject IStringLocalizer<Resources.SharedResource> Loc at the top of the Translate.razor file, and used @Loc["helloWorld"] to import the first translation to the page title. Navigating to the specified route shows the page, but the page is displaying the name of the translation as a value, instead of the translation itself.

Solution

When I remove the localization options parameter, options => options => options.ResourcesPath = "Resources", form the builder.Services.AddLocalization()-method, it suddenly works correctly. When I change the language preference in my browser, it shows the right translation.

Other note

The supportedCultures array uses a <language code>-<country/region code> code format. The SharedResources.<language code>.resx is different as those filenames do not include the <country/region code>. I think it is caused by the browser, but when removing the <country/region code> from each code (so only 'en' and 'nl'), it seems to work correctly.