Multi-language / Localisation
CAUTION
Experimental — not for production. Managed Data Mode SSR is available for evaluation and testing. Do not use it in production yet. The API and behaviour may change. Until then, continue using Webpack SSR.
Managed Data Mode SSR supports the common multi-language approach using Vocab. See Multiple languages for configuration and workflow.
When you configure languages, SSR can preload the active language chunk on the initial document. An example chunk name is en-translations. Translated text is then available without a delayed download.
Preload the language chunk
Implement a getLanguage method in your server entry. Return a name from config languages (or en-PSEUDO):
// src/server.tsx
import { defineServerEntry } from 'sku/runtime';
const server = defineServerEntry({
getLanguage({ req }) {
return req.path.startsWith('/th') ? 'th-TH' : 'en';
},
});
export default server;If you omit getLanguage, sku does not preload a language chunk. Text may load later.
VocabProvider in the root layout
Wrap VocabProvider around <html> in the root layout. Pass the active language so html lang and any non-hoistable head nodes stay in sync on client navigation.
If language is in the path, derive it from the router (as in the example). If it comes from a cookie or other context, read that in the layout the same way.
// src/RootLayout.tsx
import { VocabProvider } from '@vocab/react';
import { Outlet, useLocation } from 'react-router';
function languageFromPath(pathname: string) {
return pathname.startsWith('/th') ? 'th-TH' : 'en';
}
export const RootLayout = () => {
const { pathname } = useLocation();
const language = languageFromPath(pathname);
return (
<VocabProvider language={language}>
<html lang={language}>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<Outlet />
</body>
</html>
</VocabProvider>
);
};For Vocab configuration (languages config, .vocab folders, translation workflow), see Multiple languages.
Languages in the path
Some URL schemes serve the same page at more than one path. For example, /about can be one language and /fr/about another.
React Router matches one path per route object. You need a separate route for each concrete path.
Prefer listing supported prefixes over a dynamic :lang segment. A param would also match unsupported prefixes.
You can create each route object by hand. You can also use mapRoutePath to expand them.
mapRoutePath
mapRoutePath is an optional export from routesEntry that expands one logical route into several concrete paths. Use it when the same page should match more than one path:
// src/routes.tsx
import type { MapRoutePath, SkuRouteObject } from 'sku/runtime';
import { RootLayout } from './RootLayout';
export const mapRoutePath: MapRoutePath = ({ path }) => {
if (path === 'about') {
return ['about', 'fr/about'];
}
return [path];
};
export const routes: SkuRouteObject[] = [
{
Component: RootLayout,
children: [{ path: 'about', lazy: () => import('./pages/about/about') }],
},
];mapRoutePath is not a substitute for getLanguage. getLanguage still selects the Vocab chunk on the Document.
Hand-duplicating is fine for more control. Do not share one const pageLazy = () => import(…) across copies. That sharing breaks automatic modulepreload (the browser fetch of the route module before the browser needs it).
For nested routes, index homes, and per-site mapping, see Routing → Multiple paths with mapRoutePath.
See also
- Providers — root layout
- Request entries —
getLanguage - Routing — route composition
- Multiple languages — Vocab config and workflow
