Skip to content

Commit 890a250

Browse files
committed
🏗️ add i18n support for Mandarin, Spanish, and Japanese docs
Implement Fumadocs internationalization following the official i18n pattern: - Add i18n config with en/zh/es/ja locales (lib/i18n.ts) - Create i18n middleware for locale-based routing - Restructure app routes under [lang] dynamic segment - Add UI string translations for all locales in defineI18nUI - Create translated MDX content files for all 3 docs pages - Update source loader, layout options, and search API for locale support - Update .gitignore to allow docs/lib/ directory https://claude.ai/code/session_01UwMuBPaeouJ16zJTVa8UJm
1 parent 79768ef commit 890a250

27 files changed

Lines changed: 733 additions & 114 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ downloads/
2626
eggs/
2727
.eggs/
2828
lib/
29+
!docs/lib/
2930
lib64/
3031
parts/
3132
sdist/

docs/app/(home)/layout.tsx

Lines changed: 0 additions & 6 deletions
This file was deleted.

docs/app/(home)/page.tsx

Lines changed: 0 additions & 16 deletions
This file was deleted.

docs/app/[lang]/(home)/layout.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { HomeLayout } from "fumadocs-ui/layouts/home";
2+
import { baseOptions } from "@/lib/layout.shared";
3+
4+
export default async function Layout({
5+
params,
6+
children,
7+
}: {
8+
params: Promise<{ lang: string }>;
9+
children: React.ReactNode;
10+
}) {
11+
const { lang } = await params;
12+
return <HomeLayout {...baseOptions(lang)}>{children}</HomeLayout>;
13+
}

