如何使用语言标签(如 en-US 和 fr-CA)

了解 BCP 47 语言标签的结构和含义,助力更优的国际化决策

引言

语言标签是用于标识特定语言及其区域变体的标准化代码。这些标签在国际化工作中随处可见。当你检测用户的首选语言时,浏览器会返回语言标签。当你格式化日期或数字时,需要将语言标签传递给 Intl API。当你加载翻译内容时,也会用语言标签来决定显示哪种内容。

理解这些标签的工作原理,有助于你在语言选择、回退机制和内容组织方面做出更明智的决策。本课程将讲解语言标签的结构,并展示如何在 JavaScript 中使用它们。

什么是语言标签

语言标签是一串字符串,如 enen-USzh-Hans-CN,用于标识一种语言,并可选地指定书写系统和地区。这些标签遵循 BCP 47 标准,由互联网工程任务组(IETF)和互联网号码分配局(IANA)维护。

BCP 47 代表“最佳当前实践第 47 号”。该标准定义了如何通过称为子标签的更小组件来构建语言标签。每个子标签代表语言的某一特定方面,例如使用哪种语言、采用哪种书写系统或关联哪个国家/地区。

所有编程语言和国际化库都使用 BCP 47 标签。这种一致性意味着你可以在整个应用中使用相同的语言标识符,从浏览器检测到服务端格式化,再到翻译文件命名。

语言标签的结构

语言标签由用连字符分隔的子标签组成。最常见的三个子标签是语言、脚本和地区。这些子标签在出现时总是按照这个特定顺序排列。

语言子标签排在最前面,是唯一必需的部分。它采用 ISO 639 的两位或三位字母代码。例如,en 代表英语,fr 代表法语,zh 代表中文。

脚本子标签在出现时排在第二位。它采用 ISO 15924 的四位字母代码,用于标识书写系统。例如,Latn 代表拉丁字母,Cyrl 代表西里尔字母,Hans 代表简体中文字符。

地区子标签在出现时排在最后。它采用 ISO 3166-1 的两位字母代码,通常代表一个国家。例如,US 代表美国,CA 代表加拿大,CN 代表中国。

常见语言标签示例

以下示例展示了使用语言标签可以表达的不同细粒度级别。

仅包含语言的简单标签:

  • en - 英语(无特定地区或脚本)
  • fr - 法语(无特定地区或脚本)
  • es - 西班牙语(无特定地区或脚本)

包含语言和地区的标签:

  • en-US - 美国使用的英语
  • en-GB - 英国使用的英语
  • fr-CA - 加拿大使用的法语
  • es-MX - 墨西哥使用的西班牙语

包含语言、脚本和地区的标签:

  • zh-Hans-CN - 在中国使用简体字的中文
  • zh-Hant-TW - 在台湾使用繁体字的中文
  • sr-Latn-RS - 在塞尔维亚使用拉丁字母的塞尔维亚语
  • sr-Cyrl-RS - 在塞尔维亚使用西里尔字母的塞尔维亚语

所需的具体程度取决于你的应用场景。如果只是翻译文本,可能只需要语言和地区。如果涉及多种书写系统的语言,则需要脚本子标签。

语言标签的大小写规范

语言标签不区分大小写。en-USEN-USen-usEn-Us 都表示同一种语言。但有一些约定的大小写模式可以提升标签的可读性。

语言子标签通常使用小写字母。应写作 en,而不是 ENEn

脚本子标签通常采用首字母大写的标题格式。应写作 Latn,而不是 latnLATN

地区子标签通常使用大写字母。应写作 US,而不是 usUs

遵循这些规范可以让标签更易读,并与文档和规范中的格式保持一致。但你的代码应接受任何大小写的语言标签,因为该格式在官方上是不区分大小写的。

使用 JavaScript 解析语言标签

JavaScript 提供 Intl.Locale 构造函数来解析语言标签并提取其各个部分。该构造函数接受一个语言标签字符串,并返回一个包含每个子标签属性的对象。

const locale = new Intl.Locale("en-US");

console.log(locale.language);
// Output: "en"

