Go 多语言需用 go-i18n/v2 而非 x/text/message,因其不支持动态语言切换;关键三步是资源加载(严格 BCP 47/JSON Schema)、语言匹配(用 language.ParseAcceptLanguage + MatchStrings 缓存)、上下文注入(每请求 new Localizer)。

Go 没有内置的多语言框架,但能用 go-i18n/v2 搭出生产级方案——关键不是“选哪个库”,而是资源加载、语言匹配、上下文注入这三步不能错。
为什么不能用 golang.org/x/text/message 做页面文案翻译
它压根不支持按请求切换语言:message.NewPrinter 一创建,语言和格式规则就固化了。改 language.Tag、换系统 locale、甚至重启服务都无效,printer.Printf("hello") 永远输出英文。
- 适合场景:短文本格式化(数字、日期、货币),比如
printer.Sprint(1234567.89)输出带千分位的本地化数字 - 不适合场景:HTML 模板渲染、API 错误提示、按钮文案等需动态绑定语言的文本
- 常见错误:注册了翻译却始终返回原始 key,因为漏了
message.Catalog绑定;以为 reload 文件就能生效,实际Printer不支持热重载
go-i18n/v2 的资源文件必须严格符合 BCP 47 + JSON Schema
文件名错一个字符、字段名少一个字母、路径层级不对,bundle.ParseFS 就会静默失败——不报错,只返回空字符串或原始 key。
- 文件名必须是
active.zh-CN.json,不能是zh.json、zh_CN.json、cn.json或zh-CN.json(缺active.前缀) - JSON 结构必须含
description和translation字段:{"welcome": {"description": "homepage greeting", "translation": "欢迎"}}✅;{"welcome": "欢迎"}❌ - 加载路径要匹配
os.DirFS("./locales"):文件得放在./locales/active.zh-CN.json,不能放错目录层级 - 只加载
active.*前缀文件,inactive.*被自动忽略——这是设计行为,不是 bug
HTTP 请求中如何安全解析并匹配语言
别直接信 r.Header.Get("Accept-Language"),原始 header 可能为空、超长、含非法 tag(如 en;q=0.9, fr-XX;q=0.8),甚至触发 panic。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 必须用
language.ParseAcceptLanguage解析,它自动排序、过滤无效项、标准化标签 - 再用
language.MatchStrings(supportedLangs, candidate.String())匹配第一个合法语言,而不是硬 fallback 到en-US - 缓存解析结果:该函数内部有 map 查找开销,重复调用浪费 CPU;建议用
sync.Map缓存Accept-Language字符串到language.Tag的映射 - fallback 默认语言应是
language.Make("en"),不是en-US;更推荐显式传入[]string{"zh-CN", "en"}作为候选列表
Localizer 实例不能全局单例,但也不必每次 new
*i18n.Localizer 不是 goroutine-safe 的,也不能复用——但它初始化极轻量,只要 *i18n.Bundle 已预加载好所有资源,bundle.NewLocalizer 本质只是做一次 map 查找。
- 全局持有一个
*i18n.Bundle,启动时加载全部active.*.json文件到内存 - 每个请求根据解析出的 language tag 调用
bundle.NewLocalizer创建新实例,注入到context.Context中 - 模板里用
{{.T "button.submit"}},Handler 里用localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "home.welcome"}) - 如果用了 Gin/Echo,中间件统一处理比每个 handler 自己解析更可靠;注意
Localizer不能跨 goroutine 传递,避免 context cancel 后仍被引用
最易被忽略的是 JSON Schema 验证和 Accept-Language 解析缓存——前者导致翻译静默失效,后者在高并发下拖慢响应。这两处不设防,整个 i18n 流程就算写对了逻辑,也会在上线后悄悄崩掉。

















