Express middleware для i18next

i18next в экосистеме Express интегрируется через промежуточный слой, обеспечивающий автоматическое определение языка запроса, привязку функций перевода к объекту запроса и подготовку данных для шаблонов. В современных версиях используется пакет i18next-http-middleware, который эволюционно заменил более ранний i18next-express-middleware, сохранив архитектурные принципы и расширив поддержку Node.js-сред.


Архитектура middleware-слоя

Middleware для i18next в Express выполняет несколько ключевых задач в рамках одного запроса:

  • определение языка пользователя;
  • привязка функции перевода t к req;
  • синхронизация языка между req, res и i18next-инстансом;
  • подготовка локалей для серверного рендеринга;
  • управление fallback-языками и пространствами имён (namespaces).

Ключевая идея: каждый HTTP-запрос получает собственный контекст локализации, изолированный от других запросов.


Инициализация i18next для Express

Перед подключением middleware создаётся и конфигурируется экземпляр i18next:

import i18next from 'i18next';
import Backend from 'i18next-fs-backend';
import middleware from 'i18next-http-middleware';

i18next
  .use(Backend)
  .use(middleware.LanguageDetector)
  .init({
    fallbackLng: 'en',
    preload: ['en', 'ru'],
    ns: ['common'],
    defaultNS: 'common',
    backend: {
      loadPath: './locales/{{lng}}/{{ns}}.json'
    }
  });

Важные параметры конфигурации:

  • fallbackLng — язык, используемый при отсутствии перевода;
  • preload — предзагрузка доступных языков;
  • ns — список пространств имён переводов;
  • defaultNS — пространство имён по умолчанию;
  • backend.loadPath — путь к JSON-файлам локалей.

Подключение middleware в Express

Middleware подключается после инициализации i18next:

import express from 'express';

const app = express();

app.use(middleware.handle(i18next));

После подключения каждый запрос получает расширенный объект req:

  • req.t — функция перевода;
  • req.language — определённый язык запроса;
  • req.languages — список языков по приоритету;
  • req.i18n — экземпляр i18next;
  • res.locals.t — доступ к переводам в шаблонах.

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

LanguageDetector внутри middleware анализирует входящий запрос по нескольким источникам:

  1. query-параметры (?lng=ru);
  2. cookies;
  3. HTTP-заголовок Accept-Language;
  4. path URL (при соответствующей настройке);
  5. пользовательские детекторы.

Приоритет источников задаётся конфигурацией:

detection: {
  order: ['querystring', 'cookie', 'header'],
  caches: ['cookie']
}

Особенность: первый найденный валидный язык становится активным для запроса.


Привязка переводчика к request-объекту

После определения языка middleware создаёт контекст:

req.t('key');

Функция t уже учитывает:

  • текущий язык;
  • namespace;
  • fallback-цепочку;
  • интерполяцию значений.

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

app.get('/', (req, res) => {
  res.send(req.t('welcome_message'));
});

Работа с res.locals и шаблонами

Для серверного рендеринга middleware заполняет res.locals:

app.set('view engine', 'pug');

app.get('/', (req, res) => {
  res.render('index');
});

В шаблоне:

h1= t('title')
p= t('description')

Функция t автоматически доступна благодаря res.locals.


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

Middleware поддерживает разделение переводов по логическим модулям:

{
  "common": {
    "welcome": "Добро пожаловать"
  },
  "auth": {
    "login": "Вход"
  }
}

Использование:

req.t('login', { ns: 'auth' });

Или установка namespace по умолчанию:

i18next.init({
  defaultNS: 'common'
});

Интерполяция и динамические значения

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

req.t('greeting', { name: 'Alex' });

Перевод:

{
  "greeting": "Привет, {{name}}"
}

Результат:

Привет, Alex

Поддерживаются:

  • вложенные объекты;
  • числовые значения;
  • контекстные переменные.

Переопределение языка в рантайме

Язык может быть изменён во время обработки запроса:

req.i18n.changeLanguage('ru');

После вызова:

  • req.t начинает использовать новый язык;
  • req.language обновляется;
  • дальнейшие операции используют обновлённый контекст.

Middleware поддерживает автоматическое сохранение языка в cookie:

detection: {
  caches: ['cookie'],
  cookieName: 'i18next'
}

Поведение:

  • при первом запросе язык определяется;
  • записывается в cookie;
  • последующие запросы используют сохранённое значение.

Интеграция с backend и загрузкой переводов

С использованием i18next-fs-backend переводы загружаются с файловой системы:

locales/
  ru/
    common.json
  en/
    common.json

Middleware автоматически:

  • подгружает нужный язык;
  • кеширует результаты;
  • минимизирует обращения к диску.

Обработка отсутствующих ключей

При отсутствии перевода применяется fallback-цепочка:

  1. текущий язык;
  2. fallbackLng;
  3. ключ как строка.

Пример:

fallbackLng: 'en'

Если ключ отсутствует:

missing_key → missing_key (или значение из en)

Производительность middleware

В контексте Express ключевыми оптимизациями являются:

  • кеширование языков и ресурсов;
  • предзагрузка (preload);
  • использование memory backend в продакшене;
  • минимизация синхронных операций.

Особенно важно:

  • избегать частой перезагрузки ресурсов;
  • использовать CDN для статических локалей при необходимости;
  • ограничивать количество namespaces.

Работа с асинхронными запросами

Middleware полностью совместим с async/await:

app.get('/data', async (req, res) => {
  const message = req.t('async_loaded');
  res.json({ message });
});

Контекст языка сохраняется в рамках event loop и не теряется при асинхронных операциях.


Типовые ошибки интеграции

Часто встречающиеся проблемы:

  • подключение middleware до i18next.init;
  • отсутствие LanguageDetector;
  • неправильный порядок app.use;
  • отсутствие namespaces в preload;
  • конфликт cookie и querystring детекторов.

Использование в многоуровневой архитектуре

В сложных приложениях middleware интегрируется в цепочку:

app.use(express.json());
app.use(middleware.handle(i18next));
app.use(routes);

Порядок критичен:

  • middleware должен быть до маршрутов;
  • body-parser не влияет на i18next;
  • маршруты используют уже готовый req.t.

Контекст локализации в запросе

После обработки middleware запрос содержит полный набор локализационных данных:

  • язык запроса;
  • список предпочтительных языков;
  • функцию перевода;
  • активный i18next-инстанс;
  • доступ к ресурсам переводов.

Этот контекст используется всеми слоями приложения: контроллерами, сервисами и шаблонами.