go-i18n/v2 是目前最稳的运行时多语言方案,支持动态切语言、CLDR复数、JSON/TOML热加载、RTL适配,Bundle全局单例、Localizer每请求新建,需严格遵循文件命名、加载校验与Accept-Language解析规范。

别用 fmt.Sprintf 拼多语言文本,也别手写 map[string]map[string]string 存翻译——这两种方式上线后必然崩在复数、词序、RTL 或区域变体上,且无法维护。
go-i18n/v2 是目前最稳的运行时多语言方案
它原生支持按请求动态切换语言、CLDR 复数规则、JSON/TOML 热加载(文件系统下)、RTL 渲染适配,且 API 明确区分 Bundle(全局单例)和 Localizer(每请求新建)。golang.org/x/text/message 不是替代品:它不支持运行时语言切换,message.NewPrinter 一旦创建,language.Tag 就固化了,改请求头或 context 无效。
-
go get github.com/nicksnyder/go-i18n/v2/i18n是唯一推荐安装命令 - Bundle 必须全局初始化一次,所有
active.*.json文件都在启动时用bundle.LoadMessageFile加载,漏一个就导致后续Localize返回空字符串或原始 key - Localizer 绝不能复用或缓存:每个 HTTP 请求都应调
bundle.NewLocalizer(langTag),轻量且线程安全 - 文件名必须严格为
active.zh-CN.json,zh.json、zh_CN.json、cn.json全部被忽略
Accept-Language 解析必须用 language.ParseAcceptLanguage
浏览器发来的 Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 不是普通逗号分隔字符串,而是带权重、可嵌套、需标准化的语言标签序列。手动 strings.Split 或正则提取会丢权重、误判变体(比如把 zh-Hans 当作和 zh-CN 不兼容),最终静默 fallback 到默认语言。
- 必须调
language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))得到有序[]language.Tag - 匹配前先定义白名单:
supported := []language.Tag{language.Chinese, language.English, language.Japanese} - 用
matcher := language.NewMatcher(supported)一次初始化,再调matcher.Match(candidate)获取最匹配的language.Tag - 结果建议存入
req.Context(),避免每次 handler 都重复解析(内部有 map 查找开销)
Localize 返回空或原 key 的真实原因
这不是 bug,是配置未满足 go-i18n/v2 的硬性规范。常见静默失败点:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- JSON 结构错误:必须是
{"welcome": {"description": "homepage greeting", "translation": "欢迎"}},缺description字段、字段名写成msg或value,都会返回空 - 路径错误:用
os.DirFS("./locales"),但文件实际在./i18n/active.zh-CN.json,bundle.LoadMessageFile不报错但加载失败 - 未检查
LoadMessageFile返回的error:if err != nil { log.Fatal(err) }必须写,否则后续Localizepanic “no message found for key” - 模板参数名大小写敏感:
"welcome": "Hello, {{.Name}}!"对应代码中必须传map[string]interface{}{"Name": "Alice"},传"name"就原样输出{{.Name}}
错误信息的多语言处理不能依赖 error.Error()
Go 的 error 接口只返回固定字符串,无法动态翻译。直接给 fmt.Errorf 或第三方 error 加字段没用,调用方只认 Error() 方法。
- 必须自定义结构体,如
type LocalizedError struct { cause error; code string } - 实现
Translate(lang string) string方法,内部调用localizer.Localize(&i18n.LocalizeConfig{MessageID: e.code}) -
Error()方法仍返回英文原文,保证日志、调试、HTTP 响应头等场景一致 - HTTP handler 中对外返回错误时,必须显式调
e.Translate(lang),而非直接e.Error()
最易被忽略的是:所有 LoadMessageFile 调用后必须检查 error;所有 JSON 文件的 key 必须是纯 ASCII(如 login.title),不能含中文或空格;active.*.json 中同一组 key 在所有语言文件里必须严格对齐,缺一个就会 fallback 到原始 key —— 而不是报错。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










