import { cookies } from "next/headers"; import { getRequestConfig } from "next-intl/server"; // i18n WITHOUT routing: the locale is chosen by a `NEXT_LOCALE` cookie (set by // the language switcher) rather than a URL segment, so every existing route and // the access-guard middleware keep working unchanged. English is the fallback. export const SUPPORTED_LOCALES = ["en", "it", "nl", "de", "fr", "es"] as const; export type AppLocale = (typeof SUPPORTED_LOCALES)[number]; export const DEFAULT_LOCALE: AppLocale = "en"; export function isSupportedLocale(value: string | undefined): value is AppLocale { return !!value && (SUPPORTED_LOCALES as readonly string[]).includes(value); } export default getRequestConfig(async () => { const store = await cookies(); const cookieLocale = store.get("NEXT_LOCALE")?.value; const locale: AppLocale = isSupportedLocale(cookieLocale) ? cookieLocale : DEFAULT_LOCALE; // Safe: `locale` is validated against SUPPORTED_LOCALES above (only en/it/nl/de/fr/es). const messages = (await import(`../messages/${locale}.json`)).default; // English is the source of truth; fall back to it for any key missing from a // translation so the UI never shows a raw key path. const fallback = locale === DEFAULT_LOCALE ? messages : (await import(`../messages/${DEFAULT_LOCALE}.json`)).default; return { locale, messages, // Never throw on a missing message — use the English value, or a humanised // key as a last resort. getMessageFallback({ key }: { key: string }) { const fromEnglish = key .split(".") .reduce((o, k) => (o && typeof o === "object" ? (o as Record)[k] : undefined), fallback); if (typeof fromEnglish === "string") return fromEnglish; const seg = key.split(".").pop() ?? key; return seg.replace(/[._-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); }, onError() { // Swallow MISSING_MESSAGE etc. — getMessageFallback already degrades. }, }; });