跳到主要内容

国际化

VEF 内置了应用级 i18n 层,用于框架消息、验证输出和错误响应。

支持的语言

框架当前内置支持:

语言代码
zh-CN
en

默认应用语言:

配置项
默认语言zh-CN

i18n.GetSupportedLanguages() 返回 supported-language slice 的副本,所以修改返回值不会改变框架内部状态。当前随包提供的 en.jsonzh-CN.json catalogs 各包含同一组 195 个 message IDs。

公共 API

github.com/coldsmirk/vef-framework-go/i18ngithub.com/coldsmirk/vef-framework-go/i18n/locales 的完整公开 API 是:

API作用
i18n.DefaultLanguage默认语言常量(zh-CN
i18n.GetSupportedLanguages()返回支持的语言代码副本(zh-CNen
i18n.IsLanguageSupported(code)判断语言代码是否受支持
i18n.CurrentLanguage()读取当前全局语言代码
i18n.SetLanguage(code)原子切换进程级全局 translator
i18n.T(messageID, data...)翻译并在失败时回退到 message ID
i18n.Te(messageID, data...)翻译并显式返回错误
i18n.New(config)根据 i18n.Config 创建独立 translator 实例
i18n.Config独立 translator 构造配置类型
i18n.Config.Localesi18n.New(config) 使用的嵌入式 locale file set
i18n.Translator独立 translator 实例实现的接口
i18n.Translator.T(messageID, data...)在独立 translator 上翻译,并在失败时回退
i18n.Translator.Te(messageID, data...)在独立 translator 上翻译,并显式返回错误
i18n.ErrUnsupportedLanguage请求语言不受支持时被包装的 sentinel
i18n.ErrMessageIDEmptyTe 收到空 message ID 时返回的 sentinel
locales.EmbedLocales框架随包提供的嵌入式 locale file set

绝大多数应用代码只需要 i18n.T(...)

Translator 模型

公共抽象是 i18n.Translator

方法作用
T(messageID, data...)翻译并在失败时回退到 message ID
Te(messageID, data...)翻译并返回显式错误

翻译行为刻意保持简单:

调用行为
i18n.T("known_key")从当前全局 translator 返回本地化消息
i18n.T("missing_key")记录翻译错误,并把 "missing_key" 作为 fallback 返回
i18n.Te("known_key")返回本地化消息和 nil error
i18n.Te("missing_key")返回 "" 和包装后的 localization failure
i18n.Te("")返回 ""i18n.ErrMessageIDEmpty

templateData 设计成 variadic 是为了调用方便,但实现只使用第一个 map[string]any。额外的 map 会被忽略。

配置模型

构造器使用的配置类型是:

类型作用
i18n.Config通过 Locales embed.FS 提供嵌入式语言文件
locales.EmbedLocales默认内置语言文件集合

locales.EmbedLocales 会 embed 框架随包提供的 *.json locale files,包括内置 zh-CNen catalogs。

i18n.New(config) 会创建独立的 i18n.Translator。它在构造时根据 VEF_I18N_LANGUAGEi18n.DefaultLanguage 选择语言,并且不会改变包级 i18n.T(...)i18n.Te(...) 使用的进程级全局 translator。

i18n.Config.Locales 必须提供所有受支持的 locale 文件。以当前 supported-language list 来说,就是同时提供 zh-CN.jsonen.json;空 embed.FS 或缺少某个受支持文件都会让 i18n.New(config) 返回 error。

错误哨兵

错误含义
i18n.ErrUnsupportedLanguage请求的语言代码不受支持
i18n.ErrMessageIDEmpty翻译 message ID 为空

i18n.SetLanguage(...) 会包装 i18n.ErrUnsupportedLanguage,因此调用方可以用 errors.Is(err, i18n.ErrUnsupportedLanguage) 判断。i18n.Te("") 会直接返回 i18n.ErrMessageIDEmpty,所以 errors.Is(err, i18n.ErrMessageIDEmpty) 也可用。

语言选择

框架启动时按以下顺序决定语言:

优先级来源
1VEF_I18N_LANGUAGE
2框架默认语言 zh-CN

VEF_I18N_LANGUAGEconfig 包以 config.EnvI18NLanguage 暴露;它不是 i18n 包 API。

包初始化时,全局 translator 会从 locales.EmbedLocales 创建。如果内置 catalogs 无法加载,初始化会 panic,因为框架消息在这种状态下不可用。

之后也可以通过 i18n.SetLanguage(...) 切换当前进程级 translator,这在测试里尤其有用。传入空字符串 i18n.SetLanguage("") 会重新读取 VEF_I18N_LANGUAGE,如果环境变量为空则回退到 i18n.DefaultLanguage。语言切换时会保留当前 active locale file set。

包级 i18n.T(...)i18n.Te(...) 通过 atomic pointer 读取当前 translator,i18n.SetLanguage(...) 也通过同一个 pointer 发布新的 immutable translation state。因此翻译调用和语言切换并发时不会产生 data race;在切换边界上的请求自然可能观察到旧语言或新语言。

i18n 自动生效的地方

即使你从不手动调用 i18n,它也已经影响这些位置:

区域效果
result.Ok(...)result.Err(...)成功/错误消息自动本地化
验证输出字段标签和自定义规则消息自动翻译
内置资源错误框架返回值自动本地化

所以在 VEF 里,i18n 不只是 UI 文本问题,它本身就是后端响应契约的一部分。

最小示例

package userinfo

import (
"github.com/gofiber/fiber/v3"

"github.com/coldsmirk/vef-framework-go/i18n"
"github.com/coldsmirk/vef-framework-go/result"
"github.com/coldsmirk/vef-framework-go/security"
)

func UserInfoNotImplemented(ctx fiber.Ctx) error {
return result.ErrNotImplemented(
i18n.T(security.ErrMessageUserInfoLoaderNotImplemented),
)
}

与验证的集成

验证与 i18n 的集成主要体现在两点:

机制效果
label_i18n字段标签可翻译
自定义 validator 规则翻译框架自定义规则会返回本地化消息

这也是为什么真实项目里的请求验证错误会比默认 validator 输出更可读。

实践建议

  • 普通业务代码优先使用 i18n.T(...)
  • 只有在你需要显式处理翻译失败时,才使用 i18n.Te(...)
  • i18n.SetLanguage(...) 更适合作为测试工具或受控运行时切换工具
  • 要意识到 result 消息本身已经是本地化内容,因此后端响应天然属于 i18n 表面的一部分

下一步

继续阅读 验证,看翻译后的字段标签和验证消息是如何出现在请求错误里的。