mcpField
R
rtl-document-translation

rtl-document-translation

github.com/belumume文档处理4675 次安装6 周前更新
代码仓库

关于 rtl-document-translation

rtl-document-translation 是一项面向需要将 DOCX 业务文档转换为阿拉伯语、希伯来语等 RTL 语言的技能,重点保持原有结构与视觉样式。它采用分阶段分析、翻译、生成和校验流程,处理表格及嵌套内容。

主要功能

  1. 01分阶段文档转换流程
  2. 02保留表格与段落结构
  3. 03应用 RTL 文字与对齐格式
  4. 04检测并保留颜色样式
  5. 05处理嵌套表格内容
  6. 06生成结果校验与视觉比对

适用场景

  1. 01制作阿拉伯语财务 DOCX
  2. 02翻译含复杂表格的表单
  3. 03制作与英文版布局一致的 RTL 文档

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/belumume/claude-skills --skill rtl-document-translation

安装后在 Claude Code 中输入/rtl-document-translation确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
REFERENCE.md
examples/
translation_dictionary_template.json
utils/
create_translation.py
verify_document.py

详细介绍

RTL 文档翻译技能

将结构化的商务文档翻译成从右向左(RTL)语言,同时保持像素级精准的格式、颜色、表格结构和专业外观。

何时使用此技能

在用户请求以下操作时调用此技能:

  • 将 DOCX 文件翻译成阿拉伯语、希伯来语、乌尔都语或其他 RTL 语言
  • 保留文档的精确结构(表格、分节、格式)
  • 保持颜色、背景和视觉样式
  • 将商务/财务文档转换为 RTL 格式
  • 创建与英文原稿完全一致的 RTL 版本

请勿用于:

  • 简单文本翻译(直接使用翻译 API)
  • 从头创建新文档
  • 仅处理 PDF 的工作流(此技能适用于 DOCX)

核心方法

1. 分阶段处理(关键)

阶段 1:分析 → 阶段 2:翻译词典 → 阶段 3:文档生成 → 阶段 4:验证

切勿跳过分析直接生成文档。结构分析可以避免以下灾难性错误:

  • 将多行单元格拆分为多行
  • 遗漏表格尺寸
  • 分节方向不正确
2. RTL 格式(3 个层级)

RTL 文档需要设置三个不同的格式层级:

层级 1 - 文本方向:

paragraph.paragraph_format.bidi = True
run.font.rtl = True
run.font.complex_script = True

层级 2 - 文本对齐:

paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT

层级 3 - 布局方向: 对于数据/财务表格:保持列从左到右的顺序

  • 时间序列(第 1 个月、第 2 个月、第 3 个月……)从左向右排列
  • 行标签保持在与英文相同的位置
  • 只有单元格内的文本采用 RTL 方向

示例:月份标题应为:

[الشهر] [1] [2] [3] [4]  ← Correct (columns L→R, text RTL)
[4] [3] [2] [1] [الشهر]  ← Wrong (mirrored columns)

实现模式

模式 1:检测背景颜色

问题: 简单的属性访问会失败 解决方案: 使用 XML 遍历

from docx.oxml.ns import qn

def get_cell_background(cell):
    """Reliably extract cell background color"""
    tc = cell._element
    tcPr = tc.tcPr if hasattr(tc, 'tcPr') and tc.tcPr is not None else None

    if tcPr is None:
        return None

    # CRITICAL: Use findall(), not direct attribute access
    shd_list = tcPr.findall(qn('w:shd'))
    for shd in shd_list:
        fill = shd.get(qn('w:fill'))
        if fill and fill != 'auto':
            return fill.upper()

    return None

原因: tcPr.shading 无法始终正常工作。XML 遍历非常可靠。

模式 2:设置单元格背景
from docx.oxml import OxmlElement

def set_cell_background(cell, rgb_hex):
    """Set cell background color (e.g., 'CC0029' for red)"""
    tc = cell._element
    tcPr = tc.get_or_add_tcPr()

    # Remove existing shading
    for shd in tcPr.findall(qn('w:shd')):
        tcPr.remove(shd)

    # Add new shading
    shd = OxmlElement('w:shd')
    shd.set(qn('w:fill'), rgb_hex)
    tcPr.append(shd)
模式 3:引号标准化

