如何获取支持的日历、货币和时区列表

了解您的 JavaScript 环境支持哪些国际化值

介绍

在构建国际化应用程序时,您经常需要创建用户界面元素,例如下拉菜单、选择器或表单,让用户选择日历、货币、时区或其他本地化选项。为了构建这些组件,您需要知道 JavaScript 环境支持哪些值。

与其维护可能过时或包含浏览器不支持值的硬编码列表,不如使用 JavaScript 提供的方法在运行时发现支持的值。这可以确保您的应用程序仅提供在用户环境中能正确运行的选项。

Intl.supportedValuesOf() 方法返回支持的国际化值列表。您可以通过查询此方法获取 JavaScript 环境支持的日历、货币、时区、数字系统、排序类型和测量单位。

Intl.supportedValuesOf 返回的内容

Intl.supportedValuesOf() 方法接受一个字符串参数,用于指定返回哪种类型的值。它返回一个字符串数组,表示该类型支持的值。

const calendars = Intl.supportedValuesOf("calendar");
console.log(calendars);
// 输出: ["buddhist", "chinese", "coptic", "dangi", ...]

该方法支持六种不同的键类型:

  • "calendar" 返回支持的日历系统
  • "currency" 返回支持的货币代码
  • "timeZone" 返回支持的时区标识符
  • "numberingSystem" 返回支持的数字系统
  • "collation" 返回支持的排序类型
  • "unit" 返回支持的测量单位

返回的数组始终按字母升序排序,不包含重复值,并使用符合 Unicode 标准的规范标识符。

如果传递了无效的键,该方法会抛出一个 RangeError

try {
  Intl.supportedValuesOf("invalid");
} catch (error) {
  console.error(error.name);
  // 输出: "RangeError"
}

此错误表明您使用了该方法无法识别的键。

获取支持的日历

日历系统定义了日期的计算和显示方式。不同的文化使用不同的日历系统,JavaScript 通过 Intl API 支持许多这些系统。

"calendar" 键返回 JavaScript 环境支持的所有日历系统:

const calendars = Intl.supportedValuesOf("calendar");
console.log(calendars);
// 输出: ["buddhist", "chinese", "coptic", "dangi", "ethioaa",
//          "ethiopic", "gregory", "hebrew", "indian", "islamic",
//          "islamic-civil", "islamic-rgsa", "islamic-tbla",
//          "islamic-umalqura", "iso8601", "japanese", "persian",
//          "roc"]

最常见的日历是 "gregory",它表示世界大部分地区使用的公历。其他日历包括:

  • "buddhist" 表示泰国佛教日历
  • "chinese" 表示传统的中国农历
  • "islamic" 表示伊斯兰教的回历
  • "hebrew" 表示希伯来日历
  • "japanese" 表示带有年号的日本日历

每种日历都会影响日期的格式化和计算方式。当您使用带有特定日历的 Intl.DateTimeFormat 时,日期会根据该日历的规则显示:

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

const gregorianFormat = new Intl.DateTimeFormat("en-US", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const islamicFormat = new Intl.DateTimeFormat("en-US", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(gregorianFormat.format(date));
// 输出: "October 15, 2025"

console.log(islamicFormat.format(date));
// 输出: "Rabi' II 13, 1447 AH"

同一个 JavaScript 日期在使用不同日历格式化时会显示不同的结果。

使用支持的日历构建选择器

在用户界面中构建日历选择器时,可以查询支持的日历并为每个日历创建选项:

const calendars = Intl.supportedValuesOf("calendar");

const select = document.createElement("select");

calendars.forEach(calendar => {
  const option = document.createElement("option");
  option.value = calendar;
  option.textContent = calendar;
  select.appendChild(option);
});

document.body.appendChild(select);

这将创建一个下拉菜单,其中仅包含当前环境中可用的日历。

您可以通过使用 Intl.DisplayNames 来显示人类可读的日历名称,从而增强此选择器:

const calendars = Intl.supportedValuesOf("calendar");
const displayNames = new Intl.DisplayNames(["en-US"], { type: "calendar" });

calendars.forEach(calendar => {
  const option = document.createElement("option");
  option.value = calendar;
  option.textContent = displayNames.of(calendar);
  // 创建诸如 "Gregorian Calendar"、"Islamic Calendar" 等选项。
});

这将显示描述性名称,而不是技术标识符。

获取支持的货币

货币代码用于标识全球范围内使用的货币系统。JavaScript 支持基于 ISO 4217 标准的数百种货币代码。

"currency" 键返回 JavaScript 环境支持的所有货币代码:

const currencies = Intl.supportedValuesOf("currency");
console.log(currencies.length);
// 输出:通常超过 300

console.log(currencies.slice(0, 10));
// 输出:["ADP", "AED", "AFA", "AFN", "ALK", "ALL", "AMD", "ANG", "AOA", "AOK"]

货币代码由三个大写字母组成。常见示例包括:

  • "USD" 表示美元
  • "EUR" 表示欧元
  • "GBP" 表示英镑
  • "JPY" 表示日元
  • "CNY" 表示人民币

该列表包括当前流通的货币以及一些已停止流通的历史货币。

在格式化货币值时,您必须指定使用哪种货币:

const amount = 1234.56;

const usdFormat = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

const eurFormat = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "EUR"
});

