當前位置: 妍妍網 > 碼農

Blazor 互動式在地化(多語言)實作

2024-05-12碼農

本文件站點的在地化基於 AntDesign.Extensions.Localization 類別庫實作,主要提供可互動在地化服務,能夠整合官方和第三方的在地化提供者實作在執行時無重新整理切換語言。另外還實作了簡單的嵌入 JSON 提供者。

安裝

dotnet add package AntDesign.Extensions.Localization

使用可互動在地化元件

  • Program.cs 檔中添加以下程式碼:

    builder.Services.AddInteractiveStringLocalizer();
    services.AddLocalization(options =>
    {
    options.ResourcesPath = "Resources";
    });

  • 在計畫的 Resources 目錄下建立多語言檔,格式為 {ResourceName}.{language}.resx ,例如 Index.en-US.resx Index.zh-CN.resx

    Index.en-US.resx:

    Hello Hello!
    Goodbye Goodbye!

    Index.zh-CN.resx:

    Hello Hello!
    Goodbye Goodbye!
  • 使用時在 razor 註入 IStringLocalizer 服務,例如:

    @inject IStringLocalizer<Index> localizer
    <p>@localizer["Hello"]</p>
    <p>@localizer["Goodbye"]</p>

  • 在 Layout.razor 訂閱語言變更事件,即可切換 UI 上的語言

    @implements IDisposable
    @inject ILocalizationService LocalizationService
    <ButtonOnClick="()=>LocalizationService.SetLanguage("en-US")" >English</Button>
    <ButtonOnClick="()=>LocalizationService.SetLanguage("zh-CN")" >中文</Button>
    @code {
    protected override void OnInitialized()
    {
    LocalizationService.LanguageChanged += OnLanguageChanged;
    }
    private void OnLanguageChanged(object sender, CultureInfo args)
    {
    InvokeAsync(StateHasChanged);
    }
    public void Dispose()
    {
    LocalizationService.LanguageChanged -= OnLanguageChanged;
    }
    }

  • 需要使用第三方 Localization 提供者,或者其他配置,可參考 官方文件。

  • 表單驗證訊息的在地化

    Form 元件的預設驗證器是 ObjectGraphDataAnnotationsValidator,支持 DataAnnotations 的在地化。

    範例:Form 表單 - 在地化

    註意:暫不支持 AntDesign locales 中的驗證資訊配置。

    DisplayAttribute 支持在地化

    元件中有部份 UI 繫結利用了實體模型或者列舉型別的 DisplayAttribute 特性用作 Label 顯示,如 FormItem、EnumSelect、EnumRadioGroup、EnumCheckboxGroup 等。

    public classModel
    {
    [Display(Name = nameof(Resources.App.UserName), ResourceType = typeof(Resources.App))]
    publicstring Username { getset; }
    }
    publicenum Province
    {
    [Display(Name = nameof(Resources.App.Shanghai), ResourceType = typeof(Resources.App))]
    Shanghai,
    Jiangsu
    }

    使用簡單嵌入 JSON 提供者

    雖然我們推薦官方的在地化方案,但是也可以用本元件文件站點的方案,利用簡單嵌入 JSON 提供者實作多語言。需要註意的是,它僅支持直接註入 IStringLocalizer,不支持其泛型,在地化檔也僅支持單一檔。

  • Program.cs 檔中添加以下程式碼,此方法內部已呼叫 AddInteractiveStringLocalizer

    builder.Services.AddSimpleEmbeddedJsonLocalization(options =>
    {
    options.ResourcesPath = "Resources";
    });

  • 在計畫的 Resources 目錄下建立多語言檔,格式為 {language}.json ,例如 en-US.json zh-CN.json

    // en-US.json
    {
    "Hello""Hello!",
    "Goodbye""Goodbye!"
    }
    // zh-CN.json
    {
    "Hello""你好!",
    "Goodbye""再見!"
    }

  • 把JSON檔的生成操作設定為嵌入的資源

    <ItemGroup>
    <EmbeddedResourceInclude="Resources\*.json" />
    </ItemGroup>

  • 使用時在 razor 註入 IStringLocalizer 服務(沒有泛型),例如:

    @inject IStringLocalizer localizer
    <p>@localizer["Hello"]</p>
    <p>@localizer["Goodbye"]</p>

  • 實作路由上的語言標識

    從本站點建立之初,就實作了路由的語言標記,有如下特點:

    1. 進入頁面時可根據瀏覽器環境,自動在路由上添加 {locale} 參數,例如 /en-US/Index

    2. 可以根據路由上已有標識切換語言,因此要切換語言時只需跳轉到對應語言的路徑即可。

    以下是實作方式:

  • 首先,在 Routes.razor 檔實作 Router 元件的 OnNavigateAsync 方法,呼叫 LocalizationService.SetLanguage 方法切換語言。

  • @using System.Reflection
    @using System.Globalization
    <ConfigProvider>
    <Router AppAssembly="typeof(App).Assembly" OnNavigateAsync="OnNavigateAsync">
    <Found Context="routeData">
    <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
    <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>
    <NotFound>
    <Result Status="404" />
    </NotFound>
    </Router>
    <AntContainer />
    </ConfigProvider>
    @inject ILocalizationService LocalizationService;
    @inject NavigationManager NavigationManager;
    @code{
    async Task OnNavigateAsync(NavigationContext navigationContext)
    {
    var relativeUri = navigationContext.Path;
    var currentCulture = LocalizationService.CurrentCulture;
    var segment = relativeUri.IndexOf('/') > 0 ? relativeUri.Substring(0, relativeUri.IndexOf('/')) : relativeUri;
    if (string.IsNullOrWhiteSpace(segment))
    {
    NavigationManager.NavigateTo($"{currentCulture.Name}/{relativeUri}");
    return;
    }
    else
    {
    if (segment.IsIn("zh-CN", "en-US"))
    {
    LocalizationService.SetLanguage(CultureInfo.GetCultureInfo(segment));
    }
    else if (currentCulture.Name.IsIn("zh-CN", "en-US"))
    {
    NavigationManager.NavigateTo($"{currentCulture.Name}/{relativeUri}");
    }
    else
    {
    NavigationManager.NavigateTo($"en-US/{relativeUri}");
    return;
    }
    }
    }
    }


  • 最後,給頁面元件統一添加 {locale} 參數,使切換後的路由匹配對應的頁面。

  • @page "/{locale}/Index"
    @inject IStringLocalizer<Index> localizer
    <p>@localizer["Hello"]</p>
    @code {
    [Parameter]
    public string Locale { get; set; }
    }