Skip to content

fix(translation): catch untranslated adverbs the residue detector could not enumerate - #179

Merged
GabrielDrapor merged 4 commits into
mainfrom
fix/residue-adverbs
Aug 17, 2026
Merged

fix(translation): catch untranslated adverbs the residue detector could not enumerate#179
GabrielDrapor merged 4 commits into
mainfrom
fix/residue-adverbs

Conversation

@GabrielDrapor

@GabrielDrapor GabrielDrapor commented Aug 17, 2026

Copy link
Copy Markdown
Owner

问题

用户在《混战》(The Mixer) 后几章读到大量未翻译的副词:

曼联的历史最佳射手 arguably 从未找到自己的最佳位置
阿尔穆尼亚遭受了 relentless 的高空轰炸

detectEnglishResidue 的 mostly-CJK 分支只认一份人工维护的虚词列表(simply/exactly/further…),所以这类普通实词副词、形容词从未触发重译,直接发到读者眼前。《混战》全书 28 段命中,分布在第 7–38 章(后半更密,但不只在最后几章)。

改动

副词/形容词是列不完的,所以这一版改为同时匹配词形——拼音和罗马字都不可能出现的英语屈折后缀:

  • -ly(arguably、inevitably、momentarily)
  • 拉丁语系名词/形容词后缀 -tion/-sion/-ment/-ness/-able/-ible/-ive/-ous/-ful/-less/-ical/-istic(possession、cohesive、relentless)
  • -ing/-ed/-al,带长度下限,把 ping/ming/xing 这类拼音音节挡在外面

外加把没有屈折可依的连接词补进原列表(somehow、otherwise、meanwhile…)。

调参方式沿用上一轮 (a67c715):对生产库全部 18 万条 mostly-CJK 译文做离线扫描。原列表只命中 8 条 (0.004%),加词形后 335 条 (0.19%),抽样检查新增命中基本都是真残留;引号内英文、括号注、拼音串、Title-Case 人名等既有的 false-positive 形态仍被排除。误报的代价只是一次重译,而且只有当残留严格变少时才会采用。

四份镜像同步:translate-worker.tstranslate-core.tseval-translation.mjsscan-residue-corpus.mjs;两个测试套件都加了取自真实句子的回归用例。

新增 yarn backfill-residue

用当前检测器扫已有书的译文,对命中段落用生产同款 prompt + glossary 重译并按 row id UPDATE(不删不重插,xpath 映射不动),逐行写 before/after JSONL 回滚日志。写库前的三道闸:

  • 新译文残留必须严格更少
  • 长度不得坍缩到原文 60% 以下(截断的尾巴恰好没有残留,会骗过上一条)
  • 超过 pipeline 3000 字切分阈值的节点走 translateLargeNode 分块路径,而不是一次调用

--all 扫全库,--high-cjk-only 只处理这次修复覆盖的分支(不限分支会连版权页、目录、人名列表一起命中,那些英文本来就该留着)。为此导出了 translateText / translateLargeNode,避免脚本里再抄一份 prompt。

线上数据回填(已完成)

段数
《混战》 28
其余 62 本书 263
合计 291(0 失败,复查残留为 0)

18 段故意跳过——英文是作者有意留的:《我们赖以生存的隐喻》的语言学讨论("n't 否定的是 leave 而不是 think")、《詹姆斯·乔伊斯如何成名》的拉康法语双关(franchement / poubellication / père-version)、《从爱丁堡到印度与缅甸》的古登堡勘误注记、《比德威尔游记》的方言引语。回滚日志留存在本地。

测试

yarn test 310 passed;services/translator 173 passed。

🤖 Generated with Claude Code

https://claude.ai/code/session_01KoxwKGP9yhzhf8NQmDUcNk

GabrielDrapor and others added 3 commits August 16, 2026 23:34
…erate

detectEnglishResidue's mostly-CJK branch only flagged words from a curated
function-word list, so ordinary content words shipped untranslated inside
otherwise-Chinese paragraphs — "曼联的历史最佳射手 arguably 从未找到自己的
最佳位置", "遭受了 relentless 的高空轰炸" (reported on The Mixer, 28 such
segments across chapters 7–38, denser in the late chapters).

Enumerating adverbs and adjectives is hopeless, so the branch now also
flags word *shapes* that cannot be pinyin or romaji: -ly, the Latinate
noun/adjective suffixes (-tion/-ment/-ness/-able/-ive/-ous/-less/-ical…),
and -ing/-ed/-al above a length floor that keeps "ping"/"ming"/"xing" out.
Curated list additions cover the connectives with no inflection to key off
(somehow, otherwise, meanwhile…).

Tuned the same way as the previous pass — a scan over all 180k mostly-CJK
production segments. The list alone flagged 8 segments (0.004%); list plus
shapes flags 335 (0.19%), and sampling the new flags shows they are real
leftovers, with the known false-positive shapes (quoted terms, bracketed
glosses, pinyin runs, Title-Case names) still excluded. A false positive
costs one retry that is only kept when it strictly reduces residue.

Applied to all four mirrors (translate-worker.ts, translate-core.ts,
eval-translation.mjs, scan-residue-corpus.mjs) with regression tests from
the reported sentences in both suites.

Adds scripts/backfill-residue.ts (yarn backfill-residue): re-runs the
current detector over a book's stored translations and re-translates the
flagged rows with the production prompt + glossary, updating by row id and
writing a JSONL before/after log for rollback. translateText is now
exported so the backfill can't drift from the pipeline's prompt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KoxwKGP9yhzhf8NQmDUcNk
…etries

Running the backfill for real turned up three sharp edges: `--file` writes
upload a temp file per row and answered "fetch failed" mid-run, a whole
book's candidate rows in one response triggered the same error, and a
one-book-at-a-time flag makes a library sweep a manual chore.

Statements now go through --command as an argv element (execFileSync, no
shell, so translated text needs no escaping), reads are paged 300 rows at a
time, every wrangler call retries three times with backoff, and --all walks
every book while a GLOB prefilter keeps segments without latin text out of
the transfer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KoxwKGP9yhzhf8NQmDUcNk
… rows

Three ways the sweep could have damaged good text. Nodes over the
pipeline's 3000-char split threshold would come back from a single LLM call
truncated — and a truncated tail reads as residue-free, so the guard that
only accepts cleaner output would have waved it through; those rows are now
skipped. A replacement under 60% of the original's length is treated as a
dropped tail and rejected. And --high-cjk-only restricts a run to the
branch the word-shape fix actually changed: without it a library sweep also
picks up credits pages, TOC headings and name lists, whose English is
deliberate and predates this work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KoxwKGP9yhzhf8NQmDUcNk
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 17, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ovid fdc59e9 Commit Preview URL

Branch Preview URL
Aug 17 2026, 02:59 AM

Nodes past the pipeline's 3000-char split threshold were skipped outright.
They now go through translateLargeNode — the same paragraph-chunked path
the pipeline uses for them — so a long node gets repaired instead of left
behind, without the truncation risk that made a single call unsafe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KoxwKGP9yhzhf8NQmDUcNk
@GabrielDrapor
GabrielDrapor merged commit 840cf97 into main Aug 17, 2026
4 checks passed
@GabrielDrapor
GabrielDrapor deleted the fix/residue-adverbs branch August 17, 2026 12:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant