Internationalization
Publish your site in multiple languages.
List your languages, and Fumapress adds the URLs and links between translations. A page without a translation shows the page of the fallback language.
Setup
List the Languages
Define them in a separate file, since custom pages read them too, and pass them to createPress():
import { defineI18n } from "fumadocs-core/i18n";
export const i18n = defineI18n({
languages: ["en", "zh"],
defaultLanguage: "en",
});Translate the Pages
Add the language to the file name. This works for pages and meta.json:
A language without a file inherits the page from the fallback language. If only the English file exists, /zh/get-started still exists and shows the English page.
Inherited pages get noindex and are left out of the sitemap, RSS feed, llms-full.txt and Open
Graph images, since search engines shouldn't see them as translations.
Translate the UI
The search dialog, table of contents and other UI have their own strings. The language pack translates them for Chinese:
npm i @fumapress/languageimport { zhTW } from "@fumapress/language/zh-tw";
export default app.config({
ui: {
translations: i18n.translations().preset("zh", zhTW()),
},
});The language pack has zhCN and zhTW. For other languages, or to change single strings, use .add():
i18n
.translations()
.preset("zh", zhTW())
.add({
en: { displayName: "English" },
zh: {
displayName: "Chinese",
"Search(search dialog)": "搜尋文檔",
"Blog(blog)": "博客",
},
});See the Fumadocs Translations API for details.
Language Prefixes
Each language has a URL prefix. To drop the prefix of the default language, set hideLocale:
const i18n = defineI18n({
languages: ["en", "zh"],
defaultLanguage: "en",
hideLocale: "default-locale",
});| Source | "never" (default) | "default-locale" |
|---|---|---|
content/index.mdx | /en, /zh | /, /zh |
content/setup.mdx | /en/setup, /zh/setup | /setup, /zh/setup |
| Blog index (plugin page) | /en/blog, /zh/blog | /blog, /zh/blog |
With "never", / redirects to /en. When /en has no page, like with content under a baseDir, the root and the navbar title link to the first page of en.
hideLocale: "always" isn't supported, because Fumapress uses the prefix to tell languages apart.
Localized Links
localizePath() adds the prefix, unless hideLocale hides it. Use it in ui.layoutProps, in plugins, and with getPressContext() on custom pages.
this.localizePath("zh", "/setup"); // "/zh/setup"hreflang Tags
Pages link their translations for search engines, with tags like this one:
<link rel="alternate" hreflang="zh-Hans" href="https://acme.dev/cn/setup" />hreflang must be a BCP 47 tag. When a language code isn't one, map it:
export default app.config({
site: { hreflang: { cn: "zh-Hans", tw: "zh-Hant" } },
});Custom Pages
Your pages aren't copied per language. Add a lang param and share the i18n config:
---
import HomeLayout from "@fumapress/astro/components/HomeLayout.astro";
import { i18n } from "../../lib/i18n";
export function getStaticPaths() {
return i18n.languages.map((lang) => ({
params: { lang: lang === i18n.defaultLanguage ? undefined : lang },
}));
}
---
<HomeLayout title="About">
<main>About</main>
</HomeLayout>This example has no prefix on the default language, for hideLocale: "default-locale". HomeLayout.astro reads the language from the URL unless you pass lang.
With hideLocale: "never", / redirects to the default language unless you have a src/pages/index.astro.
Last updated on