Fumadocs

Internationalization

Support multiple languages in your documentation

Before you get started

Fumadocs is not a full-powered i18n library, you can also use other libraries like next-intl. I18n functionality of Fumadocs works without the built-in middleware.

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

Read the Next.js Docs to learn more about implementing I18n in Next.js.

Auto Setup

Initialize i18n with CLI.

pnpm fumadocs init i18n

Manual Setup

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

lib/i18n.ts
import type { I18nConfig } from 'fumadocs-core/i18n';
 
export const i18n: I18nConfig = {
  defaultLanguage: 'en',
  languages: ['en', 'cn'],
};

Change your current source configurations.

lib/source.ts
import { i18n } from '@/lib/i18n';
import { loader } from 'fumadocs-core/source';
 
export const source = loader({
  i18n,
  // other options
});

Middleware

Create a middleware that redirects users to appropriate locale.

middleware.ts
import { createI18nMiddleware } from 'fumadocs-core/i18n';
import { i18n } from '@/lib/i18n';
 
export default createI18nMiddleware(i18n);
 
export const config = {
  // Matcher ignoring `/_next/` and `/api/`
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

See 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.

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:

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:

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 for other usages.

Writing Documents

see Page Conventions to learn how to organize your documents.

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 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.

app/layout.config.tsx
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
 
export const : BaseLayoutProps = {
  : true,
};

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 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:

content.mdx
import { DynamicLink } from 'fumadocs-core/dynamic-link';
 
<DynamicLink href="/[lang]/another-page">This is a link</DynamicLink>
Edit on GitHub

Last updated on

On this page