MasaFramework -- i18n (國際化)

2023-01-03 12:01:53

概念

作為一個普通開發者, 我們負責的專案的使用群體大多數是本國的人民, 但不可避免的也有一些做外貿的業務或者給外企做的專案, 這個時候就要求我們的專案有服務全球客戶的能力, 而一個支援國際化能力的框架會讓我們專案的體驗變得更好.

關於在地化我們聽到最多的是I18N、L10N、G11N, 那它們分別代表了什麼意思呢?

  • I18N: 是"Internationalization" 的縮寫, 由於I到N之間間隔了18個字母, 也被簡稱為"i18n". 使產品或軟體具有不同國際市場的普遍適應性, 從而無需重新設計就可適應多種語言和文化習俗的過程. 真正的國際化要在軟體設計和檔案開發過程中, 使產品或軟體的功能和程式碼設計能處理多種語言和文化習俗, 具有良好的在地化能力. 它讓程式具備了支援多種語言的能力
  • L10N: 是"Localization"的縮寫, 由於L到N之間間隔了10個字母, 也被簡稱為"L10N". 是將產品或軟體針對特定國際語言和文化進行加工, 使之符合特定區域市場的過程. 真正的在地化要考慮目標區域市場的語言、文化、習俗、特徵和標準. 通常包括改變軟體的書寫系統(輸入法)、鍵盤使用、字型、日期、時間和貨幣格式等
  • G11N: 是"Globalization"的縮寫, 由於G到N之間間隔了11個字母, 也被簡稱為"G11n", 是指在全球範圍內推出產品的業務方面, 可以簡單理解為 "I18N" + "L10N"

在地化的意義

在地化包括:

  • 產品在地化
  • 技術研發在地化
  • 原材料在地化
  • 人才在地化
  • 企業文化在地化

經過研究表明, 在地化後的產品的銷量會比未經過在地化的更好, 75%的人偏好用母語購買產品, 86%的在地化廣告在點選率、轉換率上超過未經過在地化的廣告, 這些數位都說明了人們對自己母語顯示的內容更加感興趣, 通過在地化可以讓你更精準的打動客戶, 幫你迅速的進入市場, 但在地化不等於直接翻譯, 在地化是基於對當地市場情況和文化精心策劃的, 在習慣、習俗、日期和貨幣格式上是不相同的, 在地化的產品能節約溝通成本, 更容易被理解和接受.

舉個通俗的例子: 相比找一個不認識的陌生人買東西, 我們更喜歡去找那個知根知底的本地人買東西, 這無關產品的質量, 只是熟悉的人更容易獲得我們的認同感

雖然在地化包括很多模組, 但在此我們只考慮產品的在地化, 下面我們就說一下如何做使得自己的產品可以支援在地化

使用

  1. 新建ASP.NET Core 空專案Assignment.I18nDemo,並安裝Masa.Contrib.Globalization.I18n.AspNetCore
dotnet add package Masa.Contrib.Globalization.I18n.AspNetCore --version 0.7.0-preview.16
  1. 註冊I18n, 並修改Program.cs
builder.Services.AddI18n();
  1. 使用I18N
app.UseI18n();//啟用在地化中介軟體
  1. 新增多語言資原始檔, 資料夾結構如下:
- Resources
  - I18n
    - en-US.json
    - zh-CN.json
    - supportedCultures.json
  • en-US.json
{
    "Home":"Home"
}
  • zh-CN.json
{
    "Home":"首頁"
}
  • supportedCultures.json
[
    {
        "Culture":"zh-CN",
        "DisplayName":"中文簡體",
        "Icon": "{Replace-Your-Icon}"
    },
    {
        "Culture":"en-US",
        "DisplayName":"English (United States)",
        "Icon": "{Replace-Your-Icon}"
    }
]
  1. 使用I18n
app.Map("/test", (string key) => I18n.T(key));
  1. 測試多語言, 在瀏覽器存取"https://localhost:7082/get?key=Home"即可得到對應語言下鍵名為Home的值

是不是感覺用起來十分簡單呢, 但這究竟是如何做的呢

進階

國內的小夥伴根據上面的例子操作下來, 會發現請求響應的內容是中文, 那什麼情況下它會變成英文呢?

在地化中介軟體

由於.NET 提供了在地化的能力, 它提供了在地化的中介軟體, 通過它使得我們的專案具備解析當前語言的能力

目前它支援了以下三種方式進行語言切換:

  • URL 引數 方式: ?culture=en-US,此方式優先順序最高,格式為:culture=區域碼
  • Cookies 方式:cookie 格式為 c=%LANGCODE%|uic=%LANGCODE%,其中 c 是 Culture,uic 是 UICulture, 例如:
c=en-UK|uic=en-US
  • 使用者端瀏覽器語言自動匹配:如果前面兩種方式都沒有設定,支援自動根據使用者端瀏覽器語言進行匹配

語言優先順序:

URL 引數 方式 > Cookies方式 > 使用者端語言 > 預設語言

預設語言

預設語言有兩種設定方式, 它們分別是:

  • 約定設定
    • supportedCultures.json檔案中的第一個語言
  • 手動指定預設語言
    • 通過app.UseI18n("{Replace-Your-DefaultCulture}")

它們的優先順序是:

手動指定預設語言 > 約定設定

修改預設資源路徑

builder.Services.AddI18n(options =>
{
    options.ResourcesDirectory = Path.Combine("Resources", "I18n");//修改預設資源路徑
    options.SupportedCultures = new List<CultureModel>() //支援語言
    {
        new("zh-CN"),
        new("en-US")
    };
});

支援資原始檔

如果你希望將組態檔嵌入到dll檔案中, 不希望被看到修改, 那麼你需要將資源json檔案的生成操作改為嵌入的資源, 並修改I18N註冊程式碼為:

