Go多语言Push通知的核心是将语言选择权交由业务层、渲染逻辑前置、渠道适配与本地化解耦,避免在Message结构中硬编码多语言字段,所有本地化必须在调用Notify()前完成。

Go 实现多语言 Push 通知,核心不是“翻译完再发”,而是把语言选择权交给业务层、把渲染逻辑前置、把渠道适配和本地化解耦——否则同一份 Message 结构体里塞 TitleZh/TitleEn 字段,半年后加个日语就得改所有发送链路。
Push 消息的本地化必须在发送前完成
别让 WeComSender.Send() 或 DingTalkSender.Send() 去查用户语言、读 i18n 文件、调模板引擎。它们只该接收已渲染好的纯文本/HTML 片段。
- 用户语言信息(如
lang: "ja-JP")应由上游 HTTP handler 或消息触发服务传入,作为上下文参数或Message的一个字段(如Message.Locale) - 模板渲染必须在调用
notifier.Notify()前完成:用golang.org/x/text/language+golang.org/x/text/message做格式化,或用go-i18n/i18n加载对应 locale 的 JSON bundle - 不同渠道对长度、换行、富文本支持差异极大:钉钉卡片不认
\n,企微外部群不支持 base64 图片,邮件需 HTML + plain text 双版本——这些都得在渲染阶段处理,不能靠 sender 硬扛
如何安全地传递语言上下文到各渠道 sender
直接把 Message.Locale 透传给每个 Send() 方法最轻量,也最可控。不要用全局变量、context.WithValue 带语言、或依赖 HTTP header 传递——后者在异步队列(如 Redis job)场景下必然丢失。
-
Message结构体里显式定义Locale string字段(值如"zh-CN"、"en-US"),并确保它随消息一起序列化进队列 - 工厂方法
NewSender(cfg config.ChannelConfig)不感知语言,只负责构造渠道实例;语言决策完全交由上层调用方 - 若用
http.Pusher推送静态资源(如多语言 JS 提示脚本),路径要带 locale 前缀:pusher.Push("/js/notify.en.js", nil),且该文件需真实存在
常见错误:模板渲染漏掉 fallback 语言
用户语言是 "pt-BR",但你的 i18n bundle 只有 en.json 和 zh.json,没配 pt.json —— 这时 message.Printf() 会静默回退到默认语言,还是 panic?取决于你用的库。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
golang.org/x/text/message时,若未注册对应language.Tag,Printer.Sprintf()会 fallback 到language.Und(即无语言),输出原始 key,比如"notification.title" - 用
go-i18n时,默认行为是 panic,除非你显式调用i18n.Tfunc().WithFallback(true) - 务必在测试中覆盖缺失 locale 场景:手动传入
"xx-XX",验证是否返回可读降级文案(如英文),而非乱码或空字符串
移动端 Push(APNs/FCM)的语言处理更特殊
APNs 的 aps.alert.title-loc-key 和 FCM 的 data 字段都不支持运行时翻译,必须由服务端完成本地化后,把最终文案塞进 payload。
- APNs 要求多语言文案全部预置在客户端 bundle 中,服务端只传
title-loc-key和title-loc-args;但 Go 后端无法控制客户端是否含该 key,所以生产环境建议放弃此机制,直接传title字符串 - FCM 的
notification字段是纯服务端渲染,data字段可传结构化数据供客户端二次处理;但如果你的 App 不做本地化兜底,就别传data,直接把notification.title和notification.body渲染好再发 - 注意 APNs 对 payload 大小限制(2KB),长文案 + 多语言字段容易超限;优先压缩空格、避免冗余字段,而不是硬塞更多语言变体
最容易被忽略的一点:时区无关的文案(如“您有 3 条新消息”)可以复用模板,但含时间的文案(如“刚刚”、“2 小时前”)必须结合用户所在时区做格式化——而时区信息通常不在 Locale 里,得额外传 Timezone string 字段,否则日本用户看到的是北京时间。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










