Examples
Every working example of active-i18n on one page. Each one shows the file that renders it.
@workspace/active-i18n 1.1.0Developmentunreleased, in development in Foundationbaseline ce081dc3
The first two are Client examples: the dictionaries are in the example and everything runs in your browser. The third is a Server example: a React Server Component that runs on this site's server while the page renders; your browser receives only the result.
Hello Ada
2 new messages
inbox.archived
Missing keys reported: inbox.archived
// apps/docs/examples/i18n/translator.tsx'use client';
import {useState} from 'react';
import { createTranslateFunction, type LocaleTranslations,} from '@workspace/active-i18n';import {Button} from '@workspace/active-ui/components/primitives/button';import { Tabs, TabsList, TabsTrigger,} from '@workspace/active-ui/components/primitives/tabs';
// The host's dictionaries: one tree per language, keyed by namespace.const DICTIONARIES: Record<'en' | 'nl' | 'ar', LocaleTranslations> = { en: { inbox: { greeting: 'Hello {{name}}', messages: {one: '{{count}} new message', other: '{{count}} new messages'}, }, }, nl: { inbox: { greeting: 'Hallo {{name}}', messages: { one: '{{count}} nieuw bericht', other: '{{count}} nieuwe berichten', }, }, }, ar: { inbox: { greeting: 'مرحبًا {{name}}', messages: { zero: 'لا توجد رسائل جديدة', one: 'رسالة جديدة واحدة', two: 'رسالتان جديدتان', few: '{{count}} رسائل جديدة', many: '{{count}} رسالة جديدة', other: '{{count}} رسالة جديدة', }, }, },};
/** A translator per language: interpolation, CLDR plurals and a missing key. */export default function Translator() { const [locale, setLocale] = useState<'en' | 'nl' | 'ar'>('en'); const [count, setCount] = useState(2); // Filled while this render translates: the translator reports a miss // through the callback and still returns the raw key. const missing: string[] = []; const t = createTranslateFunction(DICTIONARIES[locale], locale, { onMissingKey: ({key}) => missing.push(key), }); return ( <div className="flex flex-col gap-4"> <Tabs value={locale} onValueChange={(value) => setLocale(value as 'en' | 'nl' | 'ar')} > <TabsList aria-label="Language"> <TabsTrigger value="en">English</TabsTrigger> <TabsTrigger value="nl">Nederlands</TabsTrigger> <TabsTrigger value="ar">العربية</TabsTrigger> </TabsList> </Tabs> <div lang={locale} dir={locale === 'ar' ? 'rtl' : 'ltr'} className="flex flex-col gap-1" > <p data-i18n-greeting>{t('inbox.greeting', {name: 'Ada'})}</p> <p data-i18n-count={count}>{t('inbox.messages', {count})}</p> <p className="text-muted-foreground font-mono text-sm"> {t('inbox.archived')} </p> </div> <div className="flex flex-wrap items-center gap-2"> <Button variant="outline" size="sm" onClick={() => setCount((n) => Math.max(0, n - 1))} > Fewer </Button> <Button variant="outline" size="sm" onClick={() => setCount((n) => n + 1)} > More </Button> <span className="text-muted-foreground text-sm">count = {count}</span> </div> <p className="text-muted-foreground text-sm"> Missing keys reported:{' '} {missing.length > 0 ? missing.join(', ') : 'none'} </p> </div> );}الطلب 1042
- تاريخ الطلب
- 21/10/2026، 11:30 ص
- المجموع
- 1,234.50 €
// apps/docs/examples/i18n/direction.tsx'use client';
import {useState} from 'react';
import {toIanaTimeZone} from '@workspace/active-format';import type {LanguageConfig, LocaleTranslations} from '@workspace/active-i18n';import { fixedTimeZone, I18nProvider, TimeZoneProvider, useFormatters, useLocale, useTranslations,} from '@workspace/active-i18n/react';import {DirectionProvider} from '@workspace/active-ui/components/primitives/direction';import {Slider} from '@workspace/active-ui/components/primitives/slider';import { Tabs, TabsList, TabsTrigger,} from '@workspace/active-ui/components/primitives/tabs';
// The host's language set: the direction of each language is the host's data.const LANGUAGES: readonly LanguageConfig[] = [ {code: 'en', label: 'English', direction: 'ltr'}, {code: 'ar', label: 'العربية', direction: 'rtl'},];
const TRANSLATIONS: Record<string, LocaleTranslations> = { en: { order: { title: 'Order {{id}}', placed: 'Placed on', total: 'Total', progress: 'Progress', }, }, ar: { order: { title: 'الطلب {{id}}', placed: 'تاريخ الطلب', total: 'المجموع', progress: 'التقدم', }, },};
function OrderSummary() { const t = useTranslations(); const locale = useLocale(); const format = useFormatters(); return ( <div className="flex flex-col gap-3"> <p className="font-medium" data-i18n-locale={locale}> {t('order.title', {id: '1042'})} </p> <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm"> <dt className="text-muted-foreground">{t('order.placed')}</dt> <dd>{format.dateTime('2026-10-21T09:30:00Z')}</dd> <dt className="text-muted-foreground">{t('order.total')}</dt> <dd>{format.currency({amount: 1234.5, currency: 'EUR'})}</dd> </dl> <Slider defaultValue={30} aria-label={t('order.progress')} /> </div> );}
/** * The language decides the words and the formats; the host decides the * direction from its language data and sets `dir` and `DirectionProvider`, * so the layout and the arrow keys follow the reading direction. */export default function Direction() { const [code, setCode] = useState('ar'); const language = LANGUAGES.find((entry) => entry.code === code)!; const direction = language.direction ?? 'ltr'; return ( <div className="flex w-full max-w-md flex-col gap-4"> <Tabs value={code} onValueChange={(value) => setCode(String(value))}> <TabsList aria-label="Language"> {LANGUAGES.map((entry) => ( <TabsTrigger key={entry.code} value={entry.code}> {entry.label} </TabsTrigger> ))} </TabsList> </Tabs> {/* A real host navigates to the other language (setLocale); this demonstration remounts the provider instead. */} <I18nProvider key={code} locale={code} translations={TRANSLATIONS[code]!} supportedLanguages={LANGUAGES} allowLocaleSwitching={false} > <TimeZoneProvider value={fixedTimeZone(toIanaTimeZone('Europe/Amsterdam'))} > <DirectionProvider direction={direction}> <div lang={code} dir={direction} className="rounded-lg border p-4" data-i18n-direction={direction} > <OrderSummary /> </div> </DirectionProvider> </TimeZoneProvider> </I18nProvider> </div> );}Server translations for the requested languageServerCalls 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> );}