Session 12: i18n (10 languages, cookie-based), Africa tier .99, locale switcher, RTL Arabic (1305 tests)
This commit is contained in:
@@ -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
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user