chrome-plugin-manga-translator

Round 8 audit — 同一件事,界面上只能有一个数字

第七轮修的是「没有人点过的请求」。修完之后再审一遍钱,发现的已经不是 「谁授权的」,而是更朴素的两条:

承诺要么兑现,要么改写。

同一件事,界面上只能有一个数字。

第一条针对对外的文案(商店页面、审核说明),第二条针对界面上重复出现的 同一件事(失败了几张、要花多少钱)。这一轮里,六个问题全部落在这两句话上。


P1-1 — 「清除本页译文」把整页还给了观察器

现象

用户在自动续翻的站点了「清除本页译文」:译文没了,图上的气泡也走了。 他以为这只是「把画面擦干净」。

账单继续涨。

根因

第七轮引入了 attemptedImageKeys——「这张图问过了」的记录。观察器只捡 从来没问过的图,这条规则就守住了钱。

但「清除本页译文」把三个集合一起清空了,其中一个是「问过了」。记录一 没,下一张图加载进来,观察器就认为「这一页还有没问过的图」——整页重新 来一遍,最多二十轮。

修法

清空时把「问过了」的记录留下:

resetPerImageState({ keepAttempted: true })

覆盖层走,记录不走。用户想让某一页重来,还是点「翻译当前页」——那个按钮 本来就带着「重新问一遍」的语义,而且用户点得到。


P1-2 — 一张「没有文字」的图,每次访问都要再付一次钱

现象

商店页面写着「翻译缓存:相同图片不重复请求」。审核说明里也写着「再次 翻译同一页面(启用缓存)预期不再发出任何服务商请求」。

一整页没有文字的漫画(扉页、纯画面的跨页),这两句话都不成立。

根因

整图路径有一条质量门:模型返回 success 但没有文字区 → 不写缓存。 理由本身是对的——一次请求很便宜,模型看漏的页面还有翻身的机会。

但它把两种「空」当成了一种:

情况 是什么 该不该记
模型框出了气泡,但译文全是空 假成功:模型找到了位置却没翻出来 不该记——下次换个提示词也许能翻出来
模型(和 OCR)都说「这里没有文字」 一个答案:这张图确实没有文字 该记——下次凭什么再问一次

条漫路径上一轮已经按后一种口径写缓存了(一片没有文字 = 一个答案)。整图 路径没跟上,于是「不重复请求」这句话对普通页面不成立。

修法

两种「空」分开判断:译文为空且有框 → 假成功,不记;一个字都没有 → 答案,记下来。


P1-3 — 关于费用的那句话,只说了一半

现象

界面上写着:

长条图会拆成多片分别请求,实际请求次数可能高于图片数。

这句话是对的,但不全。

真实的账

一张图变成多次请求有两条路:

  1. 长条拆片——一句话就说了;
  2. OCR 兜底——模型一个文字都没读出来时,扩展会用 Tesseract 再问一次, 十个区域一批(regionBatchSize)。默认的整图路径也会走这条路。

只说第一条,读者会以为「一张图就是一次请求」——除非他正好在翻条漫。

修法

共享常量 REQUEST_COUNT_COST_NOTE 改名了:它现在讲的不只是拆片,而是 「一张图可能变成多次请求」,两种原因都写在里面。原来的名字 (TILING_COST_NOTE)在改完之后会变成一句谎话,所以它跟着一起改。


P1-4 — 同一个失败,界面上有两个数字

现象

弹窗上半部分:

成功翻译 2 张

下半部分,一个亮着的按钮:

重新翻译失败的 3 张

页面上那个 HUD 同时写着:

成功 2,失败 0,缓存 0

三张图失败了,用户看见的是「失败 0」。

根因

两个地方数的是不同的东西:

自动续翻会让两者分叉:一张图在某一轮失败(清单留着),下一轮自动续翻 成功了两张别的图(计数器归零)。清单是用户的,计数器是那一轮的。

按钮用的是清单(对的),摘要和 HUD 用的是计数器(错的)。

修法

界面上凡是「还有几张要重试」的地方,一律用清单:

位置 之前 现在
弹窗结果摘要 failedCount retryTargetCount
HUD「失败 N」 failedCount retryTargetCount
弹窗重试按钮 retryTargetCount 不变

一条事实,一个数字。


P2 — 剩下的几条

  1. 审核说明里关于 PNG 导出的一句话不准确。 原文说导出「不会新增任何 外发请求」。导出确实会再取一次图(走翻译时同一条 FETCH_IMAGE_BYTES 通道),只是不新增目的地、不到新的地方去。改成实话。
  2. HUD 的取消按钮写「取消」。 弹窗写的是「取消当前翻译」。旁边是进度 条的时候,「取消」很容易被读成「撤销译文」,而它其实只是停掉这一轮 请求。统一成弹窗的说法。
  3. ETA 数的是图,不是请求。 一页 20 张图、其中 3 张是长条,ETA 说「还 剩 12 张」,实际可能还有 30 次请求。这一条没有改:ETA 要准就得把 请求数算准,而请求数取决于运行时才知道的事(哪一片会被再问一次、哪 一片会失败重试),估一个更贵的数字又会让用户以为被多算了。两害相权, 维持现状。
  4. 一个只写不读的死集合。 failedImageKeys 被写、从不被读,而它的注 释还写着「retranslateFailedImages 是按 failedImageKeys 取的」—— 一句假话。删掉,注释跟着改成真的。

这次验证的方式

每条修复都配一条「去掉修复就会失败」的回归测试:

测试 去掉修复后
同一张没有文字的图,第二次不再问模型 请求数 1 → 2
模型框出了气泡却没翻出来,仍然要再问一次 请求数 1 → 1(这条必须通过,防止修过头)
结果摘要数的是还没翻成功的张数 摘要里没有「3 张待重试」,语气是「成功」
HUD 数的是还没翻成功的张数 HUD 报「失败 0」
关于费用的话说了 OCR 这条路 句子里没有「OCR」
HUD 的取消按钮写清楚取消的是什么 按钮上是「取消」

模式

第六轮的结论是「承诺要写在执行它的代码旁边」,第七轮补上「没有人点过的 请求,先问谁授权的」。这一轮补第三句:

同一个事实,界面上只能有一个数字;它要么是用户能核对的那个,要么 都不是。

「3 张待重试」是用户能核对的——点下去就会重试这 3 张。「失败 0」不是, 它只是某一轮的计数器,而用户看的是这一页现在的状态。