console.log(usdFormat.format(amount));
// 输出:"$1,234.56"

console.log(eurFormat.format(amount));
// 输出:"€1,234.56"

货币代码决定了显示的符号以及值的格式。

为用户选择过滤货币

大多数应用程序只需要显示当前常用的货币。您可以过滤支持的货币列表,仅包含对您的用户相关的货币:

const allCurrencies = Intl.supportedValuesOf("currency");
const commonCurrencies = ["USD", "EUR", "GBP", "JPY", "CNY", "INR", "CAD", "AUD"];

const availableCurrencies = commonCurrencies.filter(currency =>
  allCurrencies.includes(currency)
);

console.log(availableCurrencies);
// 输出:["USD", "EUR", "GBP", "JPY", "CNY", "INR", "CAD", "AUD"]

这可以确保您只提供既满足需求又受浏览器支持的货币。

显示货币名称

在构建货币选择器时,显示描述性名称而不是三字母代码:

const currencies = ["USD", "EUR", "GBP", "JPY"];
const displayNames = new Intl.DisplayNames(["en-US"], { type: "currency" });

currencies.forEach(currency => {
  const name = displayNames.of(currency);
  console.log(`${currency}: ${name}`);
});
// 输出:
// USD: US Dollar
// EUR: Euro
// GBP: British Pound
// JPY: Japanese Yen

通过显示每个代码所代表的内容,可以提供更好的用户体验。

获取支持的时区

时区表示观察相同标准时间的地理区域。JavaScript 支持基于 IANA 时区数据库的数百个时区标识符。

"timeZone" 键返回 JavaScript 环境支持的所有时区标识符:

const timeZones = Intl.supportedValuesOf("timeZone");
console.log(timeZones.length);
// 输出:通常超过 400

console.log(timeZones.slice(0, 10));
// 输出:["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa",
//          "Africa/Algiers", "Africa/Asmara", "Africa/Bamako",
//          "Africa/Bangui", "Africa/Banjul", "Africa/Bissau", "Africa/Blantyre"]

时区标识符遵循 Continent/City 格式,例如 "America/New_York""Europe/London"。某些标识符包含额外的组件,例如 "America/Indiana/Indianapolis"

在格式化日期和时间时,时区会影响显示的本地时间:

const date = new Date("2025-10-15T12:00:00Z");

const newYorkFormat = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric",
  timeZoneName: "short"
});

const tokyoFormat = new Intl.DateTimeFormat("en-US", {
  timeZone: "Asia/Tokyo",
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric",
  timeZoneName: "short"
});

console.log(newYorkFormat.format(date));
// 输出:"2025年10月15日 上午8:00 EDT"

console.log(tokyoFormat.format(date));
// 输出:"2025年10月15日 下午9:00 JST"

同一时刻在不同的时区会显示为不同的本地时间。

构建时区选择器

时区选择器需要以用户可以理解的方式呈现数百个选项。通过按区域对时区进行分组,可以使选择更加容易:

const timeZones = Intl.supportedValuesOf("timeZone");

