При серверном рендеринге (SSR) интернационализация становится одной
из наиболее дорогих операций в конвейере генерации HTML. Основные
затраты связаны не столько с форматированием строк, сколько с созданием
и повторным созданием объектов Intl:
Intl.DateTimeFormatIntl.NumberFormatIntl.PluralRulesIntl.RelativeTimeFormatКаждый из этих объектов в JavaScript создаётся относительно дорого, особенно при большом количестве локалей и частых запросах. При SSR (например, в среде Node.js или фреймворках вроде React в составе Next.js) нагрузка масштабируется линейно с числом запросов.
Ключевая проблема заключается в повторяемости вычислений:
Это делает кэширование не оптимизацией, а необходимостью.
FormatJS предоставляет встроенную концепцию кеша через
createIntlCache.
Основная идея:
Типовая структура:
import { createIntl, createIntlCache } from 'react-intl';
const cache = createIntlCache();
const intl = createIntl(
{
locale: 'ru-RU',
messages: {}
},
cache
);
Кеш в FormatJS не является глобальным. Это намеренное решение:
Использование глобальных структур вида:
const globalCache = new Map();
приводит к проблемам в серверной среде:
Утечка памяти
Пересечение контекста
Непредсказуемое поведение при смене локали
Поэтому в SSR-контексте кеш должен быть:
Оптимальная модель при SSR:
HTTP Request
↓
createIntlCache()
↓
createIntl(locale, messages, cache)
↓
Render React tree
↓
Destroy cache
Такой подход гарантирует:
Наиболее затратная часть — создание объектов Intl.
FormatJS внутренне опирается на переиспользование:
DateTimeFormatNumberFormatPluralRulesКлючевой механизм — memoization по параметрам:
Пример логики кэш-ключа:
locale + type + JSON.stringify(options)
Частая ошибка SSR-систем — создание новых объектов конфигурации:
formatDate(date, {
year: 'numeric',
month: 'long',
day: '2-digit'
});
Если объект создаётся каждый раз заново, кеш теряет эффективность.
Оптимизация:
const DATE_FORMAT = {
year: 'numeric',
month: 'long',
day: '2-digit'
};
В контексте FormatJS это увеличивает hit-rate кеша, поскольку ключи становятся стабильными.
При серверной локализации ключевой фактор — разделение по языкам.
Типовая структура кеша:
cache
├── ru-RU
│ ├── DateTimeFormat
│ ├── NumberFormat
│ └── PluralRules
├── en-US
│ ├── DateTimeFormat
│ ├── NumberFormat
Важно:
В связке SSR + клиентская гидратация в React возникает проблема согласованности:
Решение:
FormatJS использует ICU MessageFormat, который компилируется в функции.
Этапы:
Кеширование применяется на уровне:
Пример:
const messages = {
welcome: "Привет, {name}"
};
При повторных рендерах:
В высоконагруженных SSR-системах часто добавляют внешний слой LRU:
Пример стратегии:
Это особенно важно при:
При SSR в Node.js несколько запросов могут одновременно обращаться к одному кешу.
Проблемы:
Решение:
На практике наиболее дорогие операции:
DateTimeFormat.formatNumberFormat.formatПричина:
Стратегии оптимизации:
В зрелых архитектурах SSR слой выполняет роль фабрики:
Внутри FormatJS это позволяет:
Каждая из этих ошибок приводит к:
При росте трафика основная цель кеширования:
Эффективная модель:
Такая иерархия минимизирует повторные вычисления и удерживает стабильную производительность даже при большом количестве локалей и параллельных SSR-запросов.