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 数据结构时,把这些信息都打包进去。别只存一个语言代码。
回退策略:优雅降级
用户的语言设置,系统不一定都有对应的翻译。这时候就需要回退策略。
说白了,就是「找不到精确匹配,就找个最接近的」。
常见的回退链路:
- 精确匹配:
zh-CN→ 直接命中 - 语言回退:
zh-CN没有 → 找zh - 默认回退:
zh也没有 → 找en - 硬编码:连
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-cn和zh-CN在 BCP 47 里是等价的,但有些系统不认。我建议统一用zh-CN格式。 - 地区遗漏:只存
zh不存zh-CN,会导致日期格式无法区分。比如新加坡的中文和大陆的中文,日期格式就不一样。 - 回退死循环:如果
en回退到en-US,en-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 管理器写好。别等到上线了再补,那时候改起来就痛苦了。
嗯,这一章就到这儿。下一章咱们聊聊「翻译文件的组织方式」,那也是个容易踩坑的地方。