---
title: Internationalization
description: Support multiple languages in your documentation
icon: Globe
---

<Callout title='Before you get started'>

    Fumadocs is not a full-powered i18n library, you can also use other libraries like [next-intl](https://github.com/amannn/next-intl).
    I18n functionality of Fumadocs works without the built-in middleware.

</Callout>

You can setup i18n using Fumadocs CLI or update the configurations manually.

> Read the [Next.js Docs](https://nextjs.org/docs/app/building-your-application/routing/internationalization) to learn more about implementing I18n in Next.js.

## Auto Setup

Initialize i18n with CLI.

```bash
pnpm fumadocs init i18n
```

## Manual Setup

Define the i18n configurations in a file, we will import it with `@/ilb/i18n` in this guide.

```json doc-gen:file
{
  "file": "../../examples/i18n/lib/i18n.ts",
  "codeblock": {
    "lang": "ts",
    "meta": "title=\"lib/i18n.ts\""
  }
}
```

Change your current source configurations.

```ts title="lib/source.ts"
import { i18n } from '@/lib/i18n';
import { loader } from 'fumadocs-core/source';

export const source = loader({
  i18n, // [!code highlight]
  // other options
});
```

### Middleware

Create a middleware that redirects users to appropriate locale.

```json doc-gen:file
{
  "file": "../../examples/i18n/middleware.ts",
  "codeblock": {
    "lang": "ts",
    "meta": "title=\"middleware.ts\""
  }
}
```

See [Middleware](/docs/headless/internationalization#middleware) for customisable options.

### Routing

Create a dynamic route `/app/[lang]`, and move all special files from `/app` to
the folder (e.g. `page.tsx`, `layout.tsx`).

A `I18nProvider` is needed for localization.
Wrap the root provider inside your I18n provider, and provide available languages & translations to it.

Note that only English translations are provided by default.

```tsx title="app/[lang]/layout.tsx"
import { RootProvider } from 'fumadocs-ui/provider';
import { I18nProvider, type Translations } from 'fumadocs-ui/i18n';

const cn: Partial<Translations> = {
  search: 'Translated Content',
  // other translations
};

// available languages that will be displayed on UI
// make sure `locale` is consistent with your i18n config
const locales = [
  {
    name: 'English',
    locale: 'en',
  },
  {
    name: 'Chinese',
    locale: 'cn',
  },
];

export default async function RootLayout({
  params,
  children,
}: {
  params: Promise<{ lang: string }>;
  children: React.ReactNode;
}) {
  const lang = (await params).lang;

  return (
    <html lang={lang}>
      <body>
        <I18nProvider
          locale={lang}
          locales={locales}
          translations={
            {
              cn,
            }[lang]
          }
        >
          <RootProvider>{children}</RootProvider>
        </I18nProvider>
      </body>
    </html>
  );
}
```

### Source

Update the references to your `source` object to include a locale code:

```ts
import { source } from '@/lib/source';

// get page tree
source.pageTree[params.lang];

// get page
source.getPage(params.slug, params.lang);

// get pages
source.getPages(params.lang);
```

In code editors, you can click on the variable in `lib/source.ts` to quickly find its references.
For example, it looks like this for docs layout:

```tsx title="app/[lang]/docs/layout.tsx"
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/docs';
import type { ReactNode } from 'react';

export default async function Layout({
  params,
  children,
}: {
  params: Promise<{ lang: string }>;
  children: ReactNode;
}) {
  const pageTree = source.pageTree[(await params).lang];

  return <DocsLayout pageTree={pageTree}>{children}</DocsLayout>;
}
```

Note that without providing a locale code, it uses your default locale instead.
You can see [Source API](/docs/headless/source-api) for other usages.

### Writing Documents

see [Page Conventions](/docs/ui/page-conventions#internationalization) to learn how to organize your documents.

### Search

Configure i18n on your search solution.

- Built-in Search (Orama):
  - For `createFromSource` and most languages, no further changes are needed.
  - For special languages like Chinese & Japanese, they require additional config.
    See [Orama I18n](/docs/headless/search/orama#internationalization) guide.
- Cloud Solutions (e.g. Algolia):
  - They usually have official support for multilingual.

### Add Language Switch

To allow users changing their language, enable `i18n` on your layouts.

```tsx title="app/layout.config.tsx" twoslash
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';

export const baseOptions: BaseLayoutProps = {
  i18n: true,
};
```

## Navigation

Fumadocs will only handle i18n navigation for its own layouts (e.g. sidebar components),
you have to re-create components like `<Link>` from `next/link` and `useParams` hook to auto attend the locale to `href`.

In addition, the [`fumadocs-core/dynamic-link`](/docs/headless/components/link#dynamic-hrefs) component supports linking to dynamic hrefs, including the locale prefix.
For Markdown/MDX content, you may use it instead of the default anchor (`a`) component:

```mdx title="content.mdx"
import { DynamicLink } from 'fumadocs-core/dynamic-link';

<DynamicLink href="/[lang]/another-page">This is a link</DynamicLink>
```
