用x/text/message本地化需创建message.printer并用{key}占位符,不可用fmt.printf;占位符须带类型修饰如{user,gender,female}才能触发词形变化,且语言tag、消息注册、修饰符必须严格匹配。

直接说结论:用 x/text/message 做本地化格式化输出,核心是先创建 message.Printer,再调用它的 Printf、Sprintf 等方法,且所有占位符必须用 {key} 语法(不是 %s),否则本地化会失效。
为什么不能继续用 fmt.Printf 配合翻译后的字符串
常见误区是先查翻译表拿到中文字符串,再塞进 fmt.Printf("欢迎,%s!", name) —— 这在中英文间可能出问题。比如德语里“欢迎”后接人名时,词序可能是 "Willkommen, {name}!",但更关键的是:德语中名词有性数格变化,name 的形式需随上下文变(如 Sehr geehrter {name}! vs Sehr geehrte {name}!)。x/text/message 的模板机制支持参数类型标注(如 {name, gender, male}),Printer 能结合语言规则动态选择词形,而拼接字符串做不到这点。
- 占位符必须是
{key}或带类型修饰的{key, type, style}形式,fmt风格的%v会被原样输出,不触发翻译 - 翻译文件(
.mo或.po)里对应条目也得写成msgstr "欢迎,{name}!",不能含%s - 若混用
fmt.Sprintf处理部分文本,那部分就彻底脱离本地化流程,后续无法按语言调整空格、标点方向(如阿拉伯语从右向左)
如何正确初始化 Printer 并加载多语言消息
关键不是“加载翻译文件”,而是让 Printer 知道当前语言环境,并能查到对应消息。推荐用 message.NewPrinter + language.Make,而非手动解析 .mo 文件(除非你有特殊打包需求)。
- 用
message.NewPrinter(language.Make("zh"))创建中文Printer,它默认查找golang.org/x/text/message/catalog中注册的消息 - 消息注册要早于
Printer实例化,通常放在init()里:func init() { catalog.Register(language.English, catalog.String("welcome", "Hello, {name}!")) catalog.Register(language.Chinese, catalog.String("welcome", "你好,{name}!")) } - 如果消息来自外部
.po文件,需用gotext工具生成 Go 代码再导入,不能运行时读取磁盘上的.po——x/text/message不提供运行时文件解析能力
占位符里的类型修饰怎么写才生效
基础 {name} 只做字符串替换;要触发性别、复数、日期等语言敏感处理,必须显式加修饰。这些修饰不会报错,但写错或漏写就退化为普通替换。
- 性别控制:
{user, gender, female}对应catalog.String("greeting", "Dear {user, gender, female}"),配合德语/法语等语言的词形变化规则库 - 复数控制:
There are {count, plural, one{# item} other{# items}},其中#是自动注入的数值,one/other匹配语言的复数类别(如俄语有 3 种) - 日期时间:
{time, datetime, short}会按语言习惯格式化,比如中文是12/05,阿拉伯语是٠٥/١٢(含 Unicode 方向标记) - 所有修饰符必须严格匹配文档定义,比如写成
{count, plural, one{# thing}}就无效——thing不是合法复数关键词,应为other
常见错误:本地化没生效,但代码看起来没问题
最常卡在这一步:字符串没变,但找不到原因。本质是 Printer 查不到对应语言的消息,又没报错,就默默回退到默认语言(通常是英文)或原始模板字符串。
- 检查
language.Make("zh-CN")和注册时用的language.Chinese是否兼容——language.Chinese等价于language.Make("zh"),不匹配zh-CN;要用language.MustParse("zh-CN")并确保注册时也用了相同 tag - 确认
catalog.Register调用发生在message.NewPrinter之前,Go 的包初始化顺序容易搞反 - 打印
printer.Language()看实际生效的语言 tag,有时系统 locale 返回zh_Hans_CN这种非标准格式,需用language.Parse+language.Compose标准化 - 调试时临时加一句
printer.Printf("debug: {key}"),如果输出仍是debug: {key},说明该 key 根本没注册过
真正麻烦的从来不是写对那一行 printer.Printf,而是确保整个链条——语言 tag 的构造、消息注册时机、占位符修饰符的拼写、复数/性别规则的覆盖范围——全部咬合。少一环,本地化就静默降级,还很难定位。











