Fumadocs

AI-перевод для Fumadocs с помощью Lingo.dev CLI

Что такое Fumadocs?

Fumadocs — это open-source фреймворк для документации. Он обеспечивает быструю, типобезопасную документацию с встроенным поиском, поддержкой интернационализации и стильным интерфейсом.

Что такое Lingo.dev CLI?

Lingo.dev — это платформа для перевода на базе искусственного интеллекта. Lingo.dev CLI читает исходные файлы, отправляет переводимый контент в большие языковые модели и записывает переведённые файлы обратно в ваш проект.

О данном гайде

В этом гайде рассказывается, как настроить Lingo.dev CLI на сайте документации Fumadocs. Вы узнаете, как создать проект на Fumadocs, настроить pipeline для перевода и посмотреть результат.

Шаг 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 для определения и перенаправления пользователей в зависимости от их языковых предпочтений:

    // 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. Войдите в Lingo.dev через CLI:

    npx lingo.dev@latest login
    
  3. Запустите pipeline перевода:

    npx lingo.dev@latest run
    

    CLI создаст директорию content/docs/es/ для хранения переведённого контента и файл i18n.lock для отслеживания переведённого (чтобы избежать лишних повторных переводов).

Шаг 9. Просмотрите переведённую документацию

  1. Запустите dev-сервер:

    npm run dev
    
  2. Перейдите по следующим URL: