网页翻译插件:DOM 选择与双语内容插入机制
摘要
网页翻译插件是打破网络语言壁垒的重要工具。其核心功能包括:(1) 从网页文档对象模型(DOM)中精准提取可翻译文本;(2) 向用户展示翻译内容。本文详细解析这些机制,重点探讨“双语模式”(同时显示原文与译文)下,如何通过追加新元素展示译文,同时避免改动原文DOM结构。
1. 选择可翻译的DOM元素
识别可翻译内容需遍历DOM、应用过滤规则并智能提取文本。
1.1 DOM树遍历
-
起点: 从
document.body
开始遍历,<head>
内容(如<title>
)通常单独处理或忽略。 - 递归迭代: 插件递归遍历起始元素及其所有子节点。
-
节点类型分析:
-
文本节点(
Node.nodeType === 3
): 主要翻译目标,提取其nodeValue
或textContent
。 -
元素节点(
Node.nodeType === 1
):-
排除非文本标签: 如
<script>
、<style>
、<noscript>
、<svg>
等,因其内容通常非用户可见文本。 -
翻译特定属性: 包括:
-
<input type="button/submit/reset">
的value
属性; -
<input>
、<textarea>
的placeholder
属性; -
<img>
的alt
属性; - 任何元素的
title
属性; - ARIA 属性(如
aria-label
、aria-placeholder
)。
-
- 递归深入: 对未排除的元素节点,继续遍历其子节点。
-
排除非文本标签: 如
-
文本节点(
1.2 过滤与排除规则
-
HTML标准: 遵循
translate="no"
属性,跳过标记元素及其子节点。 -
CSS类名/ID: 支持特定类名(如
notranslate
)或ID,供开发者标记无需翻译的内容(如品牌名、代码片段)。 -
可见性检测: 跳过不可见元素(如
display: none
),但因可见性可能动态变化,需谨慎处理。 -
可编辑内容:
<textarea>
或contenteditable="true"
的元素通常不自动翻译,以免干扰用户输入。
1.3 文本提取与聚合
- 批量处理: 收集多段文本后再翻译,避免逐一处理小片段,提升性能。
-
保留上下文: 合并相邻文本节点(即使被
<b>
、<i>
等内联标签分隔),为翻译引擎提供完整上下文,确保译文质量。 - 格式占位符: 高级插件可在翻译前将简单HTML标签替换为占位符,翻译后再恢复格式。
1.4 动态内容处理
- MutationObserver API: 监听DOM变化(如节点增删、文本或属性修改),实时扫描并翻译受影响区域。
1.5 特殊场景
-
<iframe>
: 需访问其独立文档上下文以翻译内容。 - Shadow DOM: 需穿透Shadow Root,提取并翻译Web组件内容。
2. 双语模式:插入译文内容
双语模式下,原文与译文同时展示。本节重点介绍策略1:在不包裹原文的情况下追加译文元素,以最小化对页面布局和功能的干扰。
2.1 策略概述
此策略保持原文DOM元素或文本节点不变,在其后或下方插入独立的新元素展示译文。
核心原则:
- 最小侵入: 不更改原文DOM结构。
- 清晰分离: 原文与译文作为独立DOM实体,便于样式管理和操作。
- 灵活呈现: 通过CSS控制原文与译文的视觉关系(如颜色、间距、换行)。
2.2 实现细节
-
定位原文: 识别已翻译的文本节点或元素。
-
判断原文元素类型: 优先检查CSS
display
属性,再根据标签名判断元素是行内还是块级。function isInlineElement(element) { if (!element || element.nodeType === 3) return true; // 文本节点视为行内 const computedStyle = window.getComputedStyle(element); const display = computedStyle.display; // 优先检查 CSS display 属性 if (display.includes('inline')) return true; if (display.includes('block') || display.includes('flex') || display.includes('grid')) return false; // Fallback 到标签名判断 const inlineTags = ['span', 'a', 'b', 'i', 'em', 'strong', 'small', 'code']; return inlineTags.includes(element.tagName.toLowerCase()); }
-
创建译文元素: 根据原文类型选择
<span>
(行内)或<div>
(块级)承载译文。const tag = isInlineElement(originalElement) ? 'span' : 'div'; let translatedElement = document.createElement(tag);
-
添加标识: 为译文元素设置CSS类名,用于:
- 样式控制: 应用独特视觉效果(如灰色、缩小字体)。
- 插件管理: 便于查找、更新或移除译文元素。
translatedElement.className = 'translated-text bilingual-segment';
-
填充译文: 将翻译文本设置为新元素内容。
translatedElement.textContent = '这是译文';
-
插入DOM:
-
情况1:原文为文本节点
在父元素内,译文元素作为兄弟节点紧随原文插入。-
原始DOM:
<p>Original text.</p>
-
双语DOM(行内):
<p>Original text.<span class="translated-text bilingual-segment">译文</span></p>
-
双语DOM(块级):
<p>Original text.<div class="translated-text bilingual-segment">译文</div></p>
-
样式控制: 根据元素类型调整CSS。
.translated-text.bilingual-segment { font-size: 0.9em; color: grey; margin-left: 10px; } .translated-text.bilingual-segment:not(span) { display: block; margin-top: 5px; }
-
换行处理: 块级时无需
<br>
,行内时可根据需要插入<br>
。
-
-
情况2:原文为元素文本内容
译文元素作为原文元素的子节点追加。-
原始DOM:
<h1>Original Title</h1>
-
双语DOM(块级示例):
<h1>Original Title<div class="translated-text bilingual-segment">原始标题</div></h1>
-
样式控制: 根据原文元素类型(通过
isInlineElement
判断)调整显示方式。
-
-
2.3 策略优势
- 低影响: 保留原始DOM结构,降低破坏CSS选择器或JavaScript事件的可能。
- 易恢复: 关闭双语模式时,只需移除特定类名元素。
- 清晰管理: 原文与译文分离,便于维护。
- 自适应布局: 优先基于CSS的行内/块级判断,确保译文元素自然融入页面渲染。
2.4 注意事项
- 布局控制: 复杂布局需依赖高级CSS实现并排或堆叠效果。
- 内联文本流: 在嵌套内联元素中,需谨慎处理译文插入位置以确保自然流动。
- 可访问性: 通过ARIA属性增强原文与译文的关系,提升辅助技术兼容性。
-
动态样式: CSS
display
属性可能因媒体查询或JavaScript动态变化,需考虑监听机制。
3. 结论
网页翻译插件通过智能DOM解析、精准过滤和审慎修改,实现可翻译内容的识别与呈现。双语模式下,追加译文元素的策略结合优先基于CSS的行内/块级判断,以最小侵入性平衡了内容展示与页面完整性,借助动态DOM操作和CSS渲染,确保高效、优雅的用户体验。
浏览 17 次 · 下载PDF