问题: DOCX 文件包含弯引号(U+201C、U+201D),会导致词典查找失败

解决方案: 多轮标准化

def normalize_text(text):
    """Normalize quotes and unicode spaces for reliable matching"""
    # Convert curly quotes → straight quotes
    text = text.replace('\u201c', '"').replace('\u201d', '"')
    text = text.replace('\u2018', "'").replace('\u2019', "'")

    # Normalize unicode spaces → regular spaces
    text = re.sub(r'[\u2002\u2003\u2009\u200A\u00A0]+', ' ', text)

    return text.strip()
模式 4:多轮翻译匹配

问题: 空格差异、引号和格式会导致精确字符串匹配失败

解决方案: 渐进式回退策略

def translate_text(text, translation_dict):
    """Multi-pass translation with normalization fallbacks"""
    if not text or not text.strip():
        return text

    # Pass 1: Exact match
    if text in translation_dict:
        return translation_dict[text]

    # Pass 2: Stripped
    if text.strip() in translation_dict:
        return translation_dict[text.strip()]

    # Pass 3: Normalized quotes
    normalized_quotes = text.replace('\u201c', '"').replace('\u201d', '"')
    normalized_quotes = normalized_quotes.replace('\u2018', "'").replace('\u2019', "'")
    if normalized_quotes in translation_dict:
        return translation_dict[normalized_quotes]

    # Pass 4: Stripped + normalized
    if normalized_quotes.strip() in translation_dict:
        return translation_dict[normalized_quotes.strip()]

    # Pass 5: Unicode spaces
    cleaned = re.sub(r'[\u2002\u2003\u2009\u200A\u00A0]+', ' ', text).strip()
    if cleaned in translation_dict:
        return translation_dict[cleaned]

    # Pass 6: Combined (quotes + spaces)
    cleaned_quotes = re.sub(r'[\u2002\u2003\u2009\u200A\u00A0]+', ' ', normalized_quotes).strip()
    if cleaned_quotes in translation_dict:
        return translation_dict[cleaned_quotes]

    # Pass 7: Normalized whitespace (collapse multiple spaces)
    normalized_ws = ' '.join(text.split())
    if normalized_ws in translation_dict:
        return translation_dict[normalized_ws]

    # No match found - return as-is
    return text

成功率: 超过 95%;仅使用精确匹配时为 60%

模式 5:RTL 单元格格式
def apply_rtl_to_cell(cell, arabic_text, font_size=10, bold=False, text_color=None):
    """Apply complete RTL formatting to table cell"""
    # Clear cell
    cell.text = ''

    # Add paragraph with Arabic text
    paragraph = cell.paragraphs[0]
    run = paragraph.add_run(arabic_text)

    # RTL text direction (Level 1)
    paragraph.paragraph_format.bidi = True
    run.font.rtl = True
    run.font.complex_script = True

    # Right alignment (Level 2)
    paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT

    # Font settings
    run.font.name = 'Simplified Arabic'  # or 'Times New Roman' for formal docs
    run._element.rPr.rFonts.set(qn('w:ascii'), 'Simplified Arabic')
    run._element.rPr.rFonts.set(qn('w:hAnsi'), 'Simplified Arabic')
    run._element.rPr.rFonts.set(qn('w:cs'), 'Simplified Arabic')
    run.font.size = Pt(font_size)

    if bold:
        run.font.bold = True

    if text_color:
        run.font.color.rgb = RGBColor(*text_color)

    return cell
模式 6:自动更正深色背景上的白色文本

问题: 深色背景上的文本会变得不可见

解决方案: 自动检测并更正

def apply_colors_to_cell(cell, eng_cell, ar_text, font_size=10, bold=False):
    """Apply colors with auto-correction for visibility"""
    # Get background color
    bg_color = get_cell_background(eng_cell)

    # Get text color from English
    text_color = None
    if eng_cell.paragraphs and eng_cell.paragraphs[0].runs:
        for run in eng_cell.paragraphs[0].runs:
            if run.font.color and run.font.color.rgb:
                rgb = run.font.color.rgb
                text_color = (rgb[0], rgb[1], rgb[2])
                break

    # AUTO-CORRECTION: Set white text for dark backgrounds
    if bg_color and bg_color in ['CC0029', 'C00000', '000000']:  # Red/black
        text_color = (255, 255, 255)  # White

    # Apply formatting
    apply_rtl_to_cell(cell, ar_text, font_size, bold, text_color)

    # Set background
    if bg_color:
        set_cell_background(cell, bg_color)
