如何基于changedate元数据合并存在冲突的TMX翻译记忆文件
合并TMX翻译记忆文件并保留最新版本词条
你的核心问题在于当前代码只是简单拼接整个TMX元素,没有处理重复词条,也无法根据changedate判断并保留最新版本。咱们可以通过以源文本为唯一标识,对比时间戳的方式解决这个问题,具体实现步骤如下:
核心思路
- 遍历所有TMX文件,解析每个
<tu>条目:- 提取源语言(这里是ZH-CN)的
<seg>内容作为唯一标识 - 提取目标语言的翻译内容、
changeid和changedate属性
- 提取源语言(这里是ZH-CN)的
- 用字典存储每个源文本对应的最新条目:
- 将
changedate转换为可比较的时间对象 - 遇到相同源文本时,保留时间戳更新的条目
- 将
- 重新构建完整的TMX结构,将筛选后的
<tu>条目放入<body>中,保留原文件的<header>等元数据
完整实现代码
from xml.etree import ElementTree from datetime import datetime def parse_tu(tu_element): """解析单个tu元素,返回源文本、目标翻译信息和时间戳""" source_seg = None target_tuv = None # 遍历tu下的tuv元素 for tuv in tu_element.findall('tuv'): lang = tuv.attrib.get('lang') if lang == 'ZH-CN': # 提取源文本 seg = tuv.find('seg') if seg is not None: source_seg = seg.text.strip() if seg.text else '' elif lang == 'EN-US': # 提取目标翻译和元数据 seg = tuv.find('seg') if seg is not None: target_seg = seg.text.strip() if seg.text else '' changeid = tuv.attrib.get('changeid') changedate_str = tuv.attrib.get('changedate') # 解析时间戳:格式是YYYYMMDDTHHMMSSZ changedate = datetime.strptime(changedate_str, '%Y%m%dT%H%M%SZ') target_tuv = { 'seg': target_seg, 'changeid': changeid, 'changedate': changedate, 'changedate_str': changedate_str } return source_seg, target_tuv def build_tu(source_seg, target_info): """根据源文本和目标信息构建新的tu元素""" tu = ElementTree.Element('tu') # 构建源tuv source_tuv = ElementTree.Element('tuv', attrib={'lang': 'ZH-CN'}) source_seg_elem = ElementTree.Element('seg') source_seg_elem.text = source_seg source_tuv.append(source_seg_elem) tu.append(source_tuv) # 构建目标tuv target_tuv = ElementTree.Element('tuv', attrib={ 'lang': 'EN-US', 'changeid': target_info['changeid'], 'changedate': target_info['changedate_str'] }) target_seg_elem = ElementTree.Element('seg') target_seg_elem.text = target_info['seg'] target_tuv.append(target_seg_elem) tu.append(target_tuv) return tu def merge_tmx_files(files): # 存储每个源文本的最新条目,键是源文本,值是目标信息 tu_dict = {} # 记录第一个文件的header信息,用来生成最终的tmx结构 header = None tmx_version = None doctype = None for filename in files: tree = ElementTree.parse(filename) root = tree.getroot() # 提取header和版本信息(只取第一个文件的) if header is None: header = root.find('header') tmx_version = root.attrib.get('version') # 注意:ElementTree不会保留DOCTYPE,需要手动记录 with open(filename, 'r', encoding='utf-8') as f: for line in f: if '<!DOCTYPE' in line: doctype = line.strip() break # 遍历body下的所有tu元素 body = root.find('body') if body is not None: for tu in body.findall('tu'): source_seg, target_info = parse_tu(tu) if source_seg and target_info: # 检查字典中是否已有该源文本 if source_seg not in tu_dict: tu_dict[source_seg] = target_info else: # 比较时间戳,保留更新的 existing_date = tu_dict[source_seg]['changedate'] new_date = target_info['changedate'] if new_date > existing_date: tu_dict[source_seg] = target_info # 构建最终的TMX结构 if not tu_dict: return None # 创建根元素 tmx_root = ElementTree.Element('tmx', attrib={'version': tmx_version}) # 添加header if header is not None: tmx_root.append(header) # 创建body body = ElementTree.Element('body') # 将所有tu添加到body for source_seg in tu_dict: target_info = tu_dict[source_seg] tu = build_tu(source_seg, target_info) body.append(tu) tmx_root.append(body) # 生成XML字符串,注意DOCTYPE需要手动添加 xml_str = ElementTree.tostring(tmx_root, encoding='UTF-8').decode('UTF-8') # 插入DOCTYPE和XML声明 final_str = f'<?xml version="1.0" encoding="UTF-8"?>\n{doctype}\n{xml_str}' return final_str # 使用示例 if __name__ == '__main__': merged_result = merge_tmx_files(['file1.tmx', 'file2.tmx']) if merged_result: print(merged_result) # 也可以写入文件 # with open('merged.tmx', 'w', encoding='utf-8') as f: # f.write(merged_result)
关键细节说明
- 时间戳解析:
changedate格式是YYYYMMDDTHHMMSSZ,用datetime.strptime转换成可比较的时间对象,方便判断新旧版本 - 字典去重与更新:以源文本为键,确保每个源文本只保留最新的翻译版本
- DOCTYPE处理:ElementTree默认不会保留DOCTYPE声明,所以需要手动读取原始文件的DOCTYPE行并添加到最终输出中
- 结构还原:严格按照TMX1.1的结构构建新元素,确保输出格式与原文件完全一致
用这个代码处理你提供的两个示例文件,会得到你期望的输出结果——既合并了所有唯一词条,又自动保留了changedate更新的版本。
内容的提问来源于stack exchange,提问作者Jeremy
相关产品推荐
相关产品推荐

