如何检查语言环境标识符是否有效

在使用语言环境标识符格式化日期、数字和货币之前进行验证

介绍

当您的应用程序接受来自用户输入、API 响应或配置文件的区域标识符时,您需要在使用它们之前验证它们是否有效。无效的区域标识符会导致格式化函数抛出错误或产生意外结果。

验证可以确保像 en-US 这样的字符串在结构上符合国际标准。这可以防止在将区域标识符传递给 Intl API 或其他国际化库时发生运行时错误。

JavaScript 提供了两种内置方法来验证区域标识符。这两种方法都会根据 BCP 47 标准检查标识符的结构,该标准定义了语言标签的格式。

什么是有效的区域标识符

区域标识符遵循 BCP 47 标准的语言标签格式。该标准定义了语言、脚本、地区和扩展组件的结构和组合规则。

有效的区域标识符使用连字符分隔组件,而不是下划线或其他字符。语言代码必须是公认的 ISO 639 代码,地区代码必须是公认的 ISO 3166-1 代码。

有效的区域标识符示例:

  • en(英语)
  • en-US(美式英语)
  • zh-Hans(简体中文)
  • zh-Hans-CN(中国使用的简体中文)
  • en-US-u-ca-gregory(使用公历的美式英语)

无效的区域标识符示例:

  • en_US(使用下划线而非连字符)
  • english(不是公认的语言代码)
  • en-USA(地区代码必须是两个字母,而不是三个)
  • EN-us(语言代码必须是小写)
  • abc-XY(语言代码不存在)

验证方法会检查这些结构规则,并验证代码是否符合公认的标准。

使用 Intl.getCanonicalLocales 进行验证

Intl.getCanonicalLocales() 方法用于验证语言环境标识符,并以其规范形式返回。此方法接受单个语言环境标识符或标识符数组。

const result = Intl.getCanonicalLocales("en-US");
console.log(result);
// 输出: ["en-US"]

该方法通过将输入转换为标准格式来进行规范化。即使您传递了大小写不正确的语言环境标识符,它也会返回正确的规范形式:

const result = Intl.getCanonicalLocales("EN-us");
console.log(result);
// 输出: ["en-US"]

如果您传递了无效的语言环境标识符,该方法会抛出一个 RangeError

try {
  Intl.getCanonicalLocales("en_US");
} catch (error) {
  console.error(error.name);
  // 输出: "RangeError"
  console.error(error.message);
  // 输出: "en_US is not a structurally valid language tag"
}

此错误表明标识符未遵循 BCP 47 结构。您可以捕获此错误以优雅地处理无效输入。

验证多个语言环境标识符

Intl.getCanonicalLocales() 方法接受一个数组,用于一次性验证多个语言环境标识符。该方法返回所有有效标识符的规范形式数组。

const locales = ["en-US", "fr-FR", "es-MX"];
const result = Intl.getCanonicalLocales(locales);
console.log(result);
// 输出: ["en-US", "fr-FR", "es-MX"]

该方法还会从结果中移除重复的语言环境标识符:

const locales = ["en-US", "EN-us", "en-US"];
const result = Intl.getCanonicalLocales(locales);
console.log(result);
// 输出: ["en-US"]

以上三个输入值表示相同的语言环境,因此该方法仅返回一个规范形式。

如果数组中的任何语言环境无效,整个方法会抛出错误:

try {
  Intl.getCanonicalLocales(["en-US", "invalid", "fr-FR"]);
} catch (error) {
  console.error(error.message);
  // 输出: "invalid is not a structurally valid language tag"
}

在验证用户提供的列表时,请单独验证每个语言环境,以识别具体的无效标识符。

使用 Intl.Locale 构造函数进行验证

Intl.Locale 构造函数提供了另一种验证语言环境标识符的方法。当您使用无效的标识符创建语言环境对象时,构造函数会抛出一个 RangeError

try {
  const locale = new Intl.Locale("en-US");
  console.log("有效的语言环境");
} catch (error) {
  console.error("无效的语言环境");
}
// 输出: "有效的语言环境"

对于无效的标识符,构造函数会抛出错误:

try {
  const locale = new Intl.Locale("en_US");
  console.log("有效的语言环境");
} catch (error) {
  console.error("无效的语言环境");
  console.error(error.message);
}
// 输出: "无效的语言环境"
// 输出: "invalid language subtag: en_US"

Intl.getCanonicalLocales() 不同,Intl.Locale 构造函数不会规范化大小写。它要求标识符使用正确的大小写:

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

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

大写和小写的语言代码都被接受,构造函数会将它们规范化为标准形式。

选择验证方法

两种验证方法具有不同的用途和功能。

当您需要以下操作时,请使用 Intl.getCanonicalLocales()

  • 将语言环境标识符规范化为其标准形式
  • 验证并去重语言环境标识符列表
  • 将不一致的大小写转换为标准格式
  • 在不创建对象的情况下验证语言环境标识符

当您需要以下操作时,请使用 Intl.Locale 构造函数:

  • 验证语言环境标识符并立即使用其属性
  • 从标识符中提取语言、地区或脚本等组件
  • 创建可与其他 Intl API 一起使用的语言环境对象
  • 同时验证和操作语言环境标识符

Intl.Locale 构造函数更强大,因为它会创建一个可以查询和修改的对象。然而,如果您只需要验证和规范化,Intl.getCanonicalLocales() 更简单。

验证用户输入

在接受用户输入的区域标识符时,请在将其用于应用程序之前进行验证。这可以防止错误并在用户输入无效值时提供清晰的反馈。

function validateUserLocale(input) {
  try {
    const canonicalLocales = Intl.getCanonicalLocales(input);
    return {
      valid: true,
      locale: canonicalLocales[0]
    };
  } catch (error) {
    return {
      valid: false,
      error: "请输入有效的区域标识符,例如 en-US 或 fr-FR"
    };
  }
}

const result1 = validateUserLocale("en-US");
console.log(result1);
// 输出: { valid: true, locale: "en-US" }

const result2 = validateUserLocale("english");
console.log(result2);
// 输出: { valid: false, error: "请输入有效的区域标识符..." }

此函数验证输入并返回规范化的区域标识符或用户友好的错误消息。

验证配置数据

应用程序通常从配置文件或环境变量中加载区域标识符。在应用程序启动时验证这些值,以便及早发现配置错误。

function loadLocaleConfig(configLocales) {
  const validLocales = [];
  const invalidLocales = [];

  for (const locale of configLocales) {
    try {
      const canonical = Intl.getCanonicalLocales(locale);
      validLocales.push(canonical[0]);
    } catch (error) {
      invalidLocales.push(locale);
    }
  }

  if (invalidLocales.length > 0) {
    console.warn("发现无效的区域标识符:", invalidLocales);
  }

  return validLocales;
}

const config = ["en-US", "fr-FR", "invalid", "es_MX", "de-DE"];
const locales = loadLocaleConfig(config);
console.log(locales);
// 输出: ["en-US", "fr-FR", "de-DE"]
// 警告: 发现无效的区域标识符: ["invalid", "es_MX"]

此函数过滤掉无效的区域标识符并记录警告,从而允许应用程序继续使用有效的区域标识符。

验证 API 响应

在从外部 API 接收区域标识符时,请在将其用于应用程序之前进行验证。API 可能会返回非标准格式或包含错误的区域标识符。

function processApiLocale(apiResponse) {
  const locale = apiResponse.preferredLocale;

  try {
    const validated = new Intl.Locale(locale);
    return {
      success: true,
      language: validated.language,
      region: validated.region,
      baseName: validated.baseName
    };
  } catch (error) {
    console.error("API 返回了无效的区域标识符:", locale);
    return {
      success: false,
      fallback: "en-US"
    };
  }
}

const response1 = { preferredLocale: "fr-CA" };
console.log(processApiLocale(response1));
// 输出: { success: true, language: "fr", region: "CA", baseName: "fr-CA" }

const response2 = { preferredLocale: "invalid_locale" };
console.log(processApiLocale(response2));
// 输出: { success: false, fallback: "en-US" }

此函数验证 API 响应并提取有关区域的结构化信息,或者在区域无效时提供回退值。