模式 7:提取嵌套表格内容 ⭐

问题: cell.text 属性不会包含单元格内嵌套表格中的文本。这会导致包含表单、核对清单或复杂布局的单元格显示为空。

检测:

if cell.tables:
    print(f"Cell contains {len(cell.tables)} nested table(s)")

解决方案: 使用 cell.tables 属性提取嵌套表格中的内容

def extract_cell_content_with_nested_tables(cell):
    """
    Extract all text from a cell, including text from nested tables.

    Handles Word documents that use nested tables for:
    - Checklists with options
    - Forms with checkboxes
    - Complex multi-row cell layouts
    """
    text_parts = []

    # Get direct paragraph text (not inside nested tables)
    for para in cell.paragraphs:
        para_text = para.text.strip()
        if para_text:
            text_parts.append(para_text)

    # Get content from nested tables
    if cell.tables:
        for nested_table in cell.tables:
            for nested_row in nested_table.rows:
                # Extract text from first column only (skip checkbox/form columns)
                if nested_row.cells:
                    first_col_text = nested_row.cells[0].text.strip()
                    # Filter out checkbox characters
                    if first_col_text and first_col_text not in ['', '☐', '☑', '☒']:
                        text_parts.append(first_col_text)

    return '\n'.join(text_parts) if text_parts else ''

在翻译工作流中的用法:

# Instead of:
eng_text = eng_cell.text  # ❌ Misses nested table content

# Use:
eng_text = extract_cell_content_with_nested_tables(eng_cell)  # ✓ Gets all content
ar_text = translate_text(eng_text)

重要原因:

  • 政府表单通常使用嵌套表格制作复选框网格
  • 评估表单使用嵌套表格制作评分量表
  • 商务核对清单会将选项嵌入嵌套表格
  • 如果不处理,翻译后的文档中会出现空单元格

按文档类型推荐字体

文档类型推荐字体原因
财务/商务Simplified Arabic数字和表格的呈现效果更好
学术/正式Times New Roman传统字体,更适合段落
技术Arial Unicode MS字符支持范围广
避免使用Arial阿拉伯语呈现质量较差

完整工作流

步骤 1:结构分析
def analyze_document(docx_path):
    doc = Document(docx_path)

    structure = {
        'sections': [],
        'tables': [],
        'paragraphs': len(doc.paragraphs),
        'colors': {'text': {}, 'backgrounds': {}},
        'fonts': {}
    }

    # Analyze sections
    for idx, section in enumerate(doc.sections):
        structure['sections'].append({
            'index': idx,
            'orientation': 'portrait' if section.page_width < section.page_height else 'landscape',
            'width': section.page_width.inches,
            'height': section.page_height.inches
        })

    # Analyze tables
    for idx, table in enumerate(doc.tables):
        table_info = {
            'index': idx,
            'rows': len(table.rows),
            'cols': len(table.columns),
            'multiline_cells': []
        }

        # Detect multi-line cells
        for r_idx, row in enumerate(table.rows):
            for c_idx, cell in enumerate(row.cells):
                if '\n' in cell.text:
                    table_info['multiline_cells'].append({
                        'row': r_idx,
                        'col': c_idx,
                        'content': cell.text
                    })

        structure['tables'].append(table_info)

    return structure
步骤 2:创建翻译词典
def create_translation_dictionary(docx_files, target_language='arabic'):
    """Extract unique texts and create translation map"""
    unique_texts = set()

    for docx_path in docx_files:
        doc = Document(docx_path)

        # Extract from paragraphs
        for para in doc.paragraphs:
            if para.text.strip():
                unique_texts.add(para.text.strip())

        # Extract from tables
        for table in doc.tables:
            for row in table.rows:
                for cell in row.cells:
                    if cell.text.strip():
                        unique_texts.add(cell.text.strip())

    # Create translation map
    translations = {}
    for text in unique_texts:
        # Call translation API or load from file
        arabic_text = translate_via_api(text, target_language)
        translations[text] = arabic_text

        # Also add normalized versions
        normalized = normalize_text(text)
        if normalized != text:
            translations[normalized] = arabic_text

    return translations
