Названия валют

В современном JavaScript работа с валютами выходит за рамки простого форматирования чисел. Требуется отображение человекочитаемых названий валют, адаптированных под локаль пользователя. Для этой задачи используется Intl.DisplayNames, поддерживающий тип "currency".

Ключевая особенность:

  • преобразование ISO-кодов валют (например, USD, EUR, KZT) в локализованные названия;
  • автоматическая адаптация под язык и регион;
  • отсутствие необходимости в ручных словарях перевода.

Пример создания экземпляра:

const currencyNames = new Intl.DisplayNames('ru', { type: 'currency' });

console.log(currencyNames.of('USD')); // доллар США
console.log(currencyNames.of('EUR')); // евро
console.log(currencyNames.of('JPY')); // японская иена

Внутри реализована связь с CLDR (Common Locale Data Repository), что обеспечивает единообразие терминологии в разных языках.


ISO 4217 как основа валютных идентификаторов

Валютные коды в Intl API базируются на стандарте ISO 4217, где каждая валюта представлена трёхбуквенным кодом.

Структура:

  • первые две буквы — код страны (чаще всего ISO 3166-1 alpha-2);
  • третья буква — обычно первая буква валюты.

Примеры:

Код Валюта
USD US Dollar
EUR Euro
GBP British Pound
KZT Kazakhstani Tenge
JPY Japanese Yen

Intl.DisplayNames работает строго с этими кодами. Любые отклонения от стандарта приводят к возврату undefined.


Локализация названий валют

Одно и то же значение валюты может иметь разные языковые формы в зависимости от локали.

Русская локализация

const ru = new Intl.DisplayNames('ru', { type: 'currency' });

ru.of('USD'); // доллар США
ru.of('EUR'); // евро

Английская локализация

const en = new Intl.DisplayNames('en', { type: 'currency' });

en.of('USD'); // US dollar
en.of('EUR'); // euro

Французская локализация

const fr = new Intl.DisplayNames('fr', { type: 'currency' });

fr.of('USD'); // dollar des États-Unis

Различия затрагивают:

  • порядок слов;
  • грамматические формы;
  • использование предлогов;
  • адаптацию названий под локальные нормы.

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

При передаче некорректного или несуществующего кода поведение строго определено:

const dn = new Intl.DisplayNames('ru', { type: 'currency' });

dn.of('XXX'); // undefined
dn.of('ABC'); // undefined

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

  • отсутствует исключение;
  • возвращается undefined;
  • ошибка не выбрасывается, что упрощает безопасную обработку данных.

Типичная практика — проверка результата:

const name = dn.of(code) ?? 'неизвестная валюта';

Отличие от Intl.NumberFormat

Intl.DisplayNames не занимается форматированием чисел. Его задача — название валюты, а не её отображение в денежном виде.

Intl.NumberFormat (форматирование)

const formatter = new Intl.NumberFormat('ru', {
  style: 'currency',
  currency: 'USD'
});

formatter.format(1000); // 1 000,00 $

Intl.DisplayNames (название валюты)

const names = new Intl.DisplayNames('ru', { type: 'currency' });

names.of('USD'); // доллар США

Разделение ответственности:

  • NumberFormat — денежные значения;
  • DisplayNames — текстовые названия сущностей.

Использование в интерфейсах

Выпадающие списки валют

Частый сценарий — генерация списка валют для выбора:

const currencies = ['USD', 'EUR', 'JPY', 'KZT'];

const dn = new Intl.DisplayNames('ru', { type: 'currency' });

const options = currencies.map(code => ({
  code,
  name: dn.of(code)
}));

Результат:

[
  { "code": "USD", "name": "доллар США" },
  { "code": "EUR", "name": "евро" },
  { "code": "JPY", "name": "японская иена" },
  { "code": "KZT", "name": "казахстанский тенге" }
]

Поддержка локалей и fallback-механизм

Если указана локаль, которая не поддерживается, происходит автоматический fallback:

const dn = new Intl.DisplayNames('xx-YY', { type: 'currency' });

dn.of('USD'); // будет использована ближайшая доступная локаль

Алгоритм:

  1. попытка точного совпадения локали;
  2. переход к базовой локали (например, xx);
  3. использование системного fallback (обычно английского).

Кэширование экземпляров

Создание Intl.DisplayNames — относительно дорогая операция. Поэтому применяется кэширование:

const cache = new Map();

function getCurrencyNames(locale) {
  if (!cache.has(locale)) {
    cache.set(locale, new Intl.DisplayNames(locale, { type: 'currency' }));
  }
  return cache.get(locale);
}

Преимущества:

  • снижение затрат на инициализацию;
  • ускорение рендеринга списков;
  • уменьшение нагрузки в SPA.

Интеграция с данными API

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

{
  "price": 1200,
  "currency": "USD"
}

Преобразование:

const dn = new Intl.DisplayNames('ru', { type: 'currency' });

function formatProduct(product) {
  return `${product.price} ${dn.of(product.currency)}`;
}

Особенности поведения в разных средах

Браузеры

  • полная поддержка в современных версиях;
  • обновления через ICU (International Components for Unicode).

Node.js

  • зависит от версии ICU;
  • при сборках с полной ICU поддержкой доступен полный набор локалей.

Проверка поддержки:

typeof Intl.DisplayNames === 'function'

Ограничения и нюансы

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

Сочетание с другими возможностями Intl

С Intl.NumberFormat

Комбинированное использование:

const number = new Intl.NumberFormat('ru-RU');
const names = new Intl.DisplayNames('ru', { type: 'currency' });

function format(amount, currency) {
  return `${number.format(amount)} ${names.of(currency)}`;
}

С Intl.Locale

Можно динамически выбирать локаль пользователя:

const locale = navigator.language;
const names = new Intl.DisplayNames(locale, { type: 'currency' });

Обработка больших наборов данных

При обработке массивов транзакций важна оптимизация:

const dn = new Intl.DisplayNames('ru', { type: 'currency' });

function enrichTransactions(transactions) {
  return transactions.map(t => ({
    ...t,
    currencyName: dn.of(t.currency)
  }));
}

Оптимизационный аспект:

  • один экземпляр DisplayNames на набор данных;
  • отсутствие повторного создания объектов.

Поведение при изменении локали

Экземпляр Intl.DisplayNames неизменяем после создания. Для смены локали требуется новый объект:

let dn = new Intl.DisplayNames('ru', { type: 'currency' });

dn = new Intl.DisplayNames('en', { type: 'currency' });

Практическая модель данных валют

При проектировании систем часто используется единая модель:

const Currency = {
  code: 'USD',
  getName(locale) {
    return new Intl.DisplayNames(locale, { type: 'currency' }).of(this.code);
  }
};

Такой подход:

  • централизует логику;
  • упрощает интернационализацию;
  • снижает дублирование кода.