如何格式化如 5 公里或 10 磅这样的度量单位

以符合本地习惯的格式和单位显示距离、重量、温度等度量值

引言

度量单位通过数量和单位共同传达有意义的信息。5 公里表示距离,10 磅表示重量,20 摄氏度表示温度。应用程序在显示度量值时,需要以用户易于理解的格式同时展示数值和单位。

不同的地区对度量单位的格式有不同的规范。美国人会根据上下文写作 "5 km" 或 "5 kilometers"。德国人可能会用不同的空格规范写 "5 km"。同一个度量值在不同地区和不同详细程度下,可能会显示为 "5 km"、"5km" 或 "5 kilometers"。有些地区使用公制单位,有些则用英制单位,但单位的格式化方式也会因地区而异。

JavaScript 提供了 Intl.NumberFormat API,可用于根据本地习惯格式化度量单位。本教程将介绍如何针对任意地区,正确格式化距离、重量、温度、体积和速度等度量单位的显示方式。

度量单位需要单位信息作为上下文

在许多场景下,单纯的数字没有意义。数字 5 可能代表 5 公里、5 英里、5 米或 5 英尺。如果没有单位,用户无法理解具体的度量内容。

单位必须与数值一起一致地显示。当你写 "5 公里" 时,单位 "公里" 是关键信息。当你写 "10 磅" 时,单位 "磅" 明确了这是重量而不是货币。

同一种度量类型可以有不同的单位系统。例如,距离可以用公里、英里、米、英尺等单位表示;重量可以用千克、磅、盎司或克表示;温度可以用摄氏度、华氏度或开尔文表示。应用程序需要以符合用户预期的方式格式化所用的单位系统。

使用 Intl.NumberFormat 格式化单位

当你在选项中传入 Intl.NumberFormat 构造函数时,会创建一个单位格式化器。你还需要通过 unit 选项指定要格式化的单位标识符。

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

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

这会创建一个用于美式英语、以千米为单位显示数值的格式化器。format() 方法会将数字转换为带有千米单位缩写的字符串。

unit 选项接受标准化的单位标识符。这些标识符由小写单词和连字符组成。常见的标识符包括 kilometermetermilepoundkilogramcelsiusfahrenheitlitergallon

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

const weightFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'pound'
});

console.log(distanceFormatter.format(5));
// Output: "5 km"

console.log(weightFormatter.format(10));
// Output: "10 lb"

每个格式化器会自动为指定单位应用合适的单位缩写。你无需了解每个单位标识符对应的缩写。

语言环境决定单位格式化方式

locale 参数控制单位的格式化方式,包括空格、分隔符和单位缩写。相同的单位在不同 locale 下会产生不同的输出。

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

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

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

console.log(usFormatter.format(5));
// Output: "5 km"

console.log(deFormatter.format(5));
// Output: "5 km"

console.log(frFormatter.format(5));
// Output: "5 km"

虽然千米在不同语言环境下的缩写类似,但空格和分隔符的规范会有所不同。Intl API 会自动处理这些与 locale 相关的格式化规则。

控制单位显示的详细程度

unitDisplay 选项用于控制单位是以缩写、全称还是极简形式显示。该选项接受三个值:"short" 表示缩写,"long" 表示单位全称,"narrow" 表示极简显示。

const shortFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'short'
});

const longFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

const narrowFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'narrow'
});

console.log(shortFormatter.format(5));
// Output: "5 km"

console.log(longFormatter.format(5));
// Output: "5 kilometers"

console.log(narrowFormatter.format(5));
// Output: "5km"

short 格式会使用 "km" 或 "lb" 这样的标准缩写。long 格式会使用 "kilometers" 或 "pounds" 这样的单位全称。narrow 格式则以极简方式显示,空格极少或无空格。如果未指定 unitDisplay,默认采用 short

区域设置会影响长格式单位的显示方式。完整的单位名称会被翻译,并根据每种语言的语法规则进行调整。

const enFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

const deFormatter = new Intl.NumberFormat('de-DE', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

const esFormatter = new Intl.NumberFormat('es-ES', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

console.log(enFormatter.format(5));
// Output: "5 kilometers"

console.log(deFormatter.format(5));
// Output: "5 Kilometer"

console.log(esFormatter.format(5));
// Output: "5 kilómetros"

每个区域设置都会为单位名称提供合适的翻译和语法形式。

格式化重量单位

重量单位使用如 poundkilogramouncegram 这样的单位标识符。这些标识符的用法与距离单位相同。

const poundFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'pound'
});

const kilogramFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilogram'
});

console.log(poundFormatter.format(10));
// Output: "10 lb"

console.log(kilogramFormatter.format(10));
// Output: "10 kg"

您可以使用长格式来显示完整的重量单位名称。

const formatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'pound',
  unitDisplay: 'long'
});

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

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

格式化器会根据数值自动处理单数和复数形式。一个磅使用单数形式,十磅则使用复数形式。

格式化温度单位

温度单位使用如 celsiusfahrenheit 这样的单位标识符。这些单位在短格式下会带有度数符号。

const celsiusFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'celsius'
});

const fahrenheitFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'fahrenheit'
});

console.log(celsiusFormatter.format(20));
// Output: "20°C"

