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";
    
    // 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로 이동합니다: