🌟 如果你是一位 Go 使用者,可以在我開源的學習倉庫中,找到針對各種往期歸檔文章,及學習資料。
📺 B站:白澤talk,公眾號【白澤talk】,回覆"電子書",即可獲得包含《100個Go經典錯誤場景》在內的純淨 Golang 電子書大全。
一、什麼是本地化
今天講講 i18n,無論是 ToB 還是 ToC 的業務,常常存在多語言的需求,由於使用者有時來自不同國家,因此需要對頁面展示內容,包括響應結果做多語言的適配。
hello world! -> 你好世界! err: "user not find" -> err: "使用者不存在"
⚠️ 如果發生了翻譯錯誤,可能會讓人十分困擾,參考鳴潮最近的一個類似的事故:
鳴潮日文客戶端將本次up角色忌炎的專武效果翻譯錯誤,將R技能翻譯成了E技能。
二、前後端的不同實現
在前端實現國際化
- 準備多語言資原始檔:首先,需要準備多語言的資原始檔,包括不同語言版本的字串。
- 整合國際化外掛:使用前端框架提供的國際化外掛,如 React-intl、Vue-i18n 等,或者手動實現國際化邏輯。
- 根據使用者選擇的語言載入資源:在應用載入時,根據使用者選擇的語言載入對應的資原始檔,將介面展示的文字內容替換為對應語言的字串。
在後端實現國際化
- 準備多語言內容:將不同語言版本的文字或內容儲存在後端,可以是資料庫中、檔案中或其他形式。
- 處理國際化邏輯:在後端程式碼中編寫邏輯,根據使用者的語言選擇載入相應的內容。這可以透過模板引擎、多語言資原始檔或者介面返回不同語言的資料來實現。
- 提供介面或服務:如果後端需要提供國際化服務,可以設計介面或服務來根據使用者需求返回對應語言的內容。
三、Go 實現訊息本地化
🔥 最熱門的專案:https://github.com/nicksnyder/go-i18n
本地化訊息的定義與翻譯流程
🌟 這部分具體參看 go-i18n 的 readme 更加!
- 命令列工具下載(用於從 Go 程式碼中,提取需要本地化的 Message)
go install -v github.com/nicksnyder/go-i18n/v2/goi18n@latest
- 建立兩個檔案存放翻譯結果
active.en.toml
active.zh.toml
- 在 Go 程式碼中,顯示宣告一個 Message 結構
localizer.Localize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "PersonCats",
One: "{{.Name}} has {{.Count}} cat.",
Other: "{{.Name}} has {{.Count}} cats.",
},
TemplateData: map[string]interface{}{
"Name": "Nick",
"Count": 2,
},
PluralCount: 2,
}) // Nick has 2 cats.
- 執行 CMD 命令,將 Message 資訊提取到 active.en.toml 檔案中
goi18n extract
# active.en.toml
[PersonCats]
description = "The number of cats a person has"
one = "{{.Name}} has {{.Count}} cat."
other = "{{.Name}} has {{.Count}} cats."
- 執行 CMD 命令,將待翻譯成中文的 Message 提取到 translate.zh.toml 檔案中(這個檔案是工具建立的)
goi18n merge active.*.toml
# translate.zh.toml
[HelloPerson]
hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "Hello {{.Name}}"
- 將 translate.zh.toml 翻譯成中文
# translate.zh.toml
[HelloPerson]
hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "你好 {{.Name}}"
- 執行 CMD 命令將 translate.zh.toml 內的翻譯好的內容,自動增量合併進入 active.zh.toml 檔案中
goi18n merge active.*.toml translate.*.toml
四、基於 go-i18n 進一步封裝實現一個 HTTP 服務
🌟 見 demo:https://github.com/BaiZe1998/go-learning/tree/main/kit/i18n
效果:從 HTTP 頭部中獲取 lang,得到“zh”,響應中文的錯誤訊息。
一個簡單的 HTTP 服務
建立一個具備本地化能力的 error,從請求頭提取語言,然後選擇對應語言的error資訊響應。
func helloHandler(w http.ResponseWriter, r *http.Request) {
err := NewUserNotFoundErr(123)
//err, _ := someFunc()
fmt.Fprintf(w, FormatErr(err))
}
func main() {
http.HandleFunc("/", helloHandler)
fmt.Println("Starting server on port 8080...")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
服務啟動前初始化
預設語言選擇中文,選擇將 active.zh.toml 在服務啟動前載入進入記憶體。
var (
bundle = i18n.NewBundle(language.English)
)
func init() {
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
bundle.LoadMessageFile("active.zh.toml")
}
高效封裝
以下的封裝確保每次新增一個本地化的 error,只需要組合 BaseError,即可繼承通用的本地化能力。
// 本地化方法宣告
type LocalizedError interface {
error
LocalizedID() string
TemplateData() map[string]interface{}
}
// 基礎錯誤類,實現對應本地化方法
type BaseError struct {
ID string
DefaultMessage string
TempData map[string]interface{}
}
func (b BaseError) Error() string {
return b.DefaultMessage
}
func (b BaseError) LocalizedID() string {
return b.ID
}
func (b BaseError) TemplateData() map[string]interface{} {
return b.TempData
}
// 新增一個自定義錯誤
type UserNotFoundErr struct {
BaseError
}
func NewUserNotFoundErr(userID int) LocalizedError {
msg := i18n.Message{
ID: "user_not_found",
Other: "User not found {{.UserID}}",
}
e := UserNotFoundErr{}
e.ID = msg.ID
e.DefaultMessage = msg.Other
e.TempData = map[string]interface{}{
"UserID": userID,
}
return e
}
提取本地化的 err 訊息
由於所有具備本地化能力的 err 都實現了 LocalizedError 介面,因此可以定義如下方法統一在 Handler 層提取本地化之後的錯誤內容。
// 這裡就不從 HTTP 請求頭獲取了,假設提取到了 zh
func GetLang(_ context.Context) string {
return "zh"
}
func FormatErr(err error) string {
lang := GetLang(context.Background())
loc := i18n.NewLocalizer(bundle, lang)
var i18nErr LocalizedError
if errors.As(err, &i18nErr) {
msg, _ := loc.Localize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: i18nErr.LocalizedID(),
Other: i18nErr.Error(),
},
//MessageID: i18nErr.LocalizedID(),
TemplateData: i18nErr.TemplateData(),
})
return msg
}
return err.Error()
}
五、學習資料
參考文獻:
-
Xuanwo's Blog
-
掘金:Go語言國際化 i18n
-
Go Web 程式設計
-
https://www.ituring.com.cn/article/508191?published=true
開源倉庫:
- https://github.com/nicksnyder/go-i18n
- https://github.com/gin-contrib/i18n
- https://github.com/hertz-contrib/i18n
- https://github.com/gofiber/contrib/tree/main/fiberi18n
- https://github.com/beego/i18n