Fumadocs

Astro

Support i18n routing on your Astro + Fumadocs app.

Setup

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

src/lib/i18n.ts
import { defineI18n } from 'fumadocs-core/i18n';

export const i18n = defineI18n({
  defaultLanguage: 'en',
  hideLocale: 'default-locale',
  languages: ['en', 'cn'],
});

See available options for i18n config.

Routing

Move your pages into a [...lang] folder. With hideLocale: 'default-locale', the parameter is empty for the default language (e.g. /docs), and the locale for others (e.g. /cn/docs).

index.astro
docs/[...slug].astro
search.ts

To always prefix the locale, remove hideLocale and use a [lang] folder instead.

Translations

Define your translations in lib/layout.shared.tsx, see Translations for more details (including official language packs).

You can also add locale parameter to baseOptions() for localized layout options:

src/lib/layout.shared.tsx
import { i18n } from '@/lib/i18n';
import { uiTranslations } from 'fumadocs-ui/i18n';
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';

export const translations = i18n
  .translations()
  .extend(uiTranslations())
  .add({
    en: {
      displayName: 'English',
    },
    cn: {
      displayName: 'Chinese',
    },
  });

export function baseOptions(): BaseLayoutProps { 
export function baseOptions(locale: string): BaseLayoutProps { 
  return {
    nav: {
      title: 'Astro',
      url: locale === i18n.defaultLanguage ? '/' : `/${locale}`, 
    },
  };
}

Provider

Pass translations to <RootProvider />. The MDX content renders in an island of its own, add a provider for it too, without theme and search:

src/components/provider.tsx
import { navigate } from 'astro:transitions/client';
import type { ReactNode } from 'react';
import { RootProvider } from 'fumadocs-ui/provider/astro';
import { i18nProvider } from 'fumadocs-ui/i18n'; 
import SearchDialog from '@/components/search';
import { translations } from '@/lib/layout.shared'; 

export interface ProviderProps {
  lang: string; 
  /** `Astro.url.pathname` */
  pathname: string;
  /** `Astro.params` */
  params: Record<string, string | undefined>;
  children?: ReactNode;
}

export function Provider({ pathname, params, children }: ProviderProps) { 
export function Provider({ lang, pathname, params, children }: ProviderProps) { 
  return (
    <RootProvider
      pathname={pathname}
      params={params}
      navigate={navigate}
      i18n={i18nProvider(translations, lang)}
      search={{ SearchDialog }}
    >
      {children}
    </RootProvider>
  );
}

export function ContentProvider({ lang, pathname, params, children }: ProviderProps) {
  return (
    <RootProvider
      pathname={pathname}
      params={params}
      navigate={navigate}
      i18n={i18nProvider(translations, lang)}
      theme={{ enabled: false }}
      search={{ enabled: false }}
    >
      {children}
    </RootProvider>
  );
}

The wrapper of your MDX components receives the props of <Content />, wrap the content with ContentProvider:

src/components/mdx.tsx
import { ContentProvider, type ProviderProps } from '@/components/provider'; 

// ...

export default getMDXComponents({ wrapper: DocsBody }); 
export default getMDXComponents({
  wrapper: ({ children, ...props }: ProviderProps) => (
    <ContentProvider {...props}>
      <DocsBody>{children}</DocsBody>
    </ContentProvider>
  ),
});

Pass Locale

Pass the locale to Fumadocs in your pages and layouts.

import { i18n } from '@/lib/i18n'; 

export const source = loader({
  source: toFumadocsSource(await getCollection('docs'), await getCollection('meta')),
  baseUrl: '/docs',
  i18n, 
});

The search endpoint indexes every language, the search dialog searches the current one:

import { useI18n } from 'fumadocs-ui/contexts/i18n'; 

export default function DefaultSearchDialog(props: SharedProps) {
  const search = useStaticSearch(); 
  const { locale } = useI18n();
  const search = useStaticSearch({ locale });
  // ...
}

See Internationalization for the tokenizers of other languages, like Chinese and Japanese.

Writing Documents

See i18n routing to learn how to create pages for specific locales.

Fumadocs only handles navigation for its own layouts (e.g. sidebar). For other places, prepend the locale to links.

In MDX content, register DynamicLink in your MDX components, it fills [lang] with the locale:

src/components/mdx.tsx
import { DynamicLink } from 'fumadocs-core/dynamic-link'; 

export function getMDXComponents(components?: MDXComponents) {
  return {
    ...defaultMdxComponents,
    DynamicLink, 
    ...components,
  } satisfies MDXComponents;
}
content.mdx
<DynamicLink href="/[lang]/another-page">This is a link</DynamicLink>

Components imported in MDX files are rendered by Astro, outside the providers of content.

How is this guide?

Last updated on