Language detector и автоопределение

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


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

import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

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

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


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

Ключевой параметр конфигурации — order. Он задаёт последовательность источников, из которых извлекается язык.

Типичный набор источников:

  • querystring
  • cookie
  • localStorage
  • sessionStorage
  • navigator
  • htmlTag
  • path
  • subdomain

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

querystring

Язык берётся из параметров URL.

Пример:

https://site.com?lng=ru

Настройка ключа:

detection: {
  order: ['querystring'],
  lookupQuerystring: 'lng'
}

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


Язык извлекается из cookie браузера. Подходит для сохранения выбора пользователя между сессиями.

detection: {
  order: ['cookie'],
  lookupCookie: 'i18next',
  caches: ['cookie']
}

Дополнительные параметры:

  • cookieDomain — домен, на котором сохраняется cookie
  • cookieMinutes — срок жизни
  • cookieSecure — использование secure-флага

localStorage и sessionStorage

Хранение локального выбора языка без участия сервера.

detection: {
  order: ['localStorage'],
  lookupLocalStorage: 'i18nextLng'
}

Разница между хранилищами:

  • localStorage — постоянное хранение
  • sessionStorage — сброс при закрытии вкладки

Язык берётся из настроек браузера пользователя через navigator.language или navigator.languages.

Пример значений:

  • ru-RU
  • en-US
  • de

i18next автоматически нормализует значения до доступных языков, например ru-RU → ru.


htmlTag

Язык определяется из атрибута <html lang="...">.

<html lang="fr">

Используется как резервный источник, особенно в серверно-рендеренных приложениях.


path

Язык извлекается из URL-пути.

Примеры:

/ru/home
/en/about

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

detection: {
  order: ['path'],
  lookupFromPathIndex: 0
}

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


subdomain

Язык определяется через поддомен:

ru.site.com
en.site.com

Настройка:

detection: {
  order: ['subdomain']
}

Кеширование выбранного языка

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

detection: {
  caches: ['localStorage', 'cookie']
}

Поведение кеширования:

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

Приоритет fallbackLng

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

fallbackLng: 'en'

Также возможно указание нескольких fallback-языков:

fallbackLng: {
  'ru-KZ': ['ru', 'en'],
  default: ['en']
}

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


Нормализация языков

Детектор может возвращать значения с региональными суффиксами:

  • en-US
  • en-GB
  • pt-BR

i18next использует стратегию нормализации:

  • сопоставление с доступными ресурсами
  • fallback к базовому языку (en)
  • проверка алиасов через load: 'languageOnly'
i18n.init({
  load: 'languageOnly'
});

Серверная среда и Accept-Language

В Node.js и SSR-режимах основным источником становится HTTP-заголовок:

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

Используется серверный детектор:

import LanguageDetector from 'i18next-http-middleware';

app.use(LanguageDetector.handle(i18n));

Логика:

  • парсинг заголовка
  • сортировка по приоритету q
  • сопоставление с доступными языками

Пользовательские детекторы

Система позволяет добавлять собственные источники языка. Каждый детектор реализует интерфейс с методами:

  • name
  • lookup()
  • cacheUserLanguage()

Пример:

const customDetector = {
  name: 'customDetector',

  lookup() {
    return window.myAppLanguage;
  },

  cacheUserLanguage(lng) {
    window.myAppLanguage = lng;
  }
};

Подключение:

i18n
  .use(LanguageDetector)
  .init({
    detection: {
      order: ['customDetector', 'navigator']
    }
  });

LanguageDetector.addDetector(customDetector);

Управление порядком источников

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

Типичный продакшн-порядок:

detection: {
  order: [
    'querystring',
    'cookie',
    'localStorage',
    'navigator',
    'htmlTag',
    'path',
    'subdomain'
  ],
  caches: ['localStorage', 'cookie']
}

Логика такой последовательности:

  • querystring — принудительный выбор
  • cookie — сохранённый выбор
  • localStorage — клиентское хранение
  • navigator — системный язык
  • htmlTag — серверная разметка
  • path/subdomain — архитектурные fallback-механизмы

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

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

  1. порядок order
  2. валидность языка
  3. поддержка в resources

Пример ситуации:

  • cookie: ru
  • navigator: en
  • path: de

При порядке [cookie, navigator, path] итоговым будет ru.


Особенности работы в SPA

В одностраничных приложениях детектор выполняется один раз при инициализации. Изменение URL или cookie после загрузки не приводит к автоматическому переключению языка без дополнительного вызова:

i18n.changeLanguage('en');

При необходимости синхронизации с маршрутом требуется ручная интеграция с роутером.


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

Процесс определения языка выполняется синхронно до загрузки переводов. Время работы зависит от количества источников:

  • querystring / cookie — O(1)
  • navigator — O(1)
  • path parsing — O(1)
  • custom detectors — зависит от реализации

Избыточное количество источников увеличивает только количество операций чтения окружения, но не влияет на сетевые запросы.


Типичные ошибки конфигурации

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

  • отсутствие fallbackLng
  • неверный lookupQuerystring
  • несогласованные ключи cookie и localStorage
  • конфликт load: 'languageOnly' с региональными кодами
  • неправильный порядок order, где менее приоритетный источник перекрывает важный

Особенно критична ситуация, когда navigator стоит выше cookie, из-за чего пользовательский выбор игнорируется.


Связь с загрузкой ресурсов

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

/locales/{lng}/translation.json

При языке ru загрузится:

/locales/ru/translation.json

Если язык не найден, активируется fallback-цепочка загрузки.


Интеграция с middleware и фреймворками

В React, Next.js и Express детектор часто используется совместно с серверным middleware, чтобы обеспечить единый язык на клиенте и сервере.

На сервере:

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

На клиенте:

  • повторная детекция для синхронизации
  • кеширование результата сервера

Такая схема исключает расхождения между SSR и гидратацией.