docs/app/[lang]/(home)/page.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { DynamicLink } from "fumadocs-core/dynamic-link";
2+
3+
const greetings: Record<string, string> = {
4+
en: "Hello World",
5+
zh: "你好世界",
6+
es: "Hola Mundo",
7+
ja: "こんにちは世界",
8+
};
9+
10+
const descriptions: Record<string, string> = {
11+
en: "You can open",
12+
zh: "你可以打开",
13+
es: "Puedes abrir",
14+
ja: "開くことができます",
15+
};
16+
17+
const docsText: Record<string, string> = {
18+
en: "and see the documentation.",
19+
zh: "查看文档。",
20+
es: "y ver la documentacion.",
21+
ja: "でドキュメントを確認できます。",
22+
};
23+
24+
export default async function HomePage({
25+
params,
26+
}: {
27+
params: Promise<{ lang: string }>;
28+
}) {
29+
const { lang } = await params;
30+
return (
31+
<div className="flex flex-col justify-center text-center flex-1">
32+
<h1 className="text-2xl font-bold mb-4">
33+
{greetings[lang] ?? greetings.en}
34+
</h1>
35+
<p>
36+
{descriptions[lang] ?? descriptions.en}{" "}
37+
<DynamicLink
38+
href="/[lang]/docs"
39+
className="font-medium underline"
40+
>
41+
/docs
42+
</DynamicLink>{" "}
43+
{docsText[lang] ?? docsText.en}
44+
</p>
45+
</div>
46+
);
47+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { getPageImage, source } from "@/lib/source";
2+
import {
3+
DocsBody,
4+
DocsDescription,
5+
DocsPage,
6+
DocsTitle,
7+
} from "fumadocs-ui/layouts/docs/page";
8+
import { notFound } from "next/navigation";
9+
import { getMDXComponents } from "@/mdx-components";
10+
import type { Metadata } from "next";
11+
import { createRelativeLink } from "fumadocs-ui/mdx";
12+
import { LLMCopyButton, ViewOptions } from "@/components/ai/page-actions";
13+
14+
export default async function Page({
15+
params,
16+
}: {
17+
params: Promise<{ lang: string; slug?: string[] }>;
18+
}) {
19+
const { lang, slug } = await params;
20+
const page = source.getPage(slug, lang);
21+
if (!page) notFound();
22+
23+
const MDX = page.data.body;
24+
const gitConfig = {
25+
user: "username",
26+
repo: "repo",
27+
branch: "main",
28+
};
29+
30+
return (
31+
<DocsPage toc={page.data.toc} full={page.data.full}>
32+
<DocsTitle>{page.data.title}</DocsTitle>
33+
<DocsDescription className="mb-0">
34+
{page.data.description}
35+
</DocsDescription>
36+
<div className="flex flex-row gap-2 items-center border-b pb-6">
37+
<LLMCopyButton markdownUrl={`${page.url}.mdx`} />
38+
<ViewOptions
39+
markdownUrl={`${page.url}.mdx`}
40+
githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/docs/content/docs/${page.path}`}
41+
/>
42+
</div>
43+
<DocsBody>
44+
<MDX
45+
components={getMDXComponents({
46+
a: createRelativeLink(source, page),
47+
})}
48+
/>
49+
</DocsBody>
50+
</DocsPage>
51+
);
52+
}
53+
54+
export async function generateStaticParams() {
55+
return source.generateParams();
56+
}
57+
58+
export async function generateMetadata({
59+
params,
60+
}: {
61+
params: Promise<{ lang: string; slug?: string[] }>;
62+
}): Promise<Metadata> {
63+
const { lang, slug } = await params;
64+
const page = source.getPage(slug, lang);
65+
if (!page) notFound();
66+
67+
return {
68+
title: page.data.title,
69+
description: page.data.description,
70+
openGraph: {
71+
images: getPageImage(page).url,
72+
},
73+
};
74+
}

docs/app/[lang]/docs/layout.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { source } from "@/lib/source";
2+
import { DocsLayout } from "fumadocs-ui/layouts/docs";
3+
import { baseOptions } from "@/lib/layout.shared";
4+
5+
export default async function Layout({
6+
params,
7+
children,
8+
}: {
9+
params: Promise<{ lang: string }>;
10+
children: React.ReactNode;
11+
}) {
12+
const { lang } = await params;
13+
return (
14+
<DocsLayout tree={source.getPageTree(lang)} {...baseOptions(lang)}>
15+
{children}
16+
</DocsLayout>
17+
);
18+
}

docs/app/[lang]/layout.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { RootProvider } from "fumadocs-ui/provider/next";
2+
import { Archivo } from "next/font/google";
3+
import { defineI18nUI } from "fumadocs-ui/i18n";
4+
import { i18n } from "@/lib/i18n";
5+
6+
const archivo = Archivo({
7+
subsets: ["latin", "latin-ext"],
8+
weight: ["500"],
9+
display: "swap",
10+
});
11+
12+
const { provider } = defineI18nUI(i18n, {
13+
translations: {
14+
en: {
15+
displayName: "English",
16+
},
17+
zh: {
18+
displayName: "中文",
19+
toc: "目录",
20+
search: "搜索文档",
21+
lastUpdate: "最后更新于",
22+
searchNoResult: "没有结果",
23+
previousPage: "上一页",
24+
nextPage: "下一页",
25+
chooseLanguage: "选择语言",
26+
chooseTheme: "选择主题",
27+
editOnGithub: "在 GitHub 上编辑",
28+
tocNoHeadings: "无标题",
29+
},
30+
es: {
31+
displayName: "Espanol",
32+
toc: "En esta pagina",
33+
search: "Buscar documentacion",
34+
lastUpdate: "Ultima actualizacion",
35+
searchNoResult: "Sin resultados",
36+
previousPage: "Pagina anterior",
37+
nextPage: "Pagina siguiente",
38+
chooseLanguage: "Elegir idioma",
39+
chooseTheme: "Elegir tema",
40+
editOnGithub: "Editar en GitHub",
41+
tocNoHeadings: "Sin encabezados",
42+
},
43+
ja: {
44+
displayName: "日本語",
45+
toc: "目次",
46+
search: "ドキュメントを検索",
47+
lastUpdate: "最終更新",
48+
searchNoResult: "結果なし",
49+
previousPage: "前のページ",
50+
nextPage: "次のページ",
51+
chooseLanguage: "言語を選択",
52+
chooseTheme: "テーマを選択",
53+
editOnGithub: "GitHub で編集",
54+
tocNoHeadings: "見出しなし",
55+
},
56+
},
57+
});
58+
59+
export default async function Layout({
60+
params,
61+
children,
62+
}: {
63+
params: Promise<{ lang: string }>;
64+
children: React.ReactNode;
65+
}) {
66+
const { lang } = await params;
67+
return (
68+
<html lang={lang} className={archivo.className} suppressHydrationWarning>
69+
<body className="flex flex-col min-h-screen" suppressHydrationWarning>
70+
<RootProvider i18n={provider(lang)}>{children}</RootProvider>
71+
</body>
72+
</html>
73+
);
74+
}

docs/app/api/search/route.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1-
import { source } from '@/lib/source';
2-
import { createFromSource } from 'fumadocs-core/search/server';
1+
import { source } from "@/lib/source";
2+
import { createFromSource } from "fumadocs-core/search/server";
33

44
export const { GET } = createFromSource(source, {
5-
// https://docs.orama.com/docs/orama-js/supported-languages
6-
language: 'english',
5+
localeMap: {
6+
zh: {
7+
search: {
8+
threshold: 0,
9+
tolerance: 0,
10+
},
11+
},
12+
ja: {
13+
search: {
14+
threshold: 0,
15+
tolerance: 0,
16+
},
17+
},
18+
},
719
});

docs/app/docs/[[...slug]]/page.tsx

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)