如何使用本地化货币符号进行格式化

为任意地区正确显示价格的货币符号、位置和格式

简介

货币符号用于标识价格所代表的货币类型。美元符号 $ 表示美元,欧元符号 € 表示欧元,英镑符号 £ 表示英镑。这些符号对于国际化应用至关重要,因为用户需要明确他们所看到或消费的是哪种货币。

不同国家的货币金额格式各不相同。美国人写作 $1,234.56,符号在金额前。德国人写作 1.234,56 €,符号在金额后且分隔符不同。法国格式为 1 234,56 €,数字分组之间用空格。如果你像 "$" + amount 这样硬编码货币格式,就假设了所有用户都遵循相同的规范。

JavaScript 提供了 Intl.NumberFormat API,可以根据本地习惯格式化货币金额,自动使用合适的符号和格式。本教程将介绍不同地区的货币格式差异,以及如何为任意语言或地区正确格式化价格。

货币符号因地区而异

不同货币使用不同的符号。美元用 $,欧元用 €,英镑用 £,日元用 ¥,瑞士法郎根据上下文用 Fr. 或 CHF。每个符号都能帮助用户快速识别当前所用的货币。

有些符号代表多种货币。例如,美元符号 $ 可用于美元、加元、澳元、墨西哥比索及其他多种货币。如果没有额外的上下文,用户无法判断价格具体是哪种“美元”货币。

货币符号的位置也因地区而异。英语国家通常将符号放在金额前,如 $100。许多欧洲国家则将符号放在金额后,如 100 €。有些国家金额和符号之间有空格,有些则没有。

由于这些差异,不能简单地将货币符号和数字拼接在一起。你需要具备能够理解所显示货币类型和用户所用本地化设置的格式化逻辑。

使用 Intl.NumberFormat 格式化货币

Intl.NumberFormat 构造函数在你传入 style: 'currency' 选项时会创建一个货币格式化器。你还必须通过 currency 选项,指定要格式化的货币(使用 ISO 4217 货币代码)。

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

console.log(formatter.format(1234.56));
// Output: "$1,234.56"

这会创建一个用于美国英语、以美元显示金额的格式化器。format() 方法会将数字转换为带有美元符号、千位分隔符和两位小数的字符串。

currency 选项要求使用三位字母的 ISO 4217 代码。常见代码包括 USD(美元)、EUR(欧元)、GBP(英镑)、JPY(日元)和 CAD(加元)。

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

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

const gbpFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'GBP'
});

console.log(usdFormatter.format(100));
// Output: "$100.00"

console.log(eurFormatter.format(100));
// Output: "€100.00"

console.log(gbpFormatter.format(100));
// Output: "£100.00"

每个格式化器都会自动插入相应的货币符号。你无需了解每个货币代码对应的符号。

本地化设置决定符号位置和格式

locale 参数控制货币金额的格式,包括符号位置、数字分组和小数分隔符。同一个货币代码在不同本地化设置下会产生不同的输出。

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

const deFormatter = new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR'
});

const frFormatter = new Intl.NumberFormat('fr-FR', {
  style: 'currency',
  currency: 'EUR'
});

console.log(usFormatter.format(1234.56));
// Output: "€1,234.56"

console.log(deFormatter.format(1234.56));
// Output: "1.234,56 €"

console.log(frFormatter.format(1234.56));
// Output: "1 234,56 €"

这三个格式化器都显示欧元,但各自采用不同的约定。美国英语格式化器将符号放在金额前面,小数点用作小数分隔符。德语格式化器将符号放在金额后面并加空格,千位分隔符用点,小数分隔符用逗号。法语格式化器则用空格作为千位分隔符。

Intl API 会根据你指定的 locale 自动处理这些差异。你无需了解每个 locale 的具体格式化规则。

货币格式化包含小数位

货币格式化器会根据不同货币自动确定合适的小数位数。大多数货币使用两位小数来表示分币或等值单位。

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

console.log(formatter.format(100));
// Output: "$100.00"

console.log(formatter.format(100.5));
// Output: "$100.50"

console.log(formatter.format(100.567));
// Output: "$100.57"

对于美元,格式化器始终显示两位小数,不足时补零,输入精度更高时则进行四舍五入。

有些货币没有小数位。例如,日元没有分币单位,因此金额显示时不带小数位。

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'JPY'
});

console.log(formatter.format(1234.56));
// Output: "¥1,235"

由于日元金额没有分币单位,格式化器会四舍五入到最接近的整数。Intl API 能识别每种货币的小数精度,并自动应用。

按用户 locale 格式化货币

你可以使用用户浏览器的语言偏好,而不是硬编码特定的 locale。navigator.language 属性会返回用户的首选 locale。

const userLocale = navigator.language;

const formatter = new Intl.NumberFormat(userLocale, {
  style: 'currency',
  currency: 'USD'
});

console.log(formatter.format(1234.56));
// Output varies by user's locale
// For en-US: "$1,234.56"
// For de-DE: "1.234,56 $"
// For fr-FR: "1 234,56 $US"

这种方式会根据每位用户的格式化习惯显示货币金额。例如,德国用户会看到货币符号在金额后面并使用德式分隔符,而美国用户则会看到符号在金额前并使用美式分隔符。

你还可以传递整个 navigator.languages 数组,以便在用户首选项不可用时启用回退机制。

const formatter = new Intl.NumberFormat(navigator.languages, {
  style: 'currency',
  currency: 'USD'
});

console.log(formatter.format(1234.56));

API 会使用数组中第一个支持的 locale,实现自动回退处理。

复用货币格式化器

创建新的 Intl.NumberFormat 实例时需要加载 locale 数据并处理选项。如果你要用同一 locale 和货币格式化多个价格,建议只创建一次格式化器并复用。

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

const prices = [19.99, 29.99, 49.99, 99.99];

prices.forEach(price => {
  console.log(formatter.format(price));
});
// Output:
// "$19.99"
// "$29.99"
// "$49.99"
// "$99.99"

这种模式比为每个价格创建新的格式化器更高效。当需要格式化包含大量数值的数组或列表时,性能差异会变得非常明显。

在应用程序中显示价格

无论在何处向用户展示价格,都可以使用货币格式化器。这包括产品列表、购物车、结算页面、发票以及财务仪表盘等场景。

const formatter = new Intl.NumberFormat(navigator.language, {
  style: 'currency',
  currency: 'USD'
});

const productPrice = 29.99;
const taxAmount = 2.40;
const totalPrice = productPrice + taxAmount;

document.getElementById('product-price').textContent = formatter.format(productPrice);
document.getElementById('tax-amount').textContent = formatter.format(taxAmount);
document.getElementById('total-price').textContent = formatter.format(totalPrice);

格式化后的字符串与其他字符串值一样,可以插入到文本内容、属性或任何向用户展示信息的上下文中。

处理多种货币

支持多种货币的应用需要为每种货币分别创建格式化器。货币代码决定显示的符号,而区域设置决定金额的格式化方式。

const locale = navigator.language;

const usdFormatter = new Intl.NumberFormat(locale, {
  style: 'currency',
  currency: 'USD'
});

const eurFormatter = new Intl.NumberFormat(locale, {
  style: 'currency',
  currency: 'EUR'
});

const gbpFormatter = new Intl.NumberFormat(locale, {
  style: 'currency',
  currency: 'GBP'
});

console.log(usdFormatter.format(100));
console.log(eurFormatter.format(100));
console.log(gbpFormatter.format(100));

每个格式化器都会显示相应的货币符号,并遵循用户所在区域的货币格式规范。这样可以确保无论使用哪种货币或区域组合,价格都能准确且易于阅读。