Files
sharper.ly/apps/web/app/routes/locale-layout.tsx
T
2026-09-11 02:46:56 +02:00

48 lines
1.6 KiB
TypeScript

/* ============================================================================
":lang" LAYOUT
----------------------------------------------------------------------------
Owned by the app shell, not by a page agent.
One route tree serves both languages: every page module below this layout is
rendered for /en/... and /ar/... alike, and reads its locale from
`useLocale()` (or from this route's loader data via
`useRouteLoaderData("locale")`).
The layout itself renders nothing but the outlet - the header, the footer
and <html lang/dir> live in the root route, above it - so its whole job is
validating the segment:
/en, /ar -> render
/EN, /Ar -> 301 to the lower-case canonical URL
/fr, /xx -> 404 through the root ErrorBoundary
========================================================================= */
import { data, Outlet, redirect } from "react-router";
import { dirFor, isLocale } from "~/i18n/config";
import type { Route } from "./+types/locale-layout";
export function loader({ params, request }: Route.LoaderArgs) {
const segment = params.lang;
const normalized = segment?.toLowerCase();
if (!isLocale(normalized)) {
throw data("Not Found", { status: 404, statusText: "Not Found" });
}
if (normalized !== segment) {
const url = new URL(request.url);
url.pathname = url.pathname.replace(
/^\/[^/]+/,
`/${normalized}`,
);
throw redirect(`${url.pathname}${url.search}`, 301);
}
return { locale: normalized, dir: dirFor(normalized) };
}
export default function LocaleLayout() {
return <Outlet />;
}