// Root layout. Strategy B (URL-prefix i18n): an INNER reads // the locale FROM THE URL, so the pre-render pipeline resolves the right // catalog per locale-prefixed path (and client-side nav swaps it without a // full reload). The language switcher is plain links — no JS needed, so // this stays compatible with the pages' `interactive: 'none'`. // // ``/``/`` 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 TopNav(): ReactNode { const locale = useUrlLocale() // The path WITHOUT its locale prefix — so the language switch keeps you // on the same doc (e.g. /de/docs/x → /docs/x, 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. Each language // is its own crawlable URL, reached by a full navigation. return ( <nav className="topnav"> <a href={withLocalePrefix('/', locale)}> <T id="nav.home" /> </a> <span className="spacer" /> <span className="lang"> <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> ))} </span> </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"> <TopNav /> {children} </div> </I18nProvider> ) }