star 最多的 Go 语言本地化库|GitHub 2.8K

🌟 如果你是一位 Go 用户,可以在我开源的学习仓库中,找到针对各种往期归档文章,及学习资料。

📺 B站:白泽talk,公众号【白泽talk】,回复"电子书",即可获得包含《100个Go经典错误场景》在内的纯净 Golang 电子书大全。

一、什么是本地化

今天讲讲 i18n,无论是 ToB 还是 ToC 的业务,常常存在多语言的需求,由于用户有时来自不同国家,因此需要对页面展示内容,包括响应结果做多语言的适配。

hello world! -> 你好世界! err: "user not find" -> err: "用户不存在"

⚠️ 如果发生了翻译错误,可能会让人十分困扰,参考鸣潮最近的一个类似的事故:

鸣潮日文客户端将本次up角色忌炎的专武效果翻译错误,将R技能翻译成了E技能。

二、前后端的不同实现

在前端实现国际化

  1. 准备多语言资源文件:首先,需要准备多语言的资源文件,包括不同语言版本的字符串。
  2. 集成国际化插件:使用前端框架提供的国际化插件,如 React-intl、Vue-i18n 等,或者手动实现国际化逻辑。
  3. 根据用户选择的语言加载资源:在应用加载时,根据用户选择的语言加载对应的资源文件,将界面展示的文本内容替换为对应语言的字符串。

在后端实现国际化

  1. 准备多语言内容:将不同语言版本的文本或内容保存在后端,可以是数据库中、文件中或其他形式。
  2. 处理国际化逻辑:在后端代码中编写逻辑,根据用户的语言选择加载相应的内容。这可以通过模板引擎、多语言资源文件或者接口返回不同语言的数据来实现。
  3. 提供接口或服务:如果后端需要提供国际化服务,可以设计接口或服务来根据用户需求返回对应语言的内容。

三、Go 实现消息本地化

🔥 最热门的项目:https://github.com/nicksnyder/go-i18n

本地化消息的定义与翻译流程

🌟 这部分具体参看 go-i18n 的 readme 更加!

  1. 命令行工具下载(用于从 Go 代码中,提取需要本地化的 Message)
shell 复制代码
go install -v github.com/nicksnyder/go-i18n/v2/goi18n@latest
  1. 创建两个文件存放翻译结果
shell 复制代码
active.en.toml
active.zh.toml
  1. 在 Go 代码中,显示声明一个 Message 结构
go 复制代码
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.
  1. 执行 CMD 命令,将 Message 信息提取到 active.en.toml 文件中
shell 复制代码
goi18n extract

# active.en.toml
[PersonCats]
description = "The number of cats a person has"
one = "{{.Name}} has {{.Count}} cat."
other = "{{.Name}} has {{.Count}} cats."
  1. 执行 CMD 命令,将待翻译成中文的 Message 提取到 translate.zh.toml 文件中(这个文件是工具创建的)
shell 复制代码
goi18n merge active.*.toml

# translate.zh.toml
[HelloPerson]
hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "Hello {{.Name}}"
  1. 将 translate.zh.toml 翻译成中文
shell 复制代码
# translate.zh.toml
[HelloPerson]
hash = "sha1-5b49bfdad81fedaeefb224b0ffc2acc58b09cff5"
other = "你好 {{.Name}}"
  1. 执行 CMD 命令将 translate.zh.toml 内的翻译好的内容,自动增量合并进入 active.zh.toml 文件中
shell 复制代码
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信息响应。

go 复制代码
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 在服务启动前加载进入内存。

go 复制代码
var (
	bundle = i18n.NewBundle(language.English)
)

func init() {
	bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
	bundle.LoadMessageFile("active.zh.toml")
}

高效封装

以下的封装确保每次新增一个本地化的 error,只需要组合 BaseError,即可继承通用的本地化能力。

go 复制代码
// 本地化方法声明
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 层提取本地化之后的错误内容。

go 复制代码
// 这里就不从 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()
}

五、学习资料

参考文献:

开源仓库:

相关推荐
SeaTunnel1 小时前
如何将SeaTunnel MySQL-CDC与Databend 高效整合?格式与方案全解析
数据库·mysql·开源·数据集成·seatunnel·databend
算家计算1 小时前
Meta开源V-JEPA 2:杨立昆颠覆生成式AI,世界模型性能碾压英伟达30倍
人工智能·开源
FIT2CLOUD飞致云2 小时前
干货分享|JumpServer PAM特权账号管理功能详解
开源
时序数据说3 小时前
时序数据库IoTDB数据模型建模实例详解
大数据·数据库·开源·时序数据库·iotdb
ajassi20003 小时前
开源 java android app 开发(十二)封库.aar
android·java·linux·开源
时序数据说3 小时前
时序数据库IoTDB结合SeaTunnel实现高效数据同步
大数据·数据库·开源·时序数据库·iotdb
天天打码3 小时前
Bootstrap Table开源的企业级数据表格集成
前端·开源·bootstrap
竹6685 小时前
群晖NAS如何使用docker安装雷池防火墙?
架构·开源
不念霉运5 小时前
开源生态新势能: 驱动国产 DevSecOps 与 AI 工程新进展
人工智能·开源·github·devops