如何从语言环境标识符中移除冗余信息

使用 minimize 方法创建紧凑的语言环境标识符,同时保留其含义

介绍

en-Latn-USzh-Hans-CN 这样的区域标识符包含多个组件,用于指定语言、文字和地区。然而,并非所有这些组件都是识别一个区域所必需的。一些组件是冗余的,因为它们可以从标识符的其他部分推断出来。

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);
// 输出:"en"

该方法返回一个新的 Intl.Locale 实例,其中冗余的子标签已被移除。它不会修改原始的区域对象。

最小化过程遵循 Unicode CLDR 的“移除可能子标签”算法。该算法使用一个可能子标签关联的数据库来确定哪些组件可以在不丢失信息的情况下被移除。

受 minimize 影响的组件

minimize() 方法仅影响核心语言环境组件:语言、书写系统和地区。它不会移除或修改指定格式偏好的 Unicode 扩展子标签。

const locale = new Intl.Locale("en-Latn-US-u-ca-gregory-nu-latn");
const minimized = locale.minimize();

console.log(minimized.toString());
// 输出: "en-u-ca-gregory-nu-latn"

日历(ca-gregory)和数字系统(nu-latn)扩展保持不变。仅冗余的书写系统(Latn)和地区(US)组件被移除。

最小化示例

不同的语言环境标识符根据冗余组件的不同,最小化后的长度也不同。

移除书写系统和地区

当书写系统和地区都与默认值匹配时,两者都会被移除:

const english = new Intl.Locale("en-Latn-US");
console.log(english.minimize().baseName);
// 输出: "en"

const korean = new Intl.Locale("ko-Kore-KR");
console.log(korean.minimize().baseName);
// 输出: "ko"

const japanese = new Intl.Locale("ja-Jpan-JP");
console.log(japanese.minimize().baseName);
// 输出: "ja"

保留非默认地区

当地区与默认值不同,它会保留在最小化的标识符中:

const britishEnglish = new Intl.Locale("en-Latn-GB");
console.log(britishEnglish.minimize().baseName);
// 输出: "en-GB"

const canadianFrench = new Intl.Locale("fr-Latn-CA");
console.log(canadianFrench.minimize().baseName);
// 输出: "fr-CA"

const mexicanSpanish = new Intl.Locale("es-Latn-MX");
console.log(mexicanSpanish.minimize().baseName);
// 输出: "es-MX"

书写系统组件被移除,因为它与默认值匹配,但地区被保留,因为它指定了语言的非默认变体。

保留非默认书写系统

当书写系统与默认值不同,它会保留在最小化的标识符中:

const simplifiedChinese = new Intl.Locale("zh-Hans-CN");
console.log(simplifiedChinese.minimize().baseName);
// 输出: "zh-Hans"

const traditionalChinese = new Intl.Locale("zh-Hant-TW");
console.log(traditionalChinese.minimize().baseName);
// 输出: "zh-Hant"

const serbianCyrillic = new Intl.Locale("sr-Cyrl-RS");
console.log(serbianCyrillic.minimize().baseName);
// 输出: "sr-Cyrl"

中文需要书写系统组件来区分简体和繁体变体。塞尔维亚语需要书写系统组件来区分西里尔字母和拉丁字母。

已经是最小化的标识符

当区域标识符已经是最小化形式时,该方法会返回等效的区域标识符而不做任何更改:

const minimal = new Intl.Locale("fr");
console.log(minimal.minimize().baseName);
// 输出: "fr"

与 maximize 的关系

minimize() 方法是 maximize() 的逆操作。maximize() 方法通过添加可能的子标签来创建完整的标识符,而 minimize() 方法通过移除冗余的子标签来创建紧凑的标识符。

这两个方法形成了一对,可以在完整形式和紧凑形式之间进行双向转换:

const compact = new Intl.Locale("en");
const complete = compact.maximize();
console.log(complete.baseName);
// 输出: "en-Latn-US"

const compactAgain = complete.minimize();
console.log(compactAgain.baseName);
// 输出: "en"

从紧凑形式到完整形式再回到紧凑形式的往返操作会生成原始形式。

然而,并非所有区域标识符在往返后都能返回到其完全原始的形式。该方法会生成规范的最小化形式,而不是保留原始结构:

const locale = new Intl.Locale("en-US");
const maximized = locale.maximize();
console.log(maximized.baseName);
// 输出: "en-Latn-US"

const minimized = maximized.minimize();
console.log(minimized.baseName);
// 输出: "en"

原始标识符 en-US 包含了一个非冗余的区域,但在最大化和最小化之后,它变成了 en。这是因为美国是英语的可能默认区域。

何时使用 minimize

当您需要紧凑但仍然明确的区域标识符时,可以使用 minimize()。在多种场景下,最小化操作都能带来好处。

存储区域偏好

最小化标识符可以减少数据库、本地存储或配置文件中的存储空间:

function saveUserLocale(localeString) {
  const locale = new Intl.Locale(localeString);
  const minimized = locale.minimize().toString();

  localStorage.setItem("userLocale", minimized);
}

saveUserLocale("en-Latn-US");
// 存储 "en" 而不是 "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);
// 输出: "/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"));
// 输出: true

console.log(areLocalesEquivalent("en-US", "en-Latn-US"));
// 输出: true

console.log(areLocalesEquivalent("en-US", "en-GB"));
// 输出: false

最小化生成一种规范形式,从而实现直接比较。

标准化用户输入

在接受用户或外部系统的语言环境标识符时,将其最小化为标准形式:

function normalizeLocale(localeString) {
  try {
    const locale = new Intl.Locale(localeString);
    return locale.minimize().toString();
  } catch (error) {
    return null;
  }
}

console.log(normalizeLocale("en-US"));
// 输出: "en"

console.log(normalizeLocale("en-Latn-US"));
// 输出: "en"

console.log(normalizeLocale("en-GB"));
// 输出: "en-GB"

此函数接受同一语言环境的各种形式,并返回一致的表示。

将最小化与其他语言环境操作结合使用

minimize() 方法可以与其他 Intl.Locale 功能结合使用,以创建灵活的语言环境处理。

修改语言环境属性后进行最小化

从组件构造语言环境时,最小化它以移除不必要的部分:

const locale = new Intl.Locale("en", {
  region: "US",
  script: "Latn"
});

const minimized = locale.minimize();
console.log(minimized.baseName);
// 输出: "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);
// 输出: "en-u-ca-gregory-hc-h23"

核心组件被最小化为 en,但日历和小时周期扩展部分保持不变。