Fallback стратегии

В системах интернационализации на базе i18next механизм fallback-языков определяет поведение приложения в ситуации, когда перевод отсутствует в текущей локали. Вместо «пустого» результата система последовательно переходит к альтернативным языкам, пока не найдёт подходящее значение.

Ключевая идея fallback-стратегии заключается в формировании упорядоченной цепочки языков, где каждый следующий язык выступает резервным источником переводов.


Базовая настройка fallbackLng

Параметр fallbackLng задаёт основной механизм резервирования языков. Он может быть строкой, массивом или функцией.

Простая конфигурация

import i18next from 'i18next';

i18next.init({
  lng: 'ru',
  fallbackLng: 'en',
  resources: {
    en: {
      translation: {
        greeting: "Hello"
      }
    },
    ru: {
      translation: {
        greeting: "Привет"
      }
    }
  }
});

Если ключ отсутствует в ru, система обратится к en.


Массив fallback-языков

fallbackLng: ['uk', 'pl', 'en']

При отсутствии перевода поиск выполняется последовательно:

  1. украинский
  2. польский
  3. английский

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


Контекстная логика fallback

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

Здесь используется иерархия региональных вариантов. Например, ru-KG сначала ищет перевод в киргизском варианте русского, затем в общем русском, затем в английском.


Иерархия языков и нормализация

i18next автоматически нормализует языковые коды, используя стандарты BCP 47.

Примеры цепочек:

  • ru-RUruen
  • pt-BRpten
  • zh-Hant-TWzh-Hantzhen

Эта иерархия позволяет покрывать широкий спектр локалей без дублирования ресурсов.


fallbackNS: резервные пространства имён

Помимо языков, fallback применяется и к namespace (пространствам переводов).

i18next.init({
  ns: ['common', 'home', 'errors'],
  defaultNS: 'common',
  fallbackNS: ['common', 'errors']
});

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

  1. Проверяется текущий namespace
  2. Затем common
  3. Затем errors

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


defaultNS как часть fallback-цепочки

defaultNS задаёт основной namespace, используемый при отсутствии явного указания.

defaultNS: 'common'

Если ключ запрашивается без namespace:

i18next.t('submit')

поиск выполняется сначала в common, затем в fallbackNS.


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

Если перевод не найден ни в одном языке и namespace, поведение зависит от конфигурации:

1. Возврат ключа

По умолчанию возвращается сам ключ:

missing_key → "missing_key"

2. returnNull

i18next.init({
  returnNull: true
});

В этом режиме вместо строки возвращается null.


3. returnEmptyString

returnEmptyString: false

Пустые строки считаются отсутствием перевода и активируют fallback.


Принудительный fallback через контекст

Можно явно задавать языки для поиска:

i18next.t('welcome', {
  lng: 'fr',
  fallbackLng: ['en', 'de']
});

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


fallback для pluralization

Механизм множественных форм также участвует в fallback-цепочке.

Пример:

{
  "apple_one": "яблоко",
  "apple_other": "яблок"
}

Если форма отсутствует в текущем языке, система ищет её в fallback-языке. При этом учитывается CLDR-логика plural rules, а не только наличие ключа.


detection + fallback

При использовании language detector (i18next-browser-languagedetector), цепочка формируется динамически:

  1. Язык браузера
  2. Сохранённый язык (cookie/localStorage)
  3. fallbackLng
i18next.init({
  detection: {
    order: ['querystring', 'cookie', 'localStorage', 'navigator']
  },
  fallbackLng: 'en'
});

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


fallback и интерполяция

Fallback работает после этапа разрешения интерполяции.

{
  welcome: "Hello {{name}}"
}

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


Частичные ресурсы и fallback

В реальных проектах часто встречается ситуация неполного покрытия переводов.

ru: {
  translation: {
    login: "Войти"
  }
}
en: {
  translation: {
    login: "Login",
    logout: "Logout"
  }
}

При запросе:

i18next.t('logout')

результат берётся из en, так как в ru ключ отсутствует.


Функциональный fallbackLng

fallbackLng: (code) => {
  if (code.startsWith('ru')) return ['ru', 'en'];
  return ['en'];
}

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


Принудительное отключение fallback

i18next.init({
  fallbackLng: false
});

В этом режиме отсутствует резервная логика: если ключ не найден, возвращается fallback-результат по правилам missing key handling.


Приоритеты поиска переводов

Полная последовательность разрешения ключа в i18next:

  1. Текущий язык
  2. Региональный вариант текущего языка
  3. fallbackLng список
  4. fallbackNS список
  5. defaultNS
  6. missing key behavior

Эта цепочка обеспечивает предсказуемость даже в сложных мульти-язычных конфигурациях.


Кэширование и влияние на fallback

После первого разрешения ключа результат кэшируется. Это означает, что:

  • повторные вызовы не проходят всю fallback-цепочку
  • изменение ресурсов во время выполнения может не отражаться без reload ресурсов
  • performance улучшается за счёт сокращения поиска

Особенности поведения в runtime

При динамической загрузке переводов:

i18next.loadNamespaces('dashboard');

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


Глубокая вложенность fallback

В сложных приложениях fallback может сочетаться:

  • язык → язык
  • namespace → namespace
  • key → key (через keySeparator)
  • plural → plural
  • context → context

Все уровни работают независимо, но в рамках одной общей стратегии разрешения значения.


Fallback в связке с keySeparator

{
  keySeparator: '.'
}

Если ключ auth.login.title отсутствует в текущем языке, система попытается найти его в fallback-языке с тем же путём доступа к структуре объекта ресурсов.


Поведение при частично повреждённых ресурсах

Если ресурс существует, но значение равно undefined или null, оно трактуется как отсутствие перевода и инициирует fallback-поиск. Это важное отличие от пустой строки, которая может интерпретироваться в зависимости от returnEmptyString.