Конфликты между плагинами

Архитектура i18next построена вокруг расширяемой системы плагинов, где каждый модуль может влиять на ключевые этапы жизненного цикла интернационализации: определение языка, загрузку ресурсов, обработку интерполяции, форматирование и кеширование. Такая гибкость неизбежно приводит к пересечению ответственности между плагинами, особенно в сложных приложениях, где используется сразу несколько расширений.

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

  • переопределение одинаковых хуков жизненного цикла
  • конкурирующие backend-механизмы загрузки переводов
  • дублирующиеся language detector стратегии
  • конфликтующие интерполяционные или форматирующие функции
  • повторная регистрация плагинов с побочными эффектами
  • несовместимость версий i18next и расширений

Конфликты backend-плагинов загрузки ресурсов

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

Типичная ситуация:

  • i18next-http-backend загружает переводы через API
  • кастомный backend загружает JSON из локального хранилища или файловой системы

Если оба backend зарегистрированы без явного приоритета, i18next может:

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

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

i18next
  .use(HttpBackend)
  .use(CustomBackend)
  .init({
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json'
    }
  });

В таких случаях порядок .use() имеет решающее значение, поскольку последний зарегистрированный backend часто получает приоритет в обработке запросов.


Конфликты language detector плагинов

Language detector плагины определяют язык пользователя через различные источники:

  • URL маршруты
  • cookies
  • localStorage
  • HTTP headers
  • query parameters

При подключении нескольких детекторов возникает ситуация гонки:

  • один детектор возвращает en
  • другой — ru
  • третий читает кешированное значение de

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

Типичная проблема:

i18next
  .use(LanguageDetector)
  .use(CustomDetector)

Если CustomDetector возвращает значение быстрее или имеет более высокий приоритет, он может полностью перекрыть стандартную логику.

Ключевая точка конфликта — order и lookupFrom:

detection: {
  order: ['cookie', 'localStorage', 'path', 'customDetector']
}

Нарушение порядка приводит к нестабильному выбору языка между сессиями.


Конфликты интерполяции и форматирования

Интерполяционные плагины изменяют способ подстановки переменных в строки перевода. Проблема возникает при одновременном использовании:

  • кастомного интерполятора
  • i18next-sprintf-postprocessor
  • форматтеров ICU-подобного синтаксиса

Конфликт проявляется в двойной обработке строк:

"Hello {{name}}" → "Hello %s" → "Hello undefined"

или:

"Price: {{value, currency}}" → некорректный формат из-за перекрытия formatters

Особенно критична ситуация, когда несколько форматтеров регистрируются на один и тот же ключ:

i18next.services.formatter.add('currency', (value) => ...)
i18next.services.formatter.add('currency', (value) => ...)

Последний зарегистрированный форматтер полностью заменяет предыдущий без предупреждения.


Конфликты namespace и структуры ресурсов

i18next поддерживает разделение переводов на namespaces. При подключении нескольких плагинов загрузки ресурсов возможно:

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

Пример конфликтной структуры:

// backend A
{
  "common": {
    "title": "Hello"
  }
}

// backend B
{
  "common": {
    "title": "Привет",
    "subtitle": "Мир"
  }
}

При объединении ресурсов итог зависит от порядка загрузки:

  • backend A может перезаписать backend B
  • либо произойдёт глубокое слияние с потерей части данных

Особенно проблемны ситуации с nsSeparator и keySeparator, если разные плагины используют разные соглашения.


Повторная инициализация плагинов

Некоторые плагины i18next не рассчитаны на многократное подключение в рамках одного runtime. При повторной инициализации:

  • создаются дублирующиеся event listeners
  • повторно регистрируются formatter-ы
  • возникает утечка памяти

Типичный сценарий:

i18next
  .use(Backend)
  .use(Backend) // повторное подключение
  .init(...)

Результат:

  • удвоенные сетевые запросы
  • двойная обработка событий loaded и initialized
  • нестабильное состояние store

Конфликты event emitter и lifecycle hooks

i18next использует событийную модель:

  • initialized
  • loaded
  • languageChanged
  • failedLoading

Плагины могут подписываться на одни и те же события. Конфликт возникает, когда:

  • несколько плагинов изменяют состояние i18n внутри одного события
  • один плагин вызывает changeLanguage, вызывая каскад новых событий

Пример цепной реакции:

  1. Backend загружает ресурсы
  2. Language detector меняет язык
  3. Formatter пересчитывает строки
  4. Второй backend повторно инициирует загрузку

Возникает цикл обновлений, приводящий к деградации производительности.


SSR и hydration-конфликты

В server-side rendering сценариях i18next часто используется вместе с:

  • i18next-http-middleware
  • react-i18next
  • кастомными backend-адаптерами

Конфликт возникает при несовпадении состояния между сервером и клиентом:

  • сервер загружает ru
  • клиент детектит en
  • плагины повторно загружают ресурсы

Результат — hydration mismatch:

  • текст на сервере отличается от текста на клиенте
  • происходит повторный рендер
  • ресурсы загружаются дважды

Конфликты кеширования

Плагины кеширования (например, localStorage backend cache) могут конфликтовать с:

  • HTTP backend cache-control
  • service worker cache
  • встроенным store i18next

Типичный сценарий:

  • кеш возвращает устаревшие переводы
  • backend загружает новые
  • происходит перезапись ключей в непредсказуемом порядке

Особенно проблемны ситуации с TTL-логикой, если разные плагины используют разные временные интервалы обновления.


Приоритет и порядок регистрации плагинов

Порядок .use() определяет фактическое поведение системы. Это касается:

  • backend chain
  • detector chain
  • postProcessor chain
  • formatter chain

Общая закономерность:

  • последний зарегистрированный плагин часто получает приоритет
  • но некоторые цепочки используют reverse iteration
  • отдельные плагины полностью перехватывают выполнение pipeline

Пример влияния порядка:

i18next
  .use(A)
  .use(B)
  .use(C)

Если C перехватывает backend, A и B могут не выполниться вовсе.


Несовместимость версий плагинов

Конфликты часто возникают из-за различий в API между версиями i18next и расширений:

  • изменение сигнатур backend методов (read, create, init)
  • изменение структуры options
  • deprecated lifecycle hooks

Типичный результат:

  • плагин silently fails
  • отсутствуют переводы
  • fallback language активируется без явной ошибки

Композиция конфликтов в реальных приложениях

На практике конфликты редко возникают изолированно. Чаще наблюдаются комбинированные эффекты:

  • backend конфликт + detector конфликт → нестабильный язык и пустые ресурсы
  • formatter конфликт + namespace конфликт → некорректные строки при частичной загрузке
  • SSR mismatch + кеширование → двойная загрузка и мерцание интерфейса

Такие состояния сложно диагностировать, поскольку i18next не всегда явно сигнализирует о причине деградации, ограничиваясь fallback-механизмами.