按域切分文本
This commit is contained in:
@@ -15,6 +15,9 @@ from docx.text.paragraph import Paragraph
|
|||||||
from docx.text.run import Run
|
from docx.text.run import Run
|
||||||
from docx.table import _Cell, Table
|
from docx.table import _Cell, Table
|
||||||
|
|
||||||
|
# 注意:根据最终方案,lxml 不再需要,已移除
|
||||||
|
# from lxml import etree
|
||||||
|
|
||||||
from docutranslate.agents.segments_agent import SegmentsTranslateAgentConfig, SegmentsTranslateAgent
|
from docutranslate.agents.segments_agent import SegmentsTranslateAgentConfig, SegmentsTranslateAgent
|
||||||
from docutranslate.ir.document import Document
|
from docutranslate.ir.document import Document
|
||||||
from docutranslate.translator.ai_translator.base import AiTranslatorConfig, AiTranslator
|
from docutranslate.translator.ai_translator.base import AiTranslatorConfig, AiTranslator
|
||||||
@@ -58,19 +61,21 @@ class DocxTranslator(AiTranslator):
|
|||||||
一个基于高级结构化解析的 .docx 文件翻译器。
|
一个基于高级结构化解析的 .docx 文件翻译器。
|
||||||
它能高精度保留样式,并正确处理正文、表格、页眉/脚、脚注/尾注、超链接和目录(TOC)等复杂元素。
|
它能高精度保留样式,并正确处理正文、表格、页眉/脚、脚注/尾注、超链接和目录(TOC)等复杂元素。
|
||||||
|
|
||||||
[v4.2 - 修复版]
|
[v5.3 - 语义切分修复版]
|
||||||
- 修复了对域代码(Fields)结果文本的错误跳过问题,确保目录条目可被翻译。
|
- 修复了 v5.2 方案中因过度切分格式变化文本(如 H₂O)导致翻译上下文丢失的问题。
|
||||||
- 新增了对结构化文档标签(Structured Document Tags, SDT)的递归解析,
|
- 废弃了基于 <w:rPr> 比较的切分逻辑,转而采用更稳健的语义边界切分。
|
||||||
确保由内容控件(如自动目录)包裹的内容可以被正确处理。
|
- 核心改动:在处理完一个域(Field)的结束标记(fldCharType="end")后强制刷新文本段,
|
||||||
|
这既能正确分离引用标记(如[1])与后续文本,防止格式污染,又能保持化学式等
|
||||||
|
含格式变化的连续文本的完整性。
|
||||||
|
|
||||||
|
[v5.1 - 遍历修复版]
|
||||||
|
- 重构了核心遍历函数 _traverse_container,使其能稳健处理所有类型的文本容器,
|
||||||
|
包括页眉 (header)、页脚 (footer)、脚注 (footnote) 和尾注 (endnote)。
|
||||||
|
|
||||||
[v5.0 - 增强版]
|
[v5.0 - 增强版]
|
||||||
- 引入了智能域处理状态机,精确识别并跳过 PAGEREF (页码) 和 SEQ (序号) 等不应翻译的动态域内容。
|
- 引入了智能域处理状态机,精确识别并跳过 PAGEREF (页码) 和 SEQ (序号) 等不应翻译的动态域内容。
|
||||||
- 优化了文本切分逻辑,解决了目录(TOC)和图表目录(TOF)条目被错误拆分为“标题”和“页码”两部分的问题。
|
- 优化了文本切分逻辑,解决了目录(TOC)和图表目录(TOF)条目被错误拆分为“标题”和“页码”两部分的问题。
|
||||||
- 根除了因复杂域处理不当导致的目录项重复翻译问题,确保每个条目只被提取和翻译一次。
|
- 根除了因复杂域处理不当导致的目录项重复翻译问题,确保每个条目只被提取和翻译一次。
|
||||||
|
|
||||||
[v5.1 - 遍历修复版]
|
|
||||||
- 重构了核心遍历函数 _traverse_container,使其能稳健处理所有类型的文本容器,
|
|
||||||
包括页眉 (header)、页脚 (footer)、脚注 (footnote) 和尾注 (endnote)。
|
|
||||||
"""
|
"""
|
||||||
IGNORED_TAGS = {
|
IGNORED_TAGS = {
|
||||||
qn('w:proofErr'), qn('w:lastRenderedPageBreak'), qn('w:bookmarkStart'),
|
qn('w:proofErr'), qn('w:lastRenderedPageBreak'), qn('w:bookmarkStart'),
|
||||||
@@ -80,8 +85,8 @@ class DocxTranslator(AiTranslator):
|
|||||||
RECURSIVE_CONTAINER_TAGS = {
|
RECURSIVE_CONTAINER_TAGS = {
|
||||||
qn('w:smartTag'), qn('w:sdtContent'), qn('w:hyperlink'),
|
qn('w:smartTag'), qn('w:sdtContent'), qn('w:hyperlink'),
|
||||||
}
|
}
|
||||||
# [v5.0] 定义不应翻译其结果的域指令
|
# [v5.0] 定义不应翻译其结果的域指令, [v5.3] 增加了 'REF'
|
||||||
SKIPPABLE_FIELD_INSTRUCTIONS = {'PAGEREF', 'SEQ', 'PAGE', 'NUMPAGES', 'DATE', 'TIME', 'SECTION'}
|
SKIPPABLE_FIELD_INSTRUCTIONS = {'PAGEREF', 'SEQ', 'PAGE', 'NUMPAGES', 'DATE', 'TIME', 'SECTION', 'REF'}
|
||||||
|
|
||||||
def __init__(self, config: DocxTranslatorConfig):
|
def __init__(self, config: DocxTranslatorConfig):
|
||||||
super().__init__(config=config)
|
super().__init__(config=config)
|
||||||
@@ -121,7 +126,7 @@ class DocxTranslator(AiTranslator):
|
|||||||
self._process_element_children(child, elements, texts, state)
|
self._process_element_children(child, elements, texts, state)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# --- [v5.0] 智能域处理逻辑 ---
|
# --- [v5.3] 智能域处理逻辑 ---
|
||||||
instr_text_element = child.find(qn('w:instrText')) if isinstance(child, CT_R) else None
|
instr_text_element = child.find(qn('w:instrText')) if isinstance(child, CT_R) else None
|
||||||
if instr_text_element is not None:
|
if instr_text_element is not None:
|
||||||
instr_text = instr_text_element.text.strip()
|
instr_text = instr_text_element.text.strip()
|
||||||
@@ -142,6 +147,10 @@ class DocxTranslator(AiTranslator):
|
|||||||
flush_segment()
|
flush_segment()
|
||||||
state['is_skipping_result'] = True
|
state['is_skipping_result'] = True
|
||||||
elif fld_type == 'end':
|
elif fld_type == 'end':
|
||||||
|
# ===== [v5.3] 关键改动 =====
|
||||||
|
# 在域结束后强制刷新,确保域结果(如[1])和后面的文本分开。
|
||||||
|
# 这就是我们需要的语义边界。
|
||||||
|
flush_segment()
|
||||||
state['is_in_skippable_field'] = False
|
state['is_in_skippable_field'] = False
|
||||||
state['is_skipping_result'] = False
|
state['is_skipping_result'] = False
|
||||||
continue
|
continue
|
||||||
@@ -155,6 +164,7 @@ class DocxTranslator(AiTranslator):
|
|||||||
if is_image_run(run) or is_formatting_only_run(run):
|
if is_image_run(run) or is_formatting_only_run(run):
|
||||||
flush_segment()
|
flush_segment()
|
||||||
else:
|
else:
|
||||||
|
# [v5.3] 移除了 v5.2 的 rPr 比较逻辑,允许 H₂O 合并
|
||||||
state['current_runs'].append(run)
|
state['current_runs'].append(run)
|
||||||
else:
|
else:
|
||||||
flush_segment()
|
flush_segment()
|
||||||
|
|||||||
Reference in New Issue
Block a user