go 不支持运行时全局切换语言环境,time.format 和 fmt 均不依赖系统 locale;多语言需用 golang.org/x/text/language 和 message 手动管理 per-request printer,并显式注册翻译。

Go 本身不支持运行时切换全局语言环境(locale),os.Setenv("LANG", "...") 或修改 LC_ALL 对标准库的格式化函数(如 time.Time.Format、fmt)完全无效——这是常见误判点。
为什么 time.Now().Format("2006年1月2日") 无法自动按语言显示中文月份?
Go 的 time.Format 使用硬编码的英文月份/星期名,不读取系统 locale;fmt 也不支持本地化字符串插值。所谓“动态语言切换”,必须手动管理多语言资源,而非依赖 OS 层。
- Go 标准库中没有
SetLanguage、SetLocale这类函数 -
golang.org/x/text/language和golang.org/x/text/message是官方推荐的 i18n 方案,但需显式传入language.Tag - 所有格式化操作(日期、数字、复数)都必须在调用时指定语言标签,不能“全局生效”
用 golang.org/x/text/message 实现请求级语言切换
适合 HTTP 服务:从请求头(Accept-Language)或 URL 参数提取语言标识,为每个请求构造独立的 *message.Printer。
import (
"golang.org/x/text/language"
"golang.org/x/text/message"
)
func handler(w http.ResponseWriter, r *http.Request) {
// 解析 Accept-Language,取第一个有效 tag
tags, _, _ := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
tag := language.Und
if len(tags) > 0 {
tag = tags[0]
}
p := message.NewPrinter(tag)
p.Printf("Hello, %s!\n", "World") // 自动选对应语言的翻译
}
-
message.Printer不是全局单例,必须 per-request 创建或从池中获取 - 翻译文本需提前通过
message.Catalog注册,否则 fallback 到源语言(如英文) - 若未调用
message.SetString注册翻译,p.Printf仍会输出英文原文,不会报错
如何安全地存储和切换用户语言偏好?
语言选择是用户状态,不是应用配置。不要用包级变量存当前语言(如 var CurrentLang language.Tag),这在并发请求下必然出错。
- HTTP 场景:把
language.Tag存在context.Context中,handler 开始时解析并 WithValue 注入 - CLI 工具:用命令行 flag(
--lang=zh)解析后传给各业务函数,避免全局变量 - Web 框架(如 Gin):可用中间件解析并写入
c.Set("lang", tag),后续 handler 从中取 - 切忌在
init()里调用message.SetDefault——它只影响未指定 tag 的 Printer,且不可撤销
真正的难点不在“怎么切”,而在“切完之后所有字符串输出是否都被覆盖”。日期格式、错误提示、日志内容、API 响应字段……只要漏掉一处没走 Printer 路径,就会出现中英混杂。这不是加个开关就能解决的事,得逐层检查输出链路。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











