i18next построен как расширяемая система интернационализации, в которой значительная часть функциональности выносится за пределы ядра. Такой подход позволяет подключать дополнительные возможности без изменения базовой логики: обработчики загрузки ресурсов, постобработку переводов, интеграцию с фреймворками, кэширование и динамическое определение языка. Механизм плагинов формирует слой расширения, работающий через жизненный цикл экземпляра i18next и набор стандартных хуков.
Основной принцип расширения заключается в том, что любой плагин
представляет собой объект с методом type, определяющим этап
интеграции, и методом init, который вызывается при
подключении через use(). В зависимости от типа плагина он
может внедряться в загрузчик ресурсов, обработчик языка, интерполяцию
или логирование.
Архитектура плагинов строится вокруг метода:
i18next.use(plugin).init();
При вызове use() плагин регистрируется внутри
внутреннего массива middleware. Во время инициализации
init() происходит последовательное применение всех
подключённых модулей.
Минимальная структура плагина:
const myPlugin = {
type: 'backend',
init: (services, options = {}, i18next) => {
// инициализация логики
}
};
Ключевое значение имеет поле type, которое определяет
точку подключения:
backend — загрузка переводовlanguageDetector — определение языка пользователяpostProcessor — обработка переведённых строкlogger — логированиеcache — кэшированиеПосле регистрации плагин проходит несколько стадий:
use()init() при запуске i18nextОбъект services, передаваемый в init(),
содержит внутренние компоненты:
resourceStore — хранилище переводовlanguageUtils — утилиты языкаlogger — система логированияpluralResolver — обработка множественных формBackend-плагины отвечают за загрузку переводов из внешних источников: API, файловой системы, базы данных.
const backend = {
type: 'backend',
read: (language, namespace, callback) => {
// загрузка ресурса
},
init: (services, options = {}, i18next) => {},
create: (languages, namespace, key, fallbackValue) => {}
};
readОсновной механизм получения переводов:
read(language, namespace, callback) {
fetch(`/locales/${language}/${namespace}.json`)
.then(res => res.json())
.then(data => callback(null, data))
.catch(err => callback(err));
}
Функция callback принимает два параметра:
error — ошибка загрузкиdata — объект переводовПлагины определения языка позволяют динамически вычислять язык пользователя на основе окружения.
Источники могут включать:
navigator.languageconst languageDetector = {
type: 'languageDetector',
init: (services, detectorOptions, i18nextOptions) => {},
detect: () => {
return localStorage.getItem('lang') || 'en';
},
cacheUserLanguage: (lng) => {
localStorage.setItem('lang', lng);
}
};
Детектор может возвращать массив стратегий:
detect() {
return [
localStorage.getItem('lang'),
navigator.language,
'en'
];
}
Первое валидное значение используется как активный язык.
PostProcessor применяется после получения перевода, но до возврата результата пользователю.
Используется для:
const postProcessor = {
type: 'postProcessor',
process: (value, key, options, translator) => {
return value.toUpperCase();
}
};
PostProcessor может быть цепочечным:
i18next.init({
postProcess: ['uppercase', 'trim']
});
Каждый процессор применяется последовательно.
Logger позволяет заменить стандартный механизм логирования.
const logger = {
type: 'logger',
log: (...args) => console.log(...args),
warn: (...args) => console.warn(...args),
error: (...args) => console.error(...args)
};
Используется для:
Cache backend используется для ускорения загрузки переводов.
Основная идея — сохранить уже загруженные ресурсы.
const cache = {
type: 'backend',
read: (language, namespace, callback) => {
const key = `${language}-${namespace}`;
const cached = localStorage.getItem(key);
if (cached) {
return callback(null, JSON.parse(cached));
}
callback(null, null);
},
create: (languages, namespace, key, fallbackValue) => {
const storageKey = `${languages}-${namespace}`;
localStorage.setItem(storageKey, JSON.stringify(fallbackValue));
}
};
Плагины не изолированы и могут взаимодействовать через общий сервисный слой.
Пример цепочки:
Порядок подключения влияет на поведение системы:
i18next
.use(languageDetector)
.use(cache)
.use(backend)
.use(postProcessor)
.init();
Создание собственного плагина требует соблюдения контрактов типов.
const keyTransformer = {
type: 'postProcessor',
process: (value, key) => {
if (key.startsWith('upper:')) {
return value.toUpperCase();
}
return value;
}
};
i18next.use(keyTransformer);
Backend и detector часто работают асинхронно.
Особенности:
read(language, namespace, callback) {
setTimeout(() => {
callback(null, { hello: 'world' });
}, 1000);
}
Частые проблемы:
typeinitdetectreadНекорректный плагин может нарушить цепочку интернационализации и привести к частичной потере переводов.
Плагины привязаны к версии ядра. Изменения могут затрагивать:
initservicesПоэтому плагины часто сопровождаются версионированием и проверкой совместимости через feature detection.
Плагины могут адаптировать i18next под:
Механизм остаётся одинаковым: внедрение через use() и
проксирование переводчика в контекст фреймворка.
Система допускает одновременное использование нескольких расширений одного типа.
Пример:
i18next
.use(detectorA)
.use(detectorB)
.use(backendA)
.use(backendB);
При этом порядок регистрации определяет приоритет обработки.
В основе лежит паттерн middleware. Каждый плагин расширяет поведение
экземпляра i18next, не изменяя его исходный код. Все расширения работают
через единый объект i18next.services, обеспечивая
согласованность состояния и централизованный контроль жизненного цикла
интернационализации.