iris默认不带i18n中间件,因其核心定位是高性能路由与中间件调度,语言切换属业务逻辑,需自行组合accept-language解析、翻译加载及上下文注入;推荐用golang.org/x/text实现轻量级多语言中间件。

为什么 Iris 默认不带 i18n 中间件
Iris 框架本身没有内置 i18n 或 localizer 中间件,不像 gin-i18n 那样有成熟社区封装。它的核心定位是高性能路由与中间件调度,语言切换属于业务层逻辑,需自行组合:解析语言偏好(Accept-Language)、加载翻译文件、注入上下文变量。直接依赖第三方 i18n 库(如 go-i18n 或 golang.org/x/text)更可控,也避免框架耦合。
如何用 golang.org/x/text 实现轻量级多语言中间件
推荐使用标准库生态的 golang.org/x/text + language + message 包,不引入额外运行时开销。关键点在于:在中间件中解析请求头、匹配最适语言标签、初始化 message.Printer 并挂载到 ctx.Values()。
-
Accept-Language解析必须用language.ParseAcceptLanguage,不能简单字符串切分——否则无法处理zh-CN;q=0.9, zh;q=0.8, en;q=0.7这类加权优先级 - 翻译文件建议用
.toml或.json,按语言代码组织目录(如locales/zh-CN/messages.toml),用message.NewBundle加载后调用bundle.SetMessage - 中间件里必须调用
c.Next(),否则后续 handler 拿不到挂载的printer
示例中间件片段:
func I18nMiddleware(b *message.Bundle) iris.Handler {
return func(c iris.Context) {
accept := c.GetHeader("Accept-Language")
if accept == "" {
accept = "en-US"
}
tags, _ := language.ParseAcceptLanguage(accept)
tag := tags[0] // 取最高优先级
p := message.NewPrinter(b, message.Language(tag))
c.Values().Set("i18n", p)
c.Next() // ⚠️ 忘记这行会导致后续 handler panic
}
}
怎么在 handler 里安全使用翻译结果
不要在全局变量或 init 函数里预存 Printer,它不是并发安全的;每次请求都应从 c.Values().Get("i18n") 取出新实例。常见错误包括:
- 直接调用
p.Sprintf("hello %s", name)却没提前注册对应 key 的翻译消息 → 返回原字符串(静默失败) - 在模板中用
{{ .i18n.Printf "login_title" }},但没把printer传入模板数据 → 模板渲染报nil pointer - 路径参数含中文时,
ctx.Params().Get("lang")未做url.PathUnescape处理 → 解析出乱码 tag
推荐写法:
func HomeHandler(c iris.Context) {
p, _ := c.Values().Get("i18n").(*message.Printer)
c.HTML("<h1>" + p.Sprintf("welcome_title") + "</h1>")
}
路由前缀式多语言(如 /zh-CN/login)要注意什么
若采用 URL 路径携带语言代码(而非仅靠 header),需配合 app.Party 分组,并在分组中间件中提取并验证 lang 参数:
- 用
app.Party("/{lang:zh-CN|en-US|ja-JP}")做正则约束,防止任意字符串污染上下文 - 提取后必须校验是否为合法 BCP 47 tag(可用
language.Parse尝试解析),否则 fallback 到默认语言 - 注意静态文件路由(如
/assets/)要放在所有语言分组之外,否则/zh-CN/assets/style.css会 404
容易被忽略的是:HTTP 缓存头。同一 URL(如 /login)在不同语言下内容不同,必须加 Vary: Accept-Language 或禁用共享缓存,否则 CDN 可能返回错语言版本。











