Session 12: i18n (10 languages, cookie-based), Africa tier .99, locale switcher, RTL Arabic (1305 tests)

This commit is contained in:
Kev
2026-06-10 22:24:40 -04:00
parent e5c45ecc8e
commit d957dee17b
27 changed files with 1834 additions and 29 deletions
+39
View File
@@ -753,3 +753,42 @@ body.tex-grain::before {
transition-duration: 0.01ms !important;
}
}
/* ─────────────────────────────────────────────────────────
RTL support (Session 12 — Arabic).
Toggled by <html dir="rtl">. The root layout server-
resolves the locale and sets the attribute; these rules
flip the few directional surfaces (nav flex direction,
sidebar drawers, list markers) without inverting the
whole grid (the Bloomberg-style data tables stay LTR
even in RTL mode — numbers read left-to-right
regardless).
───────────────────────────────────────────────────────── */
[dir="rtl"] body {
text-align: right;
}
[dir="rtl"] .nav-links,
[dir="rtl"] .nav-desktop {
flex-direction: row-reverse;
}
[dir="rtl"] .mono,
[dir="rtl"] [class*="font-mono"] {
/* Monospace numeric blocks stay LTR — financial data reads
left-to-right in every locale by convention. */
direction: ltr;
text-align: right;
unicode-bidi: isolate;
}
[dir="rtl"] .pricing-grid {
direction: rtl;
}
[dir="rtl"] .btn-primary,
[dir="rtl"] .btn-ghost {
/* Buttons stay LTR so chevrons / arrows render predictably. */
unicode-bidi: isolate;
}
+16 -2
View File
@@ -12,6 +12,9 @@ import MFAPrompt from '@/components/MFAPrompt';
import MFAChallenge from '@/components/MFAChallenge';
import CookieConsent from '@/components/CookieConsent';
import SentryInit from '@/components/SentryInit';
import { LocaleProvider } from '@/contexts/LocaleContext';
import { headers } from 'next/headers';
import { LOCALE_HEADER, isLocale, DEFAULT_LOCALE, LOCALE_META } from '@/lib/locales';
import './globals.css';
export const metadata: Metadata = {
@@ -85,9 +88,18 @@ export const viewport: Viewport = {
maximumScale: 5,
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
export default async function RootLayout({ children }: { children: React.ReactNode }) {
// Session 12 — resolve locale from the middleware-stamped request
// header so server components render with the right translations
// and the <html> dir attribute is set before paint (no FOUC of
// mis-directional text).
const hdrs = await headers();
const localeHeader = hdrs.get(LOCALE_HEADER);
const locale = isLocale(localeHeader) ? localeHeader : DEFAULT_LOCALE;
const dir = LOCALE_META[locale].dir;
return (
<html lang="en" className="dark">
<html lang={locale} dir={dir} className="dark">
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
@@ -97,6 +109,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
/>
</head>
<body className="antialiased tex-grain">
<LocaleProvider locale={locale}>
<PostHogProvider>
<AuthProvider>
<ExplainModeProvider>
@@ -115,6 +128,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</ExplainModeProvider>
</AuthProvider>
</PostHogProvider>
</LocaleProvider>
</body>
</html>
);