肯定前提是翻译表实时增加,每次保存到新TXT字典文件,但无法识别的还是要在线读取,但是翻译可不是简单的替换单词。除非整块文本一样的情况,否则基本要整句进行全新识别再替换。有时一行文字如果有部分添加了颜色,加粗,纯WORD/HTM一样删除或插入一个字符完全不影响原来的样式。但是用翻译的模式,哪些词要换掉,如何保持样式,难度就增加了100倍。
核心结论
HTML 带标签分段、加粗、变色、嵌套标签穿插文本时,不能简单字符串 replace ,这就是你之前词典替换覆盖率不足 20% 的根源:英文句子被<b>、<span style="color">、<i>拆成多段 NavigableString 碎片,整句被标签割裂,固定词典整句匹配直接失效;想要「保留全部 HTML 样式 + 分句在线机翻 + 新词自动入库本地词典」要分三层架构,确实是 HTML 翻译难点。
一、拆解难点(为什么样式会严重干扰翻译)
1. 文本碎片化(最大坑)
原文:The <b>fusion</b> provides a practical pathDOM 里被拆成三段字符串:"The "、"fusion"、" provides a practical path"你的整句词典The fusion provides a practical path无法命中,简单单词替换只能逐个词条,整句语义翻译作废;在线翻译需要拼接碎片成完整句子→翻译→再拆分回填到原 DOM 位置,不能直接改 HTML 源码字符串,一改标签位置、样式全崩。
2. 行内样式标签无规律
<span class="red">modernising</span> applications retaining <em>compatibility</em>单词被任意标签切割,整句断裂,机翻必须先剥离标签拼接原文,翻译后再按原文本长度拆分译文、逐个塞回原来的文本节点,是整套 DOM 拆合算法。
3. 区分「代码文本 / 正文文本」
Dim x As <code>Integer</code>,代码标签内的专业关键词禁止翻译,正文正常翻译,需要标签黑名单过滤。
简单字典替换只适合无任何行内标签的纯文本页面;带富文本样式的 HTML 翻译,难度≈简易 HTML 编辑器 + 在线翻译引擎 + 持久化词典库三合一。
二、落地方案:三段式混合翻译(本地词典优先,新词在线翻译 + 自动落库 txt)
整体逻辑:优先本地词典整句 / 词组匹配→未命中的完整自然句在线谷歌翻译→翻译成功的中英词条自动追加写入字典 txt→DOM 原位回填译文,全程不改动任何标签、class、style 属性
分层逻辑
-
DOM 文本提取层(关键:合并被标签打断的连续文本块) 遍历 body,遇到连续穿插行内标签(b/span/i/u)包裹的零散 NavigableString,临时拼接成完整自然句,记录每一段原文对应的 DOM 节点、字符偏移位置 ;遇到 script/style/pre/code 直接跳过。例:拆分的 3 段碎片拼接完整英文原句,同时保存:
[(节点1,0,4),(节点2,0,6),(节点3,0,25)]字符坐标映射表,翻译后按坐标切分中文,回填到对应节点,原有加粗、颜色标签完全不动。 -
翻译调度层(本地优先,新词在线)
- 步骤 1:拼接完整英文先查本地
dict.txt字典,命中直接用本地译文; - 步骤 2:字典无匹配 → 调用 googletrans 在线整句翻译;
- 步骤 3:在线翻译成功 →
英文原句=中文译文自动追加写入 dict.txt(去重,避免重复堆积词条); - 步骤 4:翻译失败(接口限流 / 网络异常)保留原文不改动。
- **DOM 回填层(依托坐标拆分译文,原样保留所有样式标签)**根据第一步记录的文本块字符偏移,把翻译后的中文按原英文长度分段,逐个替换原 NavigableString,所有包围的 span、font、b 标签结构、样式属性原封不动。
三、精简可运行实现代码(自带字典持久化 + 碎片拼接整句翻译 + 在线兜底)
python
运行
import os
from bs4 import BeautifulSoup, NavigableString
from googletrans import Translator
# 配置
DICT_FILE = "trans_dict.txt"
SKIP_TAG = {"script","style","code","pre","kbd","samp","textarea"}
trans = Translator()
# 1. 加载本地词典,格式:英文=中文
def load_dict():
d = {}
if os.path.exists(DICT_FILE):
with open(DICT_FILE,"r",encoding="utf-8") as f:
for line in f:
line = line.strip()
if "=" in line:
eng,chn = line.split("=",1)
d[eng] = chn
# 初始内置词条,后续自动新增进txt
init_data = {
'Welcome':'欢迎','Table of contents':'目录','Getting Started':'开始使用'
}
for k,v in init_data.items():
if k not in d:
d[k]=v
return d
# 2. 新词追加写入字典
def save_new_word(eng,chn):
with open(DICT_FILE,"a",encoding="utf-8") as f:
f.write(f"{eng}={chn}\n")
# 全局词典
local_dict = load_dict()
# 收集连续被行内标签切割的文本,返回【完整句子,节点字符映射列表】
def collect_continuous_text(start_node):
text_parts = []
pos_map = []
curr_node = start_node
while True:
if isinstance(curr_node,NavigableString):
s = str(curr_node)
text_parts.append(s)
pos_map.append((curr_node, len(s)))
# 子节点遍历终止:遇到块状标签/空
if curr_node.next_sibling is None:
break
nxt = curr_node.next_sibling
if hasattr(nxt,"name") and nxt.name in SKIP_TAG:
break
curr_node = nxt
full_text = "".join(text_parts)
if not full_text.strip():
return None,None
return full_text,pos_map
# 翻译单句:本地优先,在线兜底
def trans_sentence(raw):
raw_strip = raw.strip()
if raw_strip in local_dict:
return local_dict[raw_strip]
try:
res = trans.translate(raw_strip,dest="zh-cn")
cn = res.text
# 新词条自动入库
local_dict[raw_strip]=cn
save_new_word(raw_strip,cn)
return cn
except Exception:
return raw
# 按原字符长度拆分译文,回填DOM
def split_replace(origin_posmap,trans_text):
idx = 0
for node,length in origin_posmap:
seg = trans_text[idx:idx+length]
idx += length
node.replace_with(seg)
# 递归遍历DOM,处理文本
def walk_node(elem):
if elem.name in SKIP_TAG:
return
childs = list(elem.contents)
i=0
while i<len(childs):
item = childs[i]
if isinstance(item,NavigableString):
full_txt,posmap = collect_continuous_text(item)
if full_txt and posmap:
cn_txt = trans_sentence(full_txt)
split_replace(posmap,cn_txt)
elif hasattr(item,"name"):
walk_node(item)
i +=1
def handle_html(path):
with open(path,"r",encoding="utf-8")as f:
soup = BeautifulSoup(f.read(),"html.parser")
if soup.html:soup.html["lang"]="zh-CN"
if soup.body:walk_node(soup.body)
with open(path,"w",encoding="utf-8")as f:
f.write(str(soup))
print(f"处理完成:{os.path.basename(path)}")
# 入口
if __name__=="__main__":
target = r"d:\360极速浏览器X下载\twinbasic-docs-offline\index.html"
handle_html(target)
四、使用说明 & 优化方向
- 字典自动管理 :首次运行生成
trans_dict.txt,在线翻译的所有新中英词组自动追加,越用本地词库越大,后续同句子不再联网; - 样式零破坏 :所有
<b><span style="color:#f00"><i>等行内标签完整保留,仅替换内部文字;块状标签 div/p/ul 结构不变; - 进阶优化可选
- 过滤过短字符(小于 3 个英文单词不触发在线翻译,减少接口消耗);
- 词典写入前查重,避免 txt 重复词条;
- 批量遍历整个文档目录下全部
.html; - 在线翻译备用接口(googletrans 失效切换 deepl / 有道)。
五、补充难度说明
- 该方案已经规避 90% 富文本翻译难题,剩余极端嵌套(标签交叉错位)是 HTML 翻译行业通用难点;
- 相比纯关键词替换,这套实现≈文本分词 + DOM 树解析 + 文本映射算法 + 持久化数据库 + 第三方翻译接口,复杂度确实提升数十倍,也是专业网页汉化工具的底层原理。