Go 的 error 接口不支持动态翻译,需用错误码+本地化器解耦文案;定义 AppError 结构体携带 Code 字段,Error() 仅返回 code,翻译延至展示层;需实现 Is/Unwrap 保证 errors.Is/As 兼容性,并确保翻译资源 key 与代码一致且版本同步。
Go 的
接口本身不支持动态翻译
Go
标准库
的
是一个只含
方法的接口,返回值是固定字符串。这意味着:一旦调用
或
,错误文本就已固化,无法在运行时根据用户语言切换内容。
所以“错误国际化”不是给
加个方法就能解决的事,而是要绕过原生 error 的文本绑定逻辑,把「错误标识」和「错误文案」解耦。
用错误码(error code)+ 翻译器(localizer)替代硬
编码
字符串
核心思路是:定义可识别的错误类型(比如自定义结构体),携带唯一
字段;错误生成时不拼接中文/英文,只存码;显示或日志前,再由
本地化
器查表翻译。
定义错误类型:
创建错误实例时只传码:
翻译函数示例(基于
和
):
不要在
中直接插翻译后的字符串
常见反模式:
这会导致:错误链中丢失原始语义、无法做统一错误分类(如按
统计)、日志里混入
多语言
文本难以排查。
立即学习
“
go语言免费学习笔记(深入)
”;
React Native For Android 源码编译 中文WORD版
本文档主要讲述的是React Native For Android 源码编译;希望对大家会有帮助;感兴趣的朋友可以过来看看
下载
正确做法是保持 error 链干净,只在最终展示层(HTTP 响应体、CLI 输出、
前端
提示)调用翻译:
HTTP handler 中:
日志记录时仍打原始
(即
),便于 ELK/Grafana 按 code 聚合
注意
和
的兼容性
如果你用
包裹底层错误(如数据库超时),要确保能用标准方式判断错误类型:
实现
方法才能被
正确识别:
如果业务需要提取原始错误(如判断是否是
),
会自动穿透到
,前提是你的
返回了它
别忘了在测试里覆盖这些分支,否则上线后
可能静默失败
最易被忽略的一点:翻译资源文件(如
、
)里的 key 必须和代码中写的
完全一致,且所有服务节点加载的 bundle 版本要同步——漏掉一个 key 或版本错位,就会 fallback 到
,而这种问题在线上往往只出现在某个语言环境下,很难复现。
errorerrorError() stringerrors.New("用户名不能为空")fmt.Errorf("数据库连接失败: %w", err)errorCodetype AppError struct {
Code string
Args []interface{}
OrigErr error
}
func (e *AppError) Error() string {
return e.Code // 仅返回 code,避免污染日志原始上下文
}
func (e *AppError) Unwrap() error { return e.OrigErr }return &AppError{Code: "auth.user_required", Args: nil}golang.org/x/text/languagemessage.Printerfunc (l *Localizer) Translate(code string, args ...interface{}) string {
msg := l.bundle.Message(code)
if msg == nil {
return "unknown_error"
}
return l.printer.Sprintf(msg, args...)
}fmt.Errorferr := fmt.Errorf("用户 %s 不存在", localizer.Translate("user.not_found", username))user.not_foundif err != nil {
if appErr, ok := err.(*AppError); ok {
http.Error(w, localizer.Translate(appErr.Code, appErr.Args...), http.StatusBadRequest)
return
}
http.Error(w, localizer.Translate("internal.error"), http.StatusInternalServerError)
return
}err.Error()"auth.user_required"errors.Iserrors.As*AppErrorIserrors.Is(err, someTarget)func (e *AppError) Is(target error) bool {
if targetAs, ok := target.(*AppError); ok {
return e.Code == targetAs.Code
}
return errors.Is(e.OrigErr, target)
}*pq.Errorerrors.As(err, &pgErr)e.OrigErrUnwrap()errors.Is(err, ErrUserNotFound)en.tomlzh-CN.tomlCode"unknown_error"