i18next-scanner для извлечения ключей

i18next-scanner представляет собой инструмент статического анализа исходного кода, предназначенный для автоматического извлечения ключей переводов для библиотеки интернационализации i18next. Основная задача — избавить проект от ручного ведения словарей, минимизировать расхождения между кодом и файлами локализации, а также стандартизировать процесс обновления переводов.

Анализ выполняется на уровне исходных файлов без запуска приложения. Инструмент проходит по дереву проекта, разбирает код и ищет вызовы функций, соответствующих паттернам использования i18next.

Основные источники извлечения:

  • вызовы t('key')
  • обращения через i18next.t('key')
  • использование React-хука useTranslation
  • шаблоны с параметрами интерполяции
  • JSX-строки внутри компонентов

Парсинг основан на AST (Abstract Syntax Tree), что позволяет точно определять структуру кода, а не полагаться на регулярные выражения.

Базовая архитектура обработки

Процесс извлечения можно разделить на несколько этапов:

  1. Чтение файлов проекта
  2. Построение AST
  3. Поиск узлов, соответствующих функциям перевода
  4. Извлечение ключей и контекста
  5. Агрегация результатов
  6. Запись в JSON-файлы локализации

Каждый этап может быть расширен через плагины и пользовательские трансформеры.

Конфигурация i18next-scanner

Основной способ настройки — файл конфигурации i18next-scanner.config.js.

Пример базовой конфигурации:

module.exports = {
  input: [
    'src/**/*.{js,jsx,ts,tsx}'
  ],
  output: './',

  options: {
    debug: false,
    removeUnusedKeys: true,
    sort: true,
    func: {
      list: ['t', 'i18next.t'],
      extensions: ['.js', '.jsx', '.ts', '.tsx']
    },
    lngs: ['en', 'ru'],
    defaultLng: 'en',
    defaultNs: 'translation',
    resource: {
      loadPath: 'locales/{{lng}}/{{ns}}.json',
      savePath: 'locales/{{lng}}/{{ns}}.json',
      jsonIndent: 2
    }
  }
};

Ключевые параметры:

  • input — набор файлов для анализа
  • output — директория для результатов
  • func.list — список функций, содержащих ключи
  • lngs — список языков проекта
  • defaultNs — пространство имён переводов
  • resource.savePath — путь записи JSON

Извлечение ключей из функций

Наиболее распространённый сценарий — анализ вызовов t().

Пример кода:

t('home.title');
t('button.save');

i18next-scanner извлекает строки home.title и button.save, формируя структуру JSON:

{
  "home": {
    "title": ""
  },
  "button": {
    "save": ""
  }
}

Поддерживаются вложенные ключи через точечную нотацию.

Работа с React и useTranslation

В React-компонентах часто используется хук:

const { t } = useTranslation();

return <h1>{t('dashboard.header')}</h1>;

Инструмент корректно определяет вызов t внутри JSX и извлекает ключ dashboard.header, даже если он находится внутри выражения.

Дополнительно поддерживаются:

t('key', { value: 10 });
t('key', { defaultValue: 'text' });

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

Пространства имён (namespaces)

i18next-scanner поддерживает разделение переводов на namespaces.

Пример:

t('auth:login.title');
t('profile:settings.name');

Результат распределяется по соответствующим файлам:

locales/
  en/
    auth.json
    profile.json

Если namespace не указан, используется defaultNs.

Обработка множественных форм и контекста

Библиотека i18next поддерживает pluralization и context:

t('item', { count: 5 });
t('button', { context: 'mobile' });

i18next-scanner фиксирует базовый ключ item и button, а расширения форм формируются уже на этапе runtime i18next.

Для множественных форм структура может выглядеть так:

{
  "item": {
    "one": "",
    "other": ""
  }
}

Игнорирование и фильтрация ключей

Для исключения определённых частей кода используются ignore-паттерны:

ignore: ['**/node_modules/**', '**/*.test.js']

Также можно исключать динамические вызовы:

t(variableKey);

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

Динамические ключи и ограничения анализа

Одним из ключевых ограничений является невозможность точного извлечения динамических значений:

t(`error.${code}`);

AST-аналитика фиксирует только факт вызова функции, но не может предсказать конечное значение ключа. Это приводит к необходимости:

  • либо заранее перечислять возможные ключи
  • либо использовать ручные словари
  • либо расширять scanner через custom transformer

Кастомные трансформеры

i18next-scanner поддерживает пользовательские трансформеры AST.

Пример расширения:

module.exports = {
  transform: function (file, enc, done) {
    const parser = this.parser;

    const content = file.contents.toString();
    parser.parseFuncFromString(content, { list: ['t'] }, (key) => {
      parser.set(key, '');
    });

    done();
  }
};

Такая модель позволяет обрабатывать нестандартные конструкции, например:

  • обёртки над t()
  • кастомные функции локализации
  • альтернативные DSL для переводов

Интеграция с сборщиками

Webpack

Часто используется через отдельный loader или plugin:

const I18NextScanner = require('i18next-scanner');

module.exports = {
  plugins: [
    new I18NextScanner({
      options: {
        input: 'src/**/*.{js,jsx}'
      }
    })
  ]
};

Gulp

Классическая интеграция:

const gulp = require('gulp');
const scanner = require('i18next-scanner');

gulp.task('i18n', function () {
  return gulp
    .src('src/**/*.{js,jsx}')
    .pipe(scanner())
    .pipe(gulp.dest('./'));
});

Обработка TypeScript

При работе с TypeScript учитываются:

  • типизированные функции t<Key>()
  • интерфейсы переводов
  • enum-ключи

Scanner работает на уровне транспилированного кода или через парсинг TS AST при соответствующей настройке.

Производительность и масштабирование

При больших проектах с десятками тысяч файлов ключевыми становятся:

  • кэширование AST
  • параллельная обработка файлов
  • исключение node_modules
  • ограничение глубины обхода

Сильная сторона подхода — отсутствие необходимости запускать приложение, что ускоряет CI/CD процессы.

Типичные проблемы использования

  • потеря ключей при динамической генерации строк
  • дублирование ключей в разных namespaces
  • несоответствие структуры JSON реальной структуре кода
  • отсутствие синхронизации при ручном редактировании переводов

Особенно критичны шаблонные строки, где ключ формируется через конкатенацию или переменные.

Структурирование ключей

На практике часто применяется иерархическая модель:

t('auth.login.button.submit');
t('auth.login.error.invalid_credentials');

Такая структура позволяет:

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

Итоговая модель работы в проекте

i18next-scanner становится частью цепочки локализации:

  • разработчик добавляет t('key')
  • scanner извлекает ключи
  • обновляются JSON-файлы
  • переводчики заполняют значения
  • i18next использует обновлённые ресурсы в runtime

Связь между кодом и словарями становится формализованной и поддерживаемой на уровне сборки, а не вручную.