Работа с валютами в JavaScript через Intl API опирается на стандарты Unicode CLDR и ISO 4217, однако исторический слой валютной системы остаётся важной частью интернационализации. Многие приложения сталкиваются с необходимостью корректного отображения денежных значений в контексте времени: старые финансовые данные, архивные транзакции, исторические отчёты и локализованные системы учёта требуют поддержки валют, которые уже вышли из обращения.
В основе Intl.NumberFormat лежит список валютных кодов
ISO 4217. Каждый код представляет собой трёхбуквенный идентификатор,
например:
Многие из этих валют были заменены евро или национальными реформами. Несмотря на это, они продолжают существовать в стандартах интернационализации для целей обратной совместимости.
Intl.NumberFormat не делает различий между «активными» и
«историческими» валютами на уровне API: если код присутствует в текущем
наборе CLDR данных, он считается валидным.
Использование исторических валютных кодов в форматировании не отличается от современных:
const formatter = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'DEM'
});
formatter.format(1234.56);
Результат будет локализован в соответствии с правилами немецкой локали, с применением символа немецкой марки (если он присутствует в данных окружения):
1.234,56 DM
Аналогично можно форматировать французский франк:
new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'FRF'
}).format(2500);
Даже если валюта историческая, форматирование остаётся консистентным, поскольку движок использует таблицы символов и правил отображения, а не актуальный статус валюты.
Поддержка исторических валют зависит от реализации ICU (International Components for Unicode), на которой основан JavaScript-движок.
Возможны три сценария:
Полная поддержка кода валюты Валюта присутствует в CLDR → отображается корректно.
Частичная поддержка Код распознаётся, но отсутствует локализованный символ → используется код валюты.
Отсутствие поддержки Возникает ошибка
RangeError:
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'XXX1'
});
Типичная ошибка:
RangeError: Invalid currency code : XXX1
Исторические валюты, включённые в ISO 4217, обычно не попадают в третий сценарий.
Одним из ключевых событий, повлиявших на набор «исторических валют», стал переход на евро. В 1999 году евро был введён как безналичная валюта, а в 2002 году произошла замена наличных валют в большинстве стран еврозоны.
В результате:
JavaScript не хранит информацию о временных периодах действия валюты, поэтому разработчик сам определяет контекст использования.
Пример отображения исторических значений до перехода:
const deMark = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'DEM'
});
const euro = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
deMark.format(1000);
euro.format(1000);
Формат отличается не только символом, но и локализационными правилами, которые могли меняться со временем.
Опция currencyDisplay влияет на то, как именно
отображается валюта:
symbol — стандартный символnarrowSymbol — компактный символcode — ISO кодname — полное название валютыДля исторических валют поведение особенно важно, поскольку символ может отсутствовать или быть неоднозначным.
new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'DEM',
currencyDisplay: 'name'
}).format(500);
Результат зависит от локали и данных ICU, например:
500 Deutsche Mark
Использование code обеспечивает стабильность:
currencyDisplay: 'code'
Вывод:
500 DEM
Этот режим предпочтителен для архивных данных, где важна точность, а не эстетика символа.
Исторические финансовые системы часто требуют различного
представления отрицательных значений. Опция currencySign
позволяет учитывать бухгалтерский формат:
standard — обычное отображениеaccounting — отрицательные значения в скобкахnew Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'FRF',
currencySign: 'accounting'
}).format(-1200);
Результат:
(1,200.00 FRF)
В исторических отчётах это особенно важно, поскольку бухгалтерские стандарты в разных странах могли существенно отличаться.
Unicode CLDR содержит не только символы валют, но и правила форматирования:
Для валют вроде ITL или ESP, которые использовались до перехода на евро, локализационные данные могут отличаться между версиями ICU.
Пример:
new Intl.NumberFormat('it-IT', {
style: 'currency',
currency: 'ITL'
}).format(10000);
Результат может включать тонкие различия в пробелах или
позиционировании символа ₤.
Некоторые валюты, существовавшие в прошлом, не входят в актуальный ISO 4217, но могут встречаться в архивных данных:
Поддержка таких кодов зависит от версии ICU. В современных окружениях часть из них может отсутствовать, что требует проверки через обработку ошибок:
function safeFormat(value, currency) {
try {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency
}).format(value);
} catch (e) {
return `${value} ${currency}`;
}
}
Такой подход позволяет сохранять работоспособность при работе с архивными финансовыми системами.
Исторические валюты часто имели неоднозначные символы:
Intl API устраняет эту неоднозначность через комбинацию локали и кода валюты. Однако при исторических данных возможны конфликты интерпретации символов.
new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'FRF',
currencyDisplay: 'symbol'
}).format(99);
Если символ не уникален, движок может использовать код валюты вместо символа.
Одна из сложностей исторических валют заключается в том, что локализация сама изменяется. Например:
Intl API всегда отражает современное состояние CLDR, а не историческое поведение. Поэтому:
new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'DEM'
}).format(10);
может отображаться по современным правилам немецкой локали, а не по правилам 1980-х годов.
При работе с историческими валютами важной становится детерминированность вывода. Для этого используются:
currencyDisplay: 'code'Пример стабильного форматирования:
new Intl.NumberFormat('en-GB', {
style: 'currency',
currency: 'ITL',
currencyDisplay: 'code',
minimumFractionDigits: 0
}).format(15000);
Такой подход минимизирует зависимость от локализационных данных, которые могут меняться между версиями движка.
Некоторые валюты имели нестандартные правила дробных единиц. Например:
Intl API не хранит исторические правила округления валют. Поэтому поведение определяется стандартными настройками:
new Intl.NumberFormat('es-ES', {
style: 'currency',
currency: 'ESP',
maximumFractionDigits: 0
}).format(1234.56);
Результат зависит от указанных параметров, а не от исторической финансовой системы.
Хотя многие валюты перестали существовать, их коды сохраняются по нескольким причинам:
Intl API использует этот принцип сохранения идентификаторов, что позволяет корректно отображать значения десятилетней и более давности без необходимости ручного маппинга символов.