import { NextRequest, NextResponse } from "next/server"; import { match as matchLocale } from "@formatjs/intl-localematcher"; import Negotiator from "negotiator"; const PUBLIC_PAGES = ["/login", "/register"]; const LOCALES = ["uz", "ru", "en"]; const DEFAULT_LOCALE = "uz"; type Locale = (typeof LOCALES)[number]; function getLocaleFromPathname(pathname: string): Locale | null { const segments = pathname.split("/").filter(Boolean); const firstSegment = segments[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 unknown as string[], DEFAULT_LOCALE ) as Locale; } catch { return DEFAULT_LOCALE; } } export function middleware(request: NextRequest) { const { pathname, search } = request.nextUrl; // Skip public files and API routes if ( pathname.includes(".") || pathname.startsWith("/api") || pathname.startsWith("/_next") ) { return NextResponse.next(); } // 1. Check URL locale const localeFromPath = getLocaleFromPathname(pathname); // 2. Check cookie locale const localeFromCookie = getLocaleFromCookie(request); // 3. Check browser locale const localeFromBrowser = getLocaleFromHeaders(request); // Priority: URL > Cookie > Browser const preferredLocale = localeFromPath || localeFromCookie || localeFromBrowser; // Faqat kerakli sahifalarni redirect qilamiz const isPublicPage = PUBLIC_PAGES.some((page) => pathname === page); if (isPublicPage) { const url = request.nextUrl.clone(); url.pathname = `/${DEFAULT_LOCALE}/verify-otp`; url.search = search; // ?code=1111&phone=... return NextResponse.redirect(url); } // If URL has no locale, redirect with preferred locale if (!localeFromPath) { const newUrl = new URL(`/${preferredLocale}/${pathname}`, request.url); return NextResponse.redirect(newUrl); } // If URL locale differs from cookie, update cookie if (localeFromPath !== localeFromCookie) { const response = NextResponse.next(); // ✅ Set cookie on server side response.cookies.set("NEXT_LOCALE", localeFromPath, { path: "/", maxAge: 31536000, // 1 year sameSite: "lax", }); // ✅ Pass locale to request headers for server components const requestHeaders = new Headers(request.headers); requestHeaders.set("x-locale", localeFromPath); requestHeaders.set("x-pathname", pathname); return NextResponse.next({ request: { headers: requestHeaders, }, }); } // Normal flow - just pass locale in headers const response = NextResponse.next(); const requestHeaders = new Headers(request.headers); requestHeaders.set("x-locale", localeFromPath); requestHeaders.set("x-pathname", pathname); return NextResponse.next({ request: { headers: requestHeaders, }, }); } export const config = { matcher: [ // Match all pathnames except for // - … if they start with `/api`, `/_next` or `/_vercel` // - … the ones containing a dot (e.g. `favicon.ico`) '/((?!api|_next|_vercel|.*\\..*).*)', ], };