const grouped = timeZones.reduce((groups, tz) => {
  const [region] = tz.split("/");
  if (!groups[region]) {
    groups[region] = [];
  }
  groups[region].push(tz);
  return groups;
}, {});

console.log(Object.keys(grouped));
// 输出: ["Africa", "America", "Antarctica", "Arctic", "Asia",
//          "Atlantic", "Australia", "Europe", "Indian", "Pacific", "Etc"]

这种组织方式按大陆对时区进行分组,使用户更容易找到其位置。

您可以通过显示每个时区的当前偏移量进一步改善用户体验:

function getTimeZoneOffset(timeZone) {
  const date = new Date();
  const formatter = new Intl.DateTimeFormat("en-US", {
    timeZone,
    timeZoneName: "shortOffset"
  });
  const parts = formatter.formatToParts(date);
  const offsetPart = parts.find(part => part.type === "timeZoneName");
  return offsetPart ? offsetPart.value : "";
}

const timeZones = ["America/New_York", "Europe/London", "Asia/Tokyo"];

timeZones.forEach(tz => {
  const offset = getTimeZoneOffset(tz);
  console.log(`${tz}: ${offset}`);
});
// 输出:
// America/New_York: GMT-4
// Europe/London: GMT+1
// Asia/Tokyo: GMT+9

显示偏移量有助于用户理解时区之间的时间差异。

获取支持的数字系统

数字系统定义了数字的显示方式。虽然大多数应用程序使用标准的阿拉伯数字(0-9),但许多语言有其自己的传统数字系统。

"numberingSystem" 键返回 JavaScript 环境支持的所有数字系统:

const numberingSystems = Intl.supportedValuesOf("numberingSystem");
console.log(numberingSystems.slice(0, 10));
// 输出: ["adlm", "ahom", "arab", "arabext", "armn", "armnlow",
//          "bali", "beng", "bhks", "brah"]

常见的数字系统包括:

  • "latn" 表示标准拉丁数字(0-9)
  • "arab" 表示阿拉伯-印度数字
  • "thai" 表示泰国数字
  • "deva" 表示印地语中使用的天城文数字

不同的数字系统以不同的数字形状显示相同的数字:

const number = 12345;

const latinFormat = new Intl.NumberFormat("en-US", {
  numberingSystem: "latn"
});

const arabFormat = new Intl.NumberFormat("en-US", {
  numberingSystem: "arab"
});

const thaiFormat = new Intl.NumberFormat("en-US", {
  numberingSystem: "thai"
});

console.log(latinFormat.format(number));
// 输出: "12,345"

console.log(arabFormat.format(number));
// 输出: "١٢٬٣٤٥"

console.log(thaiFormat.format(number));
// 输出: "๑๒,๓๔๕"

相同的数值以不同的数字形状显示。

获取支持的排序类型

排序类型定义了字符串的排序和比较方式。不同语言对字母顺序有不同的规则。

"collation" 键返回 JavaScript 环境支持的所有排序类型:

const collations = Intl.supportedValuesOf("collation");
console.log(collations.slice(0, 10));
// 输出: ["big5han", "compat", "dict", "direct", "ducet", "emoji",
//          "eor", "gb2312", "phonebk", "phonetic"]

常见的排序类型包括:

  • "standard" 表示每种语言的默认排序
  • "phonetic" 表示语音排序
  • "pinyin" 表示中文拼音排序
  • "emoji" 表示表情符号排序

排序会影响字符串数组的排序方式:

const words = ["ä", "z", "a"];

const standardCollator = new Intl.Collator("de-DE", {
  collation: "standard"
});

const phoneticCollator = new Intl.Collator("de-DE", {
  collation: "phonetic"
});

console.log(words.sort(standardCollator.compare));
// 输出: ["a", "ä", "z"]

console.log(words.sort(phoneticCollator.compare));
// 输出: ["a", "ä", "z"]

不同的排序类型对相同的输入可能会产生不同的排序结果。

获取支持的单位

单位标识符表示测量单位,例如米、加仑或摄氏度。JavaScript 支持许多用于格式化测量值的单位类型。

"unit" 键返回 JavaScript 环境支持的所有单位标识符:

