// Root layout. The framework's auto-wired is COOKIE-driven // (Strategy A). For URL-prefix routing (Strategy B) we add an INNER provider // that reads the locale FROM THE URL, so client-side navigation between / and // /de swaps the catalog without a full reload. At build time it resolves to // the same locale the SSG pipeline pre-rendered. // // NOTE: the island hydrates INDEPENDENTLY of this provider, so // it can't read /useT. The page resolves its strings here (inside this // provider) and passes them down as a `labels` prop — see src/pages/index.tsx. // // ``/``/`` are owned by the framework shell — don't render // them here. Page + meta come from each page's `meta` export. import type { ReactNode } from 'react' import { I18nProvider, T } from '@voltro/i18n' import { useLocation } from '@voltro/web' import { CATALOGS, DEFAULT_LOCALE, SUPPORTED_LOCALES, stripLocalePrefix, useUrlLocale, withLocalePrefix, } from '../lib/locale' import '../globals.css' function LangSwitcher(): ReactNode { const locale = useUrlLocale() // The path WITHOUT its locale prefix — so the language switch keeps you // on the same page (e.g. /de → /, then re-prefix per locale). const barePath = stripLocalePrefix(useLocation()) // Plain <a> (not <Link>): these hrefs are COMPUTED per locale, and // <Link to> wants a statically-known `VoltroUrl` literal. The Router's // global click interceptor SPA-navigates internal <a> anyway, so the // inner provider still swaps catalogs without a full reload. return ( <nav className="langswitch"> <span className="label"> <T id="nav.language" />: </span> {SUPPORTED_LOCALES.map((code) => ( <a key={code} href={withLocalePrefix(barePath, code)} aria-current={code === locale ? 'true' : undefined} > {code.toUpperCase()} </a> ))} </nav> ) } export default function Layout({ children }: { readonly children: ReactNode }): ReactNode { const locale = useUrlLocale() return ( <I18nProvider locale={locale} messages={CATALOGS[locale]} defaultLocale={DEFAULT_LOCALE}> <div className="shell"> <LangSwitcher /> {children} </div> </I18nProvider> ) }