| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- import re
- import os
- import glob
- import fileinput
- import xml.etree.ElementTree as ET
- import pygsheets
- from optparse import OptionParser
- import logging
- logging.basicConfig(level=logging.DEBUG)
- def CDATA(text=None):
- element = ET.Element('![CDATA[')
- element.text = text
- return element
- ET._original_serialize_xml = ET._serialize_xml
- #CDATA特殊处理
- def _serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs):
- if 'name' in elem.attrib.keys() and elem.attrib['name'] in andorid_contian_cdata_keys:
- write("<{} name=\"{}\"><![CDATA[{}]]></{}>{}".format(elem.tag,elem.attrib['name'],elem.text,elem.tag,elem.tail))
- else:
- return ET._original_serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs)
- ET._serialize_xml = ET._serialize['xml'] = _serialize_xml
- andorid_root_directory = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir))
- ios_root_directory = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir))
- ios_languages = ['ar', 'en', 'hi', 'ta', 'te', 'th', 'tr', 'zh-Hans']
- #andorid_language = ['', '-ar', '-hi', '-ta', '-te', '-th', '-tr', '-zh']
- # andorid_language = ['', '-ar','-hi','-bn','-es','-in','-ms','-pt','-th','-tl','-tr','-ur','-vi','-zh','-zh-TW']ru kk ky tk tg uz
- andorid_language = ['','-zh', '-ar']
- #xml对CDATA解析有点问题, CDATA 数据需要在这里添加key手动处理
- andorid_contian_cdata_keys = ['room_high_potential_conversion_reward','ludo_exit_game_tip','message_follow',
- 'call_send_diamond','call_end_chat_get_diamonds','call_end_chat_get_coins','call_end_chat_un_get_diamonds','call_end_chat_un_get_coins',
- 'couple_love_house_no_couple_desc','game_rules_2','game_rocket_reward_record','message_follow',
- 'room_rank_list_title','wallet_diamond_balance','wallet_coin_balance','common_cp_level_not_reach','domino_exit_game_tip','ludo_load_game_go']
- def find_strings_files(root_dir,lan = "en"):
- translation_files = [f for f in glob.glob(os.path.join(root_dir, '**', lan + '.lproj', 'Localizable.strings'), recursive=True) if 'MJRefresh' not in f]
- return translation_files
- def find_xml_files(root_dir = andorid_root_directory,lan = ""):
- translation_files = glob.glob(os.path.join(root_dir, '**', 'res/values'+lan, 'strings.xml'), recursive=True)
- return translation_files
- def lan_of_path(path):
- parts = path.split('/')
- lan = parts[-2].strip('.lproj')
- return lan
- def convert_ios_strings(value):
- placeholder_pattern = r'%\d*\$?[sdf]'
- if len(re.findall(placeholder_pattern,value)) > 0:
- value = value.replace('%s', '%@')
- value = re.sub(r'%(\d+)\$s', r'%\1$@', value)
- return value
- def map_andorid_lan(lan):
- if lan == 'en':
- return ''
- elif lan == 'zh-Hans':
- return '-zh'
- else:
- return '-'+lan
- def map_sheet_lan(lan):
- if lan == 'zh-Hans':
- return 'zh'
- else:
- return lan
- def extract_xml_to_dic(lan = ''):
- count = 0
- translations = {}
- translation_files = find_xml_files(andorid_root_directory,lan)
- android_placeholder_pattern = r'%\d*\$?[sdf]'
- for fname in translation_files:
- print(f"Processing file: {fname}")
- tree = ET.parse(fname)
- root = tree.getroot()
- for child in root:
- key = child.attrib['name']
- value = child.text
- if len(re.findall(android_placeholder_pattern,value)) > 0:
- value = value.replace('%s', '%@')
- value = re.sub(r'%(\d+)\$s', r'%\1$@', value)
- translations[key] = value
- else:
- translations[key] = value
- return translations
- def read_into_table(filelist):
- table = {}
- with fileinput.input(files=filelist) as f:
- for line in f:
- if "=" in line:
- key, value = line.split("=", 1)
- table[key.strip()[1:-1]] = value.strip()[1:-2]
- return table
- def read_xml_into_table(file):
- table = {}
- with fileinput.input(files=[file]) as f:
- tree = ET.parse(file)
- root = tree.getroot()
- for child in root:
- key = child.attrib['name']
- value = child.text
- table[key] = value
- return table
- def read_xml_to_table(lan = ''):
- count = 0
- translations = {}
- translation_files = find_xml_files(andorid_root_directory,lan)
- print(f"Processing read_xml_to_table lan: {lan}")
- for fname in translation_files:
- tree = ET.parse(fname)
- root = tree.getroot()
- for child in root:
- key = child.attrib['name']
- value = child.text
- # print(f'key ==> {key}')
- translations[key] = value
- return translations
- def mergexml(lan = ""):
- # 创建一个 ElementTree 用于存储合并后的内容
- merged_tree = ET.ElementTree(ET.Element('resources'))
- # 遍历根目录及其子目录
- for root, dirs, files in os.walk(andorid_root_directory):
- for filename in files:
- if filename == 'strings.xml' and root.endswith("res/values"+lan):
- xml_file_path = os.path.join(root, filename)
- # print(xml_file_path)
- # 解析当前 XML 文件
- tree = ET.parse(xml_file_path)
- root_elem = tree.getroot()
- # 将当前 XML 文件的内容添加到合并后的 XML 文件中
- for elem in root_elem:
- merged_tree.getroot().append(elem)
- # 将合并后的内容写入总的 strings.xml 文件
- outfilename = 'merged_strings'+lan +'.xml'
- outfilepath = os.path.join('MergedXml', outfilename)
- merged_tree.write(outfilepath, encoding='utf-8', xml_declaration=True)
- print(f"{lan} 合并完成 \n")
- def findallxml():
- # 遍历根目录及其子目录
- for root, dirs, files in os.walk(andorid_root_directory):
- for filename in files:
- if filename == 'strings.xml' and "res/values" in root:
- # 找到 strings.xml 文件
- xml_file_path = os.path.join(root, filename)
- print(f"找到 strings.xml 文件:{xml_file_path}")
- # print(f'{filename}')
- def find_all_xml_files():
- for lan in andorid_language:
- find_xml_files(lan=lan)
- def merge_all_files():
- for lan in andorid_language:
- mergexml(lan=lan)
- def find_untrans_items():
- untransItems = {}
- files = find_strings_files(root_dir=ios_root_directory)
- for filename in files:
- en_tables = read_into_table(filename)
- ar_tables = read_into_table(filename.replace('en.lproj','ar.lproj'))
- result = set(en_tables.keys()) - set(ar_tables.keys())
- if len(result) > 0 :
- print(f'{filename} , untranslate keys: {result}')
- for key in result:
- print(f'{key} ==> {en_tables[key]}')
- untransItems[key] = en_tables[key]
- return untransItems
- def find_all_untrans_items():
- untransItems = {}
- files = find_strings_files(root_dir=ios_root_directory)
- for filename in files:
- en_tables = read_into_table(filename)
- ar_tables = read_into_table(filename.replace('en.lproj','ar.lproj'))
- result = set(en_tables.keys()) - set(ar_tables.keys())
- if len(result) > 0 :
- print(f'{filename} , untranslate keys: {result}')
- for key in result:
- print(f'{key} ==> {en_tables[key]}')
- untransItems[key] = en_tables[key]
- return untransItems
- def syn_translate_ios(transItems = {}):
- if len(transItems) > 0:
- files = find_strings_files(root_dir=ios_root_directory)
- for filename in files:
- en_tables = read_into_table(filename)
- processkeys = set(en_tables.keys()) & set(transItems.keys())
- if len(processkeys) > 0 :
- for lan in ios_languages:
- lanfile = filename.replace('en.lproj',f'{lan}.lproj')
- print(f'开始处理文件: {lanfile}')
- preparekey = list(processkeys)
- with open(lanfile, 'r', encoding='utf-8') as infile:
- lines = infile.readlines()
- with open(lanfile, 'w', encoding='utf-8') as outfile:
- for line in lines:
- if '=' in line:
- key, value = line.strip().split("=", 1)
- key = key.strip()[1:-1]
- value = value.strip()[1:-2]
- if key in transItems.keys():
- newvalue = convert_ios_strings(transItems[key][map_sheet_lan(lan)]).strip()
- if newvalue == '':
- newvalue = transItems[key]['en']
- if newvalue != value:
- modified_line = f'"{key}" = "{newvalue}";\n'
- outfile.write(modified_line)
- if key in preparekey:
- preparekey.remove(key)
- print(f'更新 ==>[{lan}:{key}] {value} ==> {newvalue}')
- else:
- outfile.write(line)
- if key in preparekey:
- preparekey.remove(key)
- else:
- outfile.write(line)
- for key in preparekey:
- newvalue = convert_ios_strings(transItems[key][map_sheet_lan(lan)]).strip()
- if newvalue == '':
- print(f'!!添加 ==>[{lan}:{key}] 翻译为空,默认使用英文翻译')
- newvalue = transItems[key]['en']
- newline = f'"{key}" = "{newvalue}";\n'
- outfile.write(newline)
- print(f'添加 ==>[{lan}:{key}] ==> {newvalue}')
- print(f'同步完成 \n')
- else:
- print(f'未找到英文翻译中的差异的 key \n')
- else:
- print('没有需要同步的表格数据')
- def syn_translate_andorid(transItems = {}):
- if len(transItems) > 0:
- files = find_xml_files(root_dir=andorid_root_directory)
- for filename in files:
- en_tables = read_xml_into_table(filename)
- processkeys = set(en_tables.keys()) & set(transItems.keys())
- if len(processkeys) > 0 :
- for lan in andorid_language:
- lan_key = lan.replace('-','')
- if lan_key == '':
- lan_key = 'en'
- if lan=='-zh-TW':
- lanfile = filename.replace('res/values',f'res/values-zh-rTW')
- lan_key = 'zh-TW'
- else :
- lanfile = filename.replace('res/values',f'res/values{lan}')
- # 检查目录是否存在
- lanfile_dir = os.path.dirname(lanfile)
- if not os.path.exists(lanfile_dir):
- os.makedirs(lanfile_dir) # 创建目录
- print(f"目录 {lanfile_dir} 已创建")
- # 检查文件是否存在
- if not os.path.exists(lanfile):
- with open(lanfile, 'w', encoding='utf-8') as f:
- f.write('<?xml version="1.0" encoding="utf-8"?>\n<resources>\n</resources>') # 可以初始化文件内容
- print(f"文件 {lanfile} 已创建")
- else:
- print(f"文件 {lanfile} 已存在")
- print(lan)
- print(lan_key)
- print(lanfile)
- print(f'开始处理文件: {lanfile}')
- preparekey = list(processkeys)
- tree = ET.parse(lanfile)
- root = tree.getroot()
- for child in root:
- key = child.attrib['name']
- value = child.text
- print(key)
- print(value)
- if key in transItems.keys():
- newvalue = str(transItems[key][lan_key]).strip()
- print(111)
- print(newvalue)
- if newvalue == '':
- newvalue = transItems[key]['en']
- if newvalue != value:
- child.text = newvalue
- print(f'更新 ==>[{lan}:{key}] {value} ==> {newvalue}')
- if key in preparekey:
- preparekey.remove(key)
- else:
- child.text = value
- for key in preparekey:
- newvalue = str(transItems[key][lan_key]).strip()
- if newvalue == '':
- print(f'!!添加 ==>[{lan}:{key}] 翻译为空,默认使用英文翻译')
- newvalue = transItems[key]['en']
- new_string_element = ET.Element('string')
- new_string_element.set('name', key)
- new_string_element.text = newvalue
- root.append(new_string_element)
- print(f'添加 ==>[{lan}:{key}] ==> {newvalue}')
- ET.indent(tree, ' ')
- tree.write(lanfile, encoding='utf-8',xml_declaration=False)
- #write("<?xml version='1.0' encoding='%s'?>\n" % (declared_encoding,))
- with open(lanfile,'r+') as f:
- file_data = f.read()
- f.seek(0, 0)
- f.write('<?xml version="1.0" encoding="utf-8"?>\n' + file_data)
- print(f'同步完成 \n')
- else:
- print('没有需要同步的表格数据')
- class syntransition:
- def __init__(self,fileName = 'Ludo Transaltion',export = False, platform = 1,title = ''):
- self.client = pygsheets.authorize(service_file = "localizable-key.json")
- self.sheet = self.client.open(fileName)
- self.platform = platform
- # 尝试查找工作表,如果找不到,则添加一个新工作表
- try:
- self.worksheet = self.sheet.worksheet_by_title(title)
- except pygsheets.exceptions.WorksheetNotFound:
- if export:
- # 如果工作表不存在,添加一个新工作表
- template_wks = self.sheet.worksheet_by_title('template')
- self.worksheet = self.sheet.add_worksheet(title=title, src_worksheet=template_wks)
- else:
- print('工作表不存在,请检查sheet名是否正确')
- def read_sheet(self):
- tables = {}
- for row in self.worksheet.get_all_records():
- key = 'i_key' if self.platform == 2 else 'a_key'
- trans_key = row[key]
- if trans_key.strip() != '':
- tables[trans_key] = row
- return tables
- def syn_translate(filename, title,platform,):
- #'消息设置页面'
- syn = syntransition(fileName=filename,title=title,platform=platform)
- translations = syn.read_sheet()
- print(translations)
- if platform == 1:
- syn_translate_andorid(translations)
- else:
- syn_translate_ios(translations)
- def addParser():
- parser = OptionParser()
- parser.add_option("-f", "--fileName",
- default="Ludo Transaltion",
- help="表文件名称",
- metavar="fileName")
- parser.add_option("-t", "--sheetTitle",
- help="google sheet表格名",
- metavar="sheetTitle")
- parser.add_option("-p", "--platform",
- default=1,
- help="平台(andorid: 1,ios :2 )",
- metavar="platform")
- (options, args) = parser.parse_args()
- print("options: %s, args: %s" % (options, args))
- return options
- def startConvert(options):
- filename = options.fileName
- sheetTitle = options.sheetTitle
- platform = options.platform
- print("Start converting")
- if platform != 1:
- platform = 2
- if sheetTitle is None:
- print("google sheet表格名不能为空")
- return
- syn_translate(filename=filename,title=sheetTitle,platform=1)
- if __name__ == '__main__':
- options = addParser()
- startConvert(options)
- #syn_translate(filename='Ludo Transaltion',title='ludo排行榜',platform=1)
- # 使用:表名如有空格,需要用''标识sheet表名,synxml.py -t 'ludo 排行榜'
- # synxml.py -t ludo排行榜
|