import { NextRequest, NextResponse } from 'next/server'; import { locales, defaultLocale, isLocale, LOCALE_COOKIE, type Locale } from '@/i18n/config'; // Parse the Accept-Language header into an ordered list of base language codes. function parseAcceptLanguage(header: string | null): string[] { if (!header) return []; return header .split(',') .map((part) => { const [tag, q] = part.trim().split(';q='); return { tag: tag.trim().toLowerCase(), q: q ? parseFloat(q) : 1 }; }) .sort((a, b) => b.q - a.q) .map((x) => x.tag); } function detectLocale(request: NextRequest): Locale { // 1. Explicit choice via cookie const cookieLocale = request.cookies.get(LOCALE_COOKIE)?.value; if (isLocale(cookieLocale)) return cookieLocale; // 2. Browser preference via Accept-Language const accepted = parseAcceptLanguage(request.headers.get('accept-language')); for (const tag of accepted) { const exact = locales.find((l) => l.toLowerCase() === tag); if (exact) return exact; const base = locales.find((l) => l.toLowerCase().split('-')[0] === tag.split('-')[0]); if (base) return base; } // 3. Fallback return defaultLocale; } export function middleware(request: NextRequest) { const { pathname } = request.nextUrl; const pathnameHasLocale = locales.some( (locale) => pathname === `/${locale}` || pathname.startsWith(`/${locale}/`), ); if (pathnameHasLocale) return; const locale = detectLocale(request); request.nextUrl.pathname = `/${locale}${pathname === '/' ? '' : pathname}`; const response = NextResponse.redirect(request.nextUrl); response.cookies.set(LOCALE_COOKIE, locale, { path: '/', maxAge: 60 * 60 * 24 * 365 }); return response; } export const config = { // Skip Next internals, the API, and any path containing a dot (static files). matcher: ['/((?!_next|api|.*\\..*).*)'], };