go-i18n/v2中localize不是函数而是Localizer方法,必须先创建全局Bundle、加载active.zh-CN.json等合规资源文件,再按请求新建Localizer并调用其Localize(&i18n.LocalizeConfig{MessageID: "xxx"}),文件名、JSON结构、Accept-Language解析和Localizer生命周期任一错误均导致静默返回空或原始key。

go-i18n/v2 的 localize 不是函数,是 Localizer 方法
直接写 localize("auth.login.title") 会报 undefined: localize。v2 版本彻底移除了全局函数,所有翻译必须走 *i18n.Localizer 实例的 Localize 方法。
- 全局只建一个
bundle := i18n.NewBundle(language.English),它负责加载和管理所有语言资源 - 每个 HTTP 请求中调
localizer := bundle.NewLocalizer(langTag)创建新实例(轻量、goroutine-safe) - 调用时必须传
&i18n.LocalizeConfig{MessageID: "auth.login.title"},不是字符串参数 - 别在
init()或 handler 外部提前创建Localizer——它绑定语言上下文,复用会导致错译
JSON 文件名和结构必须严格匹配 go-i18n/v2 规范
写成 zh.json 或 locales/zh-CN.json 都会静默失败:Localize 返回空字符串或原始 key,且不报错。
- 文件名必须为
active.zh-CN.json、active.en-US.json—— 前缀active.不可省略,后缀需与language.Tag完全一致 - JSON 内容必须是对象,每个 key 是 message ID,value 是含
description和translation字段的对象:{"welcome": {"description": "homepage greeting", "translation": "欢迎"}} - 用
bundle.ParseFS(fs, "locales/active.*.json")加载时,fs必须指向包含完整路径的embed.FS或os.DirFS("./locales") - 漏加载某个
active.*.json,该语言下所有MessageID都 fallback 到 key 本身
Accept-Language 解析不能靠 strings.Split,必须用 language.ParseAcceptLanguage
浏览器发来的 Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8 不是普通逗号分隔字符串,手动截取第一个值会丢权重、误判变体(如把 zh-Hans 当作不支持),最终静默降级。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 调
language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))得到有序[]language.Tag - 白名单用
[]language.Tag{language.Chinese, language.English, language.Japanese}定义,别用字符串 map - 用
matcher := language.NewMatcher(supported)一次初始化,再调matcher.Match(candidate...)获取最匹配 tag - 结果存进
req.Context(),避免同一请求内多次解析(内部有 map 查找开销)
Gin/Echo 中必须为每个请求注入独立 Localizer,不能全局复用
全局单例 Localizer 或缓存它,会导致并发请求语言互相覆盖——A 用户看到中文,B 用户却收到中文版响应,哪怕他请求头是 en-US。
立即学习“go语言免费学习笔记(深入)”;
- 在中间件里解析出
langTag后,立即调localizer := bundle.NewLocalizer(langTag) - Gin 用
c.Set("T", localizer),Echo 用c.Set("localizer", localizer) - handler 中取出来调
localizer.Localize(&i18n.LocalizeConfig{MessageID: "error.not_found"}) - 微服务间透传语言需靠
X-Request-Languageheader 或 gRPC metadata,不能只依赖本地解析
Localizer 生命周期、Accept-Language 解析方式——这四点只要错一个,Localize 就会返回空或原 key,而且没有任何 panic 或日志提示。

















