|
Документация
Заказать демоПлатформа
ПлатформаMCPCLIAPIПроцессы
Руководства
Журнал изменений

Локализация

  • Обзор
  • API локализации
  • Локализация веб-приложений
  • Локализация мобильных приложений
  • iOS и String Catalogs
  • Android и strings.xml
  • Локализация email-писем
  • Статический контент, например .md и .json
  • Next.js с Markdoc
  • Rails с i18n

Процессы

  • Настройка движка с MCP
  • Jira Triage
  • CI/CD

Локализация Next.js App Router с Markdoc

Lingo.dev CLI переводит файлы Markdoc и JSON-каталоги строк интерфейса через настроенный движок локализации. Markdoc — это формат для работы с контентом на базе Markdown с типизированными пользовательскими тегами на React — отличный выбор для сайтов на Next.js App Router, где длинные тексты сочетаются с интерактивными компонентами.

В этом руководстве разберём локализацию сайта на Next.js App Router от начала до конца: настройку CLI, организацию контента по локалям, рендеринг Markdoc в динамических маршрутах и автоматизацию переводов через GitHub Actions.

Демо-репозиторий

Клонируйте или форкните lingodotdev/markdoc-nextjs-localization-example, чтобы пройти всё на практике. В репозитории есть готовое приложение на Next.js App Router с контентом Markdoc, конфигурацией CLI Lingo.dev и процессом GitHub Actions.

Как работает локализация в Next.js + Markdoc#

В большинстве сайтов на Next.js App Router локализованный контент делится на два слоя:

СлойЧто здесь хранитсяПример файла
Длинные текстыМаркетинговые страницы, документация, посты в блогеsrc/content/[locale]/pages/home.md
Строки интерфейсаПодписи в навигации, CTA, состояния кнопокsrc/content/[locale]/ui.json

Маршруты находятся в src/app/[lang]/ и при каждом запросе читают файлы нужной локали. Middleware определяет локаль по умолчанию из заголовка браузера Accept-Language и перенаправляет пути без префикса, например /, на /en (или на наиболее подходящий вариант).

Бакет markdoc в CLI разбирает файлы Markdoc, сохраняя frontmatter и пользовательские теги, а бакет json обрабатывает каталог строк интерфейса. Оба переводят только изменения через ваш движок локализации и записывают файлы для каждой локали рядом с исходниками.

Что понадобится#

1

Создайте движок локализации

При каждом запуске CLI контент проходит через движок локализации — конфигурацию, которая определяет, какая LLM-модель, глоссарий, тональность бренда и инструкции будут применяться. Создайте его в панели Lingo.dev и сгенерируйте API-ключ.

2

Проверьте Node.js

Для CLI требуется Node.js 18 или выше:

bash
node -v
3

Настройте проект Next.js

В проекте должны быть App Router (src/app/) и директория контента по локалям. В демо-репозитории используется src/content/[locale]/ с двумя вложенными папками (pages/ и blog/) и файлом ui.json. Базовые принципы маршрутизации см. в интернационализации Next.js.

Организуйте контент#

Разделяйте контент по назначению. Длинные страницы и посты создаются в Markdoc, а короткие строки интерфейса хранятся в JSON, чтобы компоненты могли загружать их напрямую.

text
src/content/
  en/                  # Source locale
    pages/home.md      # Long-form Markdoc
    blog/hello.md
    ui.json            # UI strings (navbar, CTAs, button states)
  es/                  # Target locales – generated by Lingo.dev
  fr/
  de/

Файлы Markdoc поддерживают frontmatter для метаданных страницы (title, description, date, author) и пользовательские теги, которые рендерятся как React-компоненты. Вот как выглядит минимальная страница:

markdown
---
title: Author once in Markdoc, ship in every language.
description: An example Next.js App Router app that localizes Markdoc with Lingo.
---

{% inline-callout type="info" %}
This page is authored in Markdoc and translated by Lingo.dev.
{% /inline-callout %}

## Built from three pieces

Markdoc custom tags render as React components – even interactive ones.

Настройте CLI#

Создайте в корне проекта файл i18n.json. Объявите два бакета: один для контента Markdoc, второй — для каталога строк интерфейса:

json
{
  "$schema": "https://lingo.dev/schema/i18n.json",
  "version": "1.15",
  "locale": {
    "source": "en",
    "targets": ["es", "fr", "de"]
  },
  "buckets": {
    "markdoc": {
      "include": [
        "src/content/[locale]/pages/*.md",
        "src/content/[locale]/blog/*.md"
      ]
    },
    "json": {
      "include": ["src/content/[locale]/ui.json"]
    }
  }
}

Заполнитель [locale] подставляет код каждой настроенной локали. При использовании source: "en" CLI читает файлы из src/content/en/ и записывает переводы в src/content/es/, src/content/fr/ и src/content/de/.

