Babel плагины

FormatJS опирается на концепцию компиляции интернационализации на этапе сборки. В этой архитектуре ключевую роль выполняют Babel-плагины, обеспечивающие статический анализ сообщений, извлечение переводов и трансформацию кода в оптимизированный формат для выполнения в рантайме.

Babel используется как промежуточный слой между исходным JavaScript/TypeScript кодом и финальным бандлом. Плагины FormatJS интегрируются в pipeline трансформации AST и позволяют обрабатывать ICU-сообщения, JSX-компоненты и API интернационализации без дополнительной ручной обработки.


Архитектурная модель обработки сообщений

Основная идея Babel-плагинов FormatJS заключается в переносе части работы с интернационализацией из runtime в build-time. Это позволяет:

  • извлекать сообщения из исходного кода
  • формировать структурированные файлы переводов
  • оптимизировать идентификаторы сообщений
  • удалять метаданные в production-сборке
  • снижать стоимость выполнения форматирования строк

Сообщения интернационализации в FormatJS чаще всего используют ICU Message Format, который поддерживает:

  • plural rules
  • выбор по полу и другим категориям
  • вложенные структуры
  • форматирование чисел, дат и валют

Babel-плагины анализируют AST и находят вызовы функций или JSX-компоненты, содержащие такие сообщения.


babel-plugin-formatjs: ядро извлечения сообщений

Основной плагин экосистемы — babel-plugin-formatjs. Он выполняет статический анализ кода и извлекает сообщения из следующих конструкций:

  • defineMessages
  • <FormattedMessage />
  • intl.formatMessage
  • useIntl().formatMessage

Пример обрабатываемого кода:

import { defineMessages } from 'react-intl';

const messages = defineMessages({
  title: {
    id: 'app.title',
    defaultMessage: 'Dashboard'
  },
  unread: {
    defaultMessage: 'Unread messages: {count}'
  }
});

После обработки Babel формирует JSON-структуру:

{
  "app.title": {
    "defaultMessage": "Dashboard"
  },
  "generated_id_1": {
    "defaultMessage": "Unread messages: {count}"
  }
}

Генерация идентификаторов

Плагин поддерживает несколько стратегий идентификаторов:

  • явные id, заданные разработчиком
  • хеширование defaultMessage
  • генерация стабильных hash-based ключей
  • fallback на описательные ключи

Использование хеширования снижает вероятность коллизий и облегчает дедупликацию строк в больших приложениях.


Статический анализ AST

Работа плагина основана на обходе AST-дерева Babel. Основные этапы:

  1. Поиск импортов из модулей интернационализации
  2. Идентификация вызовов функций форматирования
  3. Анализ JSX-элементов
  4. Извлечение литеральных строк
  5. Построение таблицы сообщений

Особое внимание уделяется определению статических значений. Динамические выражения:

formatMessage({ defaultMessage: getTitle() })

не могут быть извлечены полностью и либо игнорируются, либо частично маркируются.


Обработка ICU-сообщений

ICU Message Format требует специализированного парсинга. Babel-плагин не просто извлекает строки, но и проверяет их корректность.

Пример:

formatMessage({
  defaultMessage: 'You have {count, plural, one {# message} other {# messages}}'
});

На этапе анализа выполняется:

  • разбор plural-выражений
  • проверка синтаксиса ICU
  • нормализация вложенных блоков
  • подготовка структуры для runtime-рендеринга

Ошибки в ICU-синтаксисе могут быть обнаружены на этапе сборки, что исключает runtime-исключения.


Интеграция с react-intl

Экосистема часто используется совместно с react-intl. Babel-плагины обеспечивают совместимость между JSX-компонентами и системой извлечения сообщений.

Поддерживаемые конструкции:

<FormattedMessage
  defaultMessage="Profile"
  description="User profile title"
/>

Плагин преобразует JSX в структуру сообщений, извлекая:

  • defaultMessage
  • description
  • id (если указан)

Удаление метаданных в production-сборке

Одной из задач Babel-плагина является оптимизация production-кода. В процессе трансформации могут быть удалены:

  • description полей
  • dev-only id генераторы
  • debug-метки
  • вспомогательные свойства

Это снижает размер итогового бандла и уменьшает количество ненужных вычислений.


babel-plugin-formatjs и CLI workflow

Плагин часто используется вместе с CLI-инструментами FormatJS для формирования файлов переводов.

Типичный pipeline:

  1. Babel проходит по исходному коду

  2. извлекает сообщения в JSON

  3. CLI объединяет сообщения из разных файлов

  4. формируется master catalog

  5. выполняется экспорт в форматы:

    • JSON
    • PO (gettext)
    • CSV

Структура каталога может выглядеть следующим образом:

{
  "en": {
    "app.title": "Dashboard",
    "generated_id_1": "Unread messages: {count}"
  },
  "ru": {
    "app.title": "Панель управления"
  }
}

Дедупликация и нормализация сообщений

Babel-плагин выполняет оптимизацию повторяющихся строк. Если несколько компонентов используют одинаковый defaultMessage, система:

  • объединяет сообщения
  • переиспользует идентификаторы
  • снижает размер translation catalog

Нормализация включает:

  • обрезку пробелов
  • унификацию переносов строк
  • стандартизацию ICU-формата

Поддержка TypeScript и JSX

При использовании TypeScript плагин анализирует:

  • типизированные вызовы formatMessage
  • generic-структуры MessageDescriptor
  • JSX-компоненты с пропсами интернационализации

AST-анализ выполняется независимо от типов, но учитывает синтаксическую структуру, включая:

  • TSX
  • optional chaining
  • nullish coalescing

Псевдолокализация и тестовые режимы

В рамках FormatJS Babel-плагины могут участвовать в генерации псевдолокалей (pseudo-locales), используемых для тестирования UI.

Пример трансформации:

Dashboard → [Ďåšhbôãřd]

Это позволяет выявлять:

  • переполнение UI
  • отсутствие локализации строк
  • ошибки в форматировании ICU

Ограничения статического анализа

Несмотря на мощь AST-парсинга, существуют ограничения:

  • динамически сформированные сообщения недоступны для извлечения
  • шаблоны с runtime-конкатенацией игнорируются
  • вычисляемые ключи id не могут быть стабильно определены

Пример проблемного случая:

formatMessage({
  defaultMessage: 'Hello ' + user.name
});

Такой код не подлежит корректной интерпретации Babel-плагином.


Производительность сборки

Использование Babel-плагинов FormatJS влияет на build-time:

  • увеличивается время трансформации AST
  • растёт нагрузка на парсинг JSX
  • добавляется этап генерации catalog

Однако это компенсируется:

  • отсутствием runtime-парсинга сообщений
  • снижением объёма бандла
  • ускорением выполнения formatMessage в браузере

Механизмы кеширования результатов

Некоторые конфигурации сборщиков поддерживают кеширование результатов Babel-плагинов. Это позволяет:

  • избегать повторного анализа неизменённых файлов
  • ускорять incremental builds
  • переиспользовать AST-хэши

Особенно эффективно при больших monorepo-структурах.


Конфигурационные параметры

Плагин babel-plugin-formatjs поддерживает параметры:

  • idInterpolationPattern
  • extractFromFormatMessageCall
  • removeDefaultMessage
  • additionalFunctionNames
  • ast режим

Пример конфигурации:

{
  "plugins": [
    ["formatjs", {
      "removeDefaultMessage": false,
      "idInterpolationPattern": "[sha512:contenthash:base64:6]"
    }]
  ]
}

Взаимодействие с другими Babel-плагинами

FormatJS-плагины часто комбинируются с:

  • transform-runtime
  • React Refresh
  • TypeScript preset
  • minification plugins

Порядок выполнения влияет на результат, поскольку AST может быть модифицирован до или после извлечения сообщений.


Обработка вложенных компонентов и HOC

При использовании Higher-Order Components и render props анализ усложняется:

withIntl(MyComponent)

Плагин должен:

  • отслеживать обёртки
  • находить проброшенные intl пропсы
  • анализировать глубоко вложенные вызовы

Это требует расширенного AST traversal с контекстной привязкой.


Роль в современной архитектуре интернационализации

Babel-плагины FormatJS формируют слой compile-time интернационализации, который:

  • переносит обработку сообщений из runtime в build-time
  • обеспечивает строгую структуру переводов
  • минимизирует вероятность отсутствующих ключей
  • стандартизирует ICU-сообщения в проекте

Интеграция с FormatJS и Babel делает систему частью конвейера сборки, а не runtime-библиотекой обработки текста.