Форматирование валют

Базовый принцип работы с валютами

Форматирование денежных значений в JavaScript реализуется через Intl.NumberFormat с указанием параметра style: "currency". Этот режим автоматически применяет локализацию: разделители, порядок символов, отображение валютного знака и правила округления зависят от выбранной локали и валюты.

Ключевая структура:

new Intl.NumberFormat(locale, {
  style: "currency",
  currency: "USD"
})

Параметр currency является обязательным при использовании стиля "currency". Без него форматирование не будет выполнено корректно.


Локаль и её влияние на валютный формат

Локаль определяет:

  • расположение символа валюты
  • используемые разделители (запятая, точка, пробел)
  • формат отрицательных значений
  • порядок чисел и символов

Примеры одной и той же суммы:

const value = 1234567.89;

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
}).format(value);
// $1,234,567.89

new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR"
}).format(value);
// 1.234.567,89 €

new Intl.NumberFormat("ja-JP", {
  style: "currency",
  currency: "JPY"
}).format(value);
// ¥1,234,568

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


Параметр currency и ISO-коды

Значение currency задаётся в формате ISO 4217:

  • USD — доллар США
  • EUR — евро
  • GBP — британский фунт
  • JPY — японская иена
  • CNY — китайский юань
new Intl.NumberFormat("ru-RU", {
  style: "currency",
  currency: "RUB"
}).format(9999);
// 9 999,00 ₽

Использование невалидного кода приводит к RangeError.


currencyDisplay: управление видом валюты

Параметр currencyDisplay определяет способ отображения валютного символа:

symbol (по умолчанию)

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "symbol"
}).format(100);
// $100.00

code

Отображение ISO-кода валюты:

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "code"
}).format(100);
// USD 100.00

name

Полное текстовое название валюты:

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "name"
}).format(100);
// 100.00 US dollars

narrowSymbol

Сокращённый символ, особенно полезный в ограниченном пространстве интерфейса:

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "narrowSymbol"
}).format(100);
// $100.00

currencySign: стандартный и бухгалтерский формат

Параметр currencySign влияет на отображение отрицательных значений.

standard

Обычное отображение минуса:

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  currencySign: "standard"
}).format(-50);
// -$50.00

accounting

Бухгалтерский формат: отрицательные значения оборачиваются в скобки:

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  currencySign: "accounting"
}).format(-50);
// ($50.00)

Этот формат широко используется в финансовой отчётности.


Управление округлением

Формат валюты по умолчанию применяет округление, соответствующее валюте (например, 2 знака для USD, 0 для JPY). Управление осуществляется через minimumFractionDigits и maximumFractionDigits.

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0
}).format(123.99);
// $124
new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 3
}).format(10);
// $10.000

Автоматические правила валют

Intl учитывает особенности валют:

  • JPY → 0 знаков после запятой
  • KWD (кувейтский динар) → 3 знака
  • CLP (чилийское песо) → 0 знаков
new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "KWD"
}).format(12.3456);
// KWD 12.346

Эти правила встроены в CLDR (Unicode Common Locale Data Repository).


Форматирование отрицательных значений

Отрицательные суммы отображаются в зависимости от локали и настроек currencySign.

new Intl.NumberFormat("ru-RU", {
  style: "currency",
  currency: "RUB"
}).format(-1500);
// -1 500,00 ₽
new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR",
  currencySign: "accounting"
}).format(-1500);
// (1.500,00 €)

Влияние параметра useGrouping

Разделение числа на разряды регулируется useGrouping.

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  useGrouping: false
}).format(1000000);
// $1000000.00
new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  useGrouping: true
}).format(1000000);
// $1,000,000.00

Различия между валютами с плавающей точкой и без неё

Некоторые валюты не используют дробную часть. Intl автоматически корректирует формат:

new Intl.NumberFormat("ja-JP", {
  style: "currency",
  currency: "JPY"
}).format(1500.75);
// ¥1,501

Дробная часть отбрасывается с корректным округлением по математическим правилам.


Производительность и повторное использование форматтера

Создание экземпляра Intl.NumberFormat относительно дорогостоящая операция. Эффективная практика — переиспользование:

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

usdFormatter.format(10);
usdFormatter.format(999);
usdFormatter.format(12345);

Такой подход снижает накладные расходы при массовом форматировании данных.


Форматирование в массивах и коллекциях

При обработке больших наборов данных форматтер применяется как функция:

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

const prices = [10, 20.5, 30, 99.99];

const formatted = prices.map(formatter.format);
// ["10,00 €", "20,50 €", "30,00 €", "99,99 €"]

Важно сохранять контекст метода при передаче format, иначе возможна потеря привязки:

prices.map((n) => formatter.format(n));

Особенности локализации валютных интерфейсов

В разных регионах различается не только символ валюты, но и:

  • направление отображения символа (до или после числа)
  • наличие пробела между числом и символом
  • формат отрицательных значений
  • использование неразрывных пробелов
new Intl.NumberFormat("fr-FR", {
  style: "currency",
  currency: "EUR"
}).format(42);
// 42,00 €
new Intl.NumberFormat("en-GB", {
  style: "currency",
  currency: "GBP"
}).format(42);
// £42.00

Комбинация с другими параметрами форматирования чисел

Intl.NumberFormat поддерживает сочетание валютного формата с дополнительными настройками:

  • notation (standard, compact, scientific)
  • signDisplay
  • minimumIntegerDigits

Однако при использовании style: "currency" часть параметров игнорируется или ограничивается спецификацией валютного формата.

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  signDisplay: "always"
}).format(50);
// +$50.00

Поведение при нестандартных локалях

При отсутствии поддержки локали происходит fallback на ближайшую доступную:

new Intl.NumberFormat("xx-XX", {
  style: "currency",
  currency: "USD"
}).format(100);

Результат будет соответствовать дефолтной локали окружения, сохраняя корректный валютный формат.


Практическая модель работы форматтера

Форматирование валюты внутри Intl проходит несколько этапов:

  1. Определение локали
  2. Применение правил валюты (CLDR)
  3. Округление числа
  4. Добавление символа валюты
  5. Применение локальных разделителей
  6. Формирование строки вывода

Эта последовательность обеспечивает единообразное отображение финансовых значений во всех поддерживаемых средах JavaScript.