Настройка i18next на сервере

i18next на сервере используется как слой подготовки локализованных данных до отправки HTML клиенту или API-ответов. В серверной среде библиотека решает задачи предварительного определения языка пользователя, загрузки переводов из файловой системы или внешних источников, а также формирования уже локализованного контента для SSR-приложений и микросервисов.

Серверная интеграция i18next строится вокруг трёх ключевых этапов:

  • определение языка запроса
  • загрузка ресурсов переводов
  • инициализация экземпляра i18next на каждый запрос или его переиспользование

В отличие от клиентской среды, сервер не имеет доступа к navigator.language, поэтому выбор языка выполняется через HTTP-заголовки, cookies или параметры маршрута.

Основной источник языка — заголовок Accept-Language, который разбирается и сопоставляется с доступными локалями приложения.

Базовая инициализация i18next в Node.js

Серверная среда на базе Node.js позволяет использовать файловую систему для хранения переводов и синхронную или асинхронную загрузку ресурсов.

Минимальная конфигурация:

import i18next from 'i18next';

i18next.init({
  lng: 'en',
  fallbackLng: 'en',
  resources: {
    en: {
      translation: {
        welcome: 'Welcome',
      },
    },
    ru: {
      translation: {
        welcome: 'Добро пожаловать',
      },
    },
  },
});

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

Использование файлового хранилища переводов

В серверных приложениях стандартной практикой является хранение переводов в JSON-файлах и загрузка их по мере необходимости.

Используется пакет:

  • i18next-fs-backend

Он подключает файловую систему как источник переводов.

import i18next from 'i18next';
import Backend from 'i18next-fs-backend';
import path from 'path';

i18next
  .use(Backend)
  .init({
    lng: 'ru',
    fallbackLng: 'en',
    backend: {
      loadPath: path.join(process.cwd(), '/locales/{{lng}}/{{ns}}.json'),
    },
  });

Структура проекта:

/locales
  /en
    translation.json
  /ru
    translation.json

Каждый файл представляет namespace translation.

Обработка языка запроса на сервере

Ключевая задача серверной локализации — корректное определение языка пользователя.

Обычно используется цепочка приоритетов:

  1. cookie (i18next или кастомная)
  2. query параметр (?lng=ru)
  3. заголовок Accept-Language
  4. fallback язык

Пример извлечения языка:

function detectLanguage(req) {
  const queryLng = req.query.lng;
  const cookieLng = req.cookies?.lng;
  const headerLng = req.headers['accept-language'];

  return cookieLng || queryLng || headerLng || 'en';
}

Интеграция с Express

В серверных приложениях часто используется Express.

Для каждого запроса создаётся отдельный экземпляр i18next или используется i18next-http-middleware.

Пример middleware:

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

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

const app = express();

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

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

Middleware автоматически добавляет функцию t в объект запроса.

Изоляция i18next на уровне запроса

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

1. Singleton (общий экземпляр)

Подходит для простых приложений, где язык переключается динамически:

  • быстрее по производительности
  • риск конфликтов при сложных SSR-сценариях минимален

2. Экземпляр на запрос

Используется в SSR и мультиарендных системах:

import i18next from 'i18next';

function createI18nInstance(lng) {
  const instance = i18next.createInstance();

  instance.init({
    lng,
    fallbackLng: 'en',
    resources: {},
  });

  return instance;
}

Такой подход гарантирует полную изоляцию состояния.

Server-Side Rendering (SSR)

В SSR-фреймворках локализация должна происходить до генерации HTML.

Пример для рендеринга страницы:

const lng = detectLanguage(req);

const t = req.i18n.getFixedT(lng);

const html = `
  <html>
    <body>
      <h1>${t('welcome')}</h1>
    </body>
  </html>
`;

res.send(html);

Ключевой метод getFixedT фиксирует язык и namespace, исключая повторные вычисления.

Работа с namespace на сервере

Namespace позволяют разделять переводы по доменам:

  • auth
  • dashboard
  • errors

Конфигурация:

i18next.init({
  ns: ['common', 'auth', 'dashboard'],
  defaultNS: 'common',
  backend: {
    loadPath: './locales/{{lng}}/{{ns}}.json',
  },
});

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

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

Разделение уменьшает объём загружаемых данных и ускоряет SSR.

Кэширование переводов

На сервере критично минимизировать I/O операции.

i18next поддерживает:

  • memory cache
  • filesystem cache через backend
  • внешние кеширующие слои (Redis, CDN)

Пример оптимизации:

i18next.init({
  backend: {
    loadPath: './locales/{{lng}}/{{ns}}.json',
    addPath: './locales/{{lng}}/{{ns}}.missing.json',
  },
  saveMissing: true,
});

Опция saveMissing позволяет собирать отсутствующие ключи.

Асинхронная загрузка ресурсов

При большом количестве языков используется ленивая загрузка:

i18next.init({
  preload: false,
  backend: {
    loadPath: '/locales/{{lng}}/{{ns}}.json',
  },
});

Языки подгружаются только при первом запросе.

Обработка ошибок и fallback

Fallback язык критичен для стабильности серверного API:

i18next.init({
  fallbackLng: 'en',
  saveMissing: false,
  returnNull: false,
  returnEmptyString: false,
});

Поведение при отсутствии ключа:

  • возвращается ключ
  • или fallback значение
  • или строка из fallback языка

Многоязычные API-ответы

В REST или GraphQL API локализация часто выполняется на уровне контроллера:

app.get('/profile', (req, res) => {
  const t = req.t;

  res.json({
    title: t('profile.title'),
    description: t('profile.description'),
  });
});

Это позволяет клиенту не заниматься переводами вообще.

Оптимизация производительности

Основные методы ускорения:

  • предварительный preload языков
  • минимизация namespaces
  • отключение лишних интерполяций
  • использование getFixedT
  • кэширование ресурсов

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

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

i18next поддерживает динамические значения:

t('welcome_user', { name: 'Alex' });

Перевод:

{
  "welcome_user": "Welcome, {{name}}"
}

На сервере интерполяция должна быть строго контролируемой, чтобы исключить инъекции:

i18next.init({
  interpolation: {
    escapeValue: true,
  },
});

Работа в микросервисной архитектуре

В распределённых системах каждый сервис может:

  • хранить собственные переводы
  • или использовать централизованный translation service

Часто применяют стратегию:

  • API Gateway определяет язык
  • сервисы получают lng через заголовки
  • i18next используется локально в каждом сервисе

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

Типичная архитектура включает:

  • middleware определения языка
  • backend загрузки переводов
  • изолированные i18n-инстансы
  • SSR-слой или API слой
  • кэширование ресурсов

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