阅读文本 / Reading text
This paper uses synthetic samples to demonstrate the processing method. The code has been verified using examples in an isolated environment; this does not guarantee effectiveness for all real files or products. Before processing important files, please retain the original copies and verify them against the boundaries and acceptance criteria outlined in this document.
Why does header translation always miss or duplicate?
When translating multi-chapter Word documents, a common issue is this: the main text has been perfectly replaced with the target language, yet the headers and footers still retain the original text; or, worse, in an attempt to fix this problem, the script forcibly iterates through all Section Header objects for translation, causing pages originally designed to inherit formatting from the previous section to inadvertently receive redundant content, thereby disrupting the original document’s layout inheritance logic. This is not a problem with the OCR or NMT engine, but rather stems from the way OpenXML handles…
is_linked_to_previous
Misunderstanding of the property.
In python-docx and the underlying XML structure, the header is not an independent entity on each page; rather, it is attached to the Section object. When a section’s header is in the “linked to the previous section” state, it contains no text definition of its own and merely holds a reference to the effective header of the preceding section. At this point, if one directly reads the paragraph text from that Header object, what is retrieved may be inherited content. If, without proper verification, translation is written directly into the document, a new header definition will be implicitly created, breaking the original inheritance chain. This not only results in wasted translation resources but also causes subsequent layout adjustments to lose their interdependence.
Understanding the inheritance trap of is_linked_to_previous
_Header.is_linked_to_previous
is the core switch for addressing this issue. When this attribute is
True
At that time, it indicates that the current section does not have an independent header definition, and its displayed content is entirely inherited recursively from the first parent section that has a definition. In newly created documents or in sections where no header has been explicitly set, this value defaults to
True
. Here, there is an counterintuitive behavior: even if the current section inherits content, as long as you have accessed
header.paragraphs
or attempt to modify the text, python-docx will automatically create an empty header definition for you and append
is_linked_to_previous
is set to
False
。
This means that the simple strategy of “iterate through all sections and translate the headers” is risky. For headers with link states, the correct engineering approach is not to translate them, but to skip them. Only when
is_linked_to_previous == False
Only then does the section have an independent source text that needs to be fed into the translation pipeline. If business requirements indeed mandate that a particular section begin using a different translated header from a certain point onward, it must first explicitly
is_linked_to_previous
is set to
False
, then perform content replacement, thereby simulating the “unlink” operation in Word’s UI.
synthesis example: correct and incorrect boundary handling
To validate the processing logic, we construct a set of synthetic samples containing three states. Please note that the following are synthetic examples for logical demonstration purposes and do not represent actual customer files. If you need to test real documents, please prepare a .docx file containing at least three section breaks, with a mix of “link to previous section” and independent headers, and ensure that the file is not protected by IRM or contains header content dynamically generated by VBA macros.
input setting:
1.
Section 0
: Independent header, with the text "Chapter One".
2.
Section 1
: Links to the previous section (
is_linked_to_previous=True
), with no independent definition.
3.
Section 2
: Independent header, with the text "Appendix".
Expected correct output:
- Section 0 The header is translated as "Chapter 1".
- Section 1 Remains linked and does not undergo translation; it displays the inherited "Chapter 1".
- Section 2 The header is translated as "Appendix".
error/boundary input and handling:
If the script ignores link status, it also executes for Section 1.
paragraph.text = translate(...)
, then Section 1 will generate a new header definition. Although it may appear visually correct for the time being, the document structure has already been compromised. An even more severe edge case is an empty document or a document consisting entirely of links: if all sections are
True
and with no definition, accessing paragraphs will trigger creation. The safety code must check the boolean flag before reading or writing, and make defensive judgments for cases of “fully linked but without a source,” rather than assigning values blindly.
Python implementation of secure header extraction and link breaking
The following code demonstrates how to construct samples in memory and safely handle the header translation logic. This example relies on
python-docx
library, please run it in your own locked dependency environment; the version must be compatible with API 0.8.11 or higher.
Note: This code is for educational demonstration purposes only and does not implement full Run-level format preservation or domain-code protection; it should not be used directly in a production environment.
from docx import Document
from docx.shared import Pt
def safe_translate_headers(doc, translate_fn):
"""
安全翻译页眉:仅处理独立定义的页眉,保留继承关系。
translate_fn: 接受字符串返回译文的函数
"""
processed_count = 0
for i, section in enumerate(doc.sections):
header = section.header
# 核心守卫:仅当存在独立定义时才处理
if header.is_linked_to_previous:
print(f"Section {i}: Linked, skipping to preserve inheritance.")
continue
# 此时才安全访问 paragraphs,不会意外创建新定义
for para in header.paragraphs:
original_text = para.text.strip()
if original_text:
new_text = translate_fn(original_text)
# 安全替换:保留run格式,避免直接赋值para.text
# 实际生产环境应增加域代码(Field)检测与跳过逻辑
full_text = ''.join(run.text for run in para.runs)
if full_text.strip() == original_text:
# 简单场景:文本完全由runs组成
for run in para.runs:
run.text = ""
if para.runs:
para.runs[0].text = new_text
else:
para.add_run(new_text)
else:
# 复杂场景:包含域代码或其他元素,此处仅作标记
print(f"Warning: Section {i} contains complex elements, manual review needed.")
continue
processed_count += 1
print(f"Section {i}: Translated '{original_text}' -> '{new_text}'")
return processed_count
# --- 合成示例构建与断言 ---
doc = Document()
# Sec 0: 独立页眉
doc.sections[0].header.is_linked_to_previous = False
doc.sections[0].header.paragraphs[0].text = "Original Title"
# Sec 1: 添加新节并保持链接(默认即为True)
new_section = doc.add_section()
assert new_section.header.is_linked_to_previous is True
# Sec 2: 添加新节并断开链接
appendix_sec = doc.add_section()
appendix_sec.header.is_linked_to_previous = False
appendix_sec.header.paragraphs[0].text = "Appendix Data"
# 模拟翻译函数
mock_translate = lambda x: f"[ZH]{x}"
count = safe_translate_headers(doc, mock_translate)
# 验收断言
assert count == 2, "Should only translate 2 independent headers"
assert doc.sections[1].header.is_linked_to_previous is True, "Link must be preserved"
assert "[ZH]Original Title" in doc.sections[0].header.paragraphs[0].text
print("Logic validation passed.")
Acceptance criteria and manual review red lines
“No errors” at the code level does not equate to delivery qualification. Header translation involves layout semantics and requires establishing an acceptance checklist distinct from that of the main text. The table below lists the key checkpoints after automated processing, clearly distinguishing between code demonstration results and end-to-end delivery standards.
check dimension
expected observation
processing decision
Note
inheritance integrity
link section did not generate a new XML node
✅ Pass / ❌ Rework
decompress the docx file and check whether the number of word/header*.xml files has increased abnormally
Translation length adaptation
The translation does not overflow the page margins or obscure the main text
⚠️ Manual adjustment of font size/tab stops
code cannot perceive visual rendering results and requires manual review
domain code retains
Page numbers, dates, and other fields have not been translated
✅ Through / ❌ Fixed by
translates only the plain text Run, skipping FieldRun
odd/even pages/front page
special header type was correctly identified
⚠️ Manual review
python-docx has limited support for even_page_header and similar features
Blank Header
The deliberately blank section was mistakenly filled with content
❌ Rollback
must be evaluated in context to determine whether it represents the intended design
Scope of non-applicability:
The above logic applies only to standard documents with continuous section breaks. For documents containing complex nested table headers, header content dynamically generated by VBA macros, or those protected by IRM, automated scripts should proactively skip processing and flag them for manual review. Never equate “the code runs” with “all edge cases have been covered.”
References and Next Steps
python-docx: Working with Headers and Footers
uses this checklist for document translation
Word translations often blur the distinction between content issues and formatting problems. Leave checks for styles, headers, tables, and other elements to the delivery checklist, so reviewers know where to look.
OmniDoc (product entry point)
provides an entry point for translating multi-format documents, and the official website highlights its OCR, terminology database, and bilingual alignment features. For teams that wish to start processing directly from files, this is a path worth evaluating with real-world samples. Please use de-identified files that you are authorized to upload, and verify the results against the checklist provided in this document; we do not claim that OmniDoc utilizes the open-source libraries or internal algorithms referenced herein, nor do we substitute examples for product performance testing.
- 1 -
Updated 2026-10-09