Blazor WASM App 實作多國語系紀錄

多國語系開發筆記

參考文件

Localization in Blazor: Carl Franklin's Blazor Train Ep 90

多國語言實作前期

  1. 必需在系統初期就把多國語系框架定案。否則後期很難再加入多國語系機制。

  2. 必需決定多國語言支援的程度。這影嚮實作方法。 想要支援幾個國定地區,如:只有二國:中文/英文,或三、四國。 想要支援語系文化差異程度,如:中東地區由右往左排版的大問題。 若多國語系框架良好事後增加幾個國定地區都不難,除了『文化與 UI 習慣』完全相異以外。

多國語言系統開發主要步驟

  1. 安裝相關套件

  2. 啟用

  3. 設定多國語系支援程度

  4. 指定(預設)語系

  5. 切換語系介面

  6. 套用多國語系機制

  7. FluentValidation 多國語系支援部份 (option)

相關重要指令

IStringLocalizer<T> nterface
// 自『多國語系資源區』依當時語系取值。

開發環境

平台: .NET6 IDE: Visual Studio 2022 框架: Blazor WASM App

安裝相關套件

PM> NuGet\Install-Package Microsoft.Extensions.Localization

啟用多國語系

Program.cs
using [...略...]

//## 多國語系 - FluentValidation
FluentValidation.ValidatorOptions.Global.DisplayNameResolver = (type, member, expression) => GT.ResolveDisplayName(member);

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

//## 啟用多國語系
builder.Services.AddLocalization(options =>
{
  options.ResourcesPath = "Resources"; // 指定『多國語系資源區』
});

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddMudServices();
builder.Services.AddBlazoredLocalStorageAsSingleton();

[...略...]

//§§ await builder.Build().RunAsync(); ----------------------------------------
var host = builder.Build();
await host.SetDefaultCultureAsync(); // 設定(預設)語系
await host.RunAsync();

設定語系切換機制

語系還是要切換的,在 Blazor WASM App 現在語系設定一般放在 local storage。

設定多國語系支援程度

在設定語系之前要設定多國語系支援程度。

// 指定多國語系支援程度
public static class LocalizerSettings
{
  // 預設語系
  public static CultureWithName NeutralCulture = new CultureWithName("English", "en-US");
  // 指定多國語系支援程度。可事後再多入更多語系。
  public static readonly List<CultureWithName> SupportedCulturesWithName = new List<CultureWithName>()
  {
    new CultureWithName("English", "en-US"),
    new CultureWithName("中文","zh-TW")
  };
}

// 此資料結構用於管理語系
public record CultureWithName
{
  public string Name { get; set; } = default!;
  public string Culture { get; set; } = default!;

  public CultureWithName(string name, string culture)
  {
    Name = name;
    Culture = culture;
  }
}

指定(預設)語系

將自 local storage 取得指定語系開啟畫面。

public static class ThisProjectClassExtensions
{
  /// <summary>
  /// 設定現在語系:依 localStorage 的 culture 屬性值設定現在語系。
  /// </summary>
  public async static Task SetDefaultCultureAsync(this WebAssemblyHost host)
  {
    var localStorage = host.Services.GetRequiredService<ILocalStorageService>();
    var cultureString = await localStorage.GetItemAsync<string>("culture");

    CultureInfo cultureInfo = !String.IsNullOrWhiteSpace(cultureString)
        ? new CultureInfo(cultureString)
        : new CultureInfo(LocalizerSettings.NeutralCulture.Culture);

    CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
    CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
  }
}

切換語系介面

雖說是切換語系,大部份的實作其實是指定新語系後重新啟動(程式)畫面,開啟過程中用(新)指定的語系執行。基本上沒有人會不斷切換語系。

製作切換語系的下拉元件,完成後掛到 AppBar 上。

CultureSelector.razor
@inject NavigationManager navSvc
@inject ILocalStorageService localStorage
@inject IStringLocalizer<App> loc

<span>
  @loc["Language"]:&nbsp;
  <select @onchange=HandleCultureChange>
    @foreach (var culture in LocalizerSettings.SupportedCulturesWithName)
    {
      @if (culture.Equals(selectedCulture))
      {
        <option selected value="@culture.Culture">@culture.Name</option>
      }
      else
      {
        <option value="@culture.Culture">@culture.Name</option>
      }
    }
  </select>
</span>

@code {
  CultureWithName? selectedCulture = null;
  protected override async Task OnInitializedAsync()
  {
    string culture = await localStorage.GetItemAsync<string>("culture");
    selectedCulture = String.IsNullOrEmpty(culture)
        ? LocalizerSettings.NeutralCulture
        : LocalizerSettings.SupportedCulturesWithName.FirstOrDefault(c => c.Culture == culture);
  }

  /// 切換語系 
  async Task HandleCultureChange(ChangeEventArgs args)
  {
    //## 切換語系:將指定的新語系存入 localStorage 然後(刷新)重新開啟現在網址。
    string cultureString = args.Value?.ToString()!;
    await localStorage.SetItemAsync<string>("culture", cultureString);
    navSvc.NavigateTo(navSvc.Uri, true); // refresh current page.
  }
}

套用多國語系機制

一、在『多國語系資源區』設定各個多國語系。

在成功套入多語系模組後,就可以在『多國語系資源區』為各個元件設定多國語系。以下圖為例。

在啟動時指定了『多國語系資源區』的位置為"Resources"。各元的相對位置與目標位置的相對位置是一樣的。語系資料檔名稱格式:<元件名稱>.<語系>.resx

二、用 IStringLocalizer<T> 介面取多國語系的值。

以 Counter.rezor 為例。套入IStringLocalizer<T>後的碼。

Page\Counter.razor
@page "/counter"
@inject ILogger<Counter> logger
@inject IStringLocalizer<Counter> loc   // 注入多國語系 IStringLocalizer 介面
@inject IStringLocalizer<App> locApp    // 可以注入多個 IStringLocalizer 介面

<PageTitle>Counter</PageTitle>

<MudContainer>
  <MudText Typo=Typo.h3 GutterBottom>@loc["Counter"]</MudText>

  <MudAlert Severity=Severity.Info Class="mb-3">
    <span style="font-size:2em">@String.Format(loc["click {0} times"], currentCount)</span>
  </MudAlert>

  <MudButton Variant=Variant.Filled Color=Color.Primary OnClick=IncrementCount>@loc["Click me"]</MudButton>
</MudContainer>

@code {
  int currentCount = 0;

  void IncrementCount()
  {
    currentCount++;
    logger.LogInformation("IncrementCount");
  }
}

沒圖沒真象

中英語系切換截圖

完整程式碼

(完成)

Last updated