/*
Maintains packages/share/src/middleware/language-redirect.ts as an authored share middleware module so agents can evolve it without rediscovering local boundaries.
- Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file.
- Redirect target derives from system.md i18n config — no hardcoded locale list.
- RFC-0133: backfilled MODULE_MAP and CHANGE_SUMMARY markers for compass.validate compliance.
*/
import { defineMiddleware } from "astro:middleware";
/**
* Factory for language routing middleware.
*
* [RFC-0160] The default language is served WITHOUT a URL prefix (`/`,
* `/`), and only non-default languages are prefixed (`//…`).
* Therefore unprefixed paths ARE default-language content and MUST NOT be
* rewritten to `//…`. Client-side language detection for the
* default audience happens exclusively at `/` (see the root entry page's
* soft redirect, RFC-0159).
*
* This middleware also issues a 308 redirect when the default language is
* accessed with its old prefixed URL (`//…`), so bookmarks and
* search results do not 404. In static-output production this is handled by
* the `_redirects` file; the middleware covers dev mode and SSR routes.
*/
export function createLanguageRedirectMiddleware(options: {
supportedLangs: readonly string[];
defaultLang: string;
}) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return defineMiddleware((ctx: any, next: any) => {
const url = new URL(ctx.request.url);
const segments = url.pathname.split("/").filter(Boolean);
const firstSegment = segments[0];
// Redirect // → / with a 308 Permanent Redirect.
if (firstSegment === options.defaultLang) {
const rest = segments.slice(1).join("/");
const target = "/" + (rest ? rest + url.search : url.search);
return ctx.redirect(target, 308);
}
return next();
});
}