Can't keep CultureInfo across pages on .Net Maui Blazor

1.6k Views Asked by At

Next sample page features a language selector, a label to display current language and a button to reload the page.

The language selector is binded to a variable with a setter where I update all culture settings I can think of.

The label successfully reflects any change on the selector. But when we click on the button to reload the page, it does not remember the changes.

@page "/test"
@inject NavigationManager NavigationManager
@using System.Globalization

<div class="Wrapper">
    <select @bind="@Culture">
        @foreach (var culture in cultures)
        {
            <option value="@culture">@culture.DisplayName</option>
        }
    </select>

    <div>
        Current culture: @CultureInfo.CurrentCulture.Name
    </div>

    <button @onclick="Reload">Reload the page</button>
</div>

<style>
    .Wrapper {
        display:flex;
        flex-direction:column;
        gap:15px;
        max-width:200px;
        margin:50px auto;
    }
</style>

@code {
    CultureInfo[] cultures = new[] {
        new CultureInfo("es-ES"),
        new CultureInfo("ca"),
        new CultureInfo("en-US"),
        new CultureInfo("pt-PT")
    };

    CultureInfo Culture
    {
        get => CultureInfo.CurrentCulture;
        set
        {
            if (CultureInfo.CurrentCulture != value)
            {
                Thread.CurrentThread.CurrentCulture = value;
                Thread.CurrentThread.CurrentUICulture = value;
                CultureInfo.DefaultThreadCurrentCulture = value;
                CultureInfo.DefaultThreadCurrentUICulture = value;

            }
        }
    }

    void Reload() {
        NavigationManager.NavigateTo(NavigationManager.Uri, true);
    }

}
2

There are 2 best solutions below

0
Matias Masso On BEST ANSWER

After some help from @Liyun Zhang and many trial and errors I succeeded replacing cookies storage by Preferences storage to persist selected culture:

  1. Create a new .Net Maui Blazor app project and name it "Test"
  2. Open Nuget console and type:

Install-Package Microsoft.Extensions.Localization

  1. In MauiProgram.cs inside CreateMauiApp() procedure and before "return builder.Build()" line, insert next line to register localization service:
builder.Services.AddLocalization();
  1. Following this line, insert next lines to set the initial culture to either the persisted value or a default value (Spanish in this case):
string? cc = Preferences.Get("blazorCulture", null);  
if (string.IsNullOrEmpty(cc)) {
    cc = "es-ES";
    Preferences.Default.Set("blazorCulture", cc);
    }
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(cc);
  1. Append next lines to _Imports.razor so you don't have to add them on each file:
@using System.Globalization  
@using Microsoft.Extensions.Localization  
@using Test.Resources.Languages  
  1. Under Resources folder, add a Languages folder.
    Under Languages folder, add a resources file and call it "MyStrings.resx".
    Open MyStrings.resx and enter "HelloWorld" as key and "Hello, World!" as its default value.
    Add a new resources file called "MyStrings.es-ES.resx" for European Spanish values.
    Open MyStrings.es-ES.resx and enter "HelloWorld" as key and "Hola, Mundo!" as its Spanish value.
    Add a new resources file called "MyStrings.ca.resx"
    Open MyStrings.es-ES.resx and enter "HelloWorld" as key and "Hola, Mon!" as its Catalan value.
    Add a new resources file called "MyStrings.pt-PT.resx" for portuguese european language.
    Open MyStrings.pt-PT.resx and enter "HelloWorld" as key and "Olá Mundo!" as its Portuguese value.

  2. Under Shared folder, add next Lang.razor component, which lets you select among a list of available languages and sends a callback notification to the component host when selection changes.

<select @bind="@Culture">
    @foreach (var culture in cultures)
    {
        <option value="@culture">@culture.DisplayName</option>
    }
</select>

@code {
    [Parameter] public EventCallback<CultureInfo> AfterUpdate { get; set; }
    [Parameter]
    public CultureInfo? Culture
    {
        get => _culture;
        set
        {
            if (_culture != value)
            {
                _culture = value;
                if (AfterUpdate.HasDelegate) AfterUpdate.InvokeAsync(value);
            }
        }
    }

    private CultureInfo? _culture;

    CultureInfo[] cultures = new[] {
        new CultureInfo("es-ES"),
        new CultureInfo("ca"),
        new CultureInfo("en-US"),
        new CultureInfo("pt-PT")
    };
}
  1. In order to make use of the component, replace Pages/Index.razor code by the following code. AfterLangUpdate callback delegate is responsible for a) persist the selected language setting the new value on Preferences, b) setting CurrentUICulture so Localizer can pick the right resource file to display the translation and c) reloading the page to make sure the new language is active everywhere:
@page "/"

@inject IStringLocalizer<MyStrings> Localizer
@inject NavigationManager NavigationManager

<h1>@Localizer["HelloWorld"]</h1>

<Lang Culture="CultureInfo.DefaultThreadCurrentUICulture" AfterUpdate="AfterLangUpdate"></Lang>

@code {

    private void AfterLangUpdate(CultureInfo culture) {
        Preferences.Set("blazorCulture", culture.Name);
        CultureInfo.DefaultThreadCurrentUICulture = culture;
        NavigationManager.NavigateTo(NavigationManager.Uri, true);
    }
} 
7
Liyun Zhang - MSFT On

The cause is you don't save the CultureInfo into the Cookie and just set it in the page. So when you change the page or reload this page, the CultureInfo will be refreshed. You can check this case which has the same problem.

For more information, you can check the official document.

Update

Change the mauiprogram.cs:

public static class MauiProgram
{
public static async Task<MauiApp> CreateMauiAppAsync()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        });

    builder.Services.AddMauiBlazorWebView();
    #if DEBUG
    builder.Services.AddBlazorWebViewDeveloperTools();
#endif
    
    builder.Services.AddSingleton<WeatherForecastService>();
    builder.Services.AddLocalization();
    var host = builder.Build();
    CultureInfo culture;
    var js = host.Services.GetRequiredService<IJSRuntime>();
    var result = await js.InvokeAsync<string>("blazorCulture.get");

    if (result != null)
    {
        culture = new CultureInfo(result);
    }
    else
    {
        culture = new CultureInfo("en-US");
        await js.InvokeVoidAsync("blazorCulture.set", "en-US");
    }

    CultureInfo.DefaultThreadCurrentUICulture = culture;

    return host;
}

}

And change the following code in each platform:

protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiAppAsync().Result;