import { NextRequest, NextResponse } from "next/server"; import { match as matchLocale } from "@formatjs/intl-localematcher"; import Negotiator from "negotiator"; const LOCALES = ["uz", "ru", "en"]; const DEFAULT_LOCALE = "uz"; type Locale = (typeof LOCALES)[number]; function getLocaleFromPathname(pathname: string): Locale | null { const firstSegment = pathname.split("/").filter(Boolean)[0]; if (firstSegment && LOCALES.includes(firstSegment as Locale)) { return firstSegment as Locale; } return null; } function getLocaleFromCookie(request: NextRequest): Locale | null { const cookieLocale = request.cookies.get("NEXT_LOCALE")?.value; if (cookieLocale && LOCALES.includes(cookieLocale as Locale)) { return cookieLocale as Locale; } return null; } function getLocaleFromHeaders(request: NextRequest): Locale { const negotiatorHeaders: Record = {}; request.headers.forEach((value, key) => { negotiatorHeaders[key] = value; }); const languages = new Negotiator({ headers: negotiatorHeaders }).languages(); try { return matchLocale(languages, LOCALES as string[], DEFAULT_LOCALE) as Locale; } catch { return DEFAULT_LOCALE; } } export function middleware(request: NextRequest) { const { pathname, search } = request.nextUrl; // 1. If URL has a locale, pass it through with headers (+ sync cookie if needed) const localeFromPath = getLocaleFromPathname(pathname); const localeFromCookie = getLocaleFromCookie(request); const preferredLocale = localeFromPath ?? localeFromCookie ?? getLocaleFromHeaders(request); // 2. No locale in URL → redirect to preferred locale if (!localeFromPath) { const newUrl = new URL(`/${preferredLocale}${pathname}`, request.url); newUrl.search = search; return NextResponse.redirect(newUrl); } // 3. Build response with locale headers for server components const requestHeaders = new Headers(request.headers); requestHeaders.set("x-locale", localeFromPath); requestHeaders.set("x-pathname", pathname); const response = NextResponse.next({ request: { headers: requestHeaders } }); // 4. Sync cookie if it differs from URL locale if (localeFromPath !== localeFromCookie) { response.cookies.set("NEXT_LOCALE", localeFromPath, { path: "/", maxAge: 31_536_000, // 1 year sameSite: "lax", }); } return response; } export const config = { matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"], };