const units = Intl.supportedValuesOf("unit");
console.log(units.slice(0, 10));
// 输出: ["acre", "bit", "byte", "celsius", "centimeter", "day",
//          "degree", "fahrenheit", "fluid-ounce", "foot"]

常见的单位包括:

  • 长度:"meter", "kilometer", "mile", "foot"
  • 重量:"gram", "kilogram", "pound", "ounce"
  • 温度:"celsius", "fahrenheit"
  • 体积:"liter", "gallon"
  • 数字:"byte", "kilobyte", "megabyte"

在格式化测量值时,指定要使用的单位:

const distance = 1000;

const meterFormat = new Intl.NumberFormat("en-US", {
  style: "unit",
  unit: "meter"
});

const kilometerFormat = new Intl.NumberFormat("en-US", {
  style: "unit",
  unit: "kilometer"
});

console.log(meterFormat.format(distance));
// 输出: "1,000 m"

console.log(kilometerFormat.format(1));
// 输出: "1 km"

单位决定了数字旁边显示的缩写或符号。

您可以使用格式 "unit1-per-unit2" 组合单位:

const speed = 100;

const speedFormat = new Intl.NumberFormat("en-US", {
  style: "unit",
  unit: "kilometer-per-hour"
});

console.log(speedFormat.format(speed));
// 输出: "100 km/h"

这会创建复合单位,例如每小时公里数或每加仑英里数。

构建动态用户界面

Intl.supportedValuesOf() 的主要用例是构建能够适应当前 JavaScript 环境的用户界面。与其硬编码选项,不如在运行时查询支持的值。

以下示例创建了一个设置表单,用户可以选择其首选的日历、货币和时区:

function buildSettingsForm() {
  const form = document.createElement("form");

  // 日历选择器
  const calendarSelect = buildSelector(
    "calendar",
    Intl.supportedValuesOf("calendar")
  );
  form.appendChild(createFormGroup("日历", calendarSelect));

  // 货币选择器
  const currencies = ["USD", "EUR", "GBP", "JPY", "CNY"];
  const currencySelect = buildSelector("currency", currencies);
  form.appendChild(createFormGroup("货币", currencySelect));

  // 时区选择器
  const timeZones = Intl.supportedValuesOf("timeZone");
  const timeZoneSelect = buildSelector("timeZone", timeZones);
  form.appendChild(createFormGroup("时区", timeZoneSelect));

  return form;
}

function buildSelector(name, values) {
  const select = document.createElement("select");
  select.name = name;

  values.forEach(value => {
    const option = document.createElement("option");
    option.value = value;
    option.textContent = value;
    select.appendChild(option);
  });

  return select;
}

function createFormGroup(label, input) {
  const group = document.createElement("div");
  const labelElement = document.createElement("label");
  labelElement.textContent = label;
  group.appendChild(labelElement);
  group.appendChild(input);
  return group;
}

document.body.appendChild(buildSettingsForm());

这段代码使用仅浏览器支持的值创建了一个完整的设置界面。

使用支持的值进行功能检测

您可以使用 Intl.supportedValuesOf() 检测特定值是否受支持,然后再使用它们。这有助于实现渐进增强和回退策略。

检查特定日历是否受支持:

function isCalendarSupported(calendar) {
  const supported = Intl.supportedValuesOf("calendar");
  return supported.includes(calendar);
}

if (isCalendarSupported("islamic")) {
  console.log("支持伊斯兰日历");
} else {
  console.log("不支持伊斯兰日历");
}

此模式适用于任何值类型:

function isValueSupported(type, value) {
  try {
    const supported = Intl.supportedValuesOf(type);
    return supported.includes(value);
  } catch (error) {
    return false;
  }
}

console.log(isValueSupported("currency", "USD"));
// 输出: true

console.log(isValueSupported("timeZone", "America/New_York"));
// 输出: true

try-catch 块处理了 Intl.supportedValuesOf() 不可用的环境。

您可以使用此方法有条件地加载 polyfill:

async function ensureCalendarSupport(calendar) {
  if (!isValueSupported("calendar", calendar)) {
    console.log(`正在加载 ${calendar} 日历的 polyfill`);
    await import("./calendar-polyfill.js");
  }
}

await ensureCalendarSupport("persian");

此方法仅在需要时加载额外的代码。