Revayat Novel — 英语 → 波斯语书籍翻译
请按顺序执行下面的九个步骤。每个步骤包含一个命令和一条关于如何处理其输出的规则。不要擅自更改顺序,也不要因为前一步看起来正常就跳过某一步。
一次设置四个变量,然后在各处使用它们:
SKILL_DIR— 存放此文件的文件夹。在 Claude Code 插件中,它是${CLAUDE_PLUGIN_ROOT}/skills/revayat-novel。WORK— 此书的工作文件夹,例如work/。PY— Python 解释器。只需解析一次;python3在大多数 Windows 安装中不存在,因此硬编码它的命令行只在三个平台中的两个上有效:- macOS / Linux:
PY=python3 - Windows:
PY=python— 或者如果安装了启动器,则使用PY="py -3" - 如果两者都不运行,步骤 1 中的
doctor将不会启动,这就是信号。
- macOS / Linux:
OCR_LANG— 你正在翻译的书籍中印刷的语言的 Tesseract 代码,而不是你翻译成哪种语言。对于常见的英语 → 波斯语任务,它是eng。波斯语源是fas;德语deu,法语fra,阿拉伯语ara。在每一个 OCR 步骤中都必须使用相同的值。 使用波斯语模型识别英语页面会返回自信满满的胡言乱语,这比低分更糟糕,因为下游任何步骤都无法发现它。
每个命令都是 $PY $SKILL_DIR/scripts/revayat-novel.py <stage> …。
永远不能打破的五条规则
- 永远不要反转波斯语文本使其从右向左阅读。方向由构建器设置。反转会产生一个在任何地方(除了某个查看器)都损坏的文件。
- 永远不要发明、删除、合并、拆分或重新排序
@@ id头。 准确返回你得到的那些。 - 永远不要删除
[[fn:…]]、**bold**、*italic*或`verbatim`标记。 它们会被计数;不匹配会导致 QA 失败。 - 永远不要手动编辑
book.json来修正翻译。 修正工作表然后重新运行合并。 - 永远不要缩短书籍。 不进行总结,不跳过难句,不软化内容。如果某一步骤报告内容缺失,则重新运行该块。
步骤 1 — 检查工具
$PY $SKILL_DIR/scripts/revayat-novel.py doctor
"ready": true→ 继续。"ready": false→ 运行pip install -r $SKILL_DIR/requirements.txt,然后再次运行doctor。optional_tools显示not found暂时没问题。步骤 2 会告诉你是否实际需要 OCR。
步骤 2 — 提取
$PY $SKILL_DIR/scripts/revayat-novel.py extract "<input file>" --out $WORK --ocr-lang $OCR_LANG
$OCR_LANG 是书中印刷的语言,在上面已设置一次。在这里弄错不会导致可见的失败:OCR 仍然会返回文本,但它是错误的文本。
读取它打印的 JSON 并按照表格操作:
| 你看到的内容 | 该做什么 |
|---|---|
"kind": "digital" | 什么都不做;不需要 OCR |
"kind": "scanned" 或 "mixed" | OCR 自动运行;检查 ocr.probe_after.text_share 是否高于 0.7 |
| 一个命名 OCRmyPDF 的错误 | 按照消息提示安装它,然后重新运行此步骤 |
clean_scan.cleaned 高于 0 | 彩色水印已从那么多页面中移除 |
ocr.warning 不为 null | 读取它;文件仍然可用,所以继续 |
page_scans_dropped 高于 0 | 那么多整页光栅被识别为扫描本身,而不是图片 |
清理器从不编辑你的文件:原始文件保持原样,清理后的副本写入 $WORK/cleaned.pdf,并在 clean_scan 中记录每页移除了什么以及保留了什么。传递 --clean-scan off 完全跳过它,如果水印残留则传递 --clean-scan force。
如果书籍是扫描的,现在执行这两个额外的过程。
识别置信度 — 如果没有它,误读的单词与正确的单词就无法区分,因为两者都是源语言的普通单词,位于语法正确的句子中。管道的后续部分无法区分它们,翻译器将忠实地渲染错误的单词。
$PY $SKILL_DIR/scripts/revayat-novel.py ocr-sidecar \
--pdf $WORK/ocr.pdf --out $WORK --lang $OCR_LANG --book $WORK/book.json
这里的 --lang 是你给 extract 的同一个 $OCR_LANG。它必须是:此过程重新识别相同的页面以找出引擎的置信度,因此不同的模型会读取不同的单词并对书籍未说的内容进行评分。
这会写入 source.ocr.json — 每个单词的边框、置信度和阅读顺序,按行、块和页面聚合,加上运行了哪些预处理 — 以及 source.ocr.txt,然后用它来自的区域的置信度戳记 book.json 中的每个块。评级为 low 的块会被 qa check 报告为 ocr-low-confidence,接受一个意味着查看页面图像。阈值默认为 85 和 60,并随 --high / --low 移动。
扫描中的插图 — 扫描页面是一个平面图像,因此上面的照片不是单独的图片,直到某物找到它:
mineru -p $WORK/cleaned.pdf -o $WORK/mineru -b pipeline -l en
$PY $SKILL_DIR/scripts/revayat-novel.py extract "<input file>" --out $WORK \
--figures-from-mineru $WORK/mineru
MinerU 的 -l 使用自己的代码,而不是 Tesseract 的:英语源使用 en,波斯语或阿拉伯语脚本使用 arabic。将其与书籍匹配,方式与匹配 $OCR_LANG 相同 — 布局检测使用脚本来分割页面。
在 cleaned.pdf 上运行 MinerU,而不是原始文件,否则它会将水印裁剪为图形。仅从 MinerU 获取图片。 在真实的波斯语扫描上测量,它自己识别的文本返回了单词以及里面的字母被反转的结果,而 Tesseract 正确读取了同一页面 — 因此文本继续来自上面的 OCR 过程,而 MinerU 仅用于 OCR 无法做的一件事,即找到图片在平面光栅中的位置。当 MinerU 在页面范围上运行时,使用 --figures-page-offset N。
然后在继续之前查看结果:
$PY $SKILL_DIR/scripts/revayat-novel.py qa check --book $WORK/book.json --allow-incomplete
暂时忽略 untranslated-block — 还没有任何内容被翻译。你要找的是 asset-missing。如果 stats.text_blocks 对于一本真正的书低于 20,则提取失败:请参阅 references/extraction.md。
步骤 3 — 词汇表
$PY $SKILL_DIR/scripts/revayat-novel.py glossary scan \
--book $WORK/book.json --out $WORK/glossary.json
报告列出 needs_persian,最频繁的在前。打开 $WORK/glossary.json,并为前 20 个条目中的每一个填写四个字段:
"target": "الیزابت بنت",
"later_form": "الیزابت بنت",
"first_form": "الیزابت بنت (Elizabeth Bennet)",
"locked": true
当一个条目有 aliases — 书籍单独使用的姓氏或名字 — 时,将它们的波斯语放在 alias_targets 中:
"aliases": ["Ashcroft", "Margaret"],
"alias_targets": ["اشکرافت", "مارگارت"]
当源文件只说「Ashcroft」时,波斯语应该说「اشکرافت」。留空会使漂流检查每次都需要全名,这既使波斯语变差,又是虚假警报。
保持 first_block_id 完全不变 — 管道使用它来决定哪个块引入名称。不要编辑它,也不要自己决定首次提及。
删除不是真实名称的条目。然后继续。
步骤 4 — 切割成工作表
整部小说绝不能放入一个模型上下文,因此首先要将其切割开。有两种切割方式,由源文件决定使用哪一种 — 这不是偏好:
| 源 | 路径 |
|---|---|
| PDF,原生数字或扫描 | 按页面 — 见下文 |
| EPUB, DOCX, 纯文本 | 按字符预算 — 步骤 4b |
按页面切割 PDF。 页面是书籍已有的边界:在运行间稳定,是审阅者查看的单位,也是唯一可以与渲染页面进行比较的单位。按字符预算切割的工作表会在预算用完的任何地方中断,而预算用完没有特别的位置 — 并且源中不存在的页面无法对照源进行检查。线以下的格式没有自己的页面可以切割,因此它们采用预算路径并放弃该检查。
$PY $SKILL_DIR/scripts/revayat-novel.py pages build \
--book $WORK/book.json --out $WORK/pages --glossary $WORK/glossary.json
这会为每个源页面写入一个工作表 — page0001.md,其翻译放在旁边的 out_page0001.md 中。使用这些名称,而不是步骤 5 的 chunkNNNN.md:格式相同,只是文件名不同,并且 pages next 会准确告诉你要打开哪个文件,因此无需猜测。
它还写入 $WORK/pages/source/page-0001.pdf — 每个源页面作为自己的 PDF,复制而不是重新渲染,以便边框、旋转和图像质量是书籍自己的。清单记录每个文件及其 SHA-256,并命名读取整个运行的 reference_pdf。从清单中读取源 PDF;对于原生数字书籍它是原始文件,对于扫描它是 OCR 副本,硬编码任何一个对另一个都是错误的。
每个页面任务只携带它需要的内容:适用于该页面的词汇表行、在该页面中发声的语态、OCR 在该页面不确定的单词,以及标记为不要翻译的相邻页面的有界切片。自身文本超过预算的页面会被拆分为编号部分而不是截断 — 清单中的 part 和 parts 说明这一点 — 并且在判断页面之前这些部分会重新合并。
页面分割切断的段落属于它开始的页面并且只翻译一次;它延伸到的页面只将其视为上下文。永远不要翻译出现在邻居上下文标题下的块 — 它已经属于另一个页面的工作表。
$PY $SKILL_DIR/scripts/revayat-novel.py pages status --pages $WORK/pages
$PY $SKILL_DIR/scripts/revayat-novel.py pages next --pages $WORK/pages
status 报告每个页面的状态;next 命名第一个仍需处理的页面,因此中断的运行会从停止的地方继续,而不是从头开始。
页面循环
源 PDF 是此运行从中读取的任何一个:对于原生数字 PDF 是书籍自己的,对于扫描是 OCR 副本。清单记录了它 — 从那里获取它,而不是命名一个可能不存在的文件。
SOURCE_PDF=$($PY -c "import json,sys;print(json.load(open(sys.argv[1]))['reference_pdf'])" $WORK/pages/manifest.json)
一次一页,按此顺序。每个命令需要它前面的命令产生的结果,因此过早采取的步骤会直接拒绝:
P=12 # 无论 `pages next` 刚刚命名了什么
# 3. 将翻译合并到书中
$PY $SKILL_DIR/scripts/revayat-novel.py pages merge \
--book $WORK/book.json --pages $WORK/pages --page $P --glossary $WORK/glossary.json
# 4. 单独排版该页
$PY $SKILL_DIR/scripts/revayat-novel.py pages preview \
--book $WORK/book.json --pages $WORK/pages --page $P
# 5. 与源页面比较
$PY $SKILL_DIR/scripts/revayat-novel.py render-qa \
--book $WORK/book.json --work $WORK --page $P \
--docx $WORK/previews/page-0012.docx --source-pdf $SOURCE_PDF
# 6. 查看两张图像(步骤 8 说明了要寻找什么)
$PY $SKILL_DIR/scripts/revayat-novel.py pages review \
--pages $WORK/pages --page $P \
--answer figure-placement=yes --answer script-integrity=yes \
--answer no-source-language=yes --answer hierarchy=yes \
--answer reads-as-a-book=yes --note "你看到的"
# 7. 现在才
$PY $SKILL_DIR/scripts/revayat-novel.py pages accept \
--book $WORK/book.json --pages $WORK/pages --page $P
然后再次 pages next,直到它说没有剩余内容。
步骤 4 不是可选的,如果你跳过它,步骤 5 会为你执行 — 省略 --docx,render-qa 自行构建预览。显式命令的存在是为了让你可以在 Word 中打开页面并亲自查看。
预览是一个源页面,单独排版。 它绝对不是整本书:波斯语会重排,因此源页面 12 不会成为书籍的页面 12,而且到了小说中间,漂流会达到几页。检查书籍的第十二张纸与页面 12 的预期相比,会报告一个正确的页面缺少所有文本并携带其邻居的文本。预览使用生产构建器设置 — 相同的样式、字体、RTL、图像大小调整、标题逻辑 — 因此你看到的是真实内容的真实排版。
波斯语比英语长的页面会变成两张纸。这是正常的,两者都保留为 renders/target/page-0012.png 和 page-0012-2.png,并且 QA 会读取所有它们。
accept 拒绝,除非每个门控实际上已经通过 — 它读取证据而不是相信你的话,因此不能通过断言标记页面完成。它需要四件事:对于页面发出的每个单元,书中都有波斯语;在运行记录上有渲染 QA;报告仍然这样说;以及当前的视觉审查。
重建是安全的,并且永远不会丢弃翻译。自上次切割以来源文本已更改的页面在 invalidated 下列出 — 那些,也只有那些,需要重新翻译。在具有不同预算的重建中剩余的工作表在 orphaned 下列出:它们仍在磁盘上,但没有任何内容再读取它们,因此它们的翻译不会到达书中。
步骤 4b — 当源文件没有页面时
仅适用于 EPUB, DOCX 和纯文本。对于 PDF,使用步骤 4。
$PY $SKILL_DIR/scripts/revayat-novel.py chunk build \
--book $WORK/book.json --out $WORK/chunks --glossary $WORK/glossary.json
记下块的数量。每个块成为一个翻译任务。
如果你已经翻译了一些工作表并且自那以后输入发生了变化,那么这将以 "refused": "stale-worksheets" 拒绝,而不是静默地给你不再匹配书籍的工作表。
…
[因翻译而截断]