Internationalization
Internationalize your Fumapress app.
Setup
To configure internationalization, define an i18n config:
import { defineConfig } from "fumapress";
import { defineDocs } from "fumadocs-mdx/macro";
import { defineI18n } from "fumadocs-core/i18n";
import { uiTranslations } from "fumadocs-ui/i18n";
import { fumapressTranslations } from "fumapress/i18n";
const docs = defineDocs({
dir: "content/docs",
docs: { async: true },
});
const i18n = defineI18n({
languages: ["cn", "en"],
defaultLanguage: "en",
});
const translations = i18n
.translations()
.extend(uiTranslations())
.extend(fumapressTranslations());
export default defineConfig({
content: docs.toFumadocsSource(),
translations,
});You can add translations to UI like:
const translations = i18n
.translations()
.extend(uiTranslations())
.extend(fumapressTranslations())
.add({
en: { displayName: "English" },
cn: {
displayName: "Chinese",
"Search(search dialog)": "搜尋文檔",
"Blog(blog)": "博客",
"All Tags(blog tags page)": "全部标签",
},
});If you have other integrations and want to add translations for them, include them via extend() like:
import { openapiTranslations } from "fumadocs-openapi/i18n";
import { aiTranslations } from "@fumapress/ai/i18n";
import { feedbackTranslations } from "@fumapress/feedback/i18n";
const translations = i18n
.translations()
.extend(uiTranslations())
.extend(fumapressTranslations())
.extend(openapiTranslations())
.extend(feedbackTranslations())
.extend(aiTranslations());Using Language Packs
The official language pack extends @fumadocs/language and adds translations for Fumapress.
npm i @fumapress/languageimport { zhCN } from "@fumapress/language/zh-cn";
const translations = i18n
.translations()
// add Simplified Chinese translations to `cn` locale
.preset("cn", zhCN());Available presets:
zhCNzhTW
When using a language pack, you do not need to include integrations with extend() unless you want to override the default translations.
Learn More
See Fumadocs Translations API for adding translations.
Writing Content
Add Markdown/JSON files for different languages by appending .{locale} to your file name, like:
Fallback Pages
Locales without a translation of a page inherit the file of the fallback language, so /cn/get-started exists even when only get-started.mdx does. Inherited pages are served but not advertised: they get noindex with a canonical link to the source page, and the sitemap, RSS feed, llms-full.txt and Takumi images skip them.
Routing
Every language is served under its own URL prefix, and page.url includes it. To drop the prefix of the default language, set hideLocale in your i18n config:
const i18n = defineI18n({
languages: ["cn", "en"],
defaultLanguage: "en",
hideLocale: "default-locale",
});| Source | hideLocale: "never" (default) | hideLocale: "default-locale" |
|---|---|---|
content/docs/index.mdx | /en, /cn | /, /cn |
content/docs/setup.mdx | /en/setup, /cn/setup | /setup, /cn/setup |
src/pages/about.tsx | /en/about, /cn/about | /about, /cn/about |
| Blog index (plugin routes) | /en/blog, /cn/blog | /blog, /cn/blog |
| Not found page | /404, /en/404, /cn/404 | /404, /cn/404 |
/ | redirects to /en | the index page of en |
File-based Pages
Pages and layouts in src/pages are registered once per language, under its prefix and with a lang prop:
export default function Page({ lang }: { lang: string }) {
return <h1>{lang === "cn" ? "关于" : "About"}</h1>;
}With autoI18n: false, a page is registered once at its own path (e.g. /privacy) and rendered as the default language, root layout included. See Routing for route config.
In Plugins and Layouts
Build language-aware links with localizePath() from the app context, it adds the prefix unless hideLocale hides it:
const ctx = getPressContext();
ctx.localizePath("cn", "/blog"); // "/cn/blog"
ctx.localizePath("en", "/blog"); // "/blog" with `hideLocale: "default-locale"`, otherwise "/en/blog"A plugin that creates its own pages registers one per language with createPage(): path is prefixed for lang, the page gets it as a prop, and the pages sharing a path link each other as translations:
import type { PressPlugin } from "fumapress";
const plugin: PressPlugin = {
name: "changelog",
createPages({ createPage }) {
for (const lang of this.i18nConfig?.languages ?? [undefined]) {
createPage({ path: "/changelog", lang, component: ChangelogPage });
}
},
};Without lang, the page is served without prefix as the default language.
Last updated on
