现象
GitHub 上以纯文本形式渲染的 README 完全不翻译 —— 整页一个字都不动。截图为 Linux kernel 仓库的 README(无扩展名的纯文本文件,GitHub 用等宽字体、保留空白的方式渲染,即包在 <pre> 里)。
正文全是散文式说明文字(Quick Start、Who Are You?、Find your role below: 等),不是代码,但一段都没被翻译。
根因
<pre> 被无条件整棵子树跳过,而且跳了两遍。
通用规则 —— src/dom/classify.ts:19 ,SKIP_SET 的语义是「整棵子树跳过,不再深入」:
export const SKIP_SET = new Set ( [
'html' , 'body' , 'script' , 'style' , 'noscript' ,
'input' , 'textarea' , 'select' , 'button' ,
'code' , 'pre' ,
] ) ;
GitHub 域名补丁 —— src/dom/compat.ts:48 又显式跳了一次:
'pre, ' + // GitHub 的 pre 通常是代码块
这条注释本身是成立的 —— GitHub 上的 <pre> 通常 确实是代码块。问题在于「通常」不是「总是」:纯文本 README、.txt、部分 .rst 在 GitHub 上都会被塞进 <pre>,内容是散文而非代码。
就算放开 pre,也还是翻不了
这一条很重要,决定了修复的复杂度。即便把 pre 从两处跳过规则里拿掉,这个 README 依然不会被翻译,因为还有三道独立的坎:
pre 不在 DIRECT_SET 里 (src/dom/classify.ts:10 ),isTranslationUnit() 第一步就 return false,它永远不会成为翻译单元。
整个 README 是一个 <pre> 节点 。就算把 pre 加进 DIRECT_SET,它也是「一整篇文档 = 一个翻译单元」,语义上没有意义。
长度上限会拦下它 。MAX_TEXT 3072 / MAX_HTML 4096(src/dom/classify.ts:34-35 ),这份 README 远超两者,shouldSkip() 直接拒收。
所以真正要解的不是「要不要跳过 pre」,而是**「如何把一大块预格式化文本切成有意义的翻译单元」**。
还有一层:<pre> 里的排版会被破坏
<pre> 的语义就是空白与换行原样保留。往里面插译文会带来两个问题:
render() 会把原文包进 .pt-origin、追加一个 display: block 的 .pt-trans(src/styles/presets.css:5-8 )。在 <pre> 内部插入块级元素会改变原本靠空白对齐的排版。
截图里的 README 用了 ==== / ---- 下划线做标题、* 做项目符号,这类 ASCII 排版依赖等宽对齐。译文是中文(全角),插进去之后对齐必然错位。
这不是「不能做」,而是「做之前得先决定译文以什么形式呈现」。
修复方向
按代价从小到大:
A. 什么都不做,但让用户知道为什么 (最小)
保持跳过,但在用户对整页点了翻译却零结果时给一个提示(例如 toast「本页没有可翻译的内容」)。目前的行为是静默无反应 ,用户无法区分「扩展坏了」和「这类内容不支持」。这一条独立成立,也适用于其它零结果场景,建议无论是否做 B/C 都先补上。
B. 按空白行切分预格式化文本 (中等)
纯文本文档的段落边界就是空行。可以针对「不是代码的 pre」按连续空行切块,每块作为一个翻译单元。
难点在于判断「这个 pre 是不是代码」。可用的信号:GitHub 的语法高亮容器带有明确的类名和 <span> 着色结构,纯文本 pre 则是裸文本节点;文件名/路径也能作为线索(README 无扩展名 vs .c/.py)。这些都是 GitHub 特有的,落在 compat.ts 里比较合适 —— 但要注意该文件开头的自述「每加一条都意味着一处通用逻辑的缺陷,先问能不能改进通用规则」。
切块之后 render() 的插入方式也要重新设计,至少要避免破坏 <pre> 的空白语义(例如译文用 display: block 但不引入额外缩进,或者干脆换一种呈现方式)。
C. 通用的预格式化文本支持 (最大)
把 B 的思路一般化,不限 GitHub。收益是 .txt 文档站、邮件列表归档(如 lore.kernel.org)等都能受益,代价是要设计一套「代码 vs 散文」的通用判据,误判成本高 —— 把代码翻译掉比不翻译更糟。
倾向 A 先做 (独立、低风险、直接改善「静默无反应」这个最差体验),B/C 需要先定呈现方案再动手,不建议直接开做。
需要一并确认的点
代码块绝不能翻 —— 这是 SKIP_SET 里 code/pre 存在的原因,任何放开都必须保证代码仍然被跳过。判据宁可保守(漏翻散文)也不能激进(翻掉代码)。
行内 <code> —— SKIP_SET 里 code 和 pre 是并列的,意味着散文段落中间的行内 <code> 整块也会被跳过。这对代码标识符是对的,但会不会导致含行内代码的正常段落出现问题,值得单独确认(compat.ts 的 GitHub 补丁里已经对行内 code 做了更细的判断,通用规则却没有)。
长度上限 —— 若 B 落地,切块后每块仍需满足 MAX_TEXT,超长块的处理要定。
与 长页面整页翻译等待过久:全量往返无渐进渲染,缓存查询串行,单次失败作废整批 #25 的关系 —— 大文档切成很多小块会显著增加请求数,应与 长页面整页翻译等待过久:全量往返无渐进渲染,缓存查询串行,单次失败作废整批 #25 的分批/渐进渲染一起考虑。
验证方式
打开 Linux kernel README(纯文本 <pre>),确认要么正常分段翻译、要么给出明确提示
打开任意含语法高亮代码块的 GitHub 页面,确认代码仍然不被翻译
打开含行内 <code> 的普通 Markdown README,确认正文段落翻译不受影响
整页翻译零结果时,确认有明确提示而非静默无反应
关联
另外,截图右侧边缘可以看到悬浮球只露出了一半,被视口边界切掉 —— 与 #26 记录的「位置无视口钳制」一致,可作为该 issue 的一个补充实例。
现象
GitHub 上以纯文本形式渲染的 README 完全不翻译 —— 整页一个字都不动。截图为 Linux kernel 仓库的 README(无扩展名的纯文本文件,GitHub 用等宽字体、保留空白的方式渲染,即包在
<pre>里)。正文全是散文式说明文字(
Quick Start、Who Are You?、Find your role below:等),不是代码,但一段都没被翻译。根因
<pre>被无条件整棵子树跳过,而且跳了两遍。通用规则 —— src/dom/classify.ts:19,
SKIP_SET的语义是「整棵子树跳过,不再深入」:GitHub 域名补丁 —— src/dom/compat.ts:48 又显式跳了一次:
这条注释本身是成立的 —— GitHub 上的
<pre>通常确实是代码块。问题在于「通常」不是「总是」:纯文本 README、.txt、部分.rst在 GitHub 上都会被塞进<pre>,内容是散文而非代码。就算放开
pre,也还是翻不了这一条很重要,决定了修复的复杂度。即便把
pre从两处跳过规则里拿掉,这个 README 依然不会被翻译,因为还有三道独立的坎:pre不在DIRECT_SET里(src/dom/classify.ts:10),isTranslationUnit()第一步就return false,它永远不会成为翻译单元。<pre>节点。就算把pre加进DIRECT_SET,它也是「一整篇文档 = 一个翻译单元」,语义上没有意义。MAX_TEXT3072 /MAX_HTML4096(src/dom/classify.ts:34-35),这份 README 远超两者,shouldSkip()直接拒收。所以真正要解的不是「要不要跳过
pre」,而是**「如何把一大块预格式化文本切成有意义的翻译单元」**。还有一层:
<pre>里的排版会被破坏<pre>的语义就是空白与换行原样保留。往里面插译文会带来两个问题:render()会把原文包进.pt-origin、追加一个display: block的.pt-trans(src/styles/presets.css:5-8)。在<pre>内部插入块级元素会改变原本靠空白对齐的排版。====/----下划线做标题、*做项目符号,这类 ASCII 排版依赖等宽对齐。译文是中文(全角),插进去之后对齐必然错位。这不是「不能做」,而是「做之前得先决定译文以什么形式呈现」。
修复方向
按代价从小到大:
A. 什么都不做,但让用户知道为什么(最小)
保持跳过,但在用户对整页点了翻译却零结果时给一个提示(例如 toast「本页没有可翻译的内容」)。目前的行为是静默无反应,用户无法区分「扩展坏了」和「这类内容不支持」。这一条独立成立,也适用于其它零结果场景,建议无论是否做 B/C 都先补上。
B. 按空白行切分预格式化文本(中等)
纯文本文档的段落边界就是空行。可以针对「不是代码的
pre」按连续空行切块,每块作为一个翻译单元。难点在于判断「这个
pre是不是代码」。可用的信号:GitHub 的语法高亮容器带有明确的类名和<span>着色结构,纯文本pre则是裸文本节点;文件名/路径也能作为线索(README无扩展名 vs.c/.py)。这些都是 GitHub 特有的,落在compat.ts里比较合适 —— 但要注意该文件开头的自述「每加一条都意味着一处通用逻辑的缺陷,先问能不能改进通用规则」。切块之后
render()的插入方式也要重新设计,至少要避免破坏<pre>的空白语义(例如译文用display: block但不引入额外缩进,或者干脆换一种呈现方式)。C. 通用的预格式化文本支持(最大)
把 B 的思路一般化,不限 GitHub。收益是
.txt文档站、邮件列表归档(如 lore.kernel.org)等都能受益,代价是要设计一套「代码 vs 散文」的通用判据,误判成本高 —— 把代码翻译掉比不翻译更糟。倾向 A 先做(独立、低风险、直接改善「静默无反应」这个最差体验),B/C 需要先定呈现方案再动手,不建议直接开做。
需要一并确认的点
SKIP_SET里code/pre存在的原因,任何放开都必须保证代码仍然被跳过。判据宁可保守(漏翻散文)也不能激进(翻掉代码)。<code>——SKIP_SET里code和pre是并列的,意味着散文段落中间的行内<code>整块也会被跳过。这对代码标识符是对的,但会不会导致含行内代码的正常段落出现问题,值得单独确认(compat.ts的 GitHub 补丁里已经对行内code做了更细的判断,通用规则却没有)。MAX_TEXT,超长块的处理要定。验证方式
<pre>),确认要么正常分段翻译、要么给出明确提示<code>的普通 Markdown README,确认正文段落翻译不受影响关联
classify.ts判定范围问题(那里是DIRECT_SET缺td/th,这里是SKIP_SET过宽)另外,截图右侧边缘可以看到悬浮球只露出了一半,被视口边界切掉 —— 与 #26 记录的「位置无视口钳制」一致,可作为该 issue 的一个补充实例。