builder.Services.AddI18nByEmbedded();

巢狀設定

相信瞭解前端開發的小夥伴也見到過巢狀的資源設定, 那對於Masa提供的多語言方案而言, 我們也支援這種格式的設定, 例如:

{
    "Home":"首頁",
    "User":{
        "Name":"名稱"
    }
}

我們希望拿到User節點下的Name屬性的值, 則可以通過:

var result = I18N.T("User.Name");//其中key的值不區分大小寫

當然除此之外, 我們也可以將不同資源的檔案分開存放到不同的json檔案, 然後通過新增多個資源目錄的檔案, 最終實現, 但在使用時稍微有區別:

app.Map("/test2", (string key, II18n<CustomResource> i18n) => i18n.T(key));//通過DI獲取到自定義資源下Key對應內容

如何對接遠端資源設定

多語言的遠端資源設定目前僅支援Dcc, 我們可以這樣做:

  1. 註冊MasaConfiguration並使用Dcc, 修改Program.cs
builder.Services.AddMasaConfiguration(configurationBuilder =>
{
    // configurationBuilder.UseDcc();//正確設定好Dcc設定後開啟
});
  1. 設定Dcc設定資訊, 修改appsettings.json
{
  "DccOptions": {
    "ManageServiceAddress": "{Replace-Your-DccManagerServiceHost}",
    "RedisOptions": {
      "Servers": [
        {
          "Host": "{Replace-Your-DccUseRedisHost}",
          "Port": 6379
        }
      ],
      "DefaultDatabase": 0,
      "Password": ""
    }
  }
}

對MasaConfiguration有疑問點這裡

  1. 註冊I18n
builder.Services.AddI18n(
    Path.Combine("Resources", "I18n"),
    "supportedCultures.json",
    options => options.UseDcc());

我們僅需要在/Resources/I18n目錄下存放支援語言的設定即可, 具體的語言設定將從Dcc讀取 (讀取Dcc語言的設定節點: 在Dcc設定下預設AppId下的"Culture.{語言}"), 例如:

如果支援語言為zh-CNen-US, 則預設讀取Dcc設定下預設AppId下Culture.zh-CNCulture.en-Us兩個設定物件的值, 我們僅需要修改它們的值即可, 並且如果對應的內容發生更改, 專案無需重啟即可完成自動更新

原始碼解讀

MasaFramework中, 抽象了多語言的能力, 它提供了

  • string this[string name]: 獲取指定name的值 (如果name不存在, 則返回name的值)
  • string? this[string name, bool returnKey]: 獲取指定name的值 (如果returnKey為false, 且name不存在, 則返回null)
  • string this[string name, params object[] arguments]: 獲取指定name的值, 並根據文化、輸入引數格式化響應資訊返回 (如果name不存在, 則返回name的值)
  • string? this[string name, bool returnKey, params object[] arguments]: 獲取指定name的值, 並根據文化、輸入引數格式化響應資訊返回 (如果returnKey為false, 且name不存在, 則返回null)
  • string T(string name): 獲取指定name的值, 如果name不存在, 則返回name的值
  • string? T(string name, bool returnKey): 獲取指定name的值 (如果returnKey為false, 且name不存在, 則返回null)
  • string T(string name, params object[] arguments): 獲取指定name的值, 並根據文化、輸入引數格式化響應資訊返回 (如果name不存在, 則返回name的值)
  • string? T(string name, bool returnKey, params object[] arguments): 獲取指定name的值, 並根據文化、輸入引數格式化響應資訊返回 (如果returnKey為false, 且name不存在, 則返回null)
  • CultureInfo GetCultureInfo(): 獲取當前執行緒的區域性 (被用於需要格式化響應資訊的方法)
  • void SetCulture(string cultureName, bool useUserOverride = true): 設定當前執行緒的區域性
  • void SetCulture(CultureInfo culture): 設定當前執行緒的區域性
  • CultureInfo GetUiCultureInfo(): 獲取資源管理器使用的當前區域性以便在執行時查詢區域性特定的資源
  • void SetUiCulture(string cultureName, bool useUserOverride = true): 設定資源管理器使用的當前區域性以便在執行時查詢區域性特定的資源
  • void SetUiCulture(CultureInfo culture): 設定資源管理器使用的當前區域性以便在執行時查詢區域性特定的資源

我們可以通過DI獲取到II18n進而來使用它提供的這些能力, 當然它使用的是資源型別為DefaultResource的資源 (在服務註冊時所指向的資源 services.AddI18n()), 除此之外, 我們也可以通過DI獲取到II18n<DefaultResource>來使用, 也可以通過I18n(全域性靜態類)來使用它提供的能力, 但是如果你使用了自定義資源型別, 則只能通過DI獲取II18n<{Replace-Your-ResourceType}>來使用.

除此之外, 我們還提供了支援的語言列表的能力, 它被抽象在ILanguageProvider

  • IReadOnlyList GetLanguages(): 獲取支援語言集合

我們可以通過DI獲取ILanguageProvider來使用, 也可以通過I18n.GetLanguages()來使用

總結

當然, MasaFramework絕不僅僅只是提供了這麼簡單的多語言的能力, 目前全域性異常、Caller也已經支援了多語言, 其他模組也在逐步完善多語言支援, 等之後框架的錯誤資訊也將會支援多語言, 我們的開發體驗也將會變得更加友好

參考

本章原始碼

Assignment18

https://github.com/zhenlei520/MasaFramework.Practice

開源地址

MASA.Framework:https://github.com/masastack/MASA.Framework

MASA.EShop:https://github.com/masalabs/MASA.EShop

MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor

如果你對我們的 MASA Framework 感興趣,無論是程式碼貢獻、使用、提 Issue,歡迎聯絡我們