Денежные единицы и валюты

Работа с валютами в интернационализированных интерфейсах требует строгого соблюдения локальных правил форматирования: символ валюты, его позиция, разделители разрядов, округление и правила отображения отрицательных значений зависят от локали. В экосистеме FormatJS финансовое форматирование опирается на стандарт ECMAScript Internationalization API (Intl), а именно на Intl.NumberFormat с параметром style: "currency".

Библиотека FormatJS расширяет и стабилизирует работу с i18n в JavaScript-приложениях, предоставляя унифицированные инструменты поверх нативных API браузера и Node.js. Основой валютного форматирования является спецификация ECMA-402.

Официальная документация и исходный код: FormatJS Documentation FormatJS GitHub


Основы валютного форматирования через Intl.NumberFormat

Форматирование денежных значений выполняется через:

new Intl.NumberFormat(locale, {
  style: 'currency',
  currency: 'USD'
}).format(1234.56);

Ключевой параметр — currency, определяющий ISO-код валюты.

Пример:

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

usd.format(1234.56); // $1,234.56
const eur = new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR'
});

eur.format(1234.56); // 1.234,56 €

Различие между локалями проявляется не только в символах, но и в порядке отображения и разделителях.


Поддержка валют в FormatJS-экосистеме

В контексте FormatJS работа с валютами может происходить через несколько уровней:

  • нативный Intl.NumberFormat
  • полифиллы FormatJS для старых окружений
  • интеграция с react-intl
  • ICU MessageFormat (для динамических сообщений с валютами)

Основная задача FormatJS — обеспечить одинаковое поведение форматирования во всех окружениях, включая старые браузеры, где Intl может быть неполным.


Параметры currency и их влияние

currency

ISO 4217 код валюты:

currency: 'USD' // доллар США
currency: 'EUR' // евро
currency: 'JPY' // японская иена

Неверный код вызывает RangeError.


currencyDisplay

Определяет способ отображения валюты:

  • "symbol" — $ / €
  • "code" — USD / EUR
  • "name" — US dollars / euros
  • "narrowSymbol" — компактный символ (например, $ без уточнений)

Примеры:

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'symbol'
}).format(1000);
// $1,000.00
new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'code'
}).format(1000);
// USD 1,000.00
new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'name'
}).format(1000);
// 1,000.00 US dollars

Локализация валютных форматов

Разные локали влияют на:

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

Пример сравнения:

const value = 1234567.89;

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

new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(value);
// 1 234 567,89 €

new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB' }).format(value);
// 1 234 567,89 ₽

FormatJS использует те же CLDR-данные (Unicode Common Locale Data Repository), что обеспечивает согласованность между платформами.


Роль ICU MessageFormat и валюты в сообщениях

FormatJS активно использует ICU-синтаксис через react-intl и intl-messageformat. Валюта может быть частью локализованных сообщений.

Пример:

import { FormattedNumber } from 'react-intl';

<FormattedNumber
  value={2500}
  style="currency"
  currency="EUR"
/>

В ICU-строках:

Баланс: {amount, number, currency}

С параметрами:

{
  amount: 1500
}

При этом формат валюты зависит от текущей локали контекста.


Округление и точность

Валюты требуют строгого контроля дробной части.

minimumFractionDigits и maximumFractionDigits

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  minimumFractionDigits: 0,
  maximumFractionDigits: 0
}).format(1234.56);
// $1,235

Особенности:

  • JPY обычно не использует дробную часть
  • USD и EUR используют 2 знака
  • некоторые валюты могут иметь 3–4 знака

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

ECMA-402 автоматически применяет стандартные правила валют:

  • JPY → 0 дробных знаков
  • USD → 2 дробных знака
  • KWD (кувейтский динар) → 3 дробных знака
new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
}).format(5000);
// ¥5,000

Compact notation и валюты

Для интерфейсов с большими числами используется компактная запись:

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  notation: 'compact'
}).format(1500000);
// $1.5M
new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'RUB',
  notation: 'compact'
}).format(1500000);
// 1,5 млн ₽

Сравнение currencyDisplay в разных локалях

Локаль symbol code name
en-US $1,000 USD 1,000 1,000 US dollars
de-DE 1.000 € EUR 1.000 1.000 Euro
ja-JP ¥1,000 JPY 1,000 1,000 Japanese yen

Использование FormatJS как слоя абстракции

FormatJS обеспечивает:

  • единое API поверх Intl
  • полифиллинг недостающих возможностей
  • интеграцию с React через react-intl
  • поддержку ICU MessageFormat
  • консистентное поведение между браузерами и Node.js

Валютное форматирование является одним из ключевых сценариев, где различия платформ особенно заметны.


Кастомизация вывода валют

Иногда требуется отклониться от стандартов локали.

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'narrowSymbol',
  useGrouping: false
}).format(1000000);
// $1000000

Обработка отрицательных значений

Разные локали по-разному отображают отрицательные суммы:

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(-500);
// -$500.00

Альтернативные форматы:

  • бухгалтерский стиль (accounting)
  • скобки вместо минуса (в некоторых локалях)
new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting'
}).format(-500);
// ($500.00)

Проблемы и ограничения валютного форматирования

При использовании FormatJS и Intl возникают типовые ограничения:

  • неполные данные в старых окружениях без полифиллов
  • различия между браузерами в legacy-режимах
  • невозможность расширить список валют вне ISO 4217
  • зависимость от CLDR-данных
  • различия в обновлении валютных правил между системами

Интеграция с React-экосистемой

В React-приложениях FormatJS часто используется через react-intl:

import { IntlProvider, FormattedNumber } from 'react-intl';

<IntlProvider locale="en-US">
  <FormattedNumber
    value={1234.56}
    style="currency"
    currency="USD"
  />
</IntlProvider>

Динамическое изменение локали автоматически пересчитывает валютное отображение без ручной логики.


Валюты в серверном рендеринге

FormatJS поддерживает SSR-сценарии:

  • предварительное форматирование на сервере
  • гидратация на клиенте
  • согласованность строк между окружениями
const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

formatter.format(999.99);

Сервер возвращает уже локализованную строку, уменьшая нагрузку клиента.


Использование currency в бизнес-логике UI

Валюта становится частью UI-логики:

  • отображение цен товаров
  • подписки и биллинг
  • финансовые отчёты
  • транзакции и история операций

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