当前位置: 首页 > news >正文

提取apk中的各种语言翻译成表格,python脚本

import os
import csv
import re
from xml.etree import ElementTree as ET# 定义固定的语言顺序
LANGUAGE_ORDER = ['values-zh-rCN','values-zh-rTW','values',  # 基准语言'values-de','values-es','values-ru','values-fr','values-ar','values-it','values-nl','values-pt','values-tr','values-vi','values-pl','values-ko','values-ja','values-iw','values-el','values-th','values-hr','values-cs'
]def parse_lang_tag(dir_name):"""解析目录名,返回语言标签(不带区域)"""if dir_name == 'values':return 'base'# 去掉'values-'前缀parts = dir_name.split('-', 1)[1] if dir_name.startswith('values-') else dir_name# 分离语言代码和区域代码lang_parts = parts.split('-')lang_tag = lang_parts[0]# 如果包含区域代码(如 rCN),则保留语言标签if len(lang_parts) > 1 and lang_parts[1].startswith('r'):return lang_tagreturn partsdef extract_strings_from_xml(xml_path):"""从strings.xml文件中提取字符串资源"""try:tree = ET.parse(xml_path)root = tree.getroot()strings = {}for child in root:if child.tag == 'string' and 'name' in child.attrib:name = child.attrib['name']# 处理带HTML标签的文本内容text = child.text or ""if len(child) > 0:  # 如果有嵌套标签full_text = [child.text or ""]for elem in child:full_text.append(ET.tostring(elem, encoding='unicode'))if elem.tail:full_text.append(elem.tail)text = ''.join(full_text)strings[name] = textelif child.tag == 'plurals' and 'name' in child.attrib:# 处理复数形式name = child.attrib['name']plurals = {}for item in child:if item.tag == 'item' and 'quantity' in item.attrib:quantity = item.attrib['quantity']text = item.text or ""plurals[quantity] = textif plurals:strings[name] = f"[PLURALS] {str(plurals)}"return stringsexcept ET.ParseError:print(f"解析错误: {xml_path}")return {}except FileNotFoundError:print(f"文件未找到: {xml_path}")return {}except Exception as e:print(f"处理文件 {xml_path} 时出错: {str(e)}")return {}def find_all_translations(project_root):"""在项目中查找所有翻译资源"""translations = {}lang_tag_map = {}  # 语言标签 -> 目录列表的映射# 尝试定位res目录possible_res_dirs = [os.path.join(project_root, 'app', 'src', 'main', 'res'),os.path.join(project_root, 'app', 'src', 'debug', 'res'),os.path.join(project_root, 'app', 'src', 'release', 'res'),os.path.join(project_root, 'res')]res_dir = Nonefor dir_path in possible_res_dirs:if os.path.exists(dir_path):res_dir = dir_pathbreakif not res_dir:raise FileNotFoundError("找不到res目录,请确认项目结构")print(f"使用资源目录: {res_dir}")# 收集所有语言目录found_dirs = []for dir_name in os.listdir(res_dir):if dir_name.startswith('values'):strings_path = os.path.join(res_dir, dir_name, 'strings.xml')if os.path.exists(strings_path):found_dirs.append(dir_name)strings = extract_strings_from_xml(strings_path)if strings:  # 只添加有内容的翻译translations[dir_name] = strings# 构建语言标签映射lang_tag = parse_lang_tag(dir_name)if lang_tag not in lang_tag_map:lang_tag_map[lang_tag] = []lang_tag_map[lang_tag].append(dir_name)print(f"找到 {len(found_dirs)} 个语言目录")return translations, lang_tag_mapdef find_alternative_dir(translations, lang_tag_map, target_dir):"""寻找替代目录:如果目标目录不存在,查找同语言的区域变体"""# 如果目标目录存在,直接返回if target_dir in translations:return target_dir# 解析目标目录的语言标签target_lang = parse_lang_tag(target_dir)# 查找同语言的所有目录candidate_dirs = lang_tag_map.get(target_lang, [])# 优先选择与目标目录最接近的变体if candidate_dirs:# 按目录名长度排序(优先选择不带区域的)candidate_dirs.sort(key=lambda x: len(x))return candidate_dirs[0]return Nonedef generate_translation_table(translations, lang_tag_map):"""生成翻译表格,按照固定顺序排列语言列,支持区域变体替代"""# 收集所有唯一的字符串名称all_keys = set()for strings in translations.values():all_keys.update(strings.keys())sorted_keys = sorted(all_keys)# 准备表格数据table = []# 表头:String Name + 固定顺序的语言目录header = ['String Name'] + LANGUAGE_ORDERtable.append(header)# 表格内容for key in sorted_keys:row = [key]for target_dir in LANGUAGE_ORDER:# 查找实际使用的目录(可能使用区域变体替代)actual_dir = find_alternative_dir(translations, lang_tag_map, target_dir)if actual_dir and actual_dir in translations:trans_dict = translations[actual_dir]# 获取翻译文本,处理换行和特殊字符text = trans_dict.get(key, '')if isinstance(text, str):# 处理换行符和转义字符text = text.replace('\n', '\\n').replace('\r', '\\r').replace('"', '""')row.append(text)else:# 目录不存在,保留空列row.append('')table.append(row)return tabledef save_to_csv(table, output_file='translations.csv'):"""保存表格到CSV文件"""with open(output_file, 'w', newline='', encoding='utf-8') as f:writer = csv.writer(f)writer.writerows(table)print(f"已生成翻译表格: {os.path.abspath(output_file)}")return output_filedef print_summary(translations, lang_tag_map):"""打印统计摘要"""# 计算总字符串数量total_keys = set()for strings in translations.values():total_keys.update(strings.keys())print("\n统计信息:")print(f"- 找到的语言数量: {len(translations)}")print(f"- 提取的字符串数量: {len(total_keys)}")# 列出找到的语言目录状态print("\n语言目录状态:")for target_dir in LANGUAGE_ORDER:# 查找实际使用的目录actual_dir = find_alternative_dir(translations, lang_tag_map, target_dir)if actual_dir:count = len(translations[actual_dir])status = "✓"if actual_dir != target_dir:status = f"✓ (使用 {actual_dir} 替代)"print(f"  {status} {target_dir.ljust(15)} - {count} 个字符串")else:print(f"  ✗ {target_dir.ljust(15)} - 未找到")# 列出项目中存在的额外目录all_found_dirs = set(translations.keys())specified_dirs = set(LANGUAGE_ORDER)extra_dirs = all_found_dirs - specified_dirs# 过滤掉已用作替代的目录used_alternatives = set()for target_dir in LANGUAGE_ORDER:actual_dir = find_alternative_dir(translations, lang_tag_map, target_dir)if actual_dir and actual_dir != target_dir:used_alternatives.add(actual_dir)extra_dirs -= used_alternativesif extra_dirs:print("\n额外找到的语言目录(未包含在表格中):")for extra in sorted(extra_dirs):count = len(translations[extra])print(f"  - {extra.ljust(15)} - {count} 个字符串")def find_project_root():"""尝试自动定位项目根目录"""cwd = os.getcwd()# 检查当前目录是否有res或app/src/main/resif os.path.exists(os.path.join(cwd, 'res')) or \os.path.exists(os.path.join(cwd, 'app', 'src', 'main', 'res')):return cwd# 向上搜索三级目录for i in range(3):parent = os.path.dirname(cwd)if os.path.exists(os.path.join(parent, 'res')) or \os.path.exists(os.path.join(parent, 'app', 'src', 'main', 'res')):return parentcwd = parentreturn os.getcwd()  # 返回当前目录作为默认if __name__ == '__main__':print("Android 多语言资源提取工具 - 支持区域变体替代")print("=" * 60)# 自动定位项目根目录PROJECT_ROOT = find_project_root()print(f"项目根目录: {PROJECT_ROOT}")try:translations, lang_tag_map = find_all_translations(PROJECT_ROOT)if not translations:print("未找到任何翻译文件,请检查项目结构")exit()table = generate_translation_table(translations, lang_tag_map)output_file = save_to_csv(table)print_summary(translations, lang_tag_map)print(f"\n提示: 生成的CSV文件 '{output_file}' 可用Excel或文本编辑器打开")print("      表格列顺序已按照要求固定排序:")print("      values-zh-rCN, values-zh-rTW, values, values-de, values-es, ... values-cs")print("      缺失的语言目录保留空列")print("\n区域变体替代规则:")print("  - 如果指定的语言目录不存在(如 values-iw)")print("  - 但存在其区域变体(如 values-iw-rIL)")print("  - 则使用区域变体的内容作为替代")except Exception as e:print(f"\n错误: {str(e)}")print("请确保在Android项目的根目录运行此脚本")print("或者手动指定项目根目录路径")
http://www.dtcms.com/a/302421.html

相关文章:

  • 【图像处理】霍夫变换:霍夫变换原理、霍夫空间、霍夫直线、霍夫圆详解与代码示例
  • 小智源码分析——音频部分(二)
  • java设计模式 -【责任链模式】
  • 2025 DevOps工具生态全景解读:本土化服务与智能化演进成关键赛点
  • 电商项目DevOps一体化运维实战
  • Kafka 3.9.1的KRaft模式部署
  • AR远程协作系统设计:虚实融合场景下的设备维护操作指引界面
  • 【JSqlParser】sql解析器使用案例
  • 3. 卷积网络代码参数解读分析
  • 基于百度 iframe 框架与语音解析服务的数字人交互系统实现
  • 仓颉编程语言程序基本结构
  • dapp前端⾯试题
  • 数据库审计及安全管理的解决方案
  • OpenCV(05)直方图均衡化,模板匹配,霍夫变换,图像亮度变换,形态学变换
  • 虚拟地址空间:从概念到内存管理的底层逻辑
  • 572. 另一棵树的子树
  • PyTorch武侠演义 第二卷:高塔中的注意力秘境 第1章:残卷指引
  • 11. 若依参数验证 Validated
  • 基于Python和OpenGL的3D暴力摩托游戏完整开发实践
  • Codeforces Round 1039 (Div. 2)题解
  • 架构实战——互联网架构模板(“存储层”技术)
  • redis getshell的三种方法
  • LLM Landscape:2025年大语言模型概览
  • 软工八将:软件开发全流程核心角色体系解析
  • 四、计算机组成原理——第3章:存储系统
  • 分布式渲染效能探析:关键网络性能要素
  • 科技风杂志《科技风》杂志社科技风编辑部2025年第19期目录
  • RWA 正当红,是 DeFi 的终点、拐点,还是新起点?
  • 使用LlamaIndex将私有数据接入大模型
  • 红绿灯纵向距离的评估