Server
Load your dictionaries on the server in Next, translate in Server Components, and translate page metadata.
@workspace/active-i18n 1.1.0Developmentunreleased, in development in Foundationbaseline ce081dc3
The server example below runs on this site's server while the page renders: a React Server Component calls the server integration for five requested languages against a host set of three.
Server translations for the requested languageServer: Calls a real route of this site, which does the work on the server.
| Requested | Loaded | shop.welcome |
|---|---|---|
| nl | nl | Welkom, Ada |
| en-GB | en | Welcome, Ada |
| pt-BR | pt-BR | Bem-vindo, Ada |
| pt | en | Welcome, Ada |
| fr | en | Welcome, Ada |
// apps/docs/examples/i18n/server-translations.tsx
// A Server Component: it runs on this site's server while the page renders.// `@workspace/active-i18n/server` marks itself server-only, so this file can// never end up in a browser bundle.import {createServerTranslations} from '@workspace/active-i18n/server';import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from '@workspace/active-ui/components/primitives/table';
// The host's resources. A real host imports JSON files per language and// namespace; the loader receives the language and the namespace.const RESOURCES: Record<string, Record<string, unknown>> = { en: {shop: {welcome: 'Welcome, {{name}}', basket: 'Your basket'}}, nl: {shop: {welcome: 'Welkom, {{name}}', basket: 'Je mandje'}}, 'pt-BR': {shop: {welcome: 'Bem-vindo, {{name}}', basket: 'Seu carrinho'}},};
// Bound once, at module scope, to the host's source and language set.const translations = createServerTranslations( { namespaces: ['shop'], importNamespace: async (locale, namespace) => { const tree = RESOURCES[locale]?.[namespace]; if (tree === undefined) throw Object.assign(new Error(`No ${locale}/${namespace}`), { code: 'MODULE_NOT_FOUND', }); return {default: tree}; }, }, {supportedLocales: ['en', 'nl', 'pt-BR'], fallbackLocale: 'en'},);
// The locales a request might ask for; the host resolves its reader's// language (a cookie, a profile, Accept-Language) and passes it in.const REQUESTED = ['nl', 'en-GB', 'pt-BR', 'pt', 'fr'];
/** The server translator for each requested locale, matched against the host's set. */export default async function ServerTranslations() { const rows = await Promise.all( REQUESTED.map(async (requested) => { const {t, locale} = await translations.getServerTranslator(requested); return {requested, locale, welcome: t('shop.welcome', {name: 'Ada'})}; }), ); return ( <Table> <TableHeader> <TableRow> <TableHead>Requested</TableHead> <TableHead>Loaded</TableHead> <TableHead>shop.welcome</TableHead> </TableRow> </TableHeader> <TableBody> {rows.map((row) => ( <TableRow key={row.requested} data-i18n-requested={row.requested}> <TableCell className="font-mono">{row.requested}</TableCell> <TableCell className="font-mono">{row.locale}</TableCell> <TableCell lang={row.locale}>{row.welcome}</TableCell> </TableRow> ))} </TableBody> </Table> );}Bind once, at module scope#
import {createServerTranslations} from '@workspace/active-i18n/server';
export const translations = createServerTranslations( { namespaces: ['common', 'shop'], importNamespace: (locale, namespace) => import(`./resources/${locale}/${namespace}.json`), }, {supportedLocales: ['en', 'nl', 'pt-BR'], fallbackLocale: 'en'},);The language set must be canonical BCP 47 tags, listed once, with the fallback among them; an
invalid set is refused when the instance is created. Give onMissingKey and a logger to report
misses and failures.
Translate in a Server Component#
export default async function Page({locale}: {locale: string}) { const {t} = await translations.getServerTranslator(locale); return <h1>{t('shop.welcome', {name: 'Ada'})}</h1>;}The requested locale is matched exactly first, then with subtags removed from the end (en-GB
loads en), never a sibling (pt does not load pt-BR). A miss loads the fallback with a warning.
Hand the tree to the client#
getServerTranslations(locale) returns {translations, locale}; pass both to <I18nProvider> in
your layout, so client components translate with the same tree.
Page metadata#
getTranslatedMetadata(titleKey, {preferredLocale, descriptionKey?}) returns Next Metadata with a
translated title and description, for generateMetadata.
Where the server entries run#
@workspace/active-i18n/server is marked server-only: it loads in a React Server Components
server and throws in plain Node and in a browser bundle. A worker or another server that is not Next
uses the root entry instead; see Without Next.