fiber 中必须通过中间件将 i18n.localizer 绑定到 fiber.ctx,按请求解析语言(优先路径段 > accept-language > cookie)、预加载 active..json 资源、严格命名键名与 json 结构,调用 localizer.mustlocalize() 实现安全多语言切换。

Fiber 框架本身不内置 i18n 支持,但可以无缝集成 go-i18n/v2 或更轻量的 golang.org/x/text/message 实现多语言切换——关键不在“能不能”,而在「请求语言如何识别、翻译文件如何加载、上下文如何透传」这三个环节是否闭环。
如何在 Fiber 中注入并使用 i18n 实例
不能把 i18n.Bundle 或 i18n.Localizer 当全局变量直接用,Fiber 的每个请求是并发执行的,必须绑定到 fiber.Ctx 上才能保证语言上下文隔离。
- 初始化时创建
i18n.Bundle,预加载所有语言的 JSON 文件(如en.json、zh-Hans.json) - 用
ctx.Locals("i18n", localizer)把当前请求匹配出的*i18n.Localizer存入上下文 - 后续中间件或 handler 里通过
ctx.Locals("i18n").(*i18n.Localizer)取出并调用Localizer.MustLocalize() - 切忌在 handler 外部直接调用
Bundle.Localize(),它不感知请求语言,会默认 fallback 到 bundle 首个语言
从请求中提取语言标识的常见方式
用户语言不是靠猜,得有明确来源;优先级建议:URL 路径段 > Accept-Language header > Cookie > 默认语言。
- 路径方式最可控:
/zh/user/profile,用ctx.Params("lang")提取,适合 SEO 和显式切换 -
Accept-Language可靠性中等,但浏览器可能报zh-CN,zh;q=0.9,en-US;q=0.8,需用language.MatchAcceptLanguage()解析,别手动 split - Cookie(如
lang=ja)适合记住用户上次选择,但首次访问无 cookie 时必须 fallback - 千万别只依赖 query 参数(如
?lang=fr),它无法被搜索引擎缓存,且容易污染分享链接
翻译键名设计与复用陷阱
键名不是越短越好,也不是越“语义化”越安全;实际维护中,最常踩的坑是键名重复、嵌套层级错位、参数占位符不一致。
- 避免用中文原文作 key(如
"用户名"),一旦原文微调就断掉所有语言映射 - 推荐用功能+场景命名:例如
"auth.login.button.submit"、"user.profile.form.email.label" - JSON 翻译文件里,嵌套结构必须严格对齐;
zh-Hans.json有{"auth": {"login": {"button": {"submit": "登录"}}}},ja.json少一层button就会静默 fallback - 带参数的翻译(如
"Hello {{.Name}}!")必须确保所有语言文件中{{.Name}}写法完全一致,大小写、空格、点号都不能差
性能与热重载注意事项
翻译文件改动后,Fiber 应用默认不会自动重载——这不是 bug,是设计使然;但开发期频繁改文案时,手动重启太伤节奏。
-
go-i18n/v2的Bundle支持运行时重载,调用bundle.ReloadResources()即可,但需自己监听文件变化(比如用fsnotify) - 生产环境禁用重载,否则每次请求都去检查文件 mtime,IO 开销不可控
- 如果只做简单格式化(日期/数字/货币),优先用
golang.org/x/text/message+message.Printer,它比完整 i18n bundle 更轻、无 JSON 解析开销 - Fiber 中间件里做语言解析时,记得用
ctx.Next()而非return,否则后续中间件拿不到已设置的localizer
真正难的不是加载几种语言,而是当产品开始支持阿拉伯语(RTL 布局)、泰语(无空格分词)、希伯来语(混合 LTR/RTL 文本)时,你是否还在用 string.Replace 拼接翻译结果——这时候,message.Printer 的 Printf 和 plural.Select 才是救命稻草。











