import createMiddleware from "next-intl/middleware"; import { NextResponse } from "next/server"; import { routing } from "./i18n/routing"; const intlMiddleware = createMiddleware(routing); const SUPPORTED_LOCALES = routing.locales; function parseAcceptLanguage(header) { if (!header) return []; return header .split(",") .map((part) => { const [langRange, qPart] = part.trim().split(";"); let q = 1.0; if (qPart) { const match = qPart.match(/q=([\d.]+)/); if (match) { const parsed = parseFloat(match[1]); if (!isNaN(parsed)) q = parsed; } } return { lang: langRange.trim().toLowerCase(), q }; }) .sort((a, b) => b.q - a.q); } function matchLocale(lang) { if (lang.startsWith("pt")) return "pt"; if (lang.startsWith("es")) return "es"; if (lang.startsWith("fr")) return "fr"; if (lang.startsWith("de")) return "de"; if (lang.startsWith("zh")) return "zh-CN"; if (lang.startsWith("en")) return "en"; return null; } function resolveRootLocale(request) { const cookieValue = request.cookies.get("NEXT_LOCALE")?.value; if (cookieValue && SUPPORTED_LOCALES.includes(cookieValue)) { return cookieValue; } const acceptLang = request.headers.get("accept-language"); const parsed = parseAcceptLanguage(acceptLang); for (const { lang } of parsed) { const matched = matchLocale(lang); if (matched) return matched; } return "en"; } export default function middleware(request) { const { pathname, search } = request.nextUrl; // Exclusões de caminhos e arquivos estáticos if ( pathname.startsWith("/api") || pathname.startsWith("/_next") || pathname === "/robots.txt" || pathname === "/sitemap.xml" || pathname === "/google21a790a2c23c0ec5.html" || /\.[a-zA-Z0-9]+$/.test(pathname) ) { return NextResponse.next(); } // 1. Raiz: negociação de idioma (307) if (pathname === "/") { const targetLocale = resolveRootLocale(request); const url = new URL(`/${targetLocale}/`, request.url); url.search = search; const response = NextResponse.redirect(url, 307); response.headers.set("Vary", "Accept-Language, Cookie"); response.headers.set("Cache-Control", "private, no-store"); return response; } // 2. Livro fora do português: 404 if (/^\/(en|es|fr|de|zh-CN)\/livro(\/.*)?$/.test(pathname)) { return new NextResponse(null, { status: 404 }); } // 3. URLs antigas sem prefixo reconhecido: redirecionamento 308 para /pt/ const hasLocalePrefix = SUPPORTED_LOCALES.some( (loc) => pathname === `/${loc}` || pathname.startsWith(`/${loc}/`) ); if (!hasLocalePrefix) { const url = new URL(`/pt${pathname}`, request.url); url.search = search; return NextResponse.redirect(url, 308); } // 4. Delegação para o roteamento do next-intl return intlMiddleware(request); } export const config = { matcher: [ "/((?!api|_next|_vercel|.*\\..*|robots\\.txt|sitemap\\.xml|google21a790a2c23c0ec5\\.html).*)", ], };