Развитие веб-приложений в начале 2010-х годов привело к резкому росту требований к интернационализации интерфейсов. Если ранние сайты ограничивались несколькими статичными строками и примитивной подстановкой переводов, то современные SPA-приложения начали сталкиваться с гораздо более сложными задачами:
Проблема особенно обострилась после массового перехода к архитектуре Single Page Application. Библиотеки вроде React, Angular и Vue сделали интерфейсы динамическими, однако экосистема локализации оставалась фрагментированной. Разработчики использовали:
Подобный подход быстро приводил к хаосу:
const messages = {
en: {
items: count => `${count} items`,
},
ru: {
items: count => {
if (count === 1) return `${count} товар`;
if (count < 5) return `${count} товара`;
return `${count} товаров`;
}
}
};
С ростом приложения количество подобных конструкций становилось неконтролируемым.
Ключевым событием для будущего FormatJS стало внедрение объекта
Intl в стандарт ECMAScript.
Интернационализация в JavaScript долгое время отсутствовала как полноценная часть языка. Форматирование дат выполнялось вручную или через сторонние библиотеки:
new Date().toLocaleString()
Поведение подобных методов отличалось между браузерами и не предоставляло гибкой настройки.
Ситуация изменилась после появления спецификации ECMAScript
Internationalization API, известной как Intl.
Первые стабильные возможности появились в:
Intl.NumberFormatIntl.DateTimeFormatIntl.CollatorПример:
const formatter = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
console.log(formatter.format(1000));
// 1.000,00 €
Появление Intl стало фундаментом для целого поколения
библиотек интернационализации.
Библиотека FormatJS появилась как попытка построить полноценную
экосистему интернационализации поверх стандарта Intl.
Проект был создан инженерами Yahoo. Главной целью стала унификация работы с локализацией для крупных JavaScript-приложений.
Ключевые задачи проекта:
Intl;Первые версии FormatJS представляли собой набор утилит для:
Позже проект превратился в полноценный монорепозиторий.
Одной из важнейших частей эволюции FormatJS стала интеграция ICU MessageFormat.
ICU (International Components for Unicode) — международный стандарт локализации, используемый во множестве платформ:
ICU MessageFormat позволял описывать сложные языковые конструкции декларативно.
Пример:
{count, plural,
=0 {Нет сообщений}
one {# сообщение}
few {# сообщения}
many {# сообщений}
other {# сообщения}
}
Подобный подход радикально отличался от ручных
if/else.
FormatJS сделал ICU центральным механизмом локализации JavaScript-приложений.
Настоящий рост популярности FormatJS начался вместе с распространением React.
React изменил подход к разработке интерфейсов:
Старые системы локализации плохо вписывались в React-компоненты. Требовалась библиотека, которая:
Так появился пакет react-intl.
Пример раннего использования:
<IntlProvider locale="en" messages={messages}>
<App />
</IntlProvider>
Внутри компонентов:
<FormattedMessage
id="app.welcome"
defaultMessage="Welcome, {name}"
values={{ name: 'John' }}
/>
react-intl стал одной из первых крупных систем
локализации, полностью адаптированных под React-архитектуру.
Со временем библиотека перестала быть просто React-решением.
Экосистема начала делиться на независимые пакеты:
| Пакет | Назначение |
|---|---|
react-intl |
React-интеграция |
intl-messageformat |
ICU MessageFormat |
intl-relativeformat |
Относительное время |
intl-numberformat |
Полифиллы чисел |
intl-datetimeformat |
Форматирование дат |
babel-plugin-react-intl |
Извлечение переводов |
@formatjs/cli |
CLI-инструменты |
Подобная модульность решила сразу несколько проблем:
Важной частью истории FormatJS стала борьба с несовместимостью браузеров.
Несмотря на существование стандарта Intl, ранние версии
браузеров поддерживали его частично:
Особенно проблемными были:
FormatJS начал активно развивать систему полифиллов.
Пример:
import '@formatjs/intl-pluralrules/polyfill';
import '@formatjs/intl-pluralrules/locale-data/ru';
Это позволило использовать единый API независимо от окружения.
По мере развития ECMAScript библиотека адаптировалась к новым стандартам.
Появились:
Intl.RelativeTimeFormatIntl.PluralRulesIntl.ListFormatIntl.DisplayNamesFormatJS быстро внедрял поддержку новых возможностей.
Пример относительного времени:
const rtf = new Intl.RelativeTimeFormat('ru', {
numeric: 'auto'
});
rtf.format(-1, 'day');
// вчера
Ранее подобное поведение приходилось реализовывать вручную.
Одной из ключевых проблем локализации всегда оставалось управление переводами.
Ранние проекты хранили строки прямо в коде:
t('Hello world')
Однако крупные приложения сталкивались с трудностями:
FormatJS предложил систему автоматического извлечения сообщений.
Пример:
<FormattedMessage
id="profile.title"
defaultMessage="User profile"
/>
CLI-инструменты могли автоматически извлекать все сообщения:
formatjs extract "src/**/*.{js,jsx,ts,tsx}"
Это стало важным шагом к промышленной локализации frontend-приложений.
После выхода React Hooks библиотека начала адаптироваться к новым практикам.
Ранее основным подходом были HOC и компоненты:
injectIntl(Component)
или
<FormattedMessage />
Позже появился hook useIntl.
Пример:
const intl = useIntl();
intl.formatMessage({
id: 'menu.settings'
});
Это значительно улучшило:
Рост популярности TypeScript оказал серьёзное влияние на развитие FormatJS.
Ранние версии библиотеки создавались преимущественно для JavaScript. Однако с распространением TypeScript потребовалась:
Появились типизированные подходы:
intl.formatMessage(
{
id: 'user.greeting',
defaultMessage: 'Hello {name}'
},
{
name: 'Alex'
}
);
Со временем экосистема начала включать:
На ранних этапах ICU MessageFormat считался достаточно тяжёлым.
Парсинг сообщений:
{count, plural, one {# item} other {# items}}
требовал:
В больших приложениях это создавало проблемы производительности.
FormatJS начал внедрять:
Пример предварительной компиляции:
formatjs compile messages.json
Это уменьшало нагрузку во время выполнения.
С распространением SSR и Next.js библиотека адаптировалась под серверный рендеринг.
Основные проблемы SSR:
FormatJS постепенно стал поддерживать:
Это сделало библиотеку пригодной для enterprise-уровня.
Современный FormatJS — уже не просто библиотека, а крупная инфраструктура интернационализации.
Экосистема включает:
Архитектура проекта стала ориентироваться на:
FormatJS существенно повлиял на способы локализации JavaScript-приложений.
До его появления локализация часто воспринималась как второстепенная задача. После распространения React Intl многие практики стали индустриальным стандартом:
Многие современные библиотеки заимствовали идеи FormatJS:
Современные версии FormatJS ориентированы на:
Особое внимание уделяется:
FormatJS остаётся одной из самых влиятельных библиотек интернационализации в JavaScript-экосистеме и фактически сформировал современный подход к локализации frontend-приложений.