console.log(locale.region);
// Output: "US"

Intl.Locale 对象为语言标签的每个组成部分都提供了属性。如果原始标签中没有对应的子标签,这些属性会返回 undefined。

const simple = new Intl.Locale("fr");
console.log(simple.language);
// Output: "fr"

console.log(simple.region);
// Output: undefined

你可以用相同的方法解析包含脚本子标签的标签。

const complex = new Intl.Locale("zh-Hans-CN");

console.log(complex.language);
// Output: "zh"

console.log(complex.script);
// Output: "Hans"

console.log(complex.region);
// Output: "CN"

当你需要根据语言标签的特定组成部分做出决策时,这种解析能力非常有用。例如,你可能需要根据脚本加载不同的字体,或者根据地区显示不同的内容。

何时使用特定标签与通用标签

为语言标签选择合适的特定程度,取决于你的应用需要处理哪些语言和文化方面。

当你有一个适用于所有该语言使用者的单一翻译时,可以使用仅包含语言的标签,如 enfr。这在本地化预算有限或该语言地区差异较小的应用中很常见。

当你需要考虑词汇、拼写或文化习惯的地区差异时,可以使用包含语言和地区的标签,如 en-USfr-CA。英式英语和美式英语在许多单词的拼写上不同。加拿大法语和欧洲法语在词汇和表达上也有差异。

当你处理使用多种书写系统的语言时,可以使用包含语言、脚本和地区的标签,如 zh-Hans-CN。中文可以用简体或繁体字书写,塞尔维亚语可以用拉丁字母或西里尔字母书写。脚本子标签用于区分这些变体。

提取翻译文件的语言代码

许多翻译系统会按照语言代码组织文件。你可以从完整的语言标签中提取语言和地区部分,以确定要加载哪个翻译文件。

const userLanguage = "zh-Hans-CN";
const locale = new Intl.Locale(userLanguage);

const translationKey = `${locale.language}-${locale.region}`;
console.log(translationKey);
// Output: "zh-CN"

即使用户的语言标签包含你在选择文件时不需要的组件,这种方法也同样适用。

有些应用程序只使用语言代码,不包含地区信息。

const userLanguage = "fr-CA";
const locale = new Intl.Locale(userLanguage);

const translationKey = locale.language;
console.log(translationKey);
// Output: "fr"

你为翻译文件命名选择的结构应与从语言标签中提取组件的方式保持一致。

使用语言标签与 Intl API

Intl API 在所有构造函数中都可以直接接受语言标签。除非你需要检查特定组件,否则无需自行解析标签。

const date = new Date("2025-03-15");

const usFormat = new Intl.DateTimeFormat("en-US").format(date);
console.log(usFormat);
// Output: "3/15/2025"

const gbFormat = new Intl.DateTimeFormat("en-GB").format(date);
console.log(gbFormat);
// Output: "15/03/2025"

Intl API 会根据语言标签确定应用哪些格式化规则。即使说同一种语言,不同地区在日期、数字和货币的格式上也会有所不同。

你可以将从浏览器获取的语言标签直接传递给 Intl 的构造函数。

const userLanguage = navigator.language;
const formatter = new Intl.NumberFormat(userLanguage);

console.log(formatter.format(1234.5));
// Output varies by language
// For "en-US": "1,234.5"
// For "de-DE": "1.234,5"

这是客户端国际化中最常见的模式。检测用户语言,然后在整个应用中使用该语言标签来正确格式化内容。

处理无效的语言标签

如果你传入无效的语言标签,Intl.Locale 构造函数会抛出 RangeError。处理来自不可信来源的语言标签时,应当捕获此错误。

try {
  const locale = new Intl.Locale("invalid-tag-format");
} catch (error) {
  console.log(error.name);
  // Output: "RangeError"

  console.log(error.message);
  // Output: "invalid language tag: invalid-tag-format"
}

大多数来自浏览器的语言标签都是有效的,但用户输入或外部数据源可能包含格式错误的标签。将构造函数包裹在错误处理逻辑中,可以防止这些无效标签导致应用崩溃。