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

Роль заголовка Accept-Language в серверной локализации

В HTTP-запросах клиент передаёт информацию о предпочтительных языках через заголовок Accept-Language. Этот заголовок формируется браузером или мобильным приложением на основе системных настроек пользователя и содержит упорядоченный список языков с коэффициентами приоритета (quality values).

Пример структуры заголовка:

Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7

Каждый язык сопровождается параметром q, определяющим степень предпочтения. Значение 1.0 означает максимальный приоритет, отсутствие параметра интерпретируется как 1.0.

На стороне сервера этот заголовок становится основным источником для автоматического выбора языка интерфейса.


Базовый механизм обработки заголовков в i18next

В экосистеме i18next обработка языка из HTTP-заголовков реализуется через модуль промежуточного уровня i18next-http-middleware. Он интегрируется с серверными фреймворками (Express, Koa, Fastify через адаптеры) и выполняет определение языка до обработки маршрута.

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

  • заголовки HTTP
  • query-параметры
  • cookie
  • сессия
  • localStorage (в браузере)
  • navigator (на клиенте)

Для серверной среды приоритетным источником обычно выступает именно header.


Настройка стратегии определения языка

Конфигурация i18next задаёт порядок источников:

import i18next from 'i18next';
import middleware from 'i18next-http-middleware';

i18next.use(middleware.LanguageDetector).init({
  detection: {
    order: ['header', 'querystring', 'cookie'],
    caches: ['cookie']
  },
  fallbackLng: 'en',
  supportedLngs: ['en', 'ru', 'de']
});

Ключевой параметр order определяет приоритет источников. При наличии header система анализирует Accept-Language раньше других механизмов.


Алгоритм извлечения языка из Accept-Language

Внутренняя логика обработки заголовка включает несколько этапов:

  1. Парсинг строки заголовка
  2. Разделение по запятым
  3. Извлечение языковых тегов и коэффициентов качества
  4. Сортировка по приоритету
  5. Сопоставление с поддерживаемыми языками приложения

Упрощённая схема:

Accept-Language
      ↓
разбиение на языковые токены
      ↓
учёт параметра q
      ↓
сортировка по приоритету
      ↓
поиск первого совпадения в supportedLngs

Обработка языковых тегов и нормализация

Заголовок может содержать как региональные, так и базовые языковые коды:

  • ru-RU
  • ru
  • en-US
  • en-GB

i18next выполняет нормализацию:

  • ru-RUru
  • en-USen
  • сохранение полного тега при необходимости (если включён режим расширенной поддержки локалей)

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

load: 'languageOnly'

или

load: 'currentOnly'

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

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

ru-RU → ru → en-US → en

Если поддерживаемые языки ограничены:

supportedLngs: ['ru', 'en']

алгоритм выберет первый совпадающий вариант.

Если совпадения отсутствуют, активируется fallback:

fallbackLng: 'en'

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

В серверных приложениях Express обработка заголовков происходит через middleware:

import express from 'express';
import i18next from 'i18next';
import middleware from 'i18next-http-middleware';

const app = express();

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

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

Поле req.language заполняется автоматически после анализа Accept-Language.


Кэширование определения языка

Для снижения нагрузки система может сохранять результат определения языка:

  • cookie (i18next)
  • query string (lng)
  • session storage (в зависимости от адаптера)

Приоритет кэширования ниже, чем у HTTP-заголовков, если явно не изменена конфигурация.


Конфликты источников определения языка

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

Источник Пример Приоритет
header Accept-Language: ru высокий
querystring ?lng=en средний
cookie lng=de средний

При стандартной конфигурации порядок разрешения следующий:

  1. header
  2. querystring
  3. cookie

Это означает, что значение из Accept-Language может переопределять явно заданный язык в cookie или URL.


Отключение определения через заголовки

В некоторых системах требуется игнорирование Accept-Language, например при строгом управлении локалью со стороны приложения.

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

detection: {
  order: ['querystring', 'cookie'],
  lookupHeader: undefined
}

Или через фильтрацию:

detection: {
  lookupHeader: () => undefined
}

В таком режиме заголовок перестаёт участвовать в выборе языка.


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

i18next допускает замену стандартного поведения через кастомную функцию:

detection: {
  order: ['header'],
  lookupHeader: (req) => {
    const lang = req.headers['accept-language'];
    return lang ? lang.split(',')[0].split(';')[0] : undefined;
  }
}

Такой подход позволяет:

  • ограничить выбор только первым языком
  • игнорировать коэффициенты q
  • применять собственные правила нормализации

Влияние прокси и CDN на заголовок языка

При использовании reverse proxy (Nginx, Cloudflare, API Gateway) заголовок может:

  • изменяться
  • удаляться
  • добавляться заново

Типичный случай — передача оригинального заголовка через:

X-Forwarded-For
Accept-Language

Некоторые инфраструктуры требуют явного проброса:

proxy_set_header Accept-Language $http_accept_language;

При отсутствии корректной передачи язык может определяться некорректно или переходить в fallback.


Поведение в браузерной среде

На клиенте i18next использует navigator.language и navigator.languages, но при серверном рендеринге эти значения отсутствуют, и источником становится исключительно HTTP-заголовок.

Сопоставление:

  • браузер → формирует Accept-Language
  • сервер → читает Accept-Language
  • i18next → извлекает приоритетный язык

Обработка некорректных или пустых заголовков

Возможные случаи:

  • заголовок отсутствует
  • содержит пустую строку
  • содержит некорректные значения

Поведение i18next:

  • пропуск источника header
  • переход к следующему источнику из order
  • активация fallbackLng, если все источники не дали результата

Оптимизация выбора языка в высоконагруженных системах

При большом количестве запросов обработка заголовка может стать значимой частью middleware-цепочки. Оптимизации включают:

  • кэширование результата парсинга Accept-Language
  • ограничение количества анализируемых языков
  • упрощённый парсинг без учёта q
  • фиксированный whitelist языков

Типичная стратегия:

supportedLngs: ['en', 'ru']
detection: {
  order: ['header'],
  caches: []
}

Совместимость с многоязычными API

При использовании i18next в API-сервисах заголовок становится контрактом между клиентом и сервером. Он позволяет:

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

Результат определения языка через заголовок напрямую влияет на:

  • выбор переводов
  • форматирование дат и чисел (через локализационные библиотеки)
  • структуру ответа API