import { CONSENT_COOKIE, parseConsent } from '../consent/storage.js'; import { isValidLocale, LOCALE_COOKIE_NAME, negotiateLocale } from '../i18n/index.js'; /** * First path segment of a URL pathname, or '' for root. * '/pl/o-nas' → 'pl', '/o-nas' → 'o-nas', '/' → ''. */ function firstSegment(pathname) { return pathname.split('/').filter(Boolean)[0] ?? ''; } /** * Builds locale-routing logic for Next.js middleware. * * Behavior: * - path already starts with a valid locale (/pl/...) → pass through * - any other path (/, /o-nas) → redirect to /{locale}{path}, where locale * comes from negotiateLocale (cookie → Accept-Language → default) * - the chosen locale is written to a cookie so the next visit is stable * * The plugin returns a decision; the thin middleware.ts in the client project * turns it into a NextResponse. This keeps all logic in the plugin while * respecting that middleware.ts must physically live in the client app. * * @example * // middleware.ts (client project) — one wiring file, no logic: * import { NextResponse } from 'next/server' * import { localeMiddleware } from './ipal.middleware' // created from this factory * export function proxy(req) { * const r = localeMiddleware(req) * if (r.type === 'next') return NextResponse.next() * const res = NextResponse.redirect(r.location) * // cookie is optional: only present when the visitor consented to the * // gating category (functional by default). Guard before setting. * if (r.cookie) res.cookies.set(r.cookie.name, r.cookie.value) * return res * } */ export function createLocaleMiddleware({ config, consentCategory = 'functional', consentCookieName = CONSENT_COOKIE, cookieName = LOCALE_COOKIE_NAME }) { return function localeMiddleware(request) { const { pathname } = request.nextUrl; // Already locale-prefixed → nothing to do if (isValidLocale(firstSegment(pathname), config)) { return { type: 'next' }; } // Resolve the locale to use const locale = negotiateLocale({ acceptLanguage: request.headers.get('accept-language'), config, cookieLocale: request.cookies.get(cookieName)?.value ?? null }); // Redirect to the locale-prefixed path, preserving the rest const url = request.nextUrl.clone(); url.pathname = `/${locale}${pathname === '/' ? '' : pathname}`; // Persist the locale choice ONLY if the visitor consented to the gating // category. 'necessary' is always granted, so passing consentCategory: // 'necessary' always persists. For 'functional' (default), we read the // consent cookie and only write the locale cookie when functional is true. // Without consent the locale is still detected each request (routing works), // it just isn't remembered across visits — which is the whole point of // gating a functional cookie behind consent. let mayPersist = consentCategory === 'necessary'; if (!mayPersist) { const consent = parseConsent(request.cookies.get(consentCookieName)?.value); mayPersist = consent?.[consentCategory] === true; } return { type: 'redirect', location: url.toString(), ...mayPersist ? { cookie: { name: cookieName, value: locale } } : {} }; }; } /** * Default Next.js middleware matcher: run on everything except API routes, the * admin panel, Next internals, and files with an extension (static assets). */ export const DEFAULT_MIDDLEWARE_MATCHER = [ '/((?!api|admin|_next|.*\\..*).*)' ]; //# sourceMappingURL=localeMiddleware.js.map