Нейросеть для перевода документов Word с сохранением формата
Если отправить в модель весь DOCX как текст, форматирование теряется. Переводим документ по абзацам и возвращаем текст на место, считаем стоимость.
4 мин · · автоматизация · разработка
Нейросеть для перевода документов легко справляется с текстом, но с файлом Word всё сложнее: если выгрузить содержимое одной простынёй, потеряются стили, таблицы и нумерация. Ниже рабочая схема на Python: берём DOCX, переводим абзацы по одному, кладём перевод обратно в тот же абзац и проверяем результат. Заодно считаем, сколько стоит перевод типового документа.
Почему не стоит переводить файл целиком
DOCX — это архив с XML. Текст в нём разбит на абзацы, а абзацы на «прогоны» (runs) с разным форматированием: жирный, курсив, другой шрифт. Если вставить весь текст в чат и попросить перевести, вы получите текст без структуры, и верстку придётся восстанавливать вручную. Если же менять только text внутри существующих абзацев, всё остальное остаётся на месте: стили абзацев, таблицы, колонтитулы, поля.
Плюс практическая причина: длинный документ в один запрос превращается в длинный ответ, где модель может пропустить кусок или сократить его. С маленькими фрагментами проверять проще.
Нейросеть для перевода документов: схема по абзацам
План из четырёх шагов:
- Обойти абзацы основного текста, таблиц и колонтитулов.
- Для каждого непустого абзаца отправить текст модели с глоссарием в системном сообщении.
- Записать перевод обратно в абзац.
- Сохранить копию файла и сверить числа и количество абзацев.
Установка:
pip install python-docx openaiКлюч создайте в кабинете и положите в переменную ZUKKO_API_KEY. Подробнее про клиент — в описании Python SDK.
Обход абзацев, включая таблицы
Таблицы хранят абзацы внутри ячеек, а объединённые ячейки возвращаются несколько раз, поэтому дублирующиеся ячейки отсеиваем:
from docx import Document
def iter_paragraphs(container):
for p in container.paragraphs:
yield p
for table in container.tables:
seen = set()
for row in table.rows:
for cell in row.cells:
if cell._tc in seen:
continue
seen.add(cell._tc)
yield from iter_paragraphs(cell)
def all_paragraphs(doc):
yield from iter_paragraphs(doc)
for section in doc.sections:
for part in (section.header, section.footer):
if not part.is_linked_to_previous:
yield from iter_paragraphs(part)Сноски, текстовые блоки и поля в этой версии не затрагиваются: посмотрите на них отдельно после перевода.
Запись текста без потери стиля
Самое простое решение: положить весь перевод в первый «прогон» абзаца, остальные очистить. Стиль абзаца и формат первого прогона сохранятся, но выделения внутри абзаца (одно жирное слово посреди предложения) пропадут. Чтобы их сохранить, можно отправлять модели абзац с тегами по прогонам и просить сохранить теги:
import re
def to_tagged(p):
return "".join(f"<r{i}>{r.text}</r{i}>" for i, r in enumerate(p.runs))
def apply_tagged(p, tagged):
parts = {int(i): t for i, t in re.findall(r"<r(\d+)>(.*?)</r\1>", tagged, re.S)}
if set(parts) != set(range(len(p.runs))):
return False
for i, r in enumerate(p.runs):
r.text = parts[i]
return True
def apply_plain(p, text):
if not p.runs:
p.add_run(text)
return
p.runs[0].text = text
for r in p.runs[1:]:
r.text = ""Логика: у абзацев с одним прогоном переводим обычный текст, у абзацев с несколькими пробуем размеченный вариант, а если модель сломала теги, откатываемся на apply_plain. Так форматирование теряется только в редких случаях, и их можно отследить.
Запрос к модели и глоссарий
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.zukko.pro/v1",
api_key=os.environ["ZUKKO_API_KEY"])
GLOSSARY = {
"Lessee": "Арендатор",
"Lessor": "Арендодатель",
"Force Majeure": "Форс-мажор",
}
SYSTEM = (
"Ты профессиональный переводчик с английского на русский. "Глоссарий нужен, чтобы «Lessee» не переводился в одном месте как «арендатор», а в другом как «наниматель». Для большого документа держите глоссарий коротким: он отправляется с каждым абзацем и входит в стоимость.
Основной цикл
from collections import Counter
def numbers(s):
return Counter(re.findall(r"\d+(?:[.,]\d+)?", s))
doc = Document("contract_en.docx")
problems = []
for p in all_paragraphs(doc):
src = p.text
if not src.strip():
continue
if len(p.runs) > 1:
out = translate(to_tagged(p))Сравнение чисел — грубая, но полезная проверка: расхождение показывает абзацы, где модель могла потерять сумму или дату. Форматы вроде «1,000» и «1 000» дадут ложные срабатывания, это нормально.
Как проверять результат
Проверка не заменяет вычитку специалистом, но убирает часть ошибок до неё:
- сверьте число абзацев в исходнике и переводе;
- пройдите по списку
problemsиз кода выше; - поищите в тексте остатки английских терминов из глоссария;
- откройте файл в Word и проверьте оглавление, нумерацию и поля: они обновляются вручную.
Юридические, медицинские и технические документы должен читать человек, который знает предметную область. Модель переводит текст, но ответственность за смысл остаётся на вас.
Сколько стоит перевод документа
Предположим, документ на 20 страниц: 15 000 токенов исходного текста и около 30 000 токенов перевода на русский (русский текст обычно занимает больше токенов, чем английский). Глоссарий и инструкция — примерно 300 токенов, отправляемых с каждым из 400 абзацев: 120 000 токенов. Итого около 135 000 токенов входа и 30 000 выхода, без учёта кэша. Цены на 18 октября 2026, формула: вход × цена входа + выход × цена выхода.
| Модель | Расчёт | Итого |
|---|---|---|
deepseek-v4.1-flash | 0.135 × 0.013 + 0.03 × 0.038 | около $0.003 |
gpt-6.1-sol | 0.135 × 0.27 + 0.03 × 1.32 | около $0.076 |
claude-sonnet-5-5 | 0.135 × 0.4 + 0.03 × 2 | около $0.114 |
Цифры зависят от длины документа и глоссария. Для больших объёмов пригодится статья про перевод текстов через API.
Бесплатные варианты
Запрос «бесплатная нейросеть для перевода документов» популярен, но условия бесплатных сервисов (размер файла, число страниц в день, что происходит с загруженным документом) меняются, и мы их не знаем. Если документ конфиденциальный, прочитайте условия сервиса перед загрузкой. Для разовой проверки подойдёт пробный баланс: $0.50 за подписку на @zukkopro (один раз на Telegram-аккаунт) на дешёвой модели хватает на много страниц перевода.
Частые вопросы
Работает ли это со старым форматом .doc?
Нет, python-docx читает только DOCX. Сначала пересохраните файл в DOCX из Word или другого редактора.
Как перевести документ с английского на русский и обратно?
Измените строку в SYSTEM: укажите направление и глоссарий в нужную сторону. Остальной код не меняется.
Что с PDF?
PDF не хранит абзацы как структуру, поэтому схема другая: сначала распознайте текст, затем переведите. Один из подходов описан в статье про обработку PDF через vision.
Какую модель взять?
Начните с дешёвой и проверьте 10–20 абзацев своего документа. Если терминология плывёт, переходите к более сильной модели из списка в справочнике по моделям и сравнивайте на тех же абзацах.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Перевод субтитров нейросетью: SRT без сбитых таймкодовГлавный риск при переводе SRT нейросетью — сломанная структура файла. Показываем схему, где таймкоды не попадают к модели, и скрипт на Python с проверкой.
- Редактирование текстов нейросетью: корректура, стиль и сокращениеРазные виды редактуры требуют разных промптов. Показываем формулировки для корректуры, стиля и сокращения, как получать правки с объяснением и не терять авторский голос.
- Нейросеть для написания скриптов: Bash, PowerShell и PythonМодель быстро пишет скрипты для рутины, но запускать их вслепую нельзя. Показываем, как ставить задачу, требовать безопасный режим dry-run и проверять результат до запуска.
- Dify в Docker: установка, первичная настройка и подключение моделейСтавим Dify через Docker Compose, создаём администратора и подключаем модели Claude, GPT и DeepSeek по одному ключу. Отдельно — что делать, если контейнеры не стартуют.