Сохранение выбора языка пользователя
Использование cookie для запоминания выбора
Проблема
Пользователь вручную выбирает "Французский" на сайте. Когда он закрывает браузер и позже вводит основной адрес сайта (например, example.com), приложение возвращается к языку по умолчанию (например, английскому) или автоматически определённому языку. Это отсутствие запоминания выбора заставляет пользователя каждый раз находить переключатель языка и заново выбирать язык при начале новой сессии.
Решение
Когда пользователь выбирает язык, сохраните этот выбор в cookie. В middleware, когда пользователь заходит на корневой путь (/), проверьте наличие этого cookie до проверки заголовка Accept-Language. Если найдено действительное cookie, перенаправьте пользователя на корневой путь выбранного языка (например, /fr), игнорируя настройки браузера по умолчанию.
Шаги
1. Определите конфигурацию языков
Создайте центральный файл конфигурации для хранения поддерживаемых языков, языка по умолчанию и имени cookie, которое будет использоваться для сохранения предпочтений пользователя.
// i18n.config.ts
export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const localeCookieName = 'NEXT_LOCALE';
2. Установите парсер языка
Вам всё ещё нужен парсер для заголовка Accept-Language, который будет использоваться как резервный вариант, если cookie не установлено.
npm install accept-language-parser
3. Создайте middleware
Создайте файл middleware.ts в корне вашего проекта. Этот middleware будет сначала проверять наличие cookie NEXT_LOCALE. Если оно не найдено, будет использоваться заголовок Accept-Language. Эта логика будет применяться только к запросам на корневой путь (/).
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import parser from 'accept-language-parser';
import {
locales,
defaultLocale,
localeCookieName,
} from './i18n.config';
function getPreferredLocale(request: NextRequest) {
// 1. Проверка cookie
const cookie = request.cookies.get(localeCookieName);
if (cookie) {
const locale = cookie.value;
if (locales.includes(locale)) {
return locale;
}
}
// 2. Проверка заголовка Accept-Language
const acceptLang = request.headers.get('Accept-Language');
if (acceptLang) {
const bestMatch = parser.pick(locales, acceptLang, {
loose: true,
});
if (bestMatch) {
return bestMatch;
}
}
// 3. Возврат значения по умолчанию
return defaultLocale;
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 1. Проверка, является ли запрос корневым путём
if (pathname === '/') {
// Получение предпочтительного языка пользователя (из cookie или заголовка)
const bestLocale = getPreferredLocale(request);
// Перенаправление на путь с лучшим совпадением языка
request.nextUrl.pathname = `/${bestLocale}`;
return NextResponse.redirect(request.nextUrl);
}
// 2. Для всех остальных путей продолжить как обычно
return NextResponse.next();
}
export const config = {
matcher: [
// Мы хотим запускать это только на корневом пути
'/',
// Также нужно обрабатывать некорневые пути, чтобы они проходили
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
Логика этого middleware теперь правильно отдаёт приоритет явному выбору пользователя (cookie) над его неявным предпочтением (заголовок браузера) при посещении корневого пути сайта.