console.log(fahrenheitFormatter.format(68));
// Output: "68°F"

长格式会显示完整的温标名称。

const celsiusFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'celsius',
  unitDisplay: 'long'
});

const fahrenheitFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'fahrenheit',
  unitDisplay: 'long'
});

console.log(celsiusFormatter.format(20));
// Output: "20 degrees Celsius"

console.log(fahrenheitFormatter.format(68));
// Output: "68 degrees Fahrenheit"

温度格式化会自动包含每种温标对应的度数术语。

格式化体积单位

体积单位使用如 litergallonmilliliterfluid-ounce 这样的单位标识符。它们的用法与其他单位类型类似。

const literFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'liter'
});

const gallonFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'gallon'
});

console.log(literFormatter.format(2));
// Output: "2 L"

console.log(gallonFormatter.format(2));
// Output: "2 gal"

体积单位同样支持长格式显示,并根据区域设置采用本地化拼写。

const usFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'liter',
  unitDisplay: 'long'
});

const gbFormatter = new Intl.NumberFormat('en-GB', {
  style: 'unit',
  unit: 'liter',
  unitDisplay: 'long'
});

console.log(usFormatter.format(2));
// Output: "2 liters"

console.log(gbFormatter.format(2));
// Output: "2 litres"

英式英语区域使用 "litres",而美式英语使用 "liters"。

格式化复合单位

复合单位将两个简单单位通过 "每" 的关系组合。例如,速度单位如每小时英里(miles per hour)或每小时公里(kilometers per hour)就是复合单位。燃油效率单位如每百公里升(liters per 100 kilometers)也属于复合单位。

复合单位标识符通过 -per- 将两个简单单位连接起来。例如,mile-per-hour 组合了英里和小时,kilometer-per-hour 组合了千米和小时。

const mphFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'mile-per-hour'
});

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

console.log(mphFormatter.format(60));
// Output: "60 mph"

console.log(kphFormatter.format(100));
// Output: "100 km/h"

每个复合单位会以合适的缩写格式,结合两个单位部分进行显示。

长格式会使用完整的单位名称,并根据本地化习惯添加适当的介词来显示复合单位。

const formatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'mile-per-hour',
  unitDisplay: 'long'
});

console.log(formatter.format(60));
// Output: "60 miles per hour"

格式化器会根据目标语言的语法规则,自动构建复合单位短语。

获取可用的单位标识符

Intl.supportedValuesOf() 方法会返回一个数组,包含 JavaScript 环境中支持的所有单位标识符。该方法以字符串 'unit' 作为参数。

const units = Intl.supportedValuesOf('unit');

console.log(units);
// Output: Array of unit identifiers like:
// ["acre", "bit", "byte", "celsius", "centimeter", "day",
//  "degree", "fahrenheit", "fluid-ounce", "foot", "gallon",
//  "gram", "hectare", "hour", "inch", "kilogram", "kilometer",
//  "liter", "meter", "mile", "millimeter", "ounce", "pound",
//  "second", "stone", "week", "yard", ...]

返回的数组包含所有可用于格式化的简单单位。你可以将该数组中的任意标识符与 unit 选项一起使用。

当你需要验证某个特定单位是否受支持,或想为用户提供可用单位列表时,此方法非常有用。

const units = Intl.supportedValuesOf('unit');

const hasKilometer = units.includes('kilometer');
const hasPound = units.includes('pound');

console.log(hasKilometer);
// Output: true

console.log(hasPound);
// Output: true

你可以在创建格式化器之前检查特定单位,以适配不同支持级别的运行环境。

与数字格式化选项结合使用

单位格式化器支持与其他 Intl.NumberFormat 样式相同的数字格式化选项。你可以控制小数位数、有效数字以及其他数值属性。

const formatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  maximumFractionDigits: 2
});

console.log(formatter.format(5.123));
// Output: "5.12 km"

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

格式化器会在添加单位前,先应用四舍五入和小数位规则。

你可以使用千位分隔符格式化大数字。

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

console.log(formatter.format(12345.67));
// Output: "12,345.67 km"

所有标准的数字格式化功能都适用于单位格式化。

为用户的本地化环境格式化度量单位

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

const userLocale = navigator.language;

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

console.log(formatter.format(5));
// Output varies by user's locale

这种方法会根据每个用户的格式化习惯显示测量值。不同用户会看到相同的测量数据,但会按照其本地习惯进行格式化。

在应用程序中显示测量值

无论在何处向用户展示测量数据,都可以使用单位格式化器。例如,健身应用显示距离或体重,天气应用显示温度,食谱应用显示体积,导航应用显示速度等场景。

const distanceFormatter = new Intl.NumberFormat(navigator.language, {
  style: 'unit',
  unit: 'kilometer',
  maximumFractionDigits: 1
});

const distance = 5.234;

document.getElementById('distance').textContent = distanceFormatter.format(distance);
// Displays: "5.2 km" (or locale equivalent)

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

复用单位格式化器

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

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

const distances = [1.5, 3.2, 5.0, 10.8];

distances.forEach(distance => {
  console.log(formatter.format(distance));
});
// Output:
// "1.5 km"
// "3.2 km"
// "5 km"
// "10.8 km"

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