Регистрация и использование плагинов

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

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

Типы плагинов и их роли

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

1. Backend-плагины Отвечают за загрузку переводов из внешних источников:

  • файловая система
  • HTTP API
  • CDN
  • кастомные источники (например, база данных)

2. LanguageDetector-плагины Определяют текущий язык пользователя:

  • браузерные настройки
  • URL
  • localStorage / cookies
  • заголовки HTTP (в Node.js окружении)

3. Plugin-модули интеграции фреймворков Используются для подключения i18next к UI-фреймворкам:

  • React
  • Vue
  • Angular

4. Post-processor-плагины Обрабатывают итоговую строку перевода:

  • форматирование
  • маскирование
  • кастомная логика интерполяции

Механизм подключения плагинов

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

import i18next fr om 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';

i18next
  .use(Backend)
  .use(LanguageDetector)
  .init({
    fallbackLng: 'en',
    debug: true,
    resources: {}
  });

Каждый вызов use() регистрирует плагин в внутреннем массиве middleware. При вызове init() происходит последовательная инициализация всех подключённых модулей.

Контракт плагина

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

  • init — инициализация плагина
  • read — чтение данных (для backend)
  • create — создание ресурсов (опционально)
  • detect — определение языка (для language detector)
  • process — постобработка результата

Пример структуры backend-плагина:

class CustomBackend {
  constructor(services, options = {}) {
    this.services = services;
    this.options = options;
  }

  init(services, backendOptions, i18nextOptions) {
    this.services = services;
    this.options = backendOptions;
  }

  read(language, namespace, callback) {
    fetch(`/locales/${language}/${namespace}.json`)
      .then(res => res.json())
      .then(data => callback(null, data))
      .catch(err => callback(err, false));
  }
}

export default CustomBackend;

Backend-плагины и загрузка переводов

Backend-плагины являются наиболее часто используемым типом расширений. Они позволяют абстрагировать источник переводов и динамически подгружать их при необходимости.

HTTP Backend

Наиболее распространённый вариант — загрузка JSON-файлов через HTTP:

import Backend from 'i18next-http-backend';

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

Шаблоны {{lng}} и {{ns}} подставляются автоматически, что позволяет масштабировать структуру локализации без ручной обработки.

Кастомный backend

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

class DatabaseBackend {
  read(language, namespace, callback) {
    db.query(
      'SEL ECT content FR OM translations WH ERE lang = ? AND ns = ?',
      [language, namespace],
      (err, result) => {
        if (err) return callback(err);
        callback(null, JSON.parse(result.content));
      }
    );
  }
}

Backend-плагин становится частью pipeline загрузки ресурсов и может кэшировать данные, объединять запросы и управлять стратегией обновления переводов.

LanguageDetector и определение языка

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

Пример использования стандартного детектора:

import LanguageDetector from 'i18next-browser-languagedetector';

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

Принцип работы детектора

LanguageDetector проходит по списку стратегий сверху вниз:

  1. Проверка параметров URL
  2. Проверка cookie
  3. Проверка localStorage
  4. Использование navigator.language

Каждая стратегия реализуется как функция, возвращающая строку языка или undefined.

Пример кастомной стратегии:

const customDetector = {
  name: 'customDetector',
  lookup() {
    return window.appLang || null;
  },
  cacheUserLanguage(lng) {
    window.appLang = lng;
  }
};

i18next.services.languageDetector.addDetector(customDetector);

Интеграционные плагины фреймворков

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

React интеграция

import { initReactI18next } from 'react-i18next';

i18next.use(initReactI18next).init({
  resources: {
    en: {
      translation: {
        key: 'value'
      }
    }
  }
});

Этот плагин связывает i18next с контекстом React и обеспечивает обновление компонентов при смене языка.

Механизм работы

Интеграционные плагины:

  • подписываются на события изменения языка
  • прокидывают instance через context
  • обеспечивают хук-обёртки (useTranslation)

Post-processor плагины

Post-processing применяется после получения строки перевода, но до возврата результата вызывающей стороне.

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

const uppercasePostProcessor = {
  type: 'postProcessor',
  name: 'uppercase',

  process(value) {
    return value.toUpperCase();
  }
};

i18next.use(uppercasePostProcessor);

Использование в ключе перевода:

i18next.t('key', { postProcess: 'uppercase' });

Порядок выполнения плагинов

Цепочка выполнения строго определена:

  1. LanguageDetector определяет язык
  2. Backend загружает ресурсы
  3. Плагины интерполяции обрабатывают переменные
  4. PostProcessor модифицирует результат
  5. Возвращается итоговая строка

Нарушение порядка может привести к некорректной загрузке переводов или отсутствию интерполяции.

Асинхронная модель плагинов

Большинство backend-плагинов работают асинхронно. i18next поддерживает callback- и Promise-ориентированную модель.

i18next.init((err, t) => {
  if (err) return;
  console.log(t('key'));
});

При использовании backend-загрузчиков возможна ленивое подгружение namespaces:

i18next.loadNamespaces('common', () => {
  console.log(i18next.t('common:key'));
});

Расширение системы плагинов

Система плагинов построена на сервисной архитектуре. Внутренние сервисы доступны через services:

  • resourceStore
  • languageUtils
  • logger
  • backendConnector

Плагин может модифицировать поведение этих сервисов:

class LoggerPlugin {
  init(services) {
    services.logger = {
      log: (msg) => console.log('[i18n]', msg)
    };
  }
}

Взаимодействие нескольких плагинов

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

Пример:

i18next
  .use(BackendA)
  .use(BackendB);

В таком случае BackendB может перекрыть read-логику BackendA, если они конфликтуют по сервисам.

Ошибки и обработка отказов плагинов

Плагины обязаны корректно обрабатывать ошибки через callback или reject:

read(language, namespace, callback) {
  try {
    const data = loadFromSource();
    callback(null, data);
  } catch (e) {
    callback(e, false);
  }
}

При отсутствии корректной обработки ошибок i18next переходит к fallback-логике (fallbackLng или fallback namespace).

Кэширование в плагинах

Backend-плагины часто реализуют кэширование для снижения количества запросов:

class CachedBackend {
  constructor() {
    this.cache = new Map();
  }

  read(lng, ns, cb) {
    const key = `${lng}-${ns}`;
    if (this.cache.has(key)) {
      return cb(null, this.cache.get(key));
    }

    fetch(`/locales/${lng}/${ns}.json`)
      .then(r => r.json())
      .then(data => {
        this.cache.set(key, data);
        cb(null, data);
      });
  }
}

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

Плагины зависят от версии i18next API. Основные точки несовместимости:

  • изменение сигнатуры init
  • переход на Promise-based загрузку
  • изменение структуры services

Проверка совместимости осуществляется через peerDependencies в package.json плагина и внутренние проверки при инициализации.

Итоговая модель расширения

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