如何使用千位分隔符格式化数字

使用 JavaScript 按照本地习惯显示带有分组符号的大数字

简介

大数字如果没有视觉分隔,很难阅读。数字 1234567 需要仔细数位才能判断是百万还是千万。添加分隔符后,变为 1,234,567,一眼就能看出大约是一百万。

不同国家使用不同的符号来分隔数字。美国人用逗号,德国人用句点,法语区用空格。当你的应用面向全球用户时,需要根据每位用户的习惯来格式化数字。

JavaScript 提供了 Intl.NumberFormat API,可以自动处理这些问题。本课将介绍千位分隔符的工作原理、为何因地区而异,以及如何为任何语言或地区正确格式化数字。

什么是千位分隔符

千位分隔符是一种插入在数字分组之间的符号,使大数字更易于阅读。在大多数地区,数字从右向左每三位分为一组。数字 1234567 加上逗号分隔后变为 1,234,567

“千位分隔符”这个术语来源于最常见的用法,即每三位插入分隔符。但同样的机制也适用于任何数字分组,无论是十万、百万还是十亿。

没有分隔符时,数字会连在一起,需要仔细数位。有了分隔符,眼睛可以快速识别数字的数量级。这种视觉分组能减少阅读错误,提高理解效率。

为什么千位分隔符因地区而异

不同国家制定了各自的数字书写规范。这些规范是根据本地印刷习惯、教育体系和文化偏好独立发展起来的。

在美国、英国、澳大利亚等英语国家,逗号用作千位分隔符。一百万写作 1,000,000

在德国、意大利、西班牙、葡萄牙等许多欧洲国家,句点用作千位分隔符。同样的数字写作 1.000.000

在法国及许多法语地区,空格用作千位分隔符。该数字写作 1 000 000

在瑞士,撇号用作千位分隔符。该数字写作 1'000'000

像印度这样的国家采用不同的分组方式。印度数字分组是先分三位,之后每两位一组。一百万在拉克(lakh)系统下写作 10,00,000

如果你将某个分隔符字符硬编码,就假设所有用户都遵循同样的规范。这会让你的应用对来自不同地区的用户来说更难使用。

使用 Intl.NumberFormat 添加千位分隔符

Intl.NumberFormat 构造函数可以创建一个应用本地化数字格式的格式化器。传入地区标识符作为第一个参数,然后用 format() 方法格式化数字。

const formatter = new Intl.NumberFormat('en-US');
console.log(formatter.format(1234567));
// Output: "1,234,567"

这会创建一个美式英语格式化器,使用逗号作为千位分隔符。format() 方法会将数字转换为带有适当分隔符的字符串。

你可以通过更改地区标识符,为不同地区格式化同一个数字。

const usFormatter = new Intl.NumberFormat('en-US');
console.log(usFormatter.format(1234567));
// Output: "1,234,567"

const deFormatter = new Intl.NumberFormat('de-DE');
console.log(deFormatter.format(1234567));
// Output: "1.234.567"

const frFormatter = new Intl.NumberFormat('fr-FR');
console.log(frFormatter.format(1234567));
// Output: "1 234 567"

每个格式化器都会根据其本地化约定进行处理。德语格式化器使用句点,法语格式化器使用空格,而美式格式化器使用逗号。你无需了解每个本地化所用的具体符号,API 会自动处理这些细节。

为用户的本地化格式化数字

你无需硬编码特定的本地化,可以直接使用浏览器中用户的首选语言。navigator.language 属性会返回用户的首选语言。

const userLocale = navigator.language;
const formatter = new Intl.NumberFormat(userLocale);

console.log(formatter.format(1234567));
// Output varies by user's locale
// For en-US: "1,234,567"
// For de-DE: "1.234.567"
// For fr-FR: "1 234 567"

这种方式可以根据每位用户的期望显示数字,无需他们手动选择本地化。浏览器会提供语言偏好,Intl API 会应用相应的格式化规则。

你还可以传递整个首选语言数组,以启用回退机制。

const formatter = new Intl.NumberFormat(navigator.languages);
console.log(formatter.format(1234567));

API 会使用数组中第一个支持的本地化。当用户的首选语言不可用时,这样可以更好地进行回退处理。

了解默认的分组行为

默认情况下,Intl.NumberFormat 会为所有足够大的数字添加千位分隔符。通常,四位数及以上的数字会加分隔符,但具体情况因本地化而异。

const formatter = new Intl.NumberFormat('en-US');

console.log(formatter.format(123));
// Output: "123"

console.log(formatter.format(1234));
// Output: "1,234"

console.log(formatter.format(12345));
// Output: "12,345"

console.log(formatter.format(123456));
// Output: "123,456"

123 这样的小数字不需要分隔符,会直接显示。而从 1234 开始的数字会加分隔符,因为分组能提升可读性。

API 会根据本地化约定自动判断何时需要分隔符,无需你手动检查每个数字的大小再进行格式化。

使用千位分隔符格式化小数

Intl.NumberFormat API 同时处理数字的整数部分和小数部分。千位分隔符会出现在整数部分,而小数点和小数位数则遵循本地化习惯。

const usFormatter = new Intl.NumberFormat('en-US');
console.log(usFormatter.format(1234567.89));
// Output: "1,234,567.89"

const deFormatter = new Intl.NumberFormat('de-DE');
console.log(deFormatter.format(1234567.89));
// Output: "1.234.567,89"

请注意,德语格式会将这两种习惯反转。句点用于整数部分的千位分隔符,而逗号则作为小数部分的小数点分隔符。Intl API 会根据不同的本地化设置正确处理这两方面。

处理超大数字

随着数字变大,千位分隔符变得越来越重要。没有分隔符时,七位、八位或九位的数字很难一眼准确读取。

const formatter = new Intl.NumberFormat('en-US');

console.log(formatter.format(1234567890));
// Output: "1,234,567,890"

console.log(formatter.format(9876543210));
// Output: "9,876,543,210"

格式化器会在每隔三位数字插入分隔符,即使是十亿级别的数字也能轻松阅读。这种自动分组适用于任意数量级的数字,无需手动计算分隔符位置。

复用格式化器以提升性能

创建新的 Intl.NumberFormat 实例时,需要加载本地化数据并处理相关选项。如果需要用相同的本地化和设置格式化多个数字,建议只创建一次格式化器并复用。

const formatter = new Intl.NumberFormat('en-US');

const numbers = [1234, 5678, 91011, 121314];

numbers.forEach(number => {
  console.log(formatter.format(number));
});
// Output:
// "1,234"
// "5,678"
// "91,011"
// "121,314"

这种做法比为每个数字都新建一个格式化器更高效。当需要格式化包含数百或数千个值的数组时,性能差异会非常明显。

在模板中格式化数字

你可以在任何向用户展示数字的地方使用 Intl.NumberFormat,包括将格式化后的数字插入 HTML 模板、在表格中显示数值,或在仪表盘中展示统计数据。

const formatter = new Intl.NumberFormat(navigator.language);

const totalUsers = 1234567;
const activeUsers = 891234;

document.getElementById('total-users').textContent = formatter.format(totalUsers);
document.getElementById('active-users').textContent = formatter.format(activeUsers);

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