Каталоги в одном файле

Если строки интерфейса хранятся в одном многоязычном JSON-файле, а не в отдельных файлах для каждой локали, используйте тип бакета json-per-locale. Полный список типов бакетов — в разделе Локализация статического контента.

Рендеринг Markdoc в App Router#

Обычно динамический маршрут загружает документ и рендерит преобразованное дерево. В демо-репозитории для этого есть небольшой helper:

ts
// src/lib/markdoc.ts
export async function loadDoc(
  locale: Locale,
  collection: "pages" | "blog",
  slug: string,
) {
  const raw = await fs.readFile(
    path.join(process.cwd(), "src/content", locale, collection, `${slug}.md`),
    "utf8",
  );
  const ast = Markdoc.parse(raw);
  const frontmatter = ast.attributes.frontmatter
    ? parseFrontmatter(ast.attributes.frontmatter)
    : {};
  const content = Markdoc.transform(ast, { ...schema, variables: { frontmatter } });
  return { frontmatter, content };
}

Страница App Router — это тонкая обёртка, которая связывает документ со строками интерфейса для конкретной локали:

tsx
// src/app/[lang]/page.tsx
export default async function Home({ params }: PageProps<"/[lang]">) {
  const { lang } = await params;
  const doc = await loadDoc(lang, "pages", "home");
  const { home } = await getMessages(lang);

  return (
    <main>
      <h1>{doc.frontmatter.title}</h1>
      {renderMarkdoc(doc.content)}
    </main>
  );
}

Пользовательские теги Markdoc (callout, bento, blog-hero и т. д.) объявляются в markdoc.schema.ts и подключаются к React-компонентам из src/components/markdoc/. Полное API смотрите в документации по схеме Markdoc.

Определяйте локаль в middleware#

Middleware в Next.js анализирует запрос до рендеринга маршрута. Используйте его, чтобы перенаправлять пути без префикса на наиболее подходящую локаль на основе заголовка Accept-Language:

ts
// src/middleware.ts
export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const hasLocale = locales.some(
    (locale) => pathname === `/${locale}` || pathname.startsWith(`/${locale}/`),
  );
  if (hasLocale) return;

  const locale = pickLocale(request); // parses Accept-Language
  const url = request.nextUrl.clone();
  url.pathname = `/${locale}${pathname === "/" ? "" : pathname}`;
  return NextResponse.redirect(url);
}

export const config = {
  matcher: ["/((?!_next|api|.*\\..*).*)", ],
};

Пользователи попадают на /en, /es, /fr или /de, даже если не вводят префикс вручную.

Переводите локально#

Укажите API-ключ и запустите CLI:

bash
export LINGO_API_KEY="your-api-key"
npx lingo.dev@latest run

CLI читает все файлы, подходящие под шаблоны ваших бакетов, определяет непереведённые записи с помощью lockfile, переводит только изменения через ваш движок локализации и записывает результат в директорию каждой целевой локали. Ключи frontmatter, пользовательские теги Markdoc и структура JSON сохраняются — меняется только переводимый текст.

Чтобы во время разработки работать с конкретной локалью:

bash
npx lingo.dev@latest run --target-locale es

Автоматизируйте через GitHub Actions#

Добавьте файл процесса в .github/workflows/translate.yml, чтобы запускать переводы при каждом push:

Переводы коммитятся напрямую в main — без лишних шагов, идеально для небольших команд:

yaml
name: Translate
on:
  push:
    branches: [main]
permissions:
  contents: write
jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Lingo.dev
        uses: lingodotdev/lingo.dev@main
        with:
          api-key: ${{ secrets.LINGODOTDEV_API_KEY }}

Сохраните API-ключ как LINGODOTDEV_API_KEY в Settings > Secrets and variables > Actions вашего репозитория GitHub.

Проверяйте перед деплоем#

Используйте флаг --frozen как барьер перед деплоем, чтобы гарантировать, что в production не попадёт непереведённый контент. Если какие-либо записи требуют перевода, CLI завершится с ненулевым статусом:

bash
npx lingo.dev@latest run --frozen

Добавьте это как отдельный шаг CI перед сборкой Next.js:

yaml
- name: Verify translations
  run: npx lingo.dev@latest run --frozen
- name: Build
  run: pnpm build

Что дальше#

Локализация статического контента
Markdown, MDX, JSON, YAML и другие типы бакетов
Локализация веб-приложений
Паттерны строк интерфейса для популярных веб-фреймворков
Процессы CI/CD
Паттерны для GitHub Actions, GitLab CI и Bitbucket Pipelines
Глоссарии
Зафиксируйте названия брендов и технические термины, чтобы они не переводились

Эта страница была полезной?

Max PrilutskiyMax Prilutskiy·Обновлено 4 дня назад·5 минут чтения