В экосистеме FormatJS процесс интернационализации строится вокруг извлечения текстовых сообщений из исходного кода в структурированные файлы переводов. Этот этап является ключевым для масштабируемых приложений, так как позволяет отделить пользовательский интерфейс от языковых ресурсов и автоматизировать работу с переводами.
Извлечение сообщений выполняет функцию промежуточного слоя между кодовой базой и системами локализации. Исходный код содержит декларативные описания сообщений, а итоговые файлы переводов формируются автоматически на основе анализа AST (Abstract Syntax Tree).
Основные задачи процесса:
Важным принципом является отсутствие необходимости вручную поддерживать словари переводов в исходном коде.
В React и других JavaScript-приложениях на базе FormatJS сообщения
обычно задаются через defineMessages или компонент
FormattedMessage.
Пример декларации:
import { defineMessages } from 'react-intl';
const messages = defineMessages({
welcome: {
id: 'app.welcome',
defaultMessage: 'Добро пожаловать в систему',
description: 'Приветствие на главной странице'
},
logout: {
id: 'app.logout',
defaultMessage: 'Выйти'
}
});
Каждое сообщение содержит:
При использовании компонента:
import { FormattedMessage } from 'react-intl';
<FormattedMessage
id="app.title"
defaultMessage="Панель управления"
/>
Извлечение сообщений в FormatJS основано на статическом анализе кода с использованием Babel-плагинов. Исходный код преобразуется в AST, после чего специальные плагины обходят дерево и собирают сообщения.
Основной инструмент — babel-plugin-react-intl.
Принцип работы:
defineMessages,
formatMessage, FormattedMessage.defaultMessage.Настройка плагина выполняется через Babel-конфигурацию:
{
"plugins": [
[
"react-intl",
{
"messagesDir": "./build/messages/",
"extractSourceLocation": true
}
]
]
}
Параметры конфигурации:
react-intl при необходимости.После обработки формируется JSON-файл, содержащий нормализованные сообщения:
[
{
"id": "app.welcome",
"defaultMessage": "Добро пожаловать в систему",
"description": "Приветствие на главной странице",
"file": "src/components/Home.js",
"start": {
"line": 10,
"column": 5
}
}
]
Такая структура обеспечивает:
FormatJS предоставляет CLI для автоматизации извлечения сообщений без прямой настройки Babel.
Основная команда:
formatjs extract "src/**/*.js" --out-file messages.json
Дополнительные параметры:
--id-interpolation-pattern — шаблон генерации ID;--format — формат выходного файла;--extract-source-location — включение метаданных о
позиции;--ignore — исключение файлов из обработки.Пример использования шаблона идентификаторов:
formatjs extract "src/**/*.{js,ts,tsx}" \
--id-interpolation-pattern '[sha512:contenthash:base64:6]'
Такой подход позволяет отказаться от ручного задания id,
автоматически генерируя устойчивые идентификаторы.
В FormatJS поддерживаются несколько стратегий формирования
id:
Ручной режим Идентификатор задаётся явно разработчиком.
Хеширование содержимого Используется хеш от
defaultMessage.
Интерполяция шаблона Комбинация текста, файла и контекста.
Пример хеш-идентификатора:
{
"id": "a1b2c3",
"defaultMessage": "Сохранить изменения"
}
Преимущество хеширования заключается в отсутствии зависимости от структуры кода, но изменение текста приводит к смене идентификатора.
FormatJS поддерживает ICU Message Syntax, что позволяет включать в сообщения:
Пример:
<FormattedMessage
id="cart.items"
defaultMessage="{count, plural, one {# товар} few {# товара} many {# товаров} other {# товара}}"
values={{ count: 5 }}
/>
При извлечении такие сообщения сохраняются без изменений, так как ICU
выражение является частью строки defaultMessage.
Сложность возникает при использовании динамических сообщений:
intl.formatMessage({
id: `error.${code}`,
defaultMessage: 'Неизвестная ошибка'
});
Статический анализ в таких случаях:
id;defaultMessage;Динамические конструкции считаются нежелательными, так как затрудняют генерацию стабильных переводов.
FormatJS поддерживает TypeScript через Babel-пайплайн или
ts-jest/tsc совместимые конфигурации.
Пример TS-кода:
const messages = defineMessages({
title: {
id: 'page.title',
defaultMessage: 'Заголовок страницы'
}
});
Извлечение происходит аналогично JavaScript, так как TypeScript преобразуется в AST до анализа.
Извлечение сообщений обычно включается в pipeline:
Пример шагов CI:
formatjs extract "src/**/*.{js,ts,tsx}" --out-file build/messages.json
node scripts/upload-translations.js
В некоторых системах применяется fail-fast стратегия: сборка прерывается при появлении новых неразмеченных сообщений.
Процесс статического извлечения имеет ряд ограничений:
Особенно проблемными являются случаи:
Для повышения качества извлечения применяется ряд практик:
id;defineMessages вместо inline-строк;description для контекста.Такая структура улучшает:
Извлечение сообщений является частью более широкой цепочки:
IntlProvider.Каждый этап зависит от стабильности извлечённых данных, что делает этот процесс фундаментальным элементом архитектуры FormatJS.