Fumadocs

Internationalization

Support multiple languages in your documentation

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

Setup

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

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

Change your current source configurations.

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

Update the usages to your source:

import { source } from '@/app/source';
 
// get page tree
source.pageTree[params.lang];
 
// get page
source.getPage(params.slug, params.lang);
 
// get pages
source.getPages(params.lang);

Middleware

Create a middleware that redirects users to appropriate locale.

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

Root Layout

Create a dynamic route /app/[lang], and move all special files from /app to the folder.

app/[lang]/layout.tsx
export default function Layout({
  params,
  children,
}: {
  params: { lang: string };
  children: React.ReactNode;
}) {
  return (
    <html lang={params.lang}>
      <body>{children}</body>
    </html>
  );
}

Docs Page

Make sure to update references to source and configure Static Site Generation correctly.

page.tsx
import { source } from '@/app/source';
import type { Metadata } from 'next';
import {
  DocsPage,
  DocsBody,
  DocsTitle,
  DocsDescription,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import defaultMdxComponents from 'fumadocs-ui/mdx';
 
export default async function Page({
  params,
}: {
  params: { lang: string; slug?: string[] };
}) {
  const page = source.getPage(params.slug, params.lang);
  if (!page) notFound();
 
  const MDX = page.data.body;
 
  return (
    <DocsPage toc={page.data.toc}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      <DocsBody>
        <MDX components={{ ...defaultMdxComponents }} />
      </DocsBody>
    </DocsPage>
  );
}
 
export async function generateStaticParams() {
  return source.generateParams();
}
 
export function generateMetadata({
  params,
}: {
  params: { lang: string; slug?: string[] };
}) {
  const page = source.getPage(params.slug, params.lang);
  if (!page) notFound();
 
  return {
    title: page.data.title,
    description: page.data.description,
  } satisfies Metadata;
}

Writing Documents

see Page Conventions to learn how to organize your documents.

Configure i18n on your search solution. For Flexsearch, see Setup I18n.

Configurations

You can also customise the i18n middleware from i18n.ts.

Hide Locale Prefix

To hide the locale prefix, for example, use / instead of /en, use the hideLocale option.

ModeDescription
alwaysAlways hide the prefix, detect locale from cookies
default-localeOnly hide the default locale
neverNever hide the prefix (default)

It uses NextResponse.rewrite under the hood.

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

It's not recommended to use always. On this mode, locale is stored as a cookie, read and set on the middleware.

This may cause undesired cache problems on your hosting platform, and need to pay extra attention on SEO to ensure search engines can index your pages correctly.

Last updated on

On this page

Edit on GitHub