Fumadocs
使用 Lingo.dev CLI 为 Fumadocs 提供 AI 翻译
什么是 Fumadocs?
Fumadocs 是一个开源的文档框架。它提供了一个快速、类型安全的文档网站,内置搜索功能、国际化支持以及美观的用户界面。
什么是 Lingo.dev CLI?
Lingo.dev 是一个由 AI 驱动的翻译平台。Lingo.dev CLI 读取源文件,将可翻译内容发送到大型语言模型,并将翻译后的文件写回到您的项目中。
关于本指南
本指南解释了如何在 Fumadocs 文档站点中设置 Lingo.dev CLI。您将学习如何使用 Fumadocs 搭建项目、设置翻译管道并查看结果。
第 1 步:设置 Fumadocs 项目
-
创建一个新的 Fumadocs 应用程序:
npm create fumadocs-app
-
按提示配置项目的首选设置。
-
进入项目目录:
cd <project-name>
第 2 步:配置国际化支持
Fumadocs 需要知道您的文档将支持哪些语言。您需要创建配置文件来告诉 Fumadocs 如何处理多种语言。
-
创建一个
lib/i18n.ts
文件以定义支持的语言:import { defineI18n } from 'fumadocs-core/i18n'; export const i18n = defineI18n({ defaultLanguage: 'en', languages: ['en', 'es'], parser: "dir", });
-
更新
lib/source.ts
文件以使用 i18n 设置:import { docs } from "@/.source"; import { loader } from "fumadocs-core/source"; import { i18n } from "@/lib/i18n"; // 查看更多信息:https://fumadocs.vercel.app/docs/headless/source-api export const source = loader({ // 为页面分配 URL baseUrl: "/docs", source: docs.toFumadocsSource(), i18n, });
-
创建一个中间件,根据用户的语言偏好检测并重定向用户:
// middleware.ts import { createI18nMiddleware } from 'fumadocs-core/i18n/middleware'; import { i18n } from '@/lib/i18n'; export default createI18nMiddleware(i18n); export const config = { // 忽略 `/_next/` 和 `/api/` 的匹配器 // 您可能需要调整以忽略 `/public` 文件夹中的静态资源 matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], };
第 3 步:为多语言更新应用结构
-
在
app/
文件夹中创建一个语言参数目录:mkdir app/[lang]
-
将现有页面移动到语言参数目录中:
app/docs/
→app/[lang]/docs/
app/(home)/
→app/[lang]/(home)/
-
创建一个
app/[lang]/layout.tsx
文件,用于包装所有语言特定的页面:import { RootProvider } from "fumadocs-ui/provider"; import { defineI18nUI } from "fumadocs-ui/i18n"; import { i18n } from "@/lib/i18n"; const { provider } = defineI18nUI(i18n, { translations: { en: { displayName: "English", }, es: { displayName: "Español", }, }, }); export default async function RootLayout({ params, children, }: LayoutProps<"/[lang]">) { const lang = (await params).lang; return ( <html lang={lang}> <body> <RootProvider i18n={provider(lang)}>{children}</RootProvider> </body> </html> ); }
第 4 步:创建共享布局选项
-
创建一个
lib/layout.shared.tsx
文件,用于共享布局配置:// lib/layout.shared.tsx import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; import { i18n } from "@/lib/i18n"; /** * 共享布局配置 * * 您可以单独自定义以下布局: * 首页布局:app/(home)/layout.tsx * 文档布局:app/docs/layout.tsx */ export function baseOptions(locale: string): BaseLayoutProps { return { i18n, nav: { title: ( <> <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" aria-label="Logo" > <circle cx={12} cy={12} r={12} fill="currentColor" /> </svg> My App </> ), }, // 查看 https://fumadocs.dev/docs/ui/navigation/links links: [], }; }
-
更新
app/[lang]/docs/layout.tsx
文件以使用共享选项:// app/[lang]/docs/layout.tsx import type { ReactNode } from "react"; import { source } from "@/lib/source"; import { DocsLayout } from "fumadocs-ui/layouts/docs"; import { baseOptions } from "@/lib/layout.shared"; export default async function Layout({ params, children, }: LayoutProps<"/[lang]/docs">) { const { lang } = await params; return ( <DocsLayout {...baseOptions(lang)} tree={source.pageTree[lang]}> {children} </DocsLayout> ); }
-
更新
app/[lang]/(home)/layout.tsx
文件以使用共享选项:// app/[lang]/(home)/layout.tsx import { HomeLayout } from "fumadocs-ui/layouts/home"; import { baseOptions } from "@/lib/layout.shared"; export default async function Layout({ children, params, }: LayoutProps<"/[lang]">) { const { lang } = await params; return <HomeLayout {...baseOptions(lang)}>{children}</HomeLayout>; }
第 5 步:更新页面组件
更新页面组件(例如,app/[lang]/docs/[[...slug]]/page.tsx
)以处理语言参数:
import { source } from "@/lib/source";
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from "fumadocs-ui/page";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { createRelativeLink } from "fumadocs-ui/mdx";
import { getMDXComponents } from "@/mdx-components";
export default async function Page(
props: PageProps<"/[lang]/docs/[[...slug]]">,
) {
const params = await props.params;
const page = source.getPage(params.slug, params.lang);
if (!page) notFound();
const MDXContent = page.data.body;
return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDXContent
components={getMDXComponents({
// 允许通过相对文件路径链接到其他页面
a: createRelativeLink(source, page),
})}
/>
</DocsBody>
</DocsPage>
);
}
export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(
props: PageProps<"/[lang]/docs/[[...slug]]">,
): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug, params.lang);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
};
}
第 6 步:组织内容以便翻译
-
为内容创建特定语言的目录:
mkdir -p content/docs/en
-
将现有的 MDX 文件移动到英文目录中:
content/docs/index.mdx
→content/docs/en/index.mdx
content/docs/test.mdx
→content/docs/en/test.mdx
第 7 步:配置 CLI
在项目根目录中创建一个 i18n.json
文件:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": 1.8,
"locale": {
"source": "en",
"targets": ["es"]
},
"buckets": {
"mdx": {
"include": ["content/docs/[locale]/*.mdx"]
}
}
}
此文件定义了:
- Lingo.dev CLI 应翻译的文件
- 要翻译的语言
在此配置中,MDX 文件将从英文翻译为西班牙文。
需要注意的是:
[locale]
是一个占位符,在运行时会被替换。它确保内容从一个位置读取(例如,src/content/docs/en/index.mdx
),并写入到另一个位置(例如,src/content/docs/es/index.mdx
)。- Lingo.dev CLI 不支持递归的全局模式(例如,
**/*.mdx
)。您需要创建额外的include
模式来翻译嵌套目录中的文件。
了解更多信息,请参阅 i18n.json 配置。
第 8 步. 翻译内容
-
通过 CLI 登录 Lingo.dev:
npx lingo.dev@latest login
-
运行翻译流水线:
npx lingo.dev@latest run
CLI 将会创建一个
content/docs/es/
目录用于存储翻译后的内容,以及一个i18n.lock
文件用于记录已翻译的内容(以防止不必要的重复翻译)。
第 9 步. 查看翻译后的文档
-
启动开发服务器:
npm run dev
-
访问以下 URL:
- http://localhost:3000/en/docs 用于查看英文内容
- http://localhost:3000/es/docs 用于查看西班牙文内容