Locale 设计:语言代码规范与区域设置

做国际化,第一件事就是搞清楚 Locale 怎么设计。

说白了,Locale 就是告诉系统:「我现在在哪儿,我说什么话,我看什么格式。」

我见过不少团队,上来就写死一个「zh-CN」,结果产品一出海,全崩了。嗯,今天咱们就把这块彻底讲透。

BCP 47:语言代码的「国际驾照」

BCP 47 是目前最权威的语言标签标准。它由 IETF 制定,几乎所有现代系统都在用。

它的格式很简单:语言-地区

  • 语言:用小写字母表示,比如 zh(中文)、en(英文)、ja(日文)。
  • 地区:用大写字母表示,比如 CN(中国)、US(美国)、GB(英国)。

举个例子:

  • zh-CN:中国大陆使用的中文
  • zh-TW:台湾地区使用的中文
  • en-US:美国使用的英文
  • en-GB:英国使用的英文

核心原则:语言代码必须唯一、稳定、可扩展。

我在项目中遇到过,有人把 zh-CN 写成了 zh_CN,结果浏览器解析直接报错。BCP 47 规定用连字符,不是下划线。

区域设置:不只是翻译

很多人以为国际化就是翻译文字。其实远不止这些。

区域设置(Locale)包含:

  • 日期格式:中国是 2024-01-15,美国是 01/15/2024,英国是 15/01/2024
  • 数字格式:中国用 1,234.56,德国用 1.234,56
  • 货币符号¥$
  • 排序规则:中文按拼音,日文按五十音图。
  • 时区:UTC+8、UTC-5 等等。

我建议,在设计 Locale 数据结构时,把这些信息都打包进去。别只存一个语言代码。

回退策略:优雅降级

用户的语言设置,系统不一定都有对应的翻译。这时候就需要回退策略。

说白了,就是「找不到精确匹配,就找个最接近的」。

常见的回退链路:

  1. 精确匹配zh-CN → 直接命中
  2. 语言回退zh-CN 没有 → 找 zh
  3. 默认回退zh 也没有 → 找 en
  4. 硬编码:连 en 都没有 → 显示 key 本身

我的经验:回退策略一定要写在代码里,不要依赖数据库。我曾经遇到过,数据库里某个语言的翻译被误删了,结果前端直接白屏。后来我加了一层硬编码回退,再也没出过问题。

代码示例:一个简单的 Locale 管理器

class LocaleManager {
  constructor() {
    this.locales = new Map();
    this.fallbackChain = ['zh-CN', 'zh', 'en', 'default'];
  }

  addLocale(code, translations) {
    this.locales.set(code, translations);
  }

  translate(key, locale) {
    // 精确匹配
    if (this.locales.has(locale) && this.locales.get(locale)[key]) {
      return this.locales.get(locale)[key];
    }

    // 语言回退
    const lang = locale.split('-')[0];
    if (this.locales.has(lang) && this.locales.get(lang)[key]) {
      return this.locales.get(lang)[key];
    }

    // 默认回退
    for (const fallback of this.fallbackChain) {
      if (this.locales.has(fallback) && this.locales.get(fallback)[key]) {
        return this.locales.get(fallback)[key];
      }
    }

    // 硬编码
    return key;
  }
}

这段代码看起来简单,但足够应对 90% 的场景。你想想看,很多大厂的国际化框架,核心逻辑也就这样。

常见陷阱与避坑指南

我曾经踩过的坑:

  • 大小写问题zh-cnzh-CN 在 BCP 47 里是等价的,但有些系统不认。我建议统一用 zh-CN 格式。
  • 地区遗漏:只存 zh 不存 zh-CN,会导致日期格式无法区分。比如新加坡的中文和大陆的中文,日期格式就不一样。
  • 回退死循环:如果 en 回退到 en-USen-US 又回退到 en,就死循环了。一定要设计成单向链。

表格:常见 Locale 代码对照

语言 地区 BCP 47 代码 常见用途
中文 中国大陆 zh-CN 简体中文、人民币、YYYY-MM-DD
中文 台湾 zh-TW 繁体中文、新台币、YYYY/MM/DD
英文 美国 en-US 美元、MM/DD/YYYY、12小时制
英文 英国 en-GB 英镑、DD/MM/YYYY、24小时制
日文 日本 ja-JP 日元、YYYY年MM月DD日

总结

Locale 设计,说白了就是三件事:

  • 用 BCP 47 规范命名
  • 把区域设置(日期、数字、货币)打包进去
  • 设计好回退链路,保证系统不崩

我个人习惯,在项目初期就把 Locale 管理器写好。别等到上线了再补,那时候改起来就痛苦了。

嗯,这一章就到这儿。下一章咱们聊聊「翻译文件的组织方式」,那也是个容易踩坑的地方。