Определение предпочтительного языка пользователя

Перенаправление новых посетителей на их наиболее вероятный язык

Проблема

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

Решение

Используйте middleware для перехвата запросов к корневому пути (/). Проверьте HTTP-заголовок Accept-Language, чтобы определить предпочтительный язык пользователя. Если этот язык поддерживается приложением, перенаправьте пользователя на корневую страницу этого языка (например, /fr). Если нет, перенаправьте его на язык по умолчанию (например, /en).

Шаги

1. Установите парсер языка

Заголовок Accept-Language может быть сложным (например, fr-CH, fr;q=0.9, en;q=0.8). Небольшая библиотека поможет разобрать этот заголовок и найти наилучшее совпадение из списка поддерживаемых языков.

Выполните эту команду в терминале:

npm install accept-language-parser

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

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

// i18n.config.ts

export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';

3. Создайте middleware

Создайте файл middleware.ts в корне вашего проекта. Этот файл будет обрабатывать входящие запросы, позволяя проверять путь и заголовки.

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import parser from 'accept-language-parser';
import { locales, defaultLocale } from './i18n.config';

// Вспомогательная функция для нахождения наилучшего языкового совпадения
function getBestLocale(acceptLangHeader: string | null) {
  if (!acceptLangHeader) {
    return defaultLocale;
  }

  // Используйте парсер для нахождения наилучшего поддерживаемого языка
  const bestMatch = parser.pick(locales, acceptLangHeader, {
    loose: true,
  });

  return bestMatch || defaultLocale;
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 1. Проверьте, является ли запрос корневым путем
  if (pathname === '/') {
    // Получите предпочтительный язык пользователя
    const acceptLang = request.headers.get('Accept-Language');
    const bestLocale = getBestLocale(acceptLang);

    // Перенаправьте на путь с наилучшим совпадением языка
    request.nextUrl.pathname = `/${bestLocale}`;
    return NextResponse.redirect(request.nextUrl);
  }

  // 2. Для всех остальных путей продолжайте как обычно
  return NextResponse.next();
}

export const config = {
  matcher: [
    // Пропустите все пути, начинающиеся с:
    // - api (API маршруты)
    // - _next/static (статические файлы)
    // - _next/image (файлы оптимизации изображений)
    // - favicon.ico (файл иконки)
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

Этот код выполняет логику только для корневого пути (/). Если пользователь заходит на /, проверяется его заголовок Accept-Language, находится наилучшее совпадение (например, es) и выполняется перенаправление на /es. Все остальные запросы, такие как /en/about, игнорируются этой логикой и обрабатываются как обычно.