diff --git a/i18n/routing.ts b/i18n/routing.ts index 5a4a9a9..af0470c 100644 --- a/i18n/routing.ts +++ b/i18n/routing.ts @@ -1,9 +1,17 @@ import { defineRouting } from "next-intl/routing"; -export const routing = defineRouting({ - locales: ["de", "en", "ar"], - defaultLocale: "de", - localePrefix: "as-needed", - localeCookie: false, - localeDetection: false, -}); +export const appLocales = ["de", "en", "ar"] as const; + +export type RoutingLocale = (typeof appLocales)[number]; + +export function createI18nRouting(defaultLocale: RoutingLocale = "de") { + return defineRouting({ + locales: appLocales, + defaultLocale, + localePrefix: "as-needed", + localeCookie: false, + localeDetection: false, + }); +} + +export const routing = createI18nRouting(); diff --git a/middleware.ts b/middleware.ts index 1c25008..03d472d 100644 --- a/middleware.ts +++ b/middleware.ts @@ -2,7 +2,7 @@ import createMiddleware from "next-intl/middleware"; import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; -import { routing } from "./i18n/routing"; +import { appLocales, createI18nRouting, routing } from "./i18n/routing"; import { fromDevelopmentAdminPath, getAdminBaseUrl, @@ -16,19 +16,18 @@ import { } from "./lib/admin-routing"; import { getLocalizedPathWithDefault, stripLocalePrefix } from "./lib/locale"; -const intlMiddleware = createMiddleware(routing); const ADMIN_SESSION_COOKIE = "moh_admin_session"; type SiteRuntimeState = { - defaultLocale: (typeof routing.locales)[number]; + defaultLocale: (typeof appLocales)[number]; maintenanceEnabled: boolean; }; -function isSupportedLocale(locale: string | undefined): locale is (typeof routing.locales)[number] { +function isSupportedLocale(locale: string | undefined): locale is (typeof appLocales)[number] { return locale === "ar" || locale === "en" || locale === "de"; } -function getPathLocale(pathname: string, fallbackLocale: (typeof routing.locales)[number]) { +function getPathLocale(pathname: string, fallbackLocale: (typeof appLocales)[number]) { const locale = pathname.split("/")[1]; return isSupportedLocale(locale) ? locale : fallbackLocale; @@ -71,10 +70,6 @@ async function getSiteRuntimeState(request: NextRequest): Promise pathname === `/${locale}` || pathname.startsWith(`/${locale}/`)); -} - function getAdminBasicAuthUser(): string { return process.env.ADMIN_BASIC_AUTH_USER ?? ""; } @@ -173,6 +168,7 @@ export default async function middleware(request: NextRequest) { const siteRuntimeState = await getSiteRuntimeState(request); const configuredDefaultLocale = siteRuntimeState.defaultLocale; + const intlMiddleware = createMiddleware(createI18nRouting(configuredDefaultLocale)); if ( siteRuntimeState.maintenanceEnabled && @@ -185,36 +181,6 @@ export default async function middleware(request: NextRequest) { return NextResponse.redirect(redirectUrl, 307); } - if ( - configuredDefaultLocale !== routing.defaultLocale && - (pathname === "/de" || pathname.startsWith("/de/")) - ) { - return NextResponse.next(); - } - - if ( - configuredDefaultLocale !== routing.defaultLocale && - !hasLocalePrefix(pathname) - ) { - const rewriteUrl = request.nextUrl.clone(); - rewriteUrl.pathname = getLocalizedPathWithDefault( - configuredDefaultLocale, - pathname, - routing.defaultLocale, - ); - return NextResponse.rewrite(rewriteUrl); - } - - if ( - configuredDefaultLocale === routing.defaultLocale && - (pathname === "/de" || pathname.startsWith("/de/")) - ) { - const redirectUrl = request.nextUrl.clone(); - const nextPath = pathname.slice(3) || "/"; - redirectUrl.pathname = nextPath; - return NextResponse.redirect(redirectUrl, 308); - } - return intlMiddleware(request); } diff --git a/tests/locale.test.ts b/tests/locale.test.ts new file mode 100644 index 0000000..9428637 --- /dev/null +++ b/tests/locale.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; + +import { getLocalizedPathWithDefault } from "../lib/locale"; + +describe("locale path helpers", () => { + it("keeps the configured default locale on the bare domain", () => { + expect(getLocalizedPathWithDefault("ar", "/", "ar")).toBe("/"); + expect(getLocalizedPathWithDefault("de", "/", "ar")).toBe("/de"); + expect(getLocalizedPathWithDefault("en", "/", "ar")).toBe("/en"); + }); + + it("builds nested paths against the configured default locale", () => { + expect(getLocalizedPathWithDefault("ar", "/coming-soon", "ar")).toBe("/coming-soon"); + expect(getLocalizedPathWithDefault("de", "/coming-soon", "ar")).toBe("/de/coming-soon"); + expect(getLocalizedPathWithDefault("en", "/portfolio", "de")).toBe("/en/portfolio"); + }); + + it("strips old locale prefixes before rebuilding the target path", () => { + expect(getLocalizedPathWithDefault("en", "/ar/contact", "de")).toBe("/en/contact"); + expect(getLocalizedPathWithDefault("ar", "/de/about", "ar")).toBe("/about"); + }); +});