---
id: "packages/active-i18n/server"
title: "Server"
description: "Load your dictionaries on the server in Next, translate in Server Components, and translate page metadata."
url: "/packages/active-i18n/server"
locale: "en"
revision: "e07aa4246c1acdc4"
---

# Server

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.

**Example: Server translations for the requested language**

Server: Calls a real route of this site, which does the work on the server. Source: `examples/i18n/server-translations.tsx`.

```tsx
// 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

```ts
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

```tsx
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](/packages/active-i18n/without-next).
