Fumadocs

Internationalization

Support multiple languages in your documentation

Before you get started

Fumadocs is not a full-powered i18n library, it manages only its own components and utilities.

You can use other libraries like next-intl for the rest of your app. Read the Next.js Docs to learn more about implementing I18n in Next.js.

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

Auto Setup

Initialize i18n with CLI.

pnpm i @fumadocs/cli -D
pnpm fumadocs init i18n

This works only for the file structure generated by create-fumadocs-app, you can follow the manual installation for further needs.

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.

Note that this is optional, you can also use your own middleware or the one provided by i18n libraries.

Routing

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

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

source object is the API to interact with your content source. For example, source.getPage() allows you to get a page with its slugs.

To get the page with appropriate locale, you need to pass the locale.

Update the references to your source object in your page.tsx and layout.tsx files, and include a locale code:

layout.tsx
import { source } from '@/lib/source';
 
export default async function Layout({
  params,
  children,
}: {
  params: Promise<{ lang: string }>;
  children: React.ReactNode;
}) {
  // get page tree
  source.pageTree;
  source.pageTree[(await params).lang];
}
page.tsx
import { source } from '@/lib/source';
 
export default async function Page({
  params,
}: {
  params: Promise<{ lang: string; slug?: string[] }>;
}) {
  const { slug, lang } = await params;
  // get page
  source.getPage(slug);
  source.getPage(slug, lang);
 
  // get pages
  source.getPages();
  source.getPages(lang);
}

Note

Without providing a locale code, it uses your default locale instead. You can see Source API for other usages.

Configure i18n on your search solution.

  • Built-in Search (Orama):
    • For most languages, no further changes are needed.
    • For special languages like Chinese & Japanese, they require additional config. See Orama Special Languages 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,
};

Writing Documents

You can create a Markdown or meta file for specific language by adding .{locale} to your file name, like page.fr.md and meta.fr.json.

Be aware that you must create a file for the default locale first.

For the default language, don't add the locale code to file name, just leave it like get-started.mdx.

Example

Assume your default language is en.

Name
file.mdxCorrect
file.cn.mdxCorrect
file.en.mdxDefault locale doesn't need a locale code
components.cn.mdxMissing components.mdx for default language

Fumadocs will only handle navigation for its own layouts (e.g. sidebar components). For other links, you can use the useParams hook to get the locale from url, and attend it to href.

import Link from 'next/link';
import { useParams } from 'next/navigation';
 
const { lang } = useParams();
 
return <Link href={`/${lang}/another-page`}>This is a link</Link>;

In addition, the fumadocs-core/dynamic-link component supports dynamic hrefs, you can use it to attend the locale prefix. It is useful for Markdown/MDX content.

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