步骤 3:生成文档

完整实现示例请参阅 REFERENCE.md。

步骤 4:验证
def verify_arabic_document(ar_docx_path, eng_docx_path, translation_dict):
    """Comprehensive verification checks"""
    ar_doc = Document(ar_docx_path)
    eng_doc = Document(eng_docx_path)

    results = {
        'structure': 'PASS',
        'alignment': 'PASS',
        'english_scan': 'PASS',
        'colors': 'PASS',
        'issues': []
    }

    # 1. Structure match
    if len(ar_doc.sections) != len(eng_doc.sections):
        results['structure'] = 'FAIL'
        results['issues'].append(f"Section count mismatch")

    if len(ar_doc.tables) != len(eng_doc.tables):
        results['structure'] = 'FAIL'
        results['issues'].append(f"Table count mismatch")

    # 2. Alignment check
    total_cells = 0
    right_aligned = 0
    for table in ar_doc.tables:
        for row in table.rows:
            for cell in row.cells:
                total_cells += 1
                if cell.paragraphs[0].alignment == WD_ALIGN_PARAGRAPH.RIGHT:
                    right_aligned += 1

    if right_aligned != total_cells:
        results['alignment'] = 'FAIL'
        results['issues'].append(f"Only {right_aligned}/{total_cells} cells right-aligned")

    # 3. English word scan
    allowed_english = get_allowed_english(translation_dict)
    unauthorized = scan_for_english(ar_doc, allowed_english)

    if unauthorized:
        results['english_scan'] = 'FAIL'
        results['issues'].extend([f"English found: {w}" for w in unauthorized])

    return results

常见问题与解决方案

问题 1:拆分多行单元格

错误做法:

# Treats "A\n\nEstimated costs" as multiple rows
lines = cell.text.split('\n')
for line in lines:
    new_row = table.add_row()  # ❌ Creates extra rows

正确做法:

# Preserves multi-line content in single cell
ar_cell.text = translate_text(eng_cell.text)  # ✓ Keeps \n intact
陷阱 2:部分翻译

错误:“التدفق النقدي forecast”(混合使用阿拉伯语和英语)

正确:“توقعات التدفق النقدي”(完全翻译)

**原因:**词典缺少复合短语 **解决方案:**提取完整短语,而不是逐词翻译

陷阱 3:忘记为新单元格设置 RTL

错误:

new_para = doc.add_paragraph(arabic_text)  # ❌ Missing RTL

正确:

new_para = doc.add_paragraph()
run = new_para.add_run(arabic_text)
new_para.paragraph_format.bidi = True
run.font.rtl = True
new_para.alignment = WD_ALIGN_PARAGRAPH.RIGHT  # ✓ Complete RTL
陷阱 4:未检查视觉效果

**问题:**自动检查通过,但视觉效果不正确

**解决方案:**务必生成对比图像:

# Convert to PDF then images
subprocess.run(['soffice', '--headless', '--convert-to', 'pdf', ar_docx])
subprocess.run(['pdftoppm', '-png', 'output.pdf', 'comparison'])

快速参考:核心函数

# 1. Get cell background
bg = get_cell_background(cell)

# 2. Set cell background
set_cell_background(cell, 'CC0029')

# 3. Normalize text
normalized = normalize_text(text)

# 4. Multi-pass translation
arabic = translate_text(english, translation_dict)

# 5. Apply RTL to cell
apply_rtl_to_cell(cell, arabic_text, font_size=10, bold=False)

# 6. Apply colors with auto-correction
apply_colors_to_cell(cell, eng_cell, ar_text)

# 7. Verify document
results = verify_arabic_document(ar_doc, eng_doc, trans_dict)

成功标准

在确认翻译完成前:

  • 结构完全一致(章节、表格、尺寸)
  • 所有文本均右对齐并采用 RTL 格式
  • 未发现未经授权的英语单词
  • 所有颜色和背景均已保留
  • 视觉对比显示布局一致
  • 多行单元格已保留(未拆分)
  • PDF 已成功生成

更多资源

请参阅 REFERENCE.md,了解:

  • 完整代码示例
  • 实际文档模板
  • 故障排除指南
  • 高级模式

评论

支持 Markdown