Beego中i18n只负责文案翻译,错误码系统负责语义分类与结构化,二者必须分层:错误码常量(如ErrUserNotFound=1001)为程序内唯一标识,i18n仅通过key(如"err_1001")查对应文案,中间用GetErrorMessage(code,lang)胶水函数桥接,日志必须输出原始码而非翻译字符串。

Beego 里错误码和 i18n 怎么不打架
直接说结论:Beego 的 i18n 模块管「文案翻译」,你自己的错误码系统管「语义分类 + 可扩展结构」,两者必须分层,不能混用。常见错误是把错误码字符串硬塞进 locale_zh-CN.ini 里,结果改个提示要动配置、加个新语言要重写所有错误映射、日志里只剩“用户不存在”却看不到原始 ErrUserNotFound 常量——这等于把错误码的可维护性全扔了。
正确做法是让错误码常量(如 ErrUserNotFound = 1001)始终作为程序内唯一标识,而 i18n 只负责根据当前语言和这个码查出对应文案。中间需要一层薄胶水函数,比如 GetErrorMessage(code int, lang string) string,它内部调用 i18n.Tr(lang, "err_"+strconv.Itoa(code))。
-
i18n.Tr的 key 必须带前缀(如err_1001),避免和页面文案冲突 - 错误码常量定义在
pkg/errno包里,用iota保证连续且不可变 - 不要在
locale_*.ini里写业务逻辑判断(比如 “如果 code==1001 且 user.Type==admin 则显示不同文案”),这类分支应放在 Go 层
如何让 i18n.SetMessage 加载的文件支持错误码动态 key
i18n.SetMessage 本身只认静态 key,但你可以绕过它直接用 i18n.Locale 实例做运行时翻译。关键不是“怎么让 ini 文件支持变量”,而是“别让 ini 文件承担逻辑职责”。
实操上,建议在错误码初始化阶段,把所有 errno 常量对应的 key 预注册进 i18n,例如:
// 在 init() 或 router 初始化时
for code, msg := range errno.ErrMsgMap {
// 为每个错误码生成一个固定 key,并注入默认文案(fallback)
i18n.SetMessage("en-US", "err_"+strconv.Itoa(code), msg)
i18n.SetMessage("zh-CN", "err_"+strconv.Itoa(code), errno.ZhErrMsgMap[code])
}
- 这样后续调用
i18n.Tr("zh-CN", "err_1001")就能命中,且 fallback 到英文文案 - 不需要每个错误码在 ini 文件里单独写一行;ini 文件只用于覆盖默认文案(比如运营要求把“用户不存在”改成“该账号未注册”)
- 如果项目已有大量 ini 文件,可以用脚本自动生成这批
err_*条目,避免手动维护
控制器里怎么安全地返回带多语言的错误响应
Beego 控制器不能直接 c.Data["json"] = err,因为 err 是结构体,c.ServeJSON() 默认不会触发 i18n.Tr。必须显式构造响应体。
推荐封装一个方法,比如 c.RenderError(code int, args ...interface{}),它内部做三件事:取当前请求语言(从 cookie / header / URL 参数)、查 i18n.Tr 翻译文案、组装标准 JSON 结构。
- 语言来源优先级建议:URL 参数
?lang=zh-CN> Cookielang> Accept-Language 头 > 默认语言 - args 用于支持文案占位符,比如
err_1002 = "用户 %s 余额不足",调用时传"alice" - 务必检查
i18n.Tr返回是否为空字符串,空则 fallback 到errno.ErrMsgMap[code],防止线上因漏配语言导致空白错误
为什么 Beego v2 的模块化设计让这事更容易落地
Beego v2 把 i18n 拆成独立包 github.com/beego/i18n/v2,不再强耦合于 beego.Controller。这意味着你可以把它当普通库用,甚至在非 HTTP 场景(如 CLI 工具、定时任务)里复用同一套错误码 + 翻译逻辑。
对比 v1:github.com/astaxie/beego 里 i18n 是全局单例,所有 Tr 调用共享同一份语言上下文,一旦并发切换语言就容易串;v2 允许你为每个请求 new 一个 *i18n.Locale 实例,彻底解耦。
- v2 中加载文件用
i18n.NewLocale()+l.LoadMessage("zh-CN", "conf/locale_zh-CN.ini"),更可控 - 错误码模块可以完全不 import beego,只依赖
i18n接口,方便单元测试 mock - 如果你还在用 v1,升级到 v2 不是可选项——是避免语言上下文污染的必要动作
最易被忽略的一点:错误码的「可追溯性」比「多语言」更重要。日志里打出来的必须是 code=1001 和完整堆栈,而不是翻译后的中文字符串。否则排查问题时,你得先反向查 ini 文件才能知道这是哪个错误码。


















