Миграция с i18next v20 на v21+

В переходе между i18next v20 и v21+ основная сложность миграции связана не с одной конкретной «ломающей» правкой, а с совокупностью изменений в типизации, модульной структуре, поведении загрузчиков и усилением строгих контрактов API. Переход следует рассматривать как эволюцию архитектуры интернационализации, где старые паттерны продолжают работать, но часть из них либо депрецируется, либо начинает требовать явного конфигурирования.

Ключевой принцип миграции: проверка всех точек инициализации i18next, всех подключаемых плагинов и всех мест, где используются ресурсы переводов вне стандартного flow init → loadResources → t().


Изменения инициализации и конфигурации

Усиление строгой типизации init-конфига

В v21+ заметно усиливается контроль за структурой объекта конфигурации:

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

Пример старого и нового подхода:

// v20
i18next.init({
  lng: 'ru',
  fallbackLng: 'en',
  resources: {
    ru: {
      translation: {
        hello: "Привет"
      }
    }
  }
});

В v21+ при использовании TypeScript или строгих сборок:

i18next.init({
  lng: 'ru',
  fallbackLng: ['en'],
  resources: {
    ru: {
      translation: {
        hello: "Привет"
      }
    }
  }
});

Изменение на массив fallbackLng становится предпочтительным паттерном, особенно в сценариях с несколькими резервными языками.


Модульная структура и ESM

Переход к более явной модульности

В новых версиях усиливается ориентация на ESM-экосистему:

  • предпочтение import вместо require
  • более строгая работа tree-shaking
  • минимизация side effects при импортах

Ранее распространённый CommonJS-подход:

const i18next = require('i18next');

Современный ESM-подход:

import i18next from 'i18next';

При использовании плагинов важно проверять, что они также поддерживают ESM, иначе потребуется явный interop:

import Backend from 'i18next-http-backend';

i18next
  .use(Backend)
  .init({ /* config */ });

Изменения поведения загрузчиков ресурсов

Усиление контроля асинхронной загрузки

В v21+ более предсказуемой становится работа backend-плагинов. Основные изменения проявляются в следующих аспектах:

  • более строгая обработка промисов
  • уменьшение поддержки callback-only API
  • унификация ошибок загрузки ресурсов

Старый callback-подход:

backend.load('ru', 'translation', (err, data) => {
  // обработка
});

Современный подход:

backend.load('ru', 'translation')
  .then(data => {
    // обработка
  })
  .catch(err => {
    // обработка ошибок
  });

Изменения в интерполяции и форматировании

Поведение интерполяции

Интерполяция в i18next остаётся совместимой, однако в v21+ усиливается предсказуемость:

  • экранирование значений становится более строгим
  • поведение escapeValue требует явного контроля
  • сокращается количество неявных преобразований типов

Пример:

i18next.init({
  interpolation: {
    escapeValue: false
  }
});

В миграции важно проверить все места, где ранее полагались на автоматическое экранирование.


Изменения в namespace-архитектуре

Более строгая работа с namespaces

Namespaces становятся более явно управляемыми сущностями:

  • обязательное существование fallback namespace при сложных конфигурациях
  • более строгая проверка загрузки namespace перед использованием t()

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

i18next.init({
  ns: ['common', 'auth'],
  defaultNS: 'common',
  fallbackNS: 'common'
});

Типовая проблема миграции: использование t() до полной загрузки всех namespaces.


React-интеграция и Suspense-поведение

Изменение взаимодействия с react-i18next

При использовании связки с React (через react-i18next) v21+ усиливает зависимость от корректной асинхронной модели.

Основные изменения:

  • более стабильное поведение Suspense
  • уменьшение использования legacy HOC-обёрток
  • предпочтение hooks API

Пример современного подхода:

import { useTranslation } from 'react-i18next';

function Header() {
  const { t } = useTranslation('common');

  return <h1>{t('title')}</h1>;
}

Важно учитывать, что в некоторых конфигурациях Suspense становится обязательным для корректной загрузки ресурсов:

i18next.init({
  react: {
    useSuspense: true
  }
});

Изменения TypeScript-типов

Улучшение inference и строгих контрактов

В v21+ усиливается типизация:

  • улучшенный вывод ключей переводов
  • более строгая проверка namespace
  • ограничение произвольных строк в t()

Типовая проблема миграции:

t('nonExistingKey')

Теперь может вызывать предупреждения при строгой конфигурации типов.

Решение:

t('known.key' as const)

или расширение деклараций:

declare module 'i18next' {
  interface CustomTypeOptions {
    resources: {
      common: typeof resources.common;
    };
  }
}

Изменения в fallback-логике

Более детерминированный fallback

В v21+ логика fallback становится менее «эвристической» и более явно заданной конфигурацией:

  • fallback цепочки вычисляются строго по порядку массива
  • исключаются скрытые fallback-переходы
  • усиливается контроль отсутствующих ключей

Пример:

i18next.init({
  fallbackLng: ['en', 'de', 'ru']
});

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


Удаление и депрецированные API

Сокращение legacy-методов

Часть устаревших API становится недоступной или требует включения совместимости:

  • callback-first методы постепенно вытесняются Promise-ориентированными
  • старые способы регистрации ресурсов заменяются едиными методами загрузки
  • некоторые внутренние утилиты больше не экспортируются публично

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

i18next.loadNamespaces('common', () => {});

Замена:

await i18next.loadNamespaces('common');

Изменения поведения языка и detection

Более строгий language detector

При использовании i18next-browser-languagedetector:

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

Пример:

i18next.init({
  detection: {
    order: ['querystring', 'cookie', 'localStorage', 'navigator'],
    caches: ['localStorage']
  }
});

Практическая стратегия миграции

Инкрементальный переход

Переход между версиями v20 и v21+ требует последовательной проверки:

  • инициализация i18next в единой точке входа
  • проверка всех backend-плагинов
  • ревизия namespace-загрузки
  • аудит использования t() в асинхронных контекстах
  • проверка TypeScript контрактов

Проверка совместимости плагинов

Критическим этапом становится анализ зависимостей:

  • backend
  • language detector
  • react integration
  • custom plugins

Несовместимость одного плагина часто проявляется не сразу, а в виде задержек загрузки переводов или отсутствующих fallback-значений.


Контроль регрессий

Наиболее частые регрессии при миграции:

  • пустые переводы из-за асинхронной загрузки
  • неправильный fallback язык
  • несрабатывающий Suspense в React
  • ошибки типизации ключей переводов
  • потеря namespace при динамической загрузке

Поведенческие изменения runtime

Более предсказуемый lifecycle

Lifecycle i18next становится ближе к модели:

  1. инициализация
  2. загрузка ресурсов
  3. установка языка
  4. доступ к переводу

Любое отклонение от этой последовательности теперь чаще приводит к явным ошибкам или пустым результатам, вместо «тихого деградационного поведения», характерного для v20.