Перевод английской PDF-книги в русский EPUB
В этом посте я расскажу, как перевести английскую книгу из формата PDF в русский EPUB с помощью локальной LLM (Ollama), marker-pdf и pandoc. Весь процесс выполняется на одной машине.
В качестве примера используется книга Пола Хоффмана «The Man Who Loved Only Numbers» — биография Пола Эрдёша.
Замечание. Процесс ресурсоёмкий: перевод книги среднего размера локальной моделью занимает примерно 18–24 часа. Желательно иметь GPU и достаточно RAM для модели Ollama.
0. Предварительные требования
- Ubuntu / Debian (или совместимый Linux).
- Установленный и запущенный Ollama с подходящей моделью перевода (например,
gemma4:26bили аналогичной). - Доступ в интернет.
Настройка прокси (если требуется)
Если требуется пропишите прокси в /etc/environment:
sudo nano /etc/environment
http_proxy="http://proxy.domain.com:3128"
https_proxy="http://proxy.domain.com:3128"
no_proxy="localhost,127.0.0.1,::1"
HTTP_PROXY="http://proxy.domain.com:3128"
HTTPS_PROXY="http://proxy.domain.com:3128"
NO_PROXY="localhost,127.0.0.1,::1"
source /etc/environment
Для Docker (если Ollama или вспомогательные сервисы запускаются в контейнерах):
sudo mkdir -p /etc/systemd/system/docker.service.d/
sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.domain.com:3128"
Environment="HTTPS_PROXY=http://proxy.domain.com:3128"
Environment="NO_PROXY=localhost,127.0.0.1"
sudo systemctl daemon-reload
sudo systemctl restart docker
1. Установка пакетов и подготовка окружения
sudo apt update && sudo apt install -y python3-pip python3-venv screen default-jre
Создаём рабочую директорию и виртуальное окружение Python:
sudo mkdir -p /opt/book-translate && sudo chown $USER:$USER /opt/book-translate
cd /opt/book-translate
python3 -m venv venv
source venv/bin/activate
Устанавливаем зависимости:
pip install marker-pdf
pip install requests pyyaml
2. Конвертация PDF → Markdown с помощью marker-pdf
Кладём исходный PDF в рабочую директорию:
/opt/book-translate/Paul_Hoffman_The_Man_Who_Loved_Only_Numbers.pdf
Запускаем конвертацию:
cd /opt/book-translate
source venv/bin/activate
marker_single Paul_Hoffman_The_Man_Who_Loved_Only_Numbers.pdf --output_dir ./output --page_range "0,11-302" --output_format markdown
Про номера страниц.
marker-pdfиспользует 0-индексацию: страница0— это первая страница PDF (обложка),11–302— это страницы PDF с 12-й по 303-ю (мы пропускаем оглавление, благодарности и т. п. в начале книги).
После конвертации Markdown и извлечённые изображения окажутся в ./output/Paul_Hoffman_The_Man_Who_Loved_Only_Numbers/. Переходим в неё:
cd /opt/book-translate/output/Paul_Hoffman_The_Man_Who_Loved_Only_Numbers
3. Этап 1. Очистка Markdown
marker-pdf хорошо распознаёт текст, но оставляет мусор: обрывки колонтитулов, неправильные переносы, артефакты OCR. Имеет смысл написать пару Python-скриптов для автоматического исправления типовых проблем — это сильно дешевле, чем чинить руками или платить за это токенами LLM.
- cleanup.py — первичная чистка.
- cleanup2.py — вторая итерация после визуального просмотра.
Запускаем по очереди и просматриваем результат глазами.
4. Этап 2. Сборка глоссария
Глоссарий нужен для двух вещей:
- Согласованность. Каждое имя собственное и термин переводится одинаково во всех частях книги.
- Корректность. Мы сами решаем, как пишутся имена и термины по-русски, а не отдаём это «на откуп» модели.
Скрипт build_glossary.py автоматически вытаскивает кандидатов (повторяющиеся имена, технические термины) из текста. Очевидные вещи (названия стран и т. п.) отфильтровываются — модель с ними справится сама.
python3 build_glossary.py
На выходе получается glossary.yaml, который вы вычитываете и правите вручную.
5. Этап 3. Перевод
Скрипт перевода translate.py спроектирован с учётом того, что процесс длинный и должен переживать сбои. Ключевые свойства:
- Возобновляемость. Прогресс сохраняется после каждого переведённого фрагмента — если процесс упадёт или вы оборвёте SSH, достаточно перезапустить скрипт, и он продолжит с места остановки.
- Разбиение по абзацам. Текст режется на чанки по границам абзацев — модель никогда не получает оборванный посередине абзац.
- Передача контекста. В каждый запрос подставляются последние переведённые абзацы, чтобы сохранялась связность повествования (местоимения, время, стиль).
- Подстановка глоссария. В каждый промпт подкладывается
glossary.yaml, чтобы имена и термины переводились единообразно.
Запуск перевода
Поскольку процесс идёт ~18–24 часа, запускаем его внутри screen, чтобы он пережил отключение SSH-сессии:
screen -S translate
cd /opt/book-translate/output/Paul_Hoffman_The_Man_Who_Loved_Only_Numbers
source /opt/book-translate/venv/bin/activate
python3 translate.py
Полезные команды для работы со screen:
- Отсоединиться от сессии:
Ctrl+A, затемD. - Снова подключиться:
screen -r translate.
Если скрипт упадёт — просто запустите его снова, прогресс сохранён.
Мониторинг прогресса
Быстрая проверка количества готовых чанков:
grep -c '"status"' translation_state.jsonl
Подробный отчёт со временем выполнения и количеством абзацев в каждом чанке:
python3 -c "
import json
with open('translation_state.jsonl', 'r') as f:
for line in f:
entry = json.loads(line)
src_paras = len([p for p in entry['source_text'].split('\n\n') if p.strip()])
status = entry['status']
issues = '⚠' if 'issues' in status else '✓'
elapsed = entry.get('elapsed_seconds', 0)
print(f\"{entry['chunk_id']}: {status} | src={src_paras} paras | {elapsed:.0f}s {issues}\")
"
Постобработка
После того как перевод закончится, вы вычитываете и правите итоговый файл book_translated_ru.md. Дальше — несколько проверок:
1. Поиск «мусора» от модели (например, неудалённых служебных меток вида [Pабз1] или [intро]):
grep -nE '\[P[а-яА-Я0-9]{1,8}\]|\[int[А-Яа-я0-9]+\]' book_translated_ru.md
2. Проверка заголовков глав — все ли уровни на месте, нет ли «съеденных» решёток:
grep -nE '^#{1,3} ' book_translated_ru.md
3. Проверка ссылок на изображения — что все картинки, на которые ссылается Markdown, реально существуют:
grep -oE '!\[[^]]*\]\([^)]+\)' book_translated_ru.md | sed -E 's/.*\(([^)]+)\).*/\1/' | sort -u | while read -r img; do [ -f "$img" ] || echo "MISSING $img"; done; echo "---"; echo "Total unique refs: $(grep -oE '!\[[^]]*\]\([^)]+\)' book_translated_ru.md | sed -E 's/.*\(([^)]+)\).*/\1/' | sort -u | wc -l)"
6. Этап 4. Markdown → EPUB с помощью pandoc
Ставим свежий pandoc (версия из репозиториев Ubuntu обычно сильно отстаёт). Берём .deb со страницы релизов: https://github.com/jgm/pandoc/releases.
wget https://github.com/jgm/pandoc/releases/download/3.9.0.2/pandoc-3.9.0.2-1-amd64.deb
sudo dpkg -i pandoc-3.9.0.2-1-amd64.deb
Обложка книги
В качестве обложки удобно использовать первую страницу PDF — marker-pdf уже сохранил её как изображение:
cp _page_0_Figure_1.jpeg cover.jpeg
Метаданные EPUB
Создаём файл metadata.yaml:
cat > metadata.yaml <<'EOF'
---
title: "Человек, любивший только числа"
subtitle: "История Пола Эрдёша и поиск математической истины"
author: "Пол Хоффман"
language: ru-RU
publisher: "Личное издание"
date: 2026
rights: "© 1998 Paul Hoffman (original). Перевод для личного использования."
description: |
Биография венгерского математика Пола Эрдёша — самого
плодовитого математика XX века.
...
EOF
Сборка EPUB
pandoc book_translated_ru.md --from=markdown+smart+tex_math_dollars --to=epub3 --metadata-file=metadata.yaml --epub-cover-image=cover.jpeg --toc --toc-depth=2 --split-level=1 --mathml --output=book_ru.epub
Что означают ключевые опции:
--from=markdown+smart+tex_math_dollars— расширения Markdown: «умные» кавычки/тире и поддержка формул в виде$...$и$$...$$.--to=epub3— выходной формат EPUB 3.--metadata-file=metadata.yaml— подставить метаданные книги (см. выше).--epub-cover-image=cover.jpeg— использоватьcover.jpegкак обложку.--toc --toc-depth=2— построить оглавление, включая заголовки до уровня##.--split-level=1— разбивать книгу на отдельные XHTML-файлы по заголовкам уровня#(по одному файлу на главу). Это важно для читалок: они быстрее листают и не подвисают на «одностраничной» книге.--mathml— рендерить математику в MathML (нативно поддерживается современными ридерами).
После запуска pandoc выведет предупреждения и ошибки (если они есть) и создаст файл book_ru.epub. Предупреждения стоит пробежать глазами — обычно это «битые» ссылки на изображения или неоднозначные заголовки.
7. Валидация EPUB
Быстрая проверка структуры
EPUB — это обычный ZIP-архив, поэтому быстро заглянуть внутрь можно стандартным unzip:
unzip -l book_ru.epub | head -30
Пример вывода:
Archive: book_ru.epub
Length Date Time Name
--------- ---------- ----- ----
20 2026-05-23 11:04 mimetype
252 2026-05-23 11:04 META-INF/container.xml
161 2026-05-23 11:04 META-INF/com.apple.ibooks.display-options.xml
6328 2026-05-23 11:04 EPUB/content.opf
3016 2026-05-23 11:04 EPUB/toc.ncx
2329 2026-05-23 11:04 EPUB/nav.xhtml
993 2026-05-23 11:04 EPUB/text/title_page.xhtml
3778 2026-05-23 11:04 EPUB/styles/stylesheet1.css
31574 2026-05-23 11:04 EPUB/media/file30.jpg
20557 2026-05-23 11:04 EPUB/media/file29.jpg
...
Проверяем, что архив устроен по спецификации EPUB:
# Должен вывести: application/epub+zip
unzip -p book_ru.epub mimetype
# Точка входа в книгу
unzip -p book_ru.epub META-INF/container.xml
Считаем количество глав (при --split-level=1 это один XHTML на каждый #-заголовок):
unzip -l book_ru.epub | grep -c 'ch[0-9]*\.xhtml'
Проверяем, что обложка и иллюстрации попали внутрь:
unzip -l book_ru.epub | grep -iE 'cover|page_'
Полноценная валидация через epubcheck
epubcheck от W3C — это индустриальный стандарт проверки EPUB-файлов. Для его работы нужен Java:
sudo apt install -y default-jre
Скачиваем последний релиз со страницы https://github.com/w3c/epubcheck/releases:
mkdir -p /opt/book-translate/epubcheck && cd /opt/book-translate/epubcheck
wget https://github.com/w3c/epubcheck/releases/download/v5.3.0/epubcheck-5.3.0.zip
unzip epubcheck-5.3.0.zip
Запускаем проверку:
java -jar /opt/book-translate/epubcheck/epubcheck-5.3.0/epubcheck.jar /opt/book-translate/book_ru.epub
Идеальный вывод выглядит так:
Validating using EPUB version 3.3 rules.
No errors or warnings detected.
Messages: 0 fatals / 0 errors / 0 warnings / 0 infos
EPUBCheck completed
Если есть предупреждения — обычно это либо невалидный HTML внутри глав (исправляется правкой исходного Markdown), либо мелкие проблемы с метаданными.
8. Открываем книгу
Готовый book_ru.epub можно открыть:
- в Calibre — для удобного просмотра, редактирования метаданных и конвертации в другие форматы (MOBI, AZW3 и т. д.);
- в любом ридере — Apple Books, KOReader, PocketBook, Foliate (Linux);
- скопировать на электронную книжку — большинство современных ридеров (Kindle через
send-to-kindle, PocketBook, Onyx Boox) понимают EPUB напрямую.
Итоги
В сухом остатке пайплайн получается такой:
- PDF → Markdown через
marker-pdf(с урезанием служебных страниц через--page_range). - Чистка Markdown парой Python-скриптов (
cleanup.py,cleanup2.py). - Глоссарий имён и терминов — извлекается автоматически скриптом
build_glossary.py, вычитывается вручную вglossary.yaml. - Перевод локальной LLM через Ollama: чанки по абзацам, контекст предыдущих абзацев, подстановка глоссария в каждый промпт, возобновляемость через
translation_state.jsonl. - Постобработка переведённого Markdown: проверка артефактов, заголовков, ссылок на изображения.
- Markdown → EPUB через
pandocс метаданными, обложкой и оглавлением. - Валидация результата через
epubcheckот W3C.
Что получилось хорошо
- Полностью локально. Ни PDF, ни перевод не уходят в облако — это важно, если переводите книгу с авторскими правами для личного чтения, или просто не хотите делиться текстом с чужим API.
- Дёшево. Стоимость — только электричество. У коммерческих API за книгу такого объёма счёт легко улетает в десятки долларов.
- Воспроизводимо. Скрипты + глоссарий +
translation_state.jsonl— при желании можно перезапустить любой этап и получить тот же результат. - EPUB на выходе. Это не просто «текстовый файл с переводом», а полноценная книга с обложкой, оглавлением и метаданными, которую приятно читать на ридере.