# General Translation Knowledge Base: Как перевести Markdown и MDX, не нарушив синтаксис?
URL: https://generaltranslation.com/ru/kb/translate-markdown-files.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Переводите текст, адресованный читателю, сохраняя исполняемый код, импорты, имена JSX-компонентов и машинные идентификаторы. General Translation обрабатывает Markdown и MDX напрямую, включая пропсы с отображаемым текстом и заголовки во frontmatter, а по мере обновления документации повторно использует совпадающие переводы. Перед публикацией проверьте локализованные файлы и запустите сборку сайта.
Category: Перевод
Last updated: 2026-10-30
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

В файле Markdown обычный текст перемежается с техническим синтаксисом. MDX добавляет к этому JavaScript: строки `import`, JSX-компоненты и `{expressions}`, которые компилятор должен корректно разобрать и после перевода. Прежде чем переводить, определите, какие поля содержат отображаемый текст: `title` компонента может требовать перевода, тогда как его `id` должен оставаться неизменным.

## Video

The video is narrated in English, with English captions.

[How do I translate Markdown and MDX without breaking syntax?](https://caxecubb9c64cezv.public.blob.vercel-storage.com/films/kb-translate-markdown-files.mp4) (48 с)

Translate prose, image alt text, frontmatter titles and descriptions, and display-text component props. Keep code blocks, inline code, imports, JSX component names, frontmatter keys, and link URLs unchanged, quote translated YAML values, then run your site's build. With General Translation, add an mdx or md entry under files in `gt.config.json` and run `npx gt translate`.

## Что ломается, когда Markdown проходит через универсальный переводчик

Переводчик, который воспринимает файл как обычный текст, считает языком всё его содержимое. Вот на какие сбои стоит проверить результат:

* **Блоки кода и встроенный код.** Команды и идентификаторы могут оказаться переведены: например, `npm install` может превратиться в `npm installer`.
* **URL ссылок и якоря.** Если слаг или `#fragment` в ссылке переведён, ссылка будет вести в никуда. Кроме того, переведённые заголовки получают новые сгенерированные ID, и все ссылки `#anchor` на них перестают работать; именно поэтому Docusaurus рекомендует задавать [явные ID заголовков](https://docusaurus.io/docs/i18n/tutorial#translate-markdown-files).
* **Frontmatter.** Ключи вроде `title` могут оказаться переведены, значения `slug` — изменены, а переведённое значение может сломать YAML. Во французском перед двоеточием ставится пробел, поэтому `title: Installer la CLI : guide rapide` — невалидный YAML (парсер `yaml` сообщает о вложенном сопоставлении). Такое значение нужно заключить в кавычки.
* **HTML-атрибуты.** Текст в `alt` и `title` нужно переводить. `class` и другие идентификаторы следует сохранять, а `src` или `href` менять только через явное сопоставление локализованных ресурсов или URL.
* **Импорты MDX и JSX.** Если `import Tabs from '@theme/Tabs'` переведён как обычное предложение или `<Tabs>` превратился в `<Onglets>`, сборка завершится с ошибкой. [Руководство Docusaurus по Crowdin](https://docusaurus.io/docs/i18n/crowdin#mdx) предупреждает, что инструменты, читающие JSX как HTML, могут выдавать невалидный JSX, причём чаще всего ломаются пропсы со значениями-объектами или массивами.
* **Блоки-предупреждения.** Docusaurus распознаёт `:::note`, `:::tip`, `:::info`, `:::warning` и `:::danger` как [ключевые слова](https://docusaurus.io/docs/markdown-features/admonitions). Переведённое `:::astuce` к ним не относится, поэтому блок-выноска отображается как обычный текст.
* **Таблицы.** Таблица GFM распознаётся, только если в строке заголовка столько же ячеек, сколько в строке-разделителе, а вертикальную черту внутри ячейки нужно экранировать как `\|` ([спецификация GFM](https://github.github.com/gfm/#tables-extension-)). Если переводчик добавит в ячейку вертикальную черту или перенос строки, строка таблицы сломается.

Ниже приведены исходный файл и пример результата, в котором встречаются все эти сбои. Пример искусственно собирает все проблемы в одном месте и не является выводом какого-то конкретного инструмента.

```mdx
---
title: Install the CLI
slug: /install
---

import Tabs from '@theme/Tabs';

## Install the CLI {/* #install */}

Run `npm install my-cli`, then read the [setup guide](/docs/setup#api-keys).

:::tip
Use <Tabs groupId="pm">a package manager</Tabs> you already have.
:::
```

```mdx
---
titre: Installer la CLI : guide rapide
slug: /installer
---

importer Tabs depuis '@theme/Tabs';

## Installer la CLI {/* #installer */}

Exécutez `npm installer my-cli`, puis lisez le [guide](/docs/configuration#cles-api).

:::astuce
Utilisez <Onglets groupId="gp">un gestionnaire de paquets</Onglets> que vous avez déjà.
:::
```

Во втором файле переведён ключ во frontmatter, YAML стал невалидным, изменился слаг, появился импорт, который MDX не может разобрать, сменился ID заголовка, сломалась команда, ссылка ведёт в никуда, а ещё в нём есть неизвестный блок-предупреждение и несуществующий компонент.

## Способы перевода Markdown и MDX

### a) Перевод документов через Google или DeepL

Перевод документов в [Google Cloud Translation](https://docs.cloud.google.com/translate/docs/supported-formats) (редакция Advanced) поддерживает файлы Word, PDF, PowerPoint и Excel, но не Markdown, а перевод текста работает с обычным текстом или HTML. В [DeepL API](https://developers.deepl.com/docs/api-reference/document) перевод документов Markdown (`.md`) указан как бета-функция, а MDX не упоминается вовсе. В любом случае проверьте результат на собственных файлах, прежде чем доверять сервису блоки кода и frontmatter.

### b) Используйте AST, чтобы отделить текст от синтаксиса

[remark](https://github.com/remarkjs/remark) преобразует Markdown в дерево [mdast](https://github.com/syntax-tree/mdast). Приведённый ниже пример извлечения обходит узлы `text`, пропуская код, атрибуты и frontmatter. Он хорошо подходит как отправная точка для анализа контента, но не является полноценным конвейером перевода:

```js
// extract.mjs: npm i unified remark-parse remark-stringify remark-mdx
//   remark-frontmatter remark-gfm remark-directive unist-util-visit
import { readFileSync } from 'node:fs';
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkMdx from 'remark-mdx';
import remarkDirective from 'remark-directive';
import remarkStringify from 'remark-stringify';
import { visit } from 'unist-util-visit';

const processor = unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml']) // frontmatter становится одним узлом `yaml`
  .use(remarkGfm)                   // таблицы
  .use(remarkMdx)                   // import/export и JSX
  .use(remarkDirective)             // блоки-предупреждения :::tip
  .use(remarkStringify);

const tree = processor.parse(readFileSync(process.argv[2], 'utf8'));

// Этот пример извлекает только текстовые узлы. Отображаемый текст в атрибутах
// и frontmatter в рабочем переводчике нужно обрабатывать отдельно.
const segments = [];
visit(tree, 'text', (node) => {
  if (node.value.trim()) segments.push(node);
});

console.log(segments.map((n) => JSON.stringify(n.value)).join('\n'));

// Заглушка вместо вызова перевода: заменяем каждый сегмент на месте.
for (const node of segments) node.value = node.value.toUpperCase();
console.log('\n' + processor.stringify(tree));
```

Запущенная на приведённом выше исходном файле, к которому добавлены блок кода `bash` и таблица из двух столбцов, команда `node extract.mjs guide.mdx` вывела следующие сегменты (фрагмент):

```text
"Install the CLI "
"Run "
", then read the "
"setup guide"
"."
"Use "
"a package manager"
" you already have."
```

Повторно сериализованный файл сохранил содержимое frontmatter, импорт, ID заголовка, встроенный код, адрес ссылки, имя блока-предупреждения, атрибуты JSX и содержимое блока кода, а текстовые узлы были переведены в верхний регистр. Однако форматирование сохранилось не байт в байт. Обнаружились три ограничения:

* **Предложения приходят по частям.** «Run … then read the … setup guide» — это пять сегментов, поэтому переводчик не может переставить слова вокруг кода и ссылки. Скрипты для рабочей среды переводят абзац целиком, заменяя встроенные узлы плейсхолдерами.
* **Часть текста находится вне узлов `text`.** Текст `alt` у изображений, значения атрибутов JSX (например, проп `title`) и значения frontmatter, такие как `title`, требуют отдельной обработки.
* **Вывод переформатируется.** `remark-stringify` заново выровнял столбцы таблицы, поэтому в различии появляются строки, которые вы не меняли.

Кроме того, разбор помогает обнаружить синтаксические ошибки на раннем этапе. Из-за суффикса заголовка `{#install}` в стиле Mintlify `remark-mdx` завершался с ошибкой «Could not parse expression with acorn», поскольку в MDX фигурные скобки открывают выражение JavaScript. Docusaurus также поддерживает вариант в виде комментария `{/* #install */}`, который может проставить за вас команда `docusaurus write-heading-ids --syntax mdx-comment`.

Замена на верхний регистр показывает, какой текст выбирает парсер, но ничего не говорит о качестве перевода. Прежде чем запускать конвейер перевода, сравните защищённые поля с источником, проверьте синтаксис YAML и MDX и выполните реальную сборку сайта, чтобы выявить сломанные импорты, ссылки или компоненты, специфичные для фреймворка.

### c) Система управления переводами

Система управления переводами (TMS) разбирает файл за вас. Crowdin поддерживает [Markdown](https://store.crowdin.com/md) и [MDX](https://store.crowdin.com/mdx) «из коробки», а его [конфигурация парсера](https://support.crowdin.com/parsers-configuration/) позволяет исключать блоки кода и выбранные элементы frontmatter. По умолчанию MDX-файлы загружаются как MDX v1, а для v2 нужно указать тип `mdx_v2`. При этом вам всё равно придётся сопоставлять пути к исходным и выходным файлам, синхронизировать файлы в CI и искать обходные пути для JSX, который парсер обрабатывает некорректно.

### d) Промпт для LLM с правилами

Модель, получившая файл целиком, может учитывать контекст абзацев. Укажите поля с отображаемым текстом, которые нужно перевести: основной текст, alt-текст изображений, `title` и `description` во frontmatter, а также конкретные пропсы компонентов. Запретите изменять код, структурные ключи, импорты, имена JSX-компонентов и ключевые слова `:::`; разрешите менять URL только в соответствии с вашим сопоставлением локалей и потребуйте корректно расставлять кавычки в YAML. Соблюдение правил из промпта ничем не гарантируется, поэтому разберите результат, скомпилируйте MDX и сравните защищённое содержимое с источником.

### Сравнение

| Подход                                | Понимает структуру Markdown             | MDX и JSX                                                         | Frontmatter                             | Синхронизация переводов                                         | Ваши усилия                                                                   |
| ------------------------------------- | --------------------------------------- | ----------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Перевод документов в Google или DeepL | DeepL: `.md` в бета-версии; Google: нет | Не указано                                                        | Не проверялось; уточните                | Повторный запуск для каждого файла                              | Низкие, плюс проверка                                                         |
| Скрипт на remark                      | Да, вы сами выбираете узлы              | Да, с `remark-mdx`                                                | Обрабатываете сами                      | Отслеживание изменений пишете сами                              | Высокие                                                                       |
| TMS (Crowdin)                         | Да                                      | Встроенная поддержка с задокументированными ограничениями для JSX | Можно исключить выбранные ключи         | Синхронизация через CLI в CI                                    | Средние                                                                       |
| LLM с правилами                       | Только по инструкции                    | Только по инструкции                                              | Только по инструкции                    | Автоматизируете сами                                            | Средние, плюс проверка                                                        |
| General Translation CLI               | Специализированная обработка Markdown   | Специализированная обработка MDX                                  | Переводит заголовки и описания Mintlify | При повторных запусках повторно использует совпадающие переводы | Настроить пути к исходным файлам, проверить результат, запустить сборку сайта |

Для разового файла `.md`, который вы проверите вручную, подойдёт бета-поддержка Markdown в DeepL. Если нужен полный контроль над небольшим стабильным набором файлов, напишите скрипт на remark. Если переводчики работают в редакторе TMS, используйте TMS. Для быстрых черновиков, изменения в которых вы затем просмотрите через различия, хватит промпта для LLM. Для документации, которая меняется с каждым релизом, выбирайте General Translation: он берёт на себя генерацию и повторное использование переводов, а переведённые файлы остаются в вашем репозитории.

## Как переводить Markdown и MDX с помощью General Translation

Если файлы Markdown и MDX хранятся в часто изменяющемся репозитории, CLI от General Translation — `gt` — может переводить их при каждом запуске. Он переводит [файлы MDX и Markdown](/docs/cli/reference/formats/mdx-md-files), сохраняя их синтаксис и форматирование. Выполнив [`npx gt init`](/docs/cli/reference/commands/init) по инструкции из раздела [Быстрый старт с CLI](/docs/cli/quickstart), добавьте запись `mdx` или `md` в раздел `files` файла `gt.config.json`:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["fr", "ja"],
  "files": {
    "mdx": { "include": ["docs/[locale]/**/*.mdx"] },
    "md": { "include": ["docs/[locale]/**/*.md"] }
  }
}
```

Затем выполните [`npx gt translate`](/docs/cli/reference/commands/translate). CLI считывает исходные файлы из `docs/en/` и записывает переводы в `docs/fr/` и `docs/ja/`. Для документации важно учитывать несколько моментов:

* **ID заголовков.** Суффиксы `{#id}` в стиле Mintlify остаются привязанными к своим заголовкам (`gt` версии 2.17.3 или новее).
* **Frontmatter.** Для сайтов на Mintlify заголовки и описания во frontmatter тоже переводятся.
* **Инкрементальные запуски.** Совпадающие переводы из предыдущих запусков используются повторно, а переводится и оплачивается только новый или изменённый контент. Флаг `--force` заново переводит всё и перезаписывает локальные изменения.
* **Ручные правки.** Обратная синхронизация правок требует явного включения: используйте [`npx gt translate --save-local`](/docs/cli/reference/commands/translate) или [`npx gt save-local`](/docs/cli/reference/commands/save-local).
* **Ссылки и импорты.** Перезапись URL, импортов и путей к ресурсам для каждой локали — экспериментальная функция, которая включается флагами, например `--experimental-localize-static-urls`.

Запускайте команду [в CI](/docs/cli/guides/generating-translations) перед сборкой, храните продуктовые термины в [Glossary](/docs/platform/dashboard/guides/defining-context-for-translations), общем с приложением, и проверяйте формулировки в [редакторе панели управления](/docs/platform/dashboard/guides/reviewing-translations). Перед публикацией выполните сборку и проверку ссылок для каждой локали. (Подробнее о преимуществах и недостатках см. в статьях [General Translation vs. Google Translate](/kb/general-translation-vs-google-translate) и [General Translation vs. Crowdin](/kb/general-translation-vs-crowdin).)

### Какие части MDX-компонента нужно переводить?

General Translation различает текст, который видит читатель, и идентификаторы, которые использует
сайт. В этом исходном примере заголовок блока-выноски нужно переводить точно так же,
как и его основной текст:

```mdx title="docs/en/deployment.mdx"
---
title: Deployment guide
slug: deployment-guide
---

import { Callout } from '@/components/Callout';

<Callout id="backup" type="warning" title="Back up your database first">
  Keep a backup before deploying.
</Callout>
```

| Часть источника                             | Обработка                                                           |
| ------------------------------------------- | ------------------------------------------------------------------- |
| `title` во frontmatter                      | Переведите заголовок страницы                                       |
| `title` и тело блока-выноски                | Переведите видимый текст                                            |
| `slug` во frontmatter и `id` блока-выноски  | Сохраните идентификаторы, используемые маршрутами и ссылками        |
| `Callout`, имена пропсов и `type="warning"` | Сохраните контракт компонента                                       |
| Путь импорта                                | Сохраните, если его не переписывает настроенное правило локализации |

Тот же принцип действует и внутри блоков кода: поясняющие комментарии можно
переводить, а команды и идентификаторы должны оставаться без изменений. Сравнивая
переведённый файл с источником, допускайте различия в формулировках комментариев внутри блоков кода,
а не требуйте побайтовой идентичности каждого блока. Скомпилируйте результат
в рамках сборки вашего сайта документации, где определены импортируемый компонент и его пропсы,
и проверьте отрендеренный блок-выноску и его якорь в каждой локали. Автономная
проверка синтаксиса MDX не разрешает компоненты и маршрутизацию сайта.

## Основные выводы

* Переводите предложения целиком, а также выбранные поля с отображаемым текстом, включая alt-текст, значения frontmatter и соответствующие пропсы. Сохраняйте технический синтаксис и идентификаторы; изменяйте URL только через настроенное сопоставление.
* Французская пунктуация `" : "` может сломать YAML frontmatter без кавычек, а суффиксы заголовков `{#id}` приводят к ошибке в обычном парсере MDX.
* Сервис перевода документов Google не принимает Markdown; DeepL поддерживает `.md` в бета-режиме, а MDX в списке форматов вообще нет.
* Демонстрация с remark показывает текстовые узлы, но полноценный конвейер должен сохранять контекст предложений и обрабатывать отображаемый текст за пределами этих узлов.
* CLI General Translation переводит файлы `md` и `mdx` с сохранением синтаксиса и при последующих запусках повторно использует совпадающие переводы.

## Related questions

- [Как перевести JSON-файлы i18n?](https://generaltranslation.com/ru/kb/translate-json-files.mdx): Переводите только строковые значения в JSON-файле локали. Ключи, не связанные с множественным числом, заполнители вроде {{name}} и теги оставляйте без изменений, но учтите, что ключи для форм множественного числа зависят от языка: например, для польского нужны inbox_few и inbox_many. Используйте инструмент или API с поддержкой формата, а затем разберите и проверьте результат перед вычиткой.
- [Как перевести файлы локалей в формате YAML?](https://generaltranslation.com/ru/kb/translate-yaml-files.mdx): Переводите только строковые значения, а ключи, плейсхолдеры вроде %{name} или {{name}} и якоря оставляйте без изменений. Заключайте в кавычки значения, которые содержат двоеточие или начинаются с % или {, задайте для каждого файла корневой ключ локали или имя файла и добавьте ключи форм множественного числа, которые нужны языку перевода. CLI от General Translation переводит значения YAML на месте и не трогает ключи.
- [Как сделать сайт многоязычным?](https://generaltranslation.com/ru/kb/make-website-multilingual.mdx): Выберите, где будут храниться переводы — в прокси, плагине CMS, конструкторе сайтов или в вашем собственном коде, — а затем выделите каждому языку собственный URL, как рекомендует Google. Переводите страницы целиком, включая заголовки и метаописания, связывайте языковые версии с помощью hreflang и предлагайте переключатель языка вместо автоматического перенаправления посетителей.
- [Альтернативы Google Translate: General Translation и Google Translate](https://generaltranslation.com/ru/kb/general-translation-vs-google-translate.mdx): Google Translate — быстрый способ перевести разовый текст, но чтобы выпустить продукт на другом языке, нужно переводить каждую строку с учётом контекста, соблюдать единую терминологию, правильно обрабатывать переменные и формы множественного числа, проводить ревью и выстроить процесс релизов. General Translation понимает контекст каждой строки в вашем коде и автоматизирует весь рабочий процесс локализации — от кода до ревью и каждого релиза.
- [Альтернативы Crowdin: General Translation или Crowdin](https://generaltranslation.com/ru/kb/general-translation-vs-crowdin.mdx): Выбирайте General Translation, если хотите хранить тексты прямо в компонентах и создавать переводы во время сборки. Crowdin управляет проектами перевода, выстроенными вокруг файлов ресурсов, переводчиков и процессов проверки, — инженерам приходится подключать всё это к приложению и поддерживать по мере развития продукта. При этом Crowdin по-прежнему хорошо подходит для перевода силами open-source-сообщества и для программ, которые ведут подрядчики.

## Previous and next questions

- Previous question: [Как перевести JSON-файлы i18n?](https://generaltranslation.com/ru/kb/translate-json-files.mdx)
- Next question: [Как перевести субтитры SRT, не нарушив тайминг?](https://generaltranslation.com/ru/kb/translate-srt-subtitles.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/ru/kb/sitemap.md) for every article.
