0

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);
    }

}
Matias Masso
  • 1,670
  • 3
  • 18
  • 28

2 Answers2

2

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);
    }
} 
Matias Masso
  • 1,670
  • 3
  • 18
  • 28
0

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;
Liyun Zhang - MSFT
  • 8,271
  • 1
  • 2
  • 14
  • This might be the right answer with Server side Blazor, but Blazor Maui does not use cookies. Indeed, I followed the tutorial from https://m.youtube.com/watch?v=_ZFXk3vbxwQ but it does not keep the updated culture when navigating to other pages – Matias Masso Aug 25 '22 at 07:43
  • They are the same, I can also use the `Microsoft.AspNetCore.Localization` in the maui blazor. @MatiasMasso – Liyun Zhang - MSFT Aug 25 '22 at 07:49
  • Since Maui Blazor does not have a Pages/_Host.cshtml, how/where do you set the current culture cookie so Localization Middleware can read it? – Matias Masso Aug 26 '22 at 07:22
  • The maui blazor is a blazor Webassembly application. So please check the official document about the [blazor Webassembly](https://learn.microsoft.com/en-us/aspnet/core/blazor/globalization-localization?view=aspnetcore-6.0&pivots=webassembly#dynamically-set-the-culture-by-user-preference). @MatiasMasso – Liyun Zhang - MSFT Aug 26 '22 at 08:33
  • Documentation checked; it suggests to replace builder.Build() from Program.cs with a code to get and set blazorCulture using async calls to JsInterop. Maui Blazor calls buider.Build() on a sync function CreateMauiApp() from MauiProgram.cs which does not allow -or I don't know howto- async calls to JsInterop functions. May you post any example how to solve it? – Matias Masso Aug 28 '22 at 16:10
  • 1
    @MatiasMasso Please check the update part in the answer. – Liyun Zhang - MSFT Aug 29 '22 at 06:18
  • Liyun, thanks for your patience. I followed your update but I could not succeed on invoking "blazorCulture.get" since it depends on a jscript code which I can't refer to from Maui. But your sample pointed me into the right direction. I succeeded replacing cookie storage with Microsoft.Maui.Storage.Preference and it is working Ok now. – Matias Masso Aug 29 '22 at 17:09
  • All right, and can you post your code as an answer and mark it? This will help more people which meet the same problem. Or can you just mark the answer above? @MatiasMasso – Liyun Zhang - MSFT Aug 30 '22 at 00:51