http handler 必须用 language.parseacceptlanguage 解析 accept-language 并匹配最佳 locale,错误值保持语言中立,error() 仅返回 code,translate 方法由 handler 显式调用,message.printer 不跨 goroutine 复用,翻译资源 key 启动时校验。

HTTP handler 中必须从 Accept-Language 提取 locale,别用 strings.Split
Accept-Language 是个带权重的逗号分隔列表(如 zh-CN,zh;q=0.9,en;q=0.8),直接 strings.Split(r.Header.Get("Accept-Language"), ",") 会漏掉权重、无法匹配最佳语言。错误现象是用户明明设了 zh-Hans,却 fallback 到英文,且不报错、难定位。
- 用
language.ParseAcceptLanguage解析,它返回[]language.Tag和权重,能正确处理zh匹配zh-Hans或zh-Hant - 传给
message.Printer前,先用language.MatchStrings找出最匹配已注册 bundle 的 tag - 没匹配上时 fallback 到默认语言(比如
language.English),但要记录 warn 日志,否则上线后才发现某些小语种没覆盖
错误类型不能实现 Error() 返回翻译后字符串
把翻译逻辑塞进 Error() 方法会导致日志里全是中文/日文,运维查问题时看到 "ユーザーが見つかりません" 完全没法 grep 或做聚合统计。错误值本身必须保持语言中立。
- 自定义错误结构体(如
*AppError)的Error()只返回Code字段,例如"user.not_found" - 暴露一个
Translate(p *message.Printer) string方法,由 handler 或 middleware 显式调用 - 如果用了
errors.Is或errors.As,确保Unwrap()正确返回底层 error,别让翻译包装破坏错误链
message.Printer 实例不能跨 goroutine 复用
message.Printer 内部含格式化状态(比如复数规则缓存),并发写入会 panic 或返回乱码。常见错误是全局声明一个 var printer *message.Printer,然后所有请求都用它。
- 每次 HTTP 请求在 middleware 中创建新实例:
p := message.NewPrinter(matchedTag) - 或用
p.With(...)构造临时 Printer,避免污染原实例 - 别在 DAO 层或 model 层 new error——那里没有 request context,locale 信息必然丢失;错误构造推迟到 handler 或 service 边界
翻译资源 key 必须与代码中字符串完全一致,且启动时校验
key 写错一个字符(比如 "user_not_found" vs "user_not_fount"),message.Printer.Sprintf 默认就返回原 key,前端看到的是裸 key 而不是错误信息,这种问题上线后极难发现。
启动时遍历所有预定义 error code,检查是否每个都已在对应语言 bundle 中注册:
- 用
message.Catalog.Lookup(lang, key)验证存在性 - 缺失时直接
log.Fatal,别等 runtime fallback - key 命名统一用小写字母+下划线,不带空格或标点,方便 JSON 文件管理和机器校验
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











