Intl.NumberFormat API
Форматируйте числа для любого языка с помощью встроенного API интернационализации JavaScript
Введение
Представьте, что вы отображаете число 1234567.89 в своём приложении. Использование toString() выдаёт "1234567.89", что сложно читать и предполагает, что все используют точки для десятичных знаков и читают числа слева направо. Американцы ожидают "1,234,567.89", немцы — "1.234.567,89", а пользователи из Индии — "12,34,567.89" с другими правилами группировки.
API Intl.NumberFormat решает эту задачу, форматируя числа по правилам конкретного языка. Он автоматически расставляет разделители тысяч, десятичные точки, группирует цифры, добавляет символы валют, проценты, единицы измерения и поддерживает разные системы счисления. Это избавляет от необходимости вручную обрабатывать строки или использовать сторонние библиотеки.
В этом гайде рассказывается, как использовать Intl.NumberFormat для правильного форматирования чисел для пользователей по всему миру — от базового применения до продвинутых функций, таких как форматирование валют, компактная запись и пользовательские режимы округления.
Форматирование числа по умолчанию
Создайте форматтер, вызвав new Intl.NumberFormat() с языковым кодом, затем вызовите его метод format() с числом.
const formatter = new Intl.NumberFormat('en-US');
formatter.format(1234567.89);
// "1,234,567.89"
Форматтер добавляет разделители тысяч и форматирует десятичную точку в соответствии с языком. Если язык не указан, используется язык по умолчанию, который обычно определяется настройками системы пользователя.
const formatter = new Intl.NumberFormat('de-DE');
formatter.format(1234567.89);
// "1.234.567,89"
В немецких правилах точка используется как разделитель тысяч, а запятая — для десятичных, что противоположно американским правилам. Форматтер автоматически учитывает эти различия.
Что такое языковой код
Языковой код определяет язык и, опционально, регион. Он записывается как language-REGION. Язык обозначается двухбуквенным кодом ISO 639-1, например en или es. Регион — двухбуквенным кодом ISO 3166-1, например US или MX.
new Intl.NumberFormat('en-US').format(1234.56);
// "1,234.56" (American English)
new Intl.NumberFormat('en-GB').format(1234.56);
// "1,234.56" (British English)
new Intl.NumberFormat('es-ES').format(1234.56);
// "1234,56" (European Spanish)
new Intl.NumberFormat('es-MX').format(1234.56);
// "1,234.56" (Mexican Spanish)
Обе английские версии используют одинаковое форматирование, но испанские варианты различаются. В европейском испанском для четырёхзначных чисел не ставят разделитель тысяч, а для десятичных используют запятую, тогда как в мексиканском испанском применяются американские правила.
Выбирайте локали в зависимости от местоположения пользователей или их языковых предпочтений. Обычно приложения определяют локаль по настройкам пользователя, языку браузера или IP-геолокации.
Выберите стиль форматирования
Опция style определяет категорию форматирования. Передайте объект с опциями вторым аргументом в конструктор.
new Intl.NumberFormat('en-US', {
style: 'decimal'
}).format(1234.56);
// "1,234.56"
Стиль decimal используется по умолчанию. Другие стили — это currency, percent и unit.
Форматируйте валюту с помощью символов и кодов
Стиль currency требует опцию currency с кодом валюты по ISO 4217.
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(1234.56);
// "$1,234.56"
Форматтер добавляет знак доллара и по умолчанию округляет до двух знаков после запятой, как принято для большинства валют. В разных локалях символ располагается по-разному.
new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(1234.56);
// "1.234,56 €"
В немецком формате символ евро ставится после суммы с пробелом. Опция currency определяет, какую валюту показывать, а не какие правила форматирования использовать. Локаль определяет форматирование, а валюта — символ.
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(1234.56);
// "€1,234.56"
В американском формате с символом евро знак евро ставится перед суммой, то есть используется американское расположение, а не европейское.
Управляйте форматом отображения валюты
Опция currencyDisplay меняет то, как валюта отображается в отформатированной строке.
const amount = 1234.56;
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'symbol'
}).format(amount);
// "$1,234.56"
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'code'
}).format(amount);
// "USD 1,234.56"
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'name'
}).format(amount);
// "1,234.56 US dollars"
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'narrowSymbol'
}).format(amount);
// "$1,234.56"
Опция symbol используется по умолчанию и показывает символ валюты, например $ или €. Опция code показывает трёхбуквенный код валюты. Опция name выводит полное название валюты. Опция narrowSymbol использует короткий символ валюты для локали — он может быть неочевиден, но экономит место.
Разница между symbol и narrowSymbol становится очевидной на примере валют с одинаковыми символами.
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'CAD',
currencyDisplay: 'symbol'
}).format(100);
// "CA$100.00"
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'CAD',
currencyDisplay: 'narrowSymbol'
}).format(100);
// "$100.00"
Канадские доллары отображаются как CA$ с опцией symbol, чтобы отличать их от долларов США, но отображаются просто как $ с narrowSymbol.
Форматирование отрицательных сумм в валюте по бухгалтерскому стандарту
Опция currencySign определяет, как будут отображаться отрицательные суммы.
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencySign: 'standard'
}).format(-1234.56);
// "-$1,234.56"
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencySign: 'accounting'
}).format(-1234.56);
// "($1,234.56)"
Опция standard используется по умолчанию и ставит минус перед суммой. Опция accounting заключает отрицательные суммы в скобки, как принято в бухгалтерии. Это делает отрицательные числа более заметными в финансовых отчетах.
Форматирование процентов
Стиль percent умножает число на 100 и добавляет знак процента.
new Intl.NumberFormat('en-US', {
style: 'percent'
}).format(0.1234);
// "12%"
new Intl.NumberFormat('en-US', {
style: 'percent'
}).format(0.1256);
// "13%"
По умолчанию форматер округляет до ближайшего целого. Количество знаков после запятой можно задать через параметры.
new Intl.NumberFormat('en-US', {
style: 'percent',
minimumFractionDigits: 2
}).format(0.1234);
// "12.34%"
Передавайте десятичное значение процента, а не умноженное. Форматер сам выполнит умножение.
Форматирование измерений с единицами
Стиль unit требует опцию unit с идентификатором единицы измерения.
new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer-per-hour'
}).format(100);
// "100 km/h"
new Intl.NumberFormat('en-GB', {
style: 'unit',
unit: 'mile-per-hour'
}).format(100);
// "100 mph"
Доступные единицы включают длину (meter, kilometer, mile), длительность времени (second, minute, hour), цифровое хранилище (byte, kilobyte, megabyte), температуру (celsius, fahrenheit) и многие другие. Составные единицы, такие как kilometer-per-hour, объединяют простые единицы через дефис.
Опция unitDisplay управляет форматом единицы.
const distance = 1234.5;
new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer',
unitDisplay: 'long'
}).format(distance);
// "1,234.5 kilometers"
new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer',
unitDisplay: 'short'
}).format(distance);
// "1,234.5 km"
new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer',
unitDisplay: 'narrow'
}).format(distance);
// "1,234.5km"
Опция long полностью прописывает название единицы. Опция short использует сокращения. Опция narrow применяет максимально компактную форму, которая может быть неоднозначной.
Отображение больших чисел в компактной записи
Опция notation меняет способ отображения чисел. Значение compact использует короткие формы для больших чисел, характерные для локали.
new Intl.NumberFormat('en-US', {
notation: 'compact'
}).format(1234567);
// "1.2M"
new Intl.NumberFormat('en-US', {
notation: 'compact'
}).format(987654321);
// "988M"
Компактная запись по умолчанию округляет до одного знака после запятой и добавляет суффиксы, такие как K для тысяч, M для миллионов и B для миллиардов. Такой формат часто встречается в счетчиках подписчиков в соцсетях, просмотрах видео и аналитических дашбордах.
Опция compactDisplay управляет длиной суффикса.
new Intl.NumberFormat('en-US', {
notation: 'compact',
compactDisplay: 'short'
}).format(1234567);
// "1.2M"
new Intl.NumberFormat('en-US', {
notation: 'compact',
compactDisplay: 'long'
}).format(1234567);
// "1.2 million"
Опция short используется по умолчанию и применяет символы. Опция long полностью прописывает слово, обозначающее порядок величины. В разных локалях используются разные суффиксы.
new Intl.NumberFormat('zh-CN', {
notation: 'compact'
}).format(123456789);
// "1.2亿"
В китайском используется 亿 для ста миллионов, что отражает особенности группировки чисел в языке.
Научная запись для очень больших или малых чисел
Научная запись scientific выражает числа как коэффициент, умноженный на степень десяти.
new Intl.NumberFormat('en-US', {
notation: 'scientific'
}).format(123456789);
// "1.235E8"
new Intl.NumberFormat('en-US', {
notation: 'scientific'
}).format(0.00000123);
// "1.23E-6"
Этот формат отлично подходит для очень больших чисел (астрономические расстояния, количество молекул) и очень малых (массы частиц, наномасштабные измерения). Показатель степени всегда кратен единице.
Инженерная запись для технических задач
Инженерная запись engineering похожа на научную, но ограничивает показатели степени только кратными трём.
new Intl.NumberFormat('en-US', {
notation: 'engineering'
}).format(123456789);
// "123.457E6"
new Intl.NumberFormat('en-US', {
notation: 'engineering'
}).format(1234);
// "1.234E3"
Инженерная запись согласуется с приставками СИ (кило, мега, гига), поэтому стандартна для инженерных и физических задач. Коэффициент находится в диапазоне от 1 до 999.
Управляйте количеством знаков после запятой с помощью fraction digits
Опции minimumFractionDigits и maximumFractionDigits определяют, сколько цифр будет отображаться после запятой.
new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(1234.5);
// "1,234.50"
new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(1234.567);
// "1,234.57"
Минимальное значение гарантирует появление завершающих нулей при необходимости. Максимальное значение округляет длинные дробные числа. Форматтеры валют по умолчанию используют две цифры после запятой. Форматтеры чисел по умолчанию используют минимум ноль и максимум три знака после запятой.
new Intl.NumberFormat('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(1234.567);
// "1,235"
Если оба значения задать равными нулю, число округляется до ближайшего целого.
Управляйте общей точностью с помощью significant digits
Опции minimumSignificantDigits и maximumSignificantDigits определяют общую точность вне зависимости от положения запятой.
new Intl.NumberFormat('en-US', {
minimumSignificantDigits: 3,
maximumSignificantDigits: 3
}).format(1234.567);
// "1,230"
new Intl.NumberFormat('en-US', {
minimumSignificantDigits: 3,
maximumSignificantDigits: 3
}).format(0.001234);
// "0.00123"
Значащие цифры учитывают все цифры, кроме ведущих нулей. В первом примере число округляется до трёх цифр, получается 1230. Во втором примере сохраняются три цифры после ведущих нулей, получается 0.00123.
Опции значащих цифр имеют приоритет над опциями дробных знаков, если указаны оба варианта.
Выберите стратегию округления с помощью rounding modes
Опция roundingMode определяет, как будет происходить округление при необходимости усечения числа.
const value = 1.5;
new Intl.NumberFormat('en-US', {
maximumFractionDigits: 0,
roundingMode: 'halfExpand'
}).format(value);
// "2"
new Intl.NumberFormat('en-US', {
maximumFractionDigits: 0,
roundingMode: 'halfTrunc'
}).format(value);
// "1"
Режим halfExpand используется по умолчанию и округляет 0,5 в сторону от нуля — это стандартная стратегия округления, которую обычно изучают в школе. Режим halfTrunc округляет 0,5 к нулю.
Другие режимы включают:
ceil: Всегда округлять в сторону плюс бесконечностиfloor: Всегда округлять в сторону минус бесконечностиexpand: Всегда округлять от нуляtrunc: Всегда округлять к нулюhalfCeil: Округлять 0,5 в сторону плюс бесконечностиhalfFloor: Округлять 0,5 в сторону минус бесконечностиhalfEven: Округлять 0,5 к ближайшему чётному числу
const prices = [1.5, 2.5, 3.5];
prices.map(price =>
new Intl.NumberFormat('en-US', {
maximumFractionDigits: 0,
roundingMode: 'halfEven'
}).format(price)
);
// ["2", "2", "4"]
Режим halfEven, также называемый банковским округлением, уменьшает смещение при повторных вычислениях. При округлении 0,5 выбирается ближайшее чётное число. Это даёт 2 как для 1.5, так и для 2.5, но 4 для 3.5.
В финансовых приложениях используется ceil для округления в большую сторону и floor для округления возвратов в меньшую сторону. В статистических приложениях применяется halfEven для минимизации накопленных ошибок округления.
Управляйте разделителями тысяч с помощью опций группировки
Опция useGrouping определяет, будут ли отображаться разделители тысяч.
new Intl.NumberFormat('en-US', {
useGrouping: true
}).format(123456);
// "123,456"
new Intl.NumberFormat('en-US', {
useGrouping: false
}).format(123456);
// "123456"
Значение true используется по умолчанию. Значение false убирает все разделители. Строковые значения позволяют более точно управлять отображением.
new Intl.NumberFormat('en-US', {
useGrouping: 'always'
}).format(1234);
// "1,234"
new Intl.NumberFormat('en-US', {
useGrouping: 'min2'
}).format(1234);
// "1234"
Значение always всегда использует разделители. Значение min2 пропускает разделители для четырёхзначных чисел. Значение auto следует локальным настройкам, которые обычно совпадают с поведением min2.
Компактная запись по умолчанию использует min2, так как компактные числа редко требуют внутренних разделителей.
Явное отображение знаков с помощью опций отображения знака
Опция signDisplay определяет, когда отображаются плюсы и минусы.
new Intl.NumberFormat('en-US', {
signDisplay: 'auto'
}).format(100);
// "100"
new Intl.NumberFormat('en-US', {
signDisplay: 'always'
}).format(100);
// "+100"
Значение auto используется по умолчанию и показывает минус для отрицательных чисел, но не показывает плюс для положительных. Значение always показывает оба знака.
new Intl.NumberFormat('en-US', {
signDisplay: 'exceptZero'
}).format(0);
// "0"
new Intl.NumberFormat('en-US', {
signDisplay: 'always'
}).format(0);
// "+0"
Значение exceptZero скрывает знаки для нулевых значений даже при поведении always. Это предотвращает путаницу между +0 и -0.
new Intl.NumberFormat('en-US', {
signDisplay: 'never'
}).format(-100);
// "100"
Значение never полностью убирает знаки, показывая только абсолютное значение.
В финансовых приложениях always используют для выделения прибыли с помощью плюсов. В отображении температуры exceptZero применяют, чтобы не показывать +0° или -0°.
Разделяйте форматированный вывод на части
Метод formatToParts() возвращает массив объектов, представляющих каждую часть форматированной строки.
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).formatToParts(1234.56);
Это возвращает:
[
{ type: 'currency', value: '$' },
{ type: 'integer', value: '1' },
{ type: 'group', value: ',' },
{ type: 'integer', value: '234' },
{ type: 'decimal', value: '.' },
{ type: 'fraction', value: '56' }
]
Каждый объект содержит type, определяющий компонент, и value со строкой. type может быть currency, integer, group, decimal, fraction, literal, minusSign, plusSign, percentSign и другие.
Это позволяет кастомизировать стиль отдельных компонентов.
const parts = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).formatToParts(1234.56);
const formatted = parts.map(part => {
if (part.type === 'currency') {
return `<span class="currency">${part.value}</span>`;
}
if (part.type === 'integer') {
return `<span class="integer">${part.value}</span>`;
}
return part.value;
}).join('');
// <span class="currency">$</span><span class="integer">1</span>,<span class="integer">234</span>.56
В результате получается HTML с оформленными компонентами. Такой же подход подходит для выделения цветом отрицательных значений, увеличения символов валюты или анимации отдельных цифр.
Форматируйте диапазоны чисел
Метод formatRange() форматирует два числа как диапазон.
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).formatRange(100, 200);
// "$100.00 – $200.00"
Форматтер использует разделитель диапазона, характерный для локали (в английском — длинное тире), и включает оба символа валюты. Если значения форматируются одинаково, возвращается одно значение с тильдой.
new Intl.NumberFormat('en-US', {
notation: 'compact'
}).formatRange(1200, 1800);
// "~1K"
И 1200, и 1800 форматируются как 1K в компактной записи, поэтому форматтер показывает примерно 1K.
Метод formatRangeToParts() возвращает части для диапазона.
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).formatRangeToParts(100, 200);
Это возвращает:
[
{ type: 'currency', value: '$', source: 'startRange' },
{ type: 'integer', value: '100', source: 'startRange' },
{ type: 'decimal', value: '.', source: 'startRange' },
{ type: 'fraction', value: '00', source: 'startRange' },
{ type: 'literal', value: ' – ', source: 'shared' },
{ type: 'currency', value: '$', source: 'endRange' },
{ type: 'integer', value: '200', source: 'endRange' },
{ type: 'decimal', value: '.', source: 'endRange' },
{ type: 'fraction', value: '00', source: 'endRange' }
]
Свойство source определяет, относится ли часть к начальному значению, конечному значению или разделителю диапазона.
Диапазоны цен, дат и измерений используют этот метод для корректного форматирования с учётом локали.
Просмотр разрешённых опций
Метод resolvedOptions() возвращает объект с фактическими опциями, которые использует форматтер.
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
formatter.resolvedOptions();
Это возвращает:
{
locale: 'en-US',
numberingSystem: 'latn',
style: 'currency',
currency: 'USD',
currencyDisplay: 'symbol',
currencySign: 'standard',
minimumIntegerDigits: 1,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: 'auto',
notation: 'standard',
signDisplay: 'auto',
roundingMode: 'halfExpand'
}
Объект включает явно заданные опции и значения по умолчанию. locale может отличаться от запрошенной локали, если система выбрала другую, но совместимую локаль. numberingSystem показывает, какие символы цифр использует форматтер.
Этот метод помогает отлаживать неожиданное форматирование, показывая все активные настройки.
Переиспользуйте форматтеры для лучшей производительности
Создание экземпляра NumberFormat включает загрузку данных локали и обработку опций. Переиспользуйте экземпляры при форматировании нескольких значений с одинаковыми настройками.
// Inefficient: creates new formatter for each value
prices.map(price =>
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(price)
);
// Efficient: creates formatter once
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
prices.map(price => formatter.format(price));
Второй подход значительно быстрее при форматировании большого количества значений. Создавайте форматтеры вне циклов и функций рендера компонентов.
Современные движки JavaScript кэшируют экземпляры NumberFormat внутри себя, но явное переиспользование даёт лучшую производительность и более понятный код.
Проверьте поддержку браузерами
API Intl.NumberFormat поддерживается во всех современных браузерах. Chrome, Firefox, Safari и Edge поддерживают базовый API с 2016 года. Расширенные функции, такие как formatRange(), formatRangeToParts() и дополнительные опции roundingMode появились позже, но сейчас доступны в актуальных версиях браузеров.
Проверьте поддержку с помощью:
if (typeof Intl !== 'undefined' && Intl.NumberFormat) {
// NumberFormat is supported
const formatter = new Intl.NumberFormat('en-US');
}
Проверьте поддержку конкретных функций:
const formatter = new Intl.NumberFormat('en-US');
if (typeof formatter.formatRange === 'function') {
// formatRange is supported
}
Приложениям, которым нужна поддержка старых браузеров, можно использовать полифиллы, такие как @formatjs/intl-numberformat, но большинство современных приложений могут использовать нативный API без дополнительных решений.
Когда использовать NumberFormat
Используйте Intl.NumberFormat для:
- Отображения чисел пользователям в любом UI-контексте
- Форматирования сумм в валюте в e-commerce приложениях
- Показа процентов в аналитических дашбордах
- Отображения количества подписчиков, просмотров и других соцметрик
- Форматирования измерений и научных значений
- Создания интернационализированных приложений с поддержкой нескольких локалей
Не используйте Intl.NumberFormat для:
- Внутренних вычислений или обработки данных
- Хранения значений в базах данных
- Сериализации данных для API
- Преобразования пользовательского ввода обратно в числа
NumberFormat — это инструмент для презентационного слоя. Оставляйте исходные числовые значения для вычислений и хранения, а форматирование применяйте только при отображении данных пользователям.