翻译验收

翻译后的网页出现 &,是多解码了一次吗

页面上显示出一串 `&`,第一反应常是再解码一次。但问题也可能恰好相反:同一段内容已经转义过,又被模板转义了一遍。先弄清当前拿到的是原始 HTML、DOM 文本还是准备插入页面的普通字符串,再决定该不该转换。

阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。 页面上显示出一串 &amp; ,第一反应常是再解码一次。但问题也可能恰好相反:同一段内容已经转义过,又被模板转义了一遍。先弄清当前拿到的是原始 HTML、DOM 文本还是准备插入页面的普通字符串,再决定该不该转换。 转义是输出边界的事 html.escape 把普通文本中有 HTML 含义的字符转成实体; html.unescape 则解析实体。它们不是可以随意交替调用的“清洗步骤”。翻译器需要的通常是语义文本,模板需要的则是符合当前位置规则的序列化结果。 比如产品名 R&D 作为文本显示时没有问题,写入 HTML 源码时通常表示为 R&amp;D 。再转义一次会变成 R&amp;amp;D ,浏览器便把中间的实体字面量显示出来。相反,把不可信内容解码后直接当 HTML 插入,可能重新激活其中的标签。 用三种状态做一个小实验 import html plain = 'R&D <Guide> "2026"' encoded = html.escape(plain, quote=True) double_encoded = html.escape(encoded, quote=True) assert encoded == 'R&amp;D &lt;Guide&gt; &quot;2026&quot;' assert html.unescape(encoded) == plain assert html.unescape(double_encoded) == encoded assert html.unescape(double_encoded) != plain untrusted = '&lt;img src=x onerror=alert(1)&gt;' decoded = html.unescape(untrusted) safe_as_text = html.escape(decoded, quote=True) assert decoded.startswith('<img') assert safe_as_text.startswith('&lt;img') 这是标准库的合成转换样例,不会执行 HTML 或脚本。最后两行说明的是“作为文本输出”的处理,不是安全 HTML 清洗方案,也不能推导出把任意结果放进 JavaScript、CSS 或 URL 都安全。不同上下文需要不同规则。 在管线里给数据状态起名字 可以用 source_html 、 plain_text 、 translated_text 和 rendered_html 区分阶段,避免把几个类型都叫 content 。如果项目允许,也可以用轻量的数据类包裹,明确哪个函数只接受纯文本。命名不能替代测试,却能帮助审查发现“把最终 HTML 又交给翻译”的接线错误。 当前输入 希望得到什么 应先确认 原始 HTML 可翻译文字 用解析器定位文本节点 DOM 文本 目标语言文本 通常不要再无条件解码 译文纯文本 页面可见文字 让模板按文本转义 允许的富文本 安全展示片段 使用明确的清洗策略 不要循环解码直到“不再变化” 这种写法看起来能处理多层实体,实际上会破坏用户本来就想展示的代码。教程里讲的 &lt;div&gt; 就可能是有意保留的字面量,而不是漏处理的标签。是否应该再解一层,应由输入协议决定,不能由字符串外观猜测。 正文里的链接、属性值和代码块还可能有不同的翻译政策。保留一组含尖括号、引号、与号和实体字面量的回归样本,分别检查解析后的文本与最终浏览器展示,比只比较源文件字符串更有用。 翻译验收应该保留这种小样本 评估 OmniDoc(产品入口) 等文档翻译入口时,可以把这类样本放进 HTML 测试文件,观察译文里的标签示例和产品名是否仍按预期显示。不要把“英文翻出来了”作为唯一完成条件:内容含义、转义层数和代码保护都是交付的一部分。 参考: Python html 模块 。 - 1 -
更新于 2026-10-08