Intl.Locale API
使用结构化 JavaScript 对象解析、操作和查询本地化标识符
简介
在为多语言和多地区构建应用时,你会遇到像 en-US、fr-FR 或 zh-Hans-CN 这样的本地化标识符。这些字符串常见于浏览器 API、HTTP 头和用户偏好设置中,包含了语言、地区、书写系统和格式化偏好等信息。
Intl.Locale API 能将这些不透明的字符串转换为可供检查和操作的结构化对象。你无需手动解析字符串或猜测 zh-Hans-CN 的含义,只需创建一个 locale 对象并直接读取其属性。
本指南将介绍本地化标识符的工作原理、如何使用 Intl.Locale API 进行操作,以及在何种场景下结构化 locale 对象能够帮助你解决实际问题。
了解本地化标识符
本地化标识符是一个字符串,用于指定日期、数字、货币和文本格式的文化偏好。该标识符由多个用连字符分隔的组件组成。
最常见的结构遵循 BCP 47 标准:
language-script-region-variant-extensions
除语言代码外,其他组件均为可选。
语言代码
语言代码采用 ISO 639 的两位或三位小写字母。常见示例:
en表示英语es表示西班牙语fr表示法语de表示德语ja表示日语zh表示中文ar表示阿拉伯语
语言代码始终为小写,并且在标识符中位于首位。
地区代码
地区代码使用 ISO 3166-1 的两个大写字母,指定地理区域,用于指明所用语言的变体:
en-US表示美式英语en-GB表示英式英语es-ES表示欧洲西班牙语es-MX表示墨西哥西班牙语fr-FR表示法国法语fr-CA表示加拿大法语
区域代码会影响格式规范。美式英语使用 MM/DD/YYYY 日期格式,而英式英语使用 DD/MM/YYYY。
字符集代码
字符集代码用四个字母表示,首字母大写。对于多种书写系统的语言,这一点很重要:
zh-Hans表示简体中文字符zh-Hant表示繁体中文字符sr-Cyrl表示塞尔维亚语西里尔字母sr-Latn表示塞尔维亚语拉丁字母
大多数 locale 会省略字符集代码,因为语言本身就隐含了默认书写系统。英语默认使用拉丁字母,所以你只需写 en,而不是 en-Latn。
扩展标签
扩展标签为 locale 标识符添加格式偏好。它们以 -u- 开头,后跟键值对:
en-US-u-ca-gregory-nu-latn-hc-h12
常见扩展键:
ca表示日历系统(gregory、buddhist、islamic)nu表示数字系统(latn、arab、hanidec)hc表示小时制(h12、h23、h11、h24)
扩展标签可自定义格式化器的数据展示方式,而不会改变语言或区域。
创建 locale 对象
Intl.Locale 构造函数接受一个 locale 标识符字符串,并返回一个结构化对象:
const locale = new Intl.Locale("en-US");
console.log(locale.language); // "en"
console.log(locale.region); // "US"
你还可以传递一个 options 对象来覆盖或添加属性:
const locale = new Intl.Locale("en", {
region: "GB",
hourCycle: "h23"
});
console.log(locale.baseName); // "en-GB"
console.log(locale.hourCycle); // "h23"
如果标识符无效,构造函数会抛出 RangeError:
try {
const invalid = new Intl.Locale("invalid-locale-code");
} catch (error) {
console.error(error.message); // "invalid language subtag: invalid"
}
此验证可确保你在将格式错误的 locale 标识符传递给格式化器之前及时发现问题。
读取 locale 属性
Locale 对象公开的属性对应于标识符字符串的各个组成部分。所有属性均为只读。
核心属性
language 属性返回语言代码:
const locale = new Intl.Locale("fr-CA");
console.log(locale.language); // "fr"
region 属性返回地区代码:
const locale = new Intl.Locale("fr-CA");
console.log(locale.region); // "CA"
如果存在,script 属性返回脚本代码:
const locale = new Intl.Locale("zh-Hans-CN");
console.log(locale.script); // "Hans"
baseName 属性返回不带扩展的完整核心标识符:
const locale = new Intl.Locale("en-US-u-ca-gregory-nu-latn");
console.log(locale.baseName); // "en-US"
当你只需要语言和地区而不关心格式偏好时,请使用 baseName。
扩展属性
扩展属性返回 -u- 扩展标签中的值,或在未指定时返回 undefined。
calendar 属性返回日历系统:
const locale = new Intl.Locale("ar-SA-u-ca-islamic");
console.log(locale.calendar); // "islamic"
numberingSystem 属性返回数字系统:
const locale = new Intl.Locale("ar-EG-u-nu-arab");
console.log(locale.numberingSystem); // "arab"
hourCycle 属性返回小时制偏好:
const locale = new Intl.Locale("en-US-u-hc-h23");
console.log(locale.hourCycle); // "h23"
caseFirst 属性返回排序规则的大小写偏好:
const locale = new Intl.Locale("en-US-u-kf-upper");
console.log(locale.caseFirst); // "upper"
numeric 属性指示是否启用了数字排序:
const locale = new Intl.Locale("en-US-u-kn-true");
console.log(locale.numeric); // true
这些属性可让你无需手动解析扩展字符串即可检查格式化偏好。
查询本地化信息
Intl.Locale API 提供返回某个 locale 可用选项数组的方法。这些方法有助于构建用户界面并校验格式化选项。
可用日历
getCalendars() 方法返回该 locale 常用的日历系统:
const locale = new Intl.Locale("th-TH");
const calendars = locale.getCalendars();
console.log(calendars); // ["buddhist", "gregory"]
第一个元素是默认日历。泰语 locale 默认使用佛历,但也支持公历。
可用排序规则
getCollations() 方法返回用于字符串排序的排序类型:
const locale = new Intl.Locale("de-DE");
const collations = locale.getCollations();
console.log(collations); // ["phonebk", "emoji", "eor"]
德语有电话簿排序规则,其字符串排序方式不同于标准 Unicode 排序。
可用小时制
getHourCycles() 方法返回小时制格式:
const locale = new Intl.Locale("en-US");
const hourCycles = locale.getHourCycles();
console.log(hourCycles); // ["h12"]
美式英语默认使用 12 小时制。许多其他 locale 返回 ["h23"] 以表示 24 小时制。
可用数字系统
getNumberingSystems() 方法返回该 locale 常用的数字系统:
const locale = new Intl.Locale("ar-EG");
const numberingSystems = locale.getNumberingSystems();
console.log(numberingSystems); // ["arab", "latn"]
阿拉伯语 locale 通常同时支持阿拉伯-印度数字和拉丁数字。
文字方向
getTextInfo() 方法返回文本排序信息:
const locale = new Intl.Locale("ar-SA");
const textInfo = locale.getTextInfo();
console.log(textInfo.direction); // "rtl"
像阿拉伯语和希伯来语这样的从右到左语言会返回 "rtl"。从左到右的语言会返回 "ltr"。
周规范
getWeekInfo() 方法返回该 locale 的周结构:
const locale = new Intl.Locale("en-US");
const weekInfo = locale.getWeekInfo();
console.log(weekInfo.firstDay); // 7 (Sunday)
console.log(weekInfo.weekend); // [6, 7] (Saturday, Sunday)
console.log(weekInfo.minimalDays); // 1
周规范因地区而异。美国的周从星期天开始,而大多数欧洲国家的周从星期一开始。
时区
getTimeZones() 方法返回该 locale 区域的时区:
const locale = new Intl.Locale("en-US");
const timeZones = locale.getTimeZones();
console.log(timeZones); // ["America/New_York", "America/Chicago", ...]
此方法返回该区域代码的 IANA 时区标识符。
最大化 locale 标识符
maximize() 方法会添加可能的子标签以生成完整标识符。它会根据语言数据推断缺失的脚本和区域代码。
当你只指定语言代码时,maximize() 会添加最常用的脚本和区域:
const locale = new Intl.Locale("en");
const maximized = locale.maximize();
console.log(maximized.baseName); // "en-Latn-US"
英语默认使用拉丁脚本和美国区域,因为这是最常见的组合。
中文的最大化取决于所用脚本:
const simplified = new Intl.Locale("zh-Hans");
const maximized = simplified.maximize();
console.log(maximized.baseName); // "zh-Hans-CN"
const traditional = new Intl.Locale("zh-Hant");
const maximizedTrad = traditional.maximize();
console.log(maximizedTrad.baseName); // "zh-Hant-TW"
简体中文通常关联中国,繁体中文则关联台湾。
在规范化用户输入或比较 locale 标识符时,建议使用 maximize()。它可以将隐含信息显式化。
最小化 locale 标识符
minimize() 方法会移除冗余的子标签,生成最短的等效标识符。当脚本和区域与默认值一致时,它会将其移除。
当 locale 使用默认脚本和区域时,minimize() 会将其移除:
const locale = new Intl.Locale("en-Latn-US");
const minimized = locale.minimize();
console.log(minimized.baseName); // "en"
拉丁脚本和美国区域是英语的默认值,因此可以移除而不会丢失信息。
对于具有非默认区域的 locale,minimize() 会保留区域信息:
const locale = new Intl.Locale("en-Latn-GB");
const minimized = locale.minimize();
console.log(minimized.baseName); // "en-GB"
英式英语需要区域代码,因为它与默认值不同。
使用 minimize() 可以在保留语义的同时,为存储或 URL 创建紧凑的标识符。
转换为字符串
toString() 方法会返回包含扩展的完整 locale 标识符:
const locale = new Intl.Locale("en-US", {
calendar: "gregory",
numberingSystem: "latn",
hourCycle: "h12"
});
console.log(locale.toString()); // "en-US-u-ca-gregory-hc-h12-nu-latn"
该字符串表示可用于传递给其他 Intl API 或作为配置数据存储。
你也可以将 locale 对象隐式转换为字符串:
const locale = new Intl.Locale("fr-FR");
const formatter = new Intl.DateTimeFormat(locale);
DateTimeFormat 构造函数可以直接接受 locale 对象,并在内部调用 toString()。
实用用例
Intl.Locale API 解决了构建国际化应用时的多个常见问题。
构建 locale 选择器
locale 选择器允许用户选择语言和区域。Intl.Locale API 有助于解析和校验用户选择:
function createLocaleSelector(locales) {
const options = locales.map(identifier => {
const locale = new Intl.Locale(identifier);
const displayName = new Intl.DisplayNames([identifier], { type: "language" });
return {
value: locale.toString(),
label: displayName.of(locale.language),
region: locale.region
};
});
return options;
}
const options = createLocaleSelector(["en-US", "en-GB", "fr-FR", "es-MX"]);
console.log(options);
// [
// { value: "en-US", label: "English", region: "US" },
// { value: "en-GB", label: "English", region: "GB" },
// { value: "fr-FR", label: "French", region: "FR" },
// { value: "es-MX", label: "Spanish", region: "MX" }
// ]
校验 locale 输入
当从用户输入或配置文件接收 locale 标识符时,请在使用前进行校验:
function validateLocale(identifier) {
try {
const locale = new Intl.Locale(identifier);
return {
valid: true,
locale: locale.toString(),
language: locale.language,
region: locale.region
};
} catch (error) {
return {
valid: false,
error: error.message
};
}
}
console.log(validateLocale("en-US")); // { valid: true, locale: "en-US", ... }
console.log(validateLocale("invalid")); // { valid: false, error: "..." }
提取语言用于回退
实现语言回退链时,提取不带区域的语言代码:
function getLanguageFallback(identifier) {
const locale = new Intl.Locale(identifier);
const fallbacks = [locale.toString()];
if (locale.region) {
const languageOnly = new Intl.Locale(locale.language);
fallbacks.push(languageOnly.toString());
}
fallbacks.push("en");
return fallbacks;
}
console.log(getLanguageFallback("fr-CA"));
// ["fr-CA", "fr", "en"]
这样可以创建一个回退链,先尝试特定 locale,再尝试不带区域的语言,最后使用默认语言。
配置格式化器
使用 locale 对象为 Intl 格式化器配置特定偏好:
function createFormatter(baseLocale, options = {}) {
const locale = new Intl.Locale(baseLocale, {
calendar: options.calendar || "gregory",
numberingSystem: options.numberingSystem || "latn",
hourCycle: options.hourCycle || "h23"
});
return {
date: new Intl.DateTimeFormat(locale),
number: new Intl.NumberFormat(locale),
locale: locale.toString()
};
}
const formatter = createFormatter("ar-SA", {
calendar: "islamic",
numberingSystem: "arab"
});
console.log(formatter.locale); // "ar-SA-u-ca-islamic-nu-arab"
检测用户偏好
浏览器 API 提供了可解析的 locale 字符串,用于了解用户偏好:
function getUserPreferences() {
const userLocale = new Intl.Locale(navigator.language);
const hourCycles = userLocale.getHourCycles();
const calendars = userLocale.getCalendars();
const textInfo = userLocale.getTextInfo();
return {
language: userLocale.language,
region: userLocale.region,
preferredHourCycle: hourCycles[0],
preferredCalendar: calendars[0],
textDirection: textInfo.direction
};
}
const preferences = getUserPreferences();
console.log(preferences);
// { language: "en", region: "US", preferredHourCycle: "h12", ... }
这会根据用户的浏览器设置创建其偏好配置文件。
浏览器支持
Intl.Locale API 在所有现代浏览器中均可用。Chrome、Firefox、Safari 和 Edge 完全支持本指南中描述的构造函数、属性和方法。
像 getCalendars()、getCollations()、getHourCycles()、getNumberingSystems()、getTextInfo()、getTimeZones() 和 getWeekInfo() 这样的新方法需要较新的浏览器版本。如果需要支持旧版浏览器,请在使用这些方法前查阅浏览器兼容性表。
Node.js 从 12 版本开始支持 Intl.Locale API,18 及以上版本支持全部方法。
总结
Intl.Locale API 可将 locale 标识符字符串转换为可供检查和操作的结构化对象。无需手动解析字符串,只需创建 locale 对象并读取其属性。
核心概念:
- locale 标识符包含语言、脚本、区域和扩展部分
Intl.Locale构造函数会验证标识符并创建对象language、region和calendar等属性可结构化访问getCalendars()和getHourCycles()等方法返回可用选项maximize()和minimize()方法可规范化标识符- locale 对象可直接与其他
IntlAPI 协同工作
在构建 locale 选择器、验证用户输入、实现回退链或配置格式化器时,建议使用 Intl.Locale API。它为 JavaScript 应用程序中处理 locale 标识符提供了标准化方式。