概念
作為一個普通開發者, 我們負責的專案的使用群體大多數是本國的人民, 但不可避免的也有一些做外貿的業務或者給外企做的專案, 這個時候就要求我們的專案有服務全球客戶的能力, 而一個支援國際化能力的框架會讓我們專案的體驗變得更好.
關於本地化我們聽到最多的是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%的本地化廣告在點選率、轉換率上超過未經過本地化的廣告, 這些數字都說明了人們對自己母語顯示的內容更加感興趣, 透過本地化可以讓你更精準的打動客戶, 幫你迅速的進入市場, 但本地化不等於直接翻譯, 本地化是基於對當地市場情況和文化精心策劃的, 在習慣、習俗、日期和貨幣格式上是不相同的, 本地化的產品能節約溝通成本, 更容易被理解和接受.
舉個通俗的例子: 相比找一個不認識的陌生人買東西, 我們更喜歡去找那個知根知底的本地人買東西, 這無關產品的質量, 只是熟悉的人更容易獲得我們的認同感
雖然本地化包括很多模組, 但在此我們只考慮產品的本地化, 下面我們就說一下如何做使得自己的產品可以支援本地化
使用
- 安裝.NET 6.0
- 新建ASP.NET Core 空專案
Assignment.I18nDemo
,並安裝Masa.Contrib.Globalization.I18n.AspNetCore
dotnet add package Masa.Contrib.Globalization.I18n.AspNetCore --version 0.7.0-preview.16
- 註冊
I18n
, 並修改Program.cs
builder.Services.AddI18n();
- 使用
I18N
app.UseI18n();//啟用本地化中介軟體
- 新增多語言資原始檔, 資料夾結構如下:
- 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}"
}
]
- 使用
I18n
app.Map("/test", (string key) => I18n.T(key));
- 測試多語言, 在瀏覽器訪問"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, 我們可以這樣做:
- 註冊
MasaConfiguration
並使用Dcc, 修改Program.cs
builder.Services.AddMasaConfiguration(configurationBuilder =>
{
// configurationBuilder.UseDcc();//正確配置好Dcc配置後開啟
});
- 配置Dcc配置資訊, 修改
appsettings.json
{
"DccOptions": {
"ManageServiceAddress": "{Replace-Your-DccManagerServiceHost}",
"RedisOptions": {
"Servers": [
{
"Host": "{Replace-Your-DccUseRedisHost}",
"Port": 6379
}
],
"DefaultDatabase": 0,
"Password": ""
}
}
}
對MasaConfiguration有疑問點這裡
- 註冊I18n
builder.Services.AddI18n(
Path.Combine("Resources", "I18n"),
"supportedCultures.json",
options => options.UseDcc());
我們僅需要在/Resources/I18n
目錄下存放支援語言的配置即可, 具體的語言配置將從Dcc讀取 (讀取Dcc語言的配置節點: 在Dcc配置下預設AppId下的"Culture.{語言}"), 例如:
如果支援語言為zh-CN
、en-US
, 則預設讀取Dcc配置下預設AppId下Culture.zh-CN
、Culture.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,歡迎聯絡我們