Internationalization
Support multiple languages in your Astro docs.
Fumadocs CLI
npx @fumadocs/cli init --i18n sets up a new docs with i18n, or configure it manually below.
Overview
Configure i18n routing on both Astro and Fumadocs.
Fumadocs handles i18n for the docs content and sidebar. Internationalize the rest of your app with your own i18n setup.
Setup
Define the i18n config in a file. This guide imports it as @/lib/i18n.
import { defineI18n } from 'fumadocs-core/i18n';
export const i18n = defineI18n({
defaultLanguage: 'en',
languages: ['cn', 'en'],
});See available options for i18n config.
Routing
Add a [...lang] prefix to your pages. The rest parameter lets the default language have no prefix (e.g. /docs).
Enable hideLocale, so the URLs generated by Fumadocs match these routes:
export const i18n = defineI18n({
defaultLanguage: 'en',
languages: ['cn', 'en'],
hideLocale: 'default-locale',
});Translations
Define your translations in lib/layout.shared.tsx. See Translations for more details, including the official language packs.
You can also add a locale parameter to baseOptions() for localized layout options:
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());
export function baseOptions(): BaseLayoutProps {
export function baseOptions(locale: string): BaseLayoutProps {
return {
nav: {
title: 'My App',
url: locale === i18n.defaultLanguage ? '/' : `/${locale}`,
},
};
}Pass translations to <Provider />. The MDX content is an island of its own, so it needs a provider too:
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 { translations } from '@/lib/layout.shared';
import SearchDialog from '@/components/search';
export interface ProviderProps {
lang: string;
pathname: string;
params: Record<string, string | undefined>;
children?: ReactNode;
}
export function Provider({ lang, pathname, params, children }: ProviderProps) {
return (
<RootProvider
pathname={pathname}
params={params}
navigate={navigate}
i18n={i18nProvider(translations, lang)}
search={{ SearchDialog }}
>
{children}
</RootProvider>
);
}
/**
* For the MDX content, which is an island of its own.
*/
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>
);
}Pass Locale
Pass the locale to Fumadocs in your source, layouts and pages.
import { i18n } from '@/lib/i18n';
export const source = loader({
source: toFumadocsSource(await getCollection('docs'), await getCollection('meta')),
baseUrl: '/docs',
i18n,
});Search
Configure i18n on your search solution.
- Built-in Search: See Internationalization.
- Cloud Solutions (e.g. Algolia): They usually have official support for multilingual.
Writing Documents
See i18n routing to learn how to create pages for specific locales.
Navigation
Fumadocs only handles navigation for its own layouts (e.g. sidebar).
For other places, extract the locale from Astro.params or the path and prepend it to links.
---
const { lang } = Astro.params;
---
<a href={lang ? `/${lang}/about` : '/about'}>About Us</a>The fumadocs-core/dynamic-link component supports dynamic hrefs, so you can use it to prepend the locale prefix in Markdown/MDX content.
Add it to your MDX components, since the MDX content renders them inside its own island:
import { DynamicLink } from 'fumadocs-core/dynamic-link';
export function getMDXComponents(components?: MDXComponents) {
return {
...defaultMdxComponents,
DynamicLink,
...components,
} satisfies MDXComponents;
}<DynamicLink href="/[lang]/another-page">This is a link</DynamicLink>How is this guide?
Last updated on
