Переключение языков

Universal Router — это легковесная библиотека для маршрутизации в JavaScript-приложениях, которая позволяет строить динамическую маршрутизацию с поддержкой асинхронных обработчиков и сложной логики. Одной из частых задач является организация многоязычного интерфейса с возможностью переключения языков. Для этого используются параметры маршрута, контекст и middleware-подобные функции.


Использование параметров маршрута для языка

Основной подход — включение кода языка в путь URL. Например:

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/:lang',
    async action(context) {
      const { lang } = context.params;
      context.language = lang;
      return `Выбран язык: ${lang}`;
    }
  },
  {
    path: '/:lang/about',
    async action(context) {
      return `О сайте на языке ${context.language}`;
    }
  }
];

const router = new UniversalRouter(routes);

router.resolve({ pathname: '/en/about' }).then(console.log);

Здесь :lang становится динамическим параметром маршрута. context.params позволяет получить код языка и использовать его для локализации контента или для выбора соответствующих данных.


Хранение текущего языка в контексте

Universal Router передаёт объект context каждому обработчику маршрута. Это позволяет хранить информацию о текущем языке:

const context = { language: 'ru' };

const routes = [
  {
    path: '/:lang/home',
    async action(ctx) {
      ctx.language = ctx.params.lang;
      return `Главная страница на ${ctx.language}`;
    }
  }
];

router.resolve({ pathname: '/en/home', context }).then(console.log);

context.language может использоваться в последующих маршрутах или для передачи в функции рендеринга. Такой подход упрощает поддержку глобальной локализации в приложении.


Динамическая генерация маршрутов для всех языков

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

const supportedLanguages = ['en', 'ru', 'fr'];
const baseRoutes = [
  { path: '/home', name: 'home' },
  { path: '/about', name: 'about' }
];

const routes = supportedLanguages.flatMap(lang =>
  baseRoutes.map(route => ({
    path: `/${lang}${route.path}`,
    async action(ctx) {
      ctx.language = lang;
      return `Страница ${route.name} на ${lang}`;
    }
  }))
);

const router = new UniversalRouter(routes);

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


Переключение языка без изменения пути

Иногда требуется сменить язык, не меняя основного маршрута. Для этого можно использовать middleware-подобные функции и редирект:

const routes = [
  {
    path: '/:lang/home',
    async action(ctx) {
      ctx.language = ctx.params.lang;
      return `Главная на ${ctx.language}`;
    }
  }
];

function changeLanguage(router, currentPath, newLang) {
  const newPath = currentPath.replace(/^\/[^/]+/, `/${newLang}`);
  return router.resolve({ pathname: newPath });
}

router.resolve({ pathname: '/ru/home' }).then(result => {
  console.log(result); // Главная на ru
  changeLanguage(router, '/ru/home', 'en').then(console.log); // Главная на en
});

Регулярное выражение ^\/[^/]+ заменяет первый сегмент пути на новый код языка, сохраняя остальную часть маршрута. Это удобно для интерфейсов с переключателем языка.


Интеграция с системами локализации

Universal Router не накладывает ограничений на библиотеку локализации. context.language можно передавать в i18n-движки, например i18next:

import i18next from 'i18next';

const routes = [
  {
    path: '/:lang/dashboard',
    async action(ctx) {
      i18next.changeLanguage(ctx.params.lang);
      return i18next.t('dashboard.title');
    }
  }
];

Каждый переход по маршруту автоматически устанавливает нужный язык в i18n-контексте.


Поддержка fallback-языка

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

const defaultLang = 'en';

const routes = [
  {
    path: '/:lang?/home',
    async action(ctx) {
      const lang = ctx.params.lang || defaultLang;
      ctx.language = lang;
      return `Главная на ${lang}`;
    }
  }
];

Использование ?: делает сегмент языка необязательным, а оператор || задаёт fallback. Такой подход повышает устойчивость приложения к некорректным URL.


Использование nested маршрутов для локализации

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

const routes = [
  {
    path: '/:lang',
    children: [
      { path: '/home', action: ctx => `Главная на ${ctx.params.lang}` },
      { path: '/about', action: ctx => `О сайте на ${ctx.params.lang}` }
    ]
  }
];

const router = new UniversalRouter(routes);

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


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

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

const routes = [
  {
    path: '/:lang/home',
    async action(ctx) {
      const translations = await import(`./locales/${ctx.params.lang}.json`);
      return translations.default.homeTitle;
    }
  }
];

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


Сводка ключевых техник

  • Использование параметров маршрута :lang для выбора языка.
  • Хранение текущего языка в context.
  • Динамическая генерация маршрутов для всех поддерживаемых языков.
  • Переключение языка без изменения остальной части URL.
  • Интеграция с системами i18n и поддержка fallback-языков.
  • Вложенные маршруты для группировки страниц по языкам.
  • Асинхронная подгрузка локализованных данных.

Эти методы обеспечивают гибкую и масштабируемую локализацию с использованием Universal Router, позволяя управлять маршрутами и языковыми ресурсами централизованно и эффективно.