В основе работы Intl лежит параметр
локали, определяющий правила форматирования дат, чисел,
строк и других данных. Локаль задаётся строкой, например:
ru-RU — русский (Россия)en-US — английский (США)de-DE — немецкий (Германия)Каждый форматтер в Intl принимает локаль как первый
аргумент:
const formatter = new Intl.DateTimeFormat('ru-RU');
Ключевая особенность заключается в том, что локаль не является глобальным состоянием. Она фиксируется в момент создания экземпляра форматтера, и дальнейшие изменения не влияют на уже созданные объекты.
Экземпляры Intl-классов являются
иммутабельными. После создания они не реагируют на
смену языка окружения.
let locale = 'ru-RU';
const formatter = new Intl.NumberFormat(locale);
locale = 'en-US';
formatter.format(1234567.89); // формат всё равно по ru-RU
Это поведение критично для производительности: форматтеры кэшируются движком и не пересобираются автоматически.
В прикладных интерфейсах язык часто изменяется во время работы приложения. Это требует пересоздания всех форматтеров, зависящих от локали.
Типичная задача:
Без пересоздания Intl-объектов данные остаются на старом
языке.
Базовый подход заключается в выделении глобального состояния локали:
let currentLocale = 'ru-RU';
function setLocale(newLocale) {
currentLocale = newLocale;
}
Любой форматтер создаётся на основе этого значения:
function formatDate(date) {
return new Intl.DateTimeFormat(currentLocale).format(date);
}
Такой подход гарантирует синхронность отображения данных при смене языка.
Наиболее важный механизм динамической локализации — обновление всех зависимых экземпляров Intl.
let currentLocale = 'ru-RU';
let numberFormatter = new Intl.NumberFormat(currentLocale);
let dateFormatter = new Intl.DateTimeFormat(currentLocale);
function setLocale(newLocale) {
currentLocale = newLocale;
numberFormatter = new Intl.NumberFormat(currentLocale);
dateFormatter = new Intl.DateTimeFormat(currentLocale);
}
После вызова setLocale все последующие форматирования
автоматически используют новую локаль.
Вместо хранения множества экземпляров часто используется ленивое создание:
let currentLocale = 'ru-RU';
function getNumberFormatter() {
return new Intl.NumberFormat(currentLocale);
}
function formatNumber(value) {
return getNumberFormatter().format(value);
}
Этот подход упрощает поддержку смены языка, но увеличивает количество созданий объектов.
Оптимизированный вариант — кэш по ключу локали.
const numberFormatCache = new Map();
function getNumberFormatter(locale) {
if (!numberFormatCache.has(locale)) {
numberFormatCache.set(locale, new Intl.NumberFormat(locale));
}
return numberFormatCache.get(locale);
}
Использование:
function formatNumber(value, locale) {
return getNumberFormatter(locale).format(value);
}
При смене языка достаточно менять только ключ доступа.
Динамическая смена языка в приложении требует согласованного обновления:
Пример:
function renderPrice(value) {
return new Intl.NumberFormat(currentLocale, {
style: 'currency',
currency: currentLocale === 'ru-RU' ? 'RUB' : 'USD'
}).format(value);
}
При смене локали пересоздаётся не только форматтер, но и сопутствующие параметры.
Intl.Collator отвечает за правила сортировки строк,
которые также зависят от локали.
let currentLocale = 'ru-RU';
let collator = new Intl.Collator(currentLocale);
function sortItems(items) {
return items.sort((a, b) => collator.compare(a, b));
}
После смены языка требуется обновление экземпляра:
function setLocale(newLocale) {
currentLocale = newLocale;
collator = new Intl.Collator(currentLocale);
}
Intl.DateTimeFormat учитывает локальные особенности:
let dateFormatter = new Intl.DateTimeFormat(currentLocale, {
year: 'numeric',
month: 'long',
day: 'numeric'
});
При смене языка поведение полностью меняется, несмотря на неизменность входных данных.
При частой смене локали важны следующие принципы:
Пример структурированного кэша:
const cache = {
number: new Map(),
date: new Map()
};
function getFormatter(type, locale) {
if (!cache[type].has(locale)) {
if (type === 'number') {
cache[type].set(locale, new Intl.NumberFormat(locale));
}
if (type === 'date') {
cache[type].set(locale, new Intl.DateTimeFormat(locale));
}
}
return cache[type].get(locale);
}
В реактивных архитектурах смена языка обычно триггерит перерендер:
localeПринцип:
function useLocale(locale) {
return {
formatNumber: (value) => new Intl.NumberFormat(locale).format(value),
formatDate: (date) => new Intl.DateTimeFormat(locale).format(date)
};
}
Каждое изменение локали автоматически создаёт новую «область форматирования».
Некоторые движки могут:
Однако логика разработчика не должна зависеть от этих оптимизаций. Поведение всегда строится на явном пересоздании объектов.
1. Хранение устаревших форматтеров
const formatter = new Intl.NumberFormat(locale);
function updateLocale(newLocale) {
locale = newLocale;
// formatter не обновлён
}
2. Смешивание локалей в одном кэше
Кэш без учёта locale приводит к некорректному отображению данных.
3. Использование глобального formatter без пересоздания
Приводит к рассинхронизации интерфейса.
Intl учитывает не только локаль, но и набор опций:
new Intl.NumberFormat(locale, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
При смене языка требуется учитывать, что комбинация
locale + options определяет уникальный форматтер, а значит
ключ кэша должен включать оба параметра.
Intl.PluralRules также зависит от локали:
let pluralRules = new Intl.PluralRules(currentLocale);
function getPluralForm(n) {
return pluralRules.select(n);
}
При смене языка требуется пересоздание, иначе правила множественного числа будут некорректны.
Динамическая смена языка в приложении на базе Intl
строится на трёх принципах:
Эта модель обеспечивает согласованное поведение форматирования без скрытых зависимостей от состояния окружения.