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";
    
    // 查看更多信息:https://fumadocs.vercel.app/docs/headless/source-api
    export const source = loader({
      // 为页面分配 URL
      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 = {
      // 忽略 `/_next/` 和 `/api/` 的匹配器
      // 您可能需要调整以忽略 `/public` 文件夹中的静态资源
      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";
    
    /**
     * 共享布局配置
     *
     * 您可以单独自定义以下布局:
     * 首页布局: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: [],
      };
    }
    
  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({
            // 允许通过相对文件路径链接到其他页面
            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.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 步. 翻译内容

  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: