如何移除区域设置标识符中的冗余信息
使用 minimize 方法创建精简且不丢失语义的区域设置标识符
简介
像 en-Latn-US 和 zh-Hans-CN 这样的区域设置标识符包含多个组件,用于指定语言、书写系统和地区。但并非所有组件都是唯一标识一个 locale 所必需的。有些组件是冗余的,因为它们可以从标识符的其他部分推断出来。
minimize() 方法会移除这些冗余组件,从而生成最短等价的区域设置标识符。这样可以在保留语义的同时,减少存储空间并提升可读性。
理解区域设置标识符中的冗余
当区域设置标识符中明确声明的信息已经被其他组件隐含时,就会出现冗余。冗余的原因在于每种语言通常都有默认的书写系统和地区。
以标识符 en-Latn-US 为例。该标识符包含:
- 语言:英语(
en) - 书写系统:拉丁(
Latn) - 地区:美国(
US)
英语只使用拉丁书写系统,并且在未指定地区时,默认地区为美国。因此,书写系统和地区组件是冗余的,因为它们与英语的默认值一致。标识符 en 已经表达了相同的信息。
同样的原则也适用于其他语言。例如,韩语(ko)使用谚文书写系统(Kore),主要在韩国(KR)使用。标识符 ko-Kore-KR 包含了冗余信息,因为仅 ko 就已隐含这些默认值。
minimize 方法的工作原理
minimize() 方法可用于 Intl.Locale 实例。它会分析区域设置标识符,并移除与可能默认值相符的组件。
const locale = new Intl.Locale("en-Latn-US");
const minimized = locale.minimize();
console.log(minimized.baseName);
// Output: "en"
该方法返回一个新的 Intl.Locale 实例,并移除冗余的子标签。它不会修改原始的 locale 对象。
最小化过程遵循 Unicode CLDR 的“移除可能的子标签”算法。该算法利用可能子标签关联数据库,判断哪些组件可以在不丢失信息的前提下被移除。
受 minimize 影响的组件
minimize() 方法仅影响 locale 的核心组件:语言、脚本和地区。它不会移除或修改指定格式偏好的 Unicode 扩展子标签。
const locale = new Intl.Locale("en-Latn-US-u-ca-gregory-nu-latn");
const minimized = locale.minimize();
console.log(minimized.toString());
// Output: "en-u-ca-gregory-nu-latn"
日历(ca-gregory)和数字系统(nu-latn)扩展保持不变。仅冗余的脚本(Latn)和地区(US)组件会被移除。
最小化示例
不同的 locale 标识符根据冗余组件的不同,最小化后长度也不同。
移除脚本和地区
当脚本和地区都与默认值匹配时,两者都会被移除:
const english = new Intl.Locale("en-Latn-US");
console.log(english.minimize().baseName);
// Output: "en"
const korean = new Intl.Locale("ko-Kore-KR");
console.log(korean.minimize().baseName);
// Output: "ko"
const japanese = new Intl.Locale("ja-Jpan-JP");
console.log(japanese.minimize().baseName);
// Output: "ja"
保留非默认地区
当地区与默认值不同,则会在最小化标识符中保留:
const britishEnglish = new Intl.Locale("en-Latn-GB");
console.log(britishEnglish.minimize().baseName);
// Output: "en-GB"
const canadianFrench = new Intl.Locale("fr-Latn-CA");
console.log(canadianFrench.minimize().baseName);
// Output: "fr-CA"
const mexicanSpanish = new Intl.Locale("es-Latn-MX");
console.log(mexicanSpanish.minimize().baseName);
// Output: "es-MX"
脚本组件因与默认值匹配被移除,但地区被保留,因为它指定了该语言的非默认变体。
保留非默认脚本
当脚本与默认值不同,则会在最小化标识符中保留:
const simplifiedChinese = new Intl.Locale("zh-Hans-CN");
console.log(simplifiedChinese.minimize().baseName);
// Output: "zh-Hans"
const traditionalChinese = new Intl.Locale("zh-Hant-TW");
console.log(traditionalChinese.minimize().baseName);
// Output: "zh-Hant"
const serbianCyrillic = new Intl.Locale("sr-Cyrl-RS");
console.log(serbianCyrillic.minimize().baseName);
// Output: "sr-Cyrl"
中文需要脚本组件以区分简体和繁体变体。塞尔维亚语需要脚本组件以区分西里尔和拉丁字母。
已经是最小化的标识符
当 locale 标识符已经是最小化状态时,该方法返回等效的 locale,不做任何更改:
const minimal = new Intl.Locale("fr");
console.log(minimal.minimize().baseName);
// Output: "fr"
与 maximize 的关系
minimize() 方法是 maximize() 的逆操作。maximize() 方法会添加可能的子标签以生成完整标识符,而 minimize() 则移除冗余子标签以生成紧凑标识符。
这些方法构成了一对,可以在完整形式和紧凑形式之间进行双向转换:
const compact = new Intl.Locale("en");
const complete = compact.maximize();
console.log(complete.baseName);
// Output: "en-Latn-US"
const compactAgain = complete.minimize();
console.log(compactAgain.baseName);
// Output: "en"
从紧凑到完整再回到紧凑的往返操作会生成原始形式。
但并非所有区域标识符在往返后都能恢复到完全相同的原始形式。该方法会生成规范的最小化形式,而不是保留原始结构:
const locale = new Intl.Locale("en-US");
const maximized = locale.maximize();
console.log(maximized.baseName);
// Output: "en-Latn-US"
const minimized = maximized.minimize();
console.log(minimized.baseName);
// Output: "en"
原始标识符 en-US 包含了一个非冗余的区域,但经过最大化和最小化后,变为 en。这是因为美国是 English 的默认区域。
何时使用 minimize
当你需要保持唯一性的紧凑区域标识符时,建议使用 minimize()。多种场景都适合最小化处理。
存储区域偏好
最小化标识符可减少数据库、本地存储或配置文件中的存储空间:
function saveUserLocale(localeString) {
const locale = new Intl.Locale(localeString);
const minimized = locale.minimize().toString();
localStorage.setItem("userLocale", minimized);
}
saveUserLocale("en-Latn-US");
// Stores "en" instead of "en-Latn-US"
这样可以在不丢失信息的情况下减少存储数据量。
创建可读性更高的 URL
最小化标识符可生成更简洁的语言选择 URL:
function createLocalizedURL(path, localeString) {
const locale = new Intl.Locale(localeString);
const minimized = locale.minimize().baseName;
return `/${minimized}${path}`;
}
const url = createLocalizedURL("/products", "en-Latn-US");
console.log(url);
// Output: "/en/products"
URL /en/products 比 /en-Latn-US/products 更易读。
比较区域标识符
最小化有助于判断两个区域标识符是否表示同一地区:
function areLocalesEquivalent(locale1String, locale2String) {
const locale1 = new Intl.Locale(locale1String).minimize();
const locale2 = new Intl.Locale(locale2String).minimize();
return locale1.toString() === locale2.toString();
}
console.log(areLocalesEquivalent("en", "en-Latn-US"));
// Output: true
console.log(areLocalesEquivalent("en-US", "en-Latn-US"));
// Output: true
console.log(areLocalesEquivalent("en-US", "en-GB"));
// Output: false
最小化会生成规范形式,便于直接比较。
规范化用户输入
当接收用户或外部系统的区域标识符时,可将其最小化为标准形式:
function normalizeLocale(localeString) {
try {
const locale = new Intl.Locale(localeString);
return locale.minimize().toString();
} catch (error) {
return null;
}
}
console.log(normalizeLocale("en-US"));
// Output: "en"
console.log(normalizeLocale("en-Latn-US"));
// Output: "en"
console.log(normalizeLocale("en-GB"));
// Output: "en-GB"
该函数可接受同一地区的多种形式,并返回一致的表示。
将 minimize 与其他区域操作结合使用
minimize() 方法可与其他 Intl.Locale 功能配合使用,实现灵活的 locale 处理。
修改 locale 属性后进行最小化
从各个组件构建 locale 时,建议进行最小化以移除不必要的部分:
const locale = new Intl.Locale("en", {
region: "US",
script: "Latn"
});
const minimized = locale.minimize();
console.log(minimized.baseName);
// Output: "en"
这样可以确保最终的标识符尽可能精简,仅包含输入组件允许的内容。
最小化时保留扩展
在最小化过程中,扩展部分会被保留,这样你可以在精简核心组件的同时保留格式偏好设置:
function createCompactLocaleWithPreferences(language, region, preferences) {
const locale = new Intl.Locale(language, {
region: region,
...preferences
});
return locale.minimize().toString();
}
const localeString = createCompactLocaleWithPreferences("en", "US", {
hourCycle: "h23",
calendar: "gregory"
});
console.log(localeString);
// Output: "en-u-ca-gregory-hc-h23"
核心组件会被最小化为 en,但 calendar 和 hour cycle 扩展仍然保留。