В JavaScript операции форматирования дат, чисел и валют базируются на
спецификации ECMAScript Internationalization API (Intl). При работе
через Globalize надстройка использует Intl-объекты, но
добавляет собственный слой управления данными CLDR и фабриками
форматирования.
Ключевая проблема при масштабных приложениях — частое создание форматтеров, которое приводит к лишним затратам памяти и времени. Любой вызов вида:
new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB" })
создаёт новый объект форматтера. При высокочастотных операциях (рендер списков, таблиц, отчётов) это становится узким местом.
Основная идея переиспользования заключается в том, что форматтеры являются иммутабельными и потокобезопасными в рамках одного окружения выполнения JavaScript. Это означает, что один и тот же экземпляр может безопасно использоваться многократно.
В Globalize этот принцип реализуется через:
Globalize строится вокруг нескольких уровней:
Форматтер в Globalize — это не просто объект, а функция, связанная с локалью и набором параметров.
Числовые форматтеры — наиболее часто используемые в UI (цены, проценты, количество).
Проблема:
Globalize("ru").numberFormatter({ style: "decimal" });
Globalize("ru").numberFormatter({ style: "decimal" });
Каждый вызов потенциально создаёт новый форматтер.
const globalize = Globalize("ru");
const formatNumber = globalize.numberFormatter({
style: "decimal",
minimumFractionDigits: 2
});
formatNumber(1234.5);
formatNumber(6789.1);
Здесь создаётся один экземпляр функции, который используется многократно.
Форматирование валют особенно дорого из-за обработки символов валюты и правил округления.
const formatRuble = Globalize("ru").currencyFormatter("RUB");
formatRuble(1000);
formatRuble(2500);
При правильной организации архитектуры приложения такие форматтеры должны создаваться один раз на уровне модуля или сервиса.
Для крупных приложений используется централизованная фабрика:
const formatters = (() => {
const g = Globalize("ru");
const cache = new Map();
function getNumberFormatter(options) {
const key = JSON.stringify(options);
if (!cache.has(key)) {
cache.set(key, g.numberFormatter(options));
}
return cache.get(key);
}
return {
number: getNumberFormatter
};
})();
Такой подход устраняет повторное создание форматтеров при одинаковых параметрах.
Мемоизация особенно полезна при динамических параметрах:
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (!cache.has(key)) {
cache.set(key, fn(...args));
}
return cache.get(key);
};
}
const g = Globalize("ru");
const currencyFormatter = memoize((currency) =>
g.currencyFormatter(currency)
);
const formatUSD = currencyFormatter("USD");
const formatEUR = currencyFormatter("EUR");
Форматирование дат часто встречается в логах, таблицах и интерфейсах.
const g = Globalize("ru");
const shortDate = g.dateFormatter({ date: "short" });
const longDate = g.dateFormatter({ date: "long" });
shortDate(new Date());
longDate(new Date());
Переиспользование критично при массовом рендере списков.
Каждый форматтер привязан к:
ru, en, de)Изменение локали означает невозможность повторного использования старых форматтеров.
const ru = Globalize("ru");
const en = Globalize("en");
const ruFormatter = ru.numberFormatter();
const enFormatter = en.numberFormatter();
Форматтеры разных локалей не пересекаются и должны кешироваться отдельно.
При проектировании системы кеширования важно определить уровень детализации ключей:
currency)currency + RUB)currency + RUB + accounting)Избыточная детализация увеличивает память, недостаточная — снижает эффективность кеша.
Частые анти-паттерны:
// Плохо: создание на каждый рендер
function render(price) {
return Globalize("ru")
.currencyFormatter("RUB")(price);
}
Проблема:
const g = Globalize("ru");
const formatPrice = g.currencyFormatter("RUB");
function render(price) {
return formatPrice(price);
}
Создание вынесено за пределы горячего пути выполнения.
В архитектурах с DI форматтеры передаются как зависимости:
class PriceService {
constructor(formatCurrency) {
this.formatCurrency = formatCurrency;
}
format(price) {
return this.formatCurrency(price);
}
}
const g = Globalize("ru");
const service = new PriceService(g.currencyFormatter("RUB"));
Такой подход снижает связность и позволяет тестировать логику без создания форматтеров.
В реальных приложениях переиспользование особенно важно в:
Каждое повторное создание форматтера в таких сценариях масштабируется в значительные затраты CPU.
Наиболее эффективная модель:
const formatterCache = new Map();
function getFormatter(locale, type, options) {
const key = `${locale}:${type}:${JSON.stringify(options)}`;
if (!formatterCache.has(key)) {
const g = Globalize(locale);
formatterCache.set(
key,
g[type + "Formatter"](options)
);
}
return formatterCache.get(key);
}
Форматтеры являются долгоживущими объектами. При неправильном кешировании возможны:
Решения включают:
В системах интернационализации повторное использование форматтеров становится не оптимизацией, а архитектурным стандартом. Globalize предоставляет инструменты для построения таких систем через фабрики и привязку к локали, позволяя минимизировать накладные расходы при сохранении гибкости форматирования.