mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-09 01:49:49 -10:00
fix(web): 404 for a first segment that is not a locale
Middleware leaves dotted paths alone, so /wp-login.php reached the locale layout and rendered the home page with HTTP 200 and an invalid html lang. Test: locale-layout.test.ts 2/2 (not-found case failed without the fix). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_014ZwqatxgVFxHvovngywnks
This commit is contained in:
1 parent
c37873eb00
commit
49f11f711a
2 files changed
+31
-3
No files matched your search
@@ -1,10 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Nav } from "@/components/nav";
|
||||
import { Footer } from "@/components/footer";
|
||||
import { UsageCounting } from "@/components/usage-counting";
|
||||
import { BUILD_FACTS } from "@/lib/facts";
|
||||
import { localeDirection, locales, type Locale } from "@/lib/i18n/config";
|
||||
import { isValidLocale, localeDirection, locales } from "@/lib/i18n/config";
|
||||
import { getChrome, getHome } from "@/lib/i18n/dictionaries";
|
||||
import { serializeJsonLd } from "@/lib/json-ld";
|
||||
import { buildPageMetadata } from "@/lib/page-meta";
|
||||
@@ -74,6 +75,10 @@ export default async function LocaleLayout({
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
// Middleware leaves dotted paths alone, so `/wp-login.php` reaches this
|
||||
// segment with that "locale". Without this it rendered the home page with
|
||||
// HTTP 200 and `<html lang="wp-login.php">`.
|
||||
if (!isValidLocale(locale)) notFound();
|
||||
const chrome = getChrome(locale);
|
||||
// RTL locales (e.g. ar) set the document direction from the canonical
|
||||
// registry so the browser handles bidirectional layout from the root.
|
||||
@@ -106,9 +111,9 @@ export default async function LocaleLayout({
|
||||
<a href="#main-content" className="skip-link">
|
||||
{chrome.skipToContent}
|
||||
</a>
|
||||
<Nav locale={locale as Locale} />
|
||||
<Nav locale={locale} />
|
||||
<main id="main-content">{children}</main>
|
||||
<Footer locale={locale as Locale} />
|
||||
<Footer locale={locale} />
|
||||
{/* Aggregate usage counting, on by default — see lib/telemetry. The
|
||||
choice lives on the privacy page; every opt-out stays off. */}
|
||||
<UsageCounting appVersion={BUILD_FACTS.version ?? "0.0.0"} />
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("next/font/local", () => ({ default: () => ({ variable: "font" }) }));
|
||||
|
||||
const { default: LocaleLayout } = await import("@/app/[locale]/layout");
|
||||
|
||||
function render(locale: string) {
|
||||
return LocaleLayout({ children: null, params: Promise.resolve({ locale }) });
|
||||
}
|
||||
|
||||
describe("locale layout", () => {
|
||||
// Middleware skips dotted paths, so `/wp-login.php` reaches `[locale]`.
|
||||
it("answers not-found for a segment that is not a locale", async () => {
|
||||
await expect(render("wp-login.php")).rejects.toMatchObject({
|
||||
digest: expect.stringContaining("404"),
|
||||
});
|
||||
});
|
||||
|
||||
it("renders a real locale", async () => {
|
||||
const element = await render("en");
|
||||
expect(element.props.lang).toBe("en");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user