Как форматировать измерения, например 5 километров или 10 фунтов

Отображайте расстояние, вес, температуру и другие измерения с учетом локали и правильных единиц

Введение

Измерения передают количество с помощью единиц, которые придают им смысл. Пять километров — это расстояние, десять фунтов — это вес, а двадцать градусов Цельсия — это температура. Приложения, которые показывают измерения, должны отображать и числовое значение, и единицу измерения в понятном для пользователя формате.

В разных странах измерения форматируются по-разному. Американцы пишут «5 km» или «5 километров» в зависимости от контекста. Немцы могут писать «5 km» с другими правилами пробелов. Одно и то же измерение может выглядеть как «5 km», «5km» или «5 километров» в зависимости от локали и желаемой подробности. В одних регионах используют метрическую систему, в других — имперскую, но и оформление единиц отличается в зависимости от локали.

JavaScript предоставляет API Intl.NumberFormat для форматирования измерений с учетом локали и правильного отображения единиц. В этом уроке объясняется, как форматировать такие измерения, как расстояние, вес, температура, объем и скорость, чтобы единицы отображались корректно для любой локали.

Единицы измерения нужны для контекста

Числа без единиц часто не имеют смысла. Число 5 может означать 5 километров, 5 миль, 5 метров или 5 футов. Пользователь не сможет понять значение без указания единицы измерения.

Единицы должны отображаться вместе с числовым значением. Когда вы пишете «5 километров», единица «километров» — это важная информация. Когда вы пишете «10 фунтов», единица «фунтов» указывает, что речь идет о весе, а не о валюте.

Для одного и того же типа измерения существуют разные системы единиц. Расстояние можно измерять в километрах, милях, метрах, футах и других единицах. Вес — в килограммах, фунтах, унциях или граммах. Температуру — в градусах Цельсия, Фаренгейта или Кельвина. Приложения должны форматировать используемую систему единиц так, чтобы это соответствовало ожиданиям пользователя.

Форматирование единиц с помощью Intl.NumberFormat

Конструктор Intl.NumberFormat создаёт форматтер единиц, если вы передаёте style: 'unit' в опциях. Также нужно указать, какую единицу форматировать, используя опцию unit с идентификатором единицы.

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

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

Это создаёт форматтер для американского английского, который отображает значения в километрах. Метод format() преобразует число в строку с сокращением единицы километра.

Опция unit принимает стандартизированные идентификаторы единиц. Эти идентификаторы используют строчные слова, разделённые дефисами. К распространённым идентификаторам относятся kilometer, meter, mile, pound, kilogram, celsius, fahrenheit, liter и gallon.

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"

Каждый форматтер автоматически применяет подходящее сокращение для выбранной единицы. Не нужно знать, какое сокращение соответствует каждому идентификатору единицы.

Локаль определяет форматирование единиц

Параметр локали управляет тем, как форматируются единицы: пробелы, разделители и сокращения. Одна и та же единица может отображаться по-разному в зависимости от локали.

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"

Хотя сокращения для километров похожи в разных локалях, правила пробелов и разделителей отличаются. API Intl автоматически учитывает эти особенности форматирования для каждой локали.

Управление подробностью отображения единиц

Опция 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 использует стандартные сокращения, такие как «км» или «фунт». Формат long использует полные названия, например, «километры» или «фунты». Формат narrow отображает минимально, с уменьшенными или без пробелов. По умолчанию используется short, если вы не указываете unitDisplay.

Локаль влияет на отображение длинных единиц измерения. Полные названия единиц переводятся и согласуются по грамматическим правилам каждого языка.

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"

Каждая локаль предоставляет подходящий перевод и грамматическую форму для названия единицы.

Форматирование единиц веса

Для веса используются идентификаторы единиц, такие как pound, kilogram, ounce и gram. Эти идентификаторы работают так же, как и для единиц расстояния.

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"

Форматтер автоматически выбирает единственное или множественное число в зависимости от значения. Одна фунт — это форма единственного числа, а десять фунтов — множественное.

Форматирование температурных измерений

Для температуры используются идентификаторы единиц, такие как celsius и fahrenheit. Эти единицы в короткой форме отображаются со знаком градуса.

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"

При форматировании температуры автоматически добавляется соответствующее обозначение градуса для каждой шкалы.

Форматирование единиц объёма

Для объёма используются идентификаторы единиц, такие как liter, gallon, milliliter и fluid-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".

Форматирование составных единиц

Составные единицы объединяют две простые единицы через отношение "на". Например, скорость (мили в час или километры в час) и расход топлива (литры на 100 километров) используют составные единицы.

Составные идентификаторы единиц объединяют две простые единицы с помощью -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"

Такой подход эффективнее, чем создавать новый форматтер для каждого значения. Разница в производительности становится заметной при форматировании массивов или списков с большим количеством измерений.