🌟 如果你是一位 Go 用户,可以在我开源的学习仓库中,找到针对各种往期归档文章,及学习资料。
📺 B站:白泽talk,公众号【白泽talk】,回复"电子书",即可获得包含《100个Go经典错误场景》在内的纯净 Golang 电子书大全。
今天讲讲 i18n,无论是 ToB 还是 ToC 的业务,常常存在多语言的需求,由于用户有时来自不同国家,因此需要对页面展示内容,包括响应结果做多语言的适配。
hello world! -> 你好世界! err: "user not find" -> err: "用户不存在"
⚠️ 如果发生了翻译错误,可能会让人十分困扰,参考鸣潮最近的一个类似的事故:
鸣潮日文客户端将本次up角色忌炎的专武效果翻译错误,将R技能翻译成了E技能。
🔥 最热门的项目:https://github.com/nicksnyder/go-i18n
🌟 这部分具体参看 go-i18n 的 readme 更加!
go install -v github.com/nicksnyder/go-i18n/v2/goi18n@latest
active.en.toml
active.zh.toml
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.
goi18n extract
# active.en.toml
[PersonCats]
description = "The number of cats a person has"
one = "{{.Name}} has {{.Count}} cat."
other = "{{.Name}} has {{.Count}} cats."
goi18n merge active.*.toml
# translate.zh.toml
[HelloPerson]
hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "Hello {{.Name}}"
# translate.zh.toml
[HelloPerson]
hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "你好 {{.Name}}"
goi18n merge active.*.toml translate.*.toml
🌟 见 demo:https://github.com/BaiZe1998/go-learning/tree/main/kit/i18n
效果:从 HTTP 头部中获取 lang,得到“zh”,响应中文的错误消息。
创建一个具备本地化能力的 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 都实现了 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()
}
参考文献:
开源仓库:
今天讲讲 i18n,无论是 ToB 还是 ToC 的业务,常常存在多语言的需求,由于用户有时来自不同国家,因此需要对页面展示内容,包括响应结果做多语言的适配。