go-i18n/v2是唯一推荐的运行时多语言方案,因golang.org/x/text/message不支持动态切换语言;bundle须全局单例并一次性加载全部active.*.json文件,漏加载或文件名/结构错误均静默失败;accept-language必须经language.parseacceptlanguage解析后用matcher匹配白名单语言,再为每个请求新建localizer。

直接用 go-i18n/v2,别碰 golang.org/x/text/message 做运行时语言切换——它不支持改语言,message.NewPrinter 一建好就锁死 language.Tag,后续任何请求头或上下文变更都无效。
初始化 Bundle 必须全局单例且一次加载全部 active.*.json
Bundle 是线程安全的资源管理器,必须在应用启动时完成初始化和加载,漏一个文件,对应语言的 Localize 就会静默返回空字符串或原始 key,不 panic、不报错,极难排查。
- 用
i18n.NewBundle(language.English)创建,第一个参数是默认语言 tag - 调
bundle.ParseFS(os.DirFS("./locales"), "active.*.json")或逐个LoadMessageFile("locales/active.zh-Hans.json"),路径必须真实存在(如./locales/active.en-US.json) - 只认
active.*.json前缀,zh.json、inactive.zh.json、draft.en.json全部被忽略 - 别用
i18n.MustLoadMessageFile,线上必须检查 error:_, err := bundle.LoadMessageFile(...)
Accept-Language 解析不能靠 strings.Split,必须用 language.ParseAcceptLanguage + Matcher
浏览器发来的 Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8 不是普通字符串,手动切分丢权重、误判变体(比如把 zh-Hans 当作和 zh-CN 不兼容),最终静默 fallback 到默认语言。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 调
language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))得到有序[]language.Tag - 预定义白名单:
supported := []language.Tag{language.Chinese, language.English} - 只初始化一次
matcher := language.NewMatcher(supported),再调matched, _ := matcher.Match(acceptTags)得归一化 tag(如zh-Hans) - 这个
matched才能传给bundle.NewLocalizer;别再转成字符串如"zh-CN" - 建议把
matched存进r.Context(),避免 handler 内重复解析
Localizer 必须每个请求新建,不能复用或缓存
*i18n.Localizer 是轻量、非线程安全、绑定具体语言的实例,复用会导致多语言混输出——比如 A 请求用中文,B 请求用英文,共用一个 localizer,B 可能拿到 A 的翻译结果。
- 每个 HTTP 请求中,用
bundle.NewLocalizer(matched)构造,开销极小,可高并发安全使用 - 不要缓存
*i18n.Localizer实例,也不要在 struct 里存为字段 - 若用 Gin/Echo,推荐中间件注入到
c.Request.Context(),比塞进 handler 参数更清晰 - 调用时传
&i18n.LocalizeConfig{MessageID: "auth.login.title"},注意是方法调用,不是函数:localizer.Localize(...)
JSON 文件名和结构错一个字符就静默失败
go-i18n/v2 对资源格式极其敏感,文件名或 JSON 结构错一点,Localize 就返回空字符串或原始 key,不报错也不 panic。
- 文件名必须严格为
active.zh-Hans.json(不是zh.json、zh_CN.json、zh-hans.json) - JSON 外层是对象,每个 key 是 message ID,value 必须是含
description和translation的对象:{"login.title": {"description": "page title", "translation": "登录"}} - 不能简写成
{"login.title": "登录"},也不能把字段名写成msg或value - 用
bundle.ParseFS时,确保fs.FS包含完整路径前缀;os.DirFS("./locales")要求文件实际在./locales/active.zh-Hans.json
最常踩的坑是:以为文件加载成功了,其实因为文件名大小写、路径偏差、JSON 缺 description 字段,导致整个语言包静默失效。上线前务必用 WithDebug(true) 启用调试模式,看日志里是否报 “no translation found” 或 “failed to parse”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










