错误码结构体必须嵌入lang字段实现运行时多语言,禁止硬编码或map维护文案;应通过中间件解析accept-language并注入context,文案表需支持动态加载与fallback,handler中统一用apperror封装错误响应。

错误码结构体必须嵌入语言标识字段
直接在错误码常量里硬编码中文或英文,后续加语言就只能改代码。正确做法是把语言作为运行时变量参与错误构造。比如定义 ErrorCode 结构体时,必须包含 Lang 字段(如 string 类型),而不是靠 map[string]string 静态维护多语言文案。
常见错误是写成这样:
var ErrUserNotFound = map[string]string{
"zh": "用户不存在",
"en": "User not found",
}
这会导致所有地方都要手动传 lang、查 map、做 fallback,极易漏判。应该让错误实例自己携带语言上下文:
- 定义
type LocalizedError struct { Code int `json:"code"` Msg string `json:"msg"` Lang string `json:"lang"` } - 错误码生成函数接收
lang string参数,内部查表后返回已填充Msg和Lang的实例 - 避免在 handler 里重复做语言判断,统一收口到错误构造层
HTTP 中间件提取 Accept-Language 并注入 Context
Echo 的 c.Request().Header.Get("Accept-Language") 返回的是完整字符串(如 "zh-CN,zh;q=0.9,en-US;q=0.8"),不能直接当语言标识用。必须解析出首选语言并标准化(转小写、去区域后缀),再存进 c.Set("lang", "zh")。
容易踩的坑:
- 没做
q值排序,取到的是权重最低的语言 - 没处理
en和en-US的 fallback 关系,导致英文用户请求en-US却匹配不到文案 - 中间件没设默认语言(如
en),当 header 为空或非法时 panic 或返回空文案
建议用 golang.org/x/text/language 解析,比手撕正则更可靠:
tags, _ := language.ParseAcceptLanguage(c.Request().Header.Get("Accept-Language"))
lang := tags[0].Base().String() // 得到 "zh" 或 "en"
错误码文案表必须支持动态加载与热更新
把多语言文案写死在 Go 文件里,每次加语言就要 recompile + restart,线上没法灰度。应该用 JSON/YAML 文件按语言拆分,例如 i18n/zh.json、i18n/en.json,启动时读入内存 map,再提供 GetMsg(code int, lang string) string 查询接口。
关键点:
- 文案文件路径要可配置,方便测试环境指向本地目录,生产环境走 embed 或远程配置中心
- 查询函数必须有 fallback:查不到
zh就查en,都查不到就返回默认兜底文案(如"Unknown error") - 如果用
embed,注意//go:embed i18n/*要写对路径,且文件名不能含点(zh_CN.json比zh-CN.json更稳妥)
Handler 返回错误时禁止手动拼接 Msg 字段
最典型的反模式是 handler 里写:c.JSON(400, map[string]interface{}{"code": 1001, "msg": i18n.GetMsg(1001, lang)})。这会让错误构造逻辑泄露到每个接口,一旦文案格式变更(比如加 traceID、加字段名),就得改几十个地方。
正确方式是封装统一响应结构,并让错误码类型实现 echo.HTTPError 接口:
- 定义
type AppError struct { Code int; Lang string } - 实现
func (e *AppError) Error() string { return i18n.GetMsg(e.Code, e.Lang) } - 在全局 HTTP 错误处理器中统一调用
c.JSON(status, map[string]interface{}{"code": e.Code, "msg": e.Error()})
这样只要改 Error() 方法,所有地方的文案输出自动同步。别忘了注册全局错误处理器:e.HTTPErrorHandler = customHTTPErrorHandler。
复杂点在于,有些错误来自第三方库(如数据库超时),它们不是 AppError 类型,需要在中间件里统一 wrap 成带语言上下文的错误;否则这些错误永远显示默认语言。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











