feat(plugin): i18n FR + EN (Phase 4.2)
Infrastructure i18n légère, sans deps externe : - lib/i18n/types.ts : LOCALES, DEFAULT_LOCALE, cookie name - lib/i18n/server.ts : getLocale (cookie > Accept-Language > FR), t(key) async server-side, dict(locale) - lib/i18n/client.tsx : LocaleProvider + useLocale + useT - messages/fr.json + messages/en.json : ~50 clés pour landing + header + footer - LocaleSwitcher component (cookie + router.refresh) Plugin gated : - Quand i18n-fr-en désactivé, getLocale() force FR. Le switcher ne s'affiche pas dans le hero. Pas d'impact sur le rendu existant. - Quand activé, switcher visible coin haut-droit du hero. Les composants landing/header/footer rendent en FR ou EN selon le cookie utilisateur. Composants i18n-isés : - HeroSection (eyebrow, titre, CTA) - ExperiencesSection (route/fleuve vs expédition, tous les bullets) - HowItWorksSection (3 étapes) - CESection (KPIs + body + CTA) - TestimonialsSection (eyebrow + titre, citations restent en VO) - Footer (taglines, colonnes) - SeasonBanner (3 saisons + messages) - AccessTypeBadge (labels + tooltips) Pour les ContentPage, le champ lang existait déjà. Une suite (PR ultérieure) ajoutera le filtre lang dans getContentPage + seed pages EN.
This commit is contained in:
parent
efeea16467
commit
cf9da94bb5
15 changed files with 454 additions and 116 deletions
|
|
@ -4,6 +4,8 @@ import "./globals.css";
|
|||
import { PluginProvider } from "@/lib/plugins/client";
|
||||
import { getEnabledPluginKeys, syncPluginsFromRegistry } from "@/lib/plugins/server";
|
||||
import { SeasonBanner } from "@/components/SeasonBanner";
|
||||
import { LocaleProvider } from "@/lib/i18n/client";
|
||||
import { dict, getLocale } from "@/lib/i18n/server";
|
||||
|
||||
// Le layout interroge la DB Plugin à chaque request → rendu dynamique forcé.
|
||||
// Sans ça, le layout (et donc data-theme + enabledKeys passés au client) est
|
||||
|
|
@ -67,10 +69,12 @@ export default async function RootLayout({
|
|||
}
|
||||
|
||||
const themeGuyane = enabledKeys.includes("theme-guyane");
|
||||
const locale = await getLocale();
|
||||
const messages = await dict(locale);
|
||||
|
||||
return (
|
||||
<html
|
||||
lang="fr"
|
||||
lang={locale}
|
||||
className={`${geistSans.variable} ${geistMono.variable} ${cormorant.variable} h-full antialiased`}
|
||||
>
|
||||
<body
|
||||
|
|
@ -78,8 +82,10 @@ export default async function RootLayout({
|
|||
className="min-h-full flex flex-col font-sans"
|
||||
>
|
||||
<PluginProvider enabledKeys={enabledKeys}>
|
||||
<SeasonBanner />
|
||||
{children}
|
||||
<LocaleProvider locale={locale} messages={messages}>
|
||||
<SeasonBanner />
|
||||
{children}
|
||||
</LocaleProvider>
|
||||
</PluginProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue