Universal Router — это гибкая библиотека маршрутизации для JavaScript, позволяющая управлять навигацией в веб-приложениях как на стороне клиента, так и на сервере. Одной из часто встречающихся задач является организация редиректов по языку, когда пользователь автоматически перенаправляется на версию сайта, соответствующую его предпочтениям или настройкам браузера.
Маршруты Universal Router определяются объектами, где ключевые
свойства — это path, action и
children. Для поддержки разных языков удобно строить
маршруты с использованием динамических сегментов,
например:
import UniversalRouter from 'universal-router';
const routes = [
{
path: '/:lang(en|fr|ru)',
children: [
{
path: 'home',
action: () => 'Главная страница'
},
{
path: 'about',
action: () => 'О нас'
}
]
}
];
const router = new UniversalRouter(routes);
В данном примере :lang — динамический параметр,
ограниченный тремя значениями (en, fr,
ru), что позволяет поддерживать три языка. Все дочерние
маршруты автоматически наследуют значение языка из родительского
сегмента.
Чтобы перенаправлять пользователя на язык, который он предпочитает,
можно использовать заголовок Accept-Language на сервере или
navigator.language на клиенте. Пример реализации на стороне
клиента:
const userLang = navigator.language.slice(0, 2); // 'en', 'ru', 'fr' и т.д.
const supportedLangs = ['en', 'fr', 'ru'];
const defaultLang = supportedLangs.includes(userLang) ? userLang : 'en';
router.resolve(`/${defaultLang}/home`).then(page => {
document.body.textContent = page;
});
Ключевой момент — определение языка перед разрешением маршрута, чтобы избежать ненужной загрузки неподходящей страницы.
Часто сайт имеет корневой URL без указания языка (/). В
таком случае необходимо реализовать редирект на язык по
умолчанию:
const routes = [
{
path: '/',
action: () => {
const userLang = navigator.language.slice(0, 2);
const supportedLangs = ['en', 'fr', 'ru'];
const lang = supportedLangs.includes(userLang) ? userLang : 'en';
return router.resolve(`/${lang}/home`);
}
},
{
path: '/:lang(en|fr|ru)',
children: [
{ path: 'home', action: () => 'Главная страница' },
{ path: 'about', action: () => 'О нас' }
]
}
];
Здесь корневой маршрут / не рендерит контент, а сразу
вызывает разрешение маршрута с языковым сегментом.
Universal Router поддерживает middleware-подобные
функции, которые позволяют перехватывать маршруты и выполнять
редирект до вызова основной action. Например:
const router = new UniversalRouter(routes, {
resolveRoute(context, params) {
if (context.path === '/') {
const userLang = navigator.language.slice(0, 2);
const supportedLangs = ['en', 'fr', 'ru'];
const lang = supportedLangs.includes(userLang) ? userLang : 'en';
return router.resolve(`/${lang}/home`);
}
return context.route.action ? context.route.action(context, params) : null;
}
});
Такой подход позволяет централизованно обрабатывать редиректы без дублирования кода в каждом маршруте.
Для более сложных приложений удобно хранить текущий язык в глобальном состоянии (например, через Redux, Zustand или Context API), чтобы избежать повторного вычисления языка на каждом редиректе. Пример с использованием простого объекта состояния:
const appState = { lang: null };
function determineLang() {
if (!appState.lang) {
const userLang = navigator.language.slice(0, 2);
const supportedLangs = ['en', 'fr', 'ru'];
appState.lang = supportedLangs.includes(userLang) ? userLang : 'en';
}
return appState.lang;
}
const routes = [
{
path: '/',
action: () => router.resolve(`/${determineLang()}/home`)
},
{
path: '/:lang(en|fr|ru)',
children: [
{ path: 'home', action: () => `Главная страница (${appState.lang})` },
{ path: 'about', action: () => `О нас (${appState.lang})` }
]
}
];
Это упрощает интеграцию с компонентами приложения, так как язык становится доступным в любом месте без повторного запроса к браузеру.
Если пользователь выбирает другой язык на сайте, необходимо не только изменить сегмент URL, но и переинициализировать маршрутизацию:
function changeLanguage(newLang) {
const path = window.location.pathname.split('/').slice(2).join('/');
router.resolve(`/${newLang}/${path}`).then(page => {
document.body.textContent = page;
appState.lang = newLang;
});
}
Этот метод сохраняет текущий маршрут, меняя только языковой сегмент, что улучшает пользовательский опыт.
Для корректного функционирования редиректов по языку нужно предусмотреть обработку неподдерживаемых языков и отсутствующих маршрутов:
router.resolve(window.location.pathname).catch(() => {
const fallbackLang = 'en';
router.resolve(`/${fallbackLang}/home`).then(page => {
document.body.textContent = page;
});
});
Такой подход гарантирует, что пользователь никогда не окажется на “битой” странице, даже если URL введён вручную.
Используя комбинацию динамических сегментов маршрута, middleware и глобального состояния языка, Universal Router позволяет реализовать гибкую систему редиректов по языку, обеспечивая плавную локализацию и управление пользовательскими предпочтениями.