Fumadocs

使用 Lingo.dev CLI 为 Fumadocs 提供 AI 翻译

什么是 Fumadocs?

Fumadocs 是一个开源文档框架。它提供了快速、类型安全的文档站点,内置搜索、国际化支持和美观的用户界面。

什么是 Lingo.dev CLI?

Lingo.dev 是一个由 AI 驱动的翻译平台。Lingo.dev CLI 读取源文件,将可翻译内容发送给大型语言模型,并将翻译后的文件写回到您的项目中。

关于本指南

本指南介绍如何在 Fumadocs 文档站点中配置 Lingo.dev CLI。您将学习如何使用 Fumadocs 搭建项目、配置翻译流程并查看结果。

步骤 1. 创建 Fumadocs 项目

  1. 创建一个新的 Fumadocs 应用:

    npm create fumadocs-app
    
  2. 按照提示使用首选设置配置项目。

  3. 进入项目目录:

    cd <project-name>
    

步骤 2. 配置国际化支持

Fumadocs 需要知道您的文档将支持哪些语言。您需要创建配置文件,告知 Fumadocs 如何处理多语言。

  1. 创建一个 lib/i18n.ts 文件以定义支持的语言:

    import { defineI18n } from "fumadocs-core/i18n";
    
    export const i18n = defineI18n({
      defaultLanguage: "en",
      languages: ["en", "es"],
      parser: "dir",
    });
    
  2. 更新 lib/source.ts 文件以使用 i18n 设置:

    import { docs } from "@/.source";
    import { loader } from "fumadocs-core/source";
    import { i18n } from "@/lib/i18n";
    
    // See https://fumadocs.vercel.app/docs/headless/source-api for more info
    export const source = loader({
      // it assigns a URL to your pages
      baseUrl: "/docs",
      source: docs.toFumadocsSource(),
      i18n,
    });
    
  3. 创建中间件,根据用户的语言偏好进行检测和重定向:

    // middleware.ts
    import { createI18nMiddleware } from "fumadocs-core/i18n/middleware";
    import { i18n } from "@/lib/i18n";
    
    export default createI18nMiddleware(i18n);
    
    export const config = {
      // Matcher ignoring `/_next/` and `/api/`
      // You may need to adjust it to ignore static assets in `/public` folder
      matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
    };
    

步骤 3. 为多语言更新应用结构

  1. app/ 文件夹下创建语言参数目录:

    mkdir app/[lang]
    
  2. 将现有页面移动到语言参数目录中:

    • app/docs/app/[lang]/docs/
    • app/(home)/app/[lang]/(home)/
  3. 创建一个 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 步. 创建共享布局选项

  1. 创建一个 lib/layout.shared.tsx 文件,用于共享布局配置:

    // lib/layout.shared.tsx
    import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
    import { i18n } from "@/lib/i18n";
    
    /**
     * Shared layout configurations
     *
     * you can customise layouts individually from:
     * Home Layout: app/(home)/layout.tsx
     * Docs Layout: 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
            </>
          ),
        },
        // see https://fumadocs.dev/docs/ui/navigation/links
        links: [],
      };
    }
    
  2. 更新 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>
      );
    }
    
  3. 更新 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({
            // this allows you to link to other pages with relative file paths
            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 步. 组织待翻译内容

  1. 为内容创建特定语言的目录:

    mkdir -p content/docs/en
    
  2. 将现有 MDX 文件移动到英文目录中:

    • content/docs/index.mdxcontent/docs/en/index.mdx
    • content/docs/test.mdxcontent/docs/en/test.mdx

第 7 步. 配置 CLI

在项目根目录下,创建一个 i18n.json 文件:

{
  "$schema": "https://lingo.dev/schema/i18n.json",
  "version": "1.10",
  "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 不支持递归 glob 模式(例如 **/*.mdx)。如需翻译嵌套目录下的文件,需要额外创建 include 模式。

了解更多信息,请参见 i18n.json 配置

第 8 步:翻译内容

  1. 注册 Lingo.dev 账号

  2. 通过 CLI 登录 Lingo.dev:

    npx lingo.dev@latest login
    
  3. 运行翻译流水线:

    npx lingo.dev@latest run
    

    CLI 会创建一个 content/docs/es/ 目录用于存储已翻译内容,并生成一个 i18n.lock 文件以记录已翻译内容(防止重复翻译)。

第 9 步:查看已翻译文档

  1. 启动开发服务器:

    npm run dev
    
  2. 访问以下 URL: