From 08f04c865ca71ee05f948827d4be8bf2a36ef2e2 Mon Sep 17 00:00:00 2001 From: Osama Mabkhot <99215291+O2sa@users.noreply.github.com> Date: Sat, 14 Nov 2026 02:16:37 +0300 Subject: [PATCH] perf(i18n): apply transparent rewrite, static pages, and zero-fouc theme - Route directory restructured from src/app/ to src/app/[locale]/ with generateStaticParams() pre-rendering static HTML (SSG) for English and Arabic. - Middleware transparently rewrites unprefixed URLs (/, /leaderboard, etc.) directly to localized pages with zero URL redirect flicker. - Bypasses static assets, metadata files (manifest.webmanifest, robots, sitemap), and API routes from rewrite. - Retains inline blocking script in head for 0ms initial theme FOUC prevention and keeps client-side language switching reactive without router.refresh() to avoid theme flashing. - Adds darkreader-lock to suppress browser extension hydration mismatches and disables unused logo preload. - Adds comprehensive unit test suite in src/lib/i18n/tests/middleware.test.ts. --- next.config.js | 1 - src/app/{ => [locale]}/layout.tsx | 43 +++++++------ .../leaderboard/[country]/loading.tsx | 0 .../leaderboard/[country]/page.tsx | 8 +-- .../{ => [locale]}/leaderboard/loading.tsx | 0 src/app/{ => [locale]}/leaderboard/page.tsx | 0 src/app/{ => [locale]}/page.tsx | 0 .../scoring-methodology/page.tsx | 0 .../user/[username]/loading.tsx | 0 .../{ => [locale]}/user/[username]/page.tsx | 2 +- src/components/layout/app-header.tsx | 2 +- src/components/layout/brand-logo.tsx | 2 +- src/components/providers/theme-provider.tsx | 12 ++++ src/components/seo/json-ld.tsx | 1 + .../scoring/tests/scoring-methodology.test.ts | 9 ++- src/lib/i18n/tests/middleware.test.ts | 60 ++++++++++++++++++ src/middleware.ts | 61 ++++++++++++------- 17 files changed, 150 insertions(+), 51 deletions(-) rename src/app/{ => [locale]}/layout.tsx (79%) rename src/app/{ => [locale]}/leaderboard/[country]/loading.tsx (100%) rename src/app/{ => [locale]}/leaderboard/[country]/page.tsx (95%) rename src/app/{ => [locale]}/leaderboard/loading.tsx (100%) rename src/app/{ => [locale]}/leaderboard/page.tsx (100%) rename src/app/{ => [locale]}/page.tsx (100%) rename src/app/{ => [locale]}/scoring-methodology/page.tsx (100%) rename src/app/{ => [locale]}/user/[username]/loading.tsx (100%) rename src/app/{ => [locale]}/user/[username]/page.tsx (99%) create mode 100644 src/lib/i18n/tests/middleware.test.ts diff --git a/next.config.js b/next.config.js index 073b486..bafd044 100644 --- a/next.config.js +++ b/next.config.js @@ -2,7 +2,6 @@ const nextConfig = { output: "standalone", reactStrictMode: true, - typedRoutes: true, images: { unoptimized: true, remotePatterns: [ diff --git a/src/app/layout.tsx b/src/app/[locale]/layout.tsx similarity index 79% rename from src/app/layout.tsx rename to src/app/[locale]/layout.tsx index ba575c8..963e249 100644 --- a/src/app/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -1,19 +1,11 @@ -import "./globals.css"; +import "@/app/globals.css"; import type { ReactNode } from "react"; import type { Metadata } from "next"; -import { cookies, headers } from "next/headers"; -import { - DEFAULT_LOCALE, - LOCALE_COOKIE, - getLocaleDir, - isSupportedLocale, - parseAcceptLanguage, - supportedLocales, -} from "@/lib/i18n/core"; +import { DEFAULT_LOCALE, getLocaleDir, isSupportedLocale, supportedLocales } from "@/lib/i18n/core"; import { getMetadataBase, toAbsoluteUrl } from "@/lib/seo"; import { AppHeader } from "@/components/layout/app-header"; import { AppFooter } from "@/components/layout/app-footer"; -import Providers from "./providers"; +import Providers from "@/app/providers"; const themeInitScript = ` try { @@ -96,21 +88,34 @@ export const metadata: Metadata = { shortcut: "/logo.svg", apple: "/logo.svg", }, + other: { + "darkreader-lock": "", + }, }; -export default async function RootLayout({ children }: { children: ReactNode }) { - const cookieStore = await cookies(); - const headerStore = await headers(); - const cookieLocale = cookieStore.get(LOCALE_COOKIE)?.value || headerStore.get("x-locale"); - const initialLocale = isSupportedLocale(cookieLocale) - ? cookieLocale - : parseAcceptLanguage(headerStore.get("accept-language"), supportedLocales, DEFAULT_LOCALE); +export function generateStaticParams() { + return supportedLocales.map((locale) => ({ locale })); +} + +export default async function RootLayout({ + children, + params, +}: { + children: ReactNode; + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + const initialLocale = isSupportedLocale(locale) ? locale : DEFAULT_LOCALE; const dir = getLocaleDir(initialLocale); return ( -