Fumadocs

Lingo.dev CLIによるFumadocsのAI翻訳

Fumadocsとは何ですか?

Fumadocsはオープンソースのドキュメンテーションフレームワークです。組み込み検索機能、国際化サポート、美しいUIを備えた高速で型安全なドキュメンテーションサイトを提供します。

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. i18n設定を使用するためにlib/source.tsファイルを更新します:

    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({
            // 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.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にアクセスします: