// src/middleware.ts // Next.js Middleware for locale detection and routing import { NextRequest, NextResponse } from 'next/server' import { locales, defaultLocale, isValidLocale, type Locale } from '@/lib/i18n' // Paths that should not be affected by locale routing const PUBLIC_FILE = /\.(.*)$/ const EXCLUDED_PATHS = ['/admin', '/api', '/_next', '/favicon.ico', '/robots.txt', '/sitemap.xml'] /** * Detect user's preferred locale from Accept-Language header */ function getPreferredLocale(request: NextRequest): Locale { const acceptLanguage = request.headers.get('accept-language') if (!acceptLanguage) { return defaultLocale } // Parse Accept-Language header const languages = acceptLanguage .split(',') .map((lang) => { const [code, priority] = lang.trim().split(';q=') return { code: code.split('-')[0].toLowerCase(), // Get primary language code priority: priority ? parseFloat(priority) : 1, } }) .sort((a, b) => b.priority - a.priority) // Find first matching locale for (const { code } of languages) { if (isValidLocale(code)) { return code } } return defaultLocale } /** * Get locale from cookie */ function getLocaleFromCookie(request: NextRequest): Locale | null { const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value if (cookieLocale && isValidLocale(cookieLocale)) { return cookieLocale } return null } export function middleware(request: NextRequest) { const { pathname } = request.nextUrl // Skip locale routing for excluded paths and public files if ( EXCLUDED_PATHS.some((path) => pathname.startsWith(path)) || PUBLIC_FILE.test(pathname) ) { return NextResponse.next() } // Check if pathname already has a valid locale prefix const pathnameLocale = pathname.split('/')[1] if (isValidLocale(pathnameLocale)) { // Valid locale in URL, set cookie and continue const response = NextResponse.next() response.cookies.set('NEXT_LOCALE', pathnameLocale, { maxAge: 60 * 60 * 24 * 365, // 1 year path: '/', }) return response } // No locale in URL, redirect to preferred locale const cookieLocale = getLocaleFromCookie(request) const preferredLocale = cookieLocale || getPreferredLocale(request) // Build new URL with locale prefix const newUrl = new URL(request.url) newUrl.pathname = `/${preferredLocale}${pathname === '/' ? '' : pathname}` // Redirect to localized URL const response = NextResponse.redirect(newUrl) response.cookies.set('NEXT_LOCALE', preferredLocale, { maxAge: 60 * 60 * 24 * 365, // 1 year path: '/', }) return response } export const config = { // Match all paths except static files and API routes matcher: ['/((?!api|_next/static|_next/image|admin|favicon.ico).*)'], }