#!/usr/bin/env python3 #metadata_xml2po.py #This shell has two work modes: # - Reads a folder and its subfolders and generates a .po file with all the descriptions, summaries..etc.. founded on the metainfo folders # - Updates the xml files of a folder (and its subfolders) with a given po import os import sys import xml.etree.ElementTree as ET import polib as PO def _debug(msg): global dbg if dbg==1: print(str(msg)) def usage_help(): print ("Usage:") print (sys.argv[0]+" --export|-e path_to_dirs" ) print (sys.argv[0]+" --import|-i path_to_dirs path_to_po" ) print("Options:") print("\t --debug|-d -> Enables debug mode") print("\t --xmlfix|-x -> When exporting fixes the original texts from xml files replacing \" with \' and \\n with spaces") print("\t --suffix|-s [suffix] -> Appends \"suffix\" to generated files") print("") exit(1) #def usage_help def parse_arguments(): global mode global wrkDir global poFile global suffix global xmlFix sfx=0 argList={"--export":"export","-e":"export","--import":"import","-i":"import","-d":"debug","--debug":"debug","--suffix":"suffix","-s":"suffix","-x":"xmlfix","--xmlfix":"correct"} args=sys.argv[1:] for arg in args: if sfx: suffix=arg sfx=0 elif arg in argList.keys(): if argList[arg]=='debug': global dbg dbg=1 _debug("Debug enabled") elif argList[arg]=='suffix': sfx=1 elif argList[arg]=='xmlfix': xmlFix=1 else: mode=argList[arg] elif os.path.isdir(arg): wrkDir=arg elif os.path.isfile(arg): poFile=arg if ((not mode or not wrkDir) or (mode=="import" and not poFile)): usage_help() #def parse_arguments def get_package_dirs(path): candidatePaths=[] for basedir, dirs, files in os.walk(path): for subdir in dirs: if subdir.endswith("metainfo") and "fuentes/llx-resources" in basedir: _debug(("Adding %s to listdir")%(basedir+'/'+subdir)) #Look for the xml candidatePaths.append(basedir+"/"+subdir) return candidatePaths #def get_package_dirs def generate_tmp_po(candidatePaths): global xmlFix for path in candidatePaths: dirContent = os.listdir(path) for xmlFile in dirContent: if not xmlFile.endswith(".xml"): continue try: xml = ET.parse(path+'/'+xmlFile) xmlRoot=xml.getroot() tagList=['name','summary','description'] for tag in tagList: for element in xmlRoot.iter(tag): if tag=='description': for xmlDesc in element: if not (xmlDesc.attrib): msgid=str(xmlDesc.text).replace("\"","\'") msgid=str(msgid).replace("\n"," ") xmlDesc.text=msgid msgIdList.append(msgid) else: save_locale(xmlDesc.attrib['{http://www.w3.org/XML/1998/namespace}lang'],msgid,xmlDesc.text) else: if not (element.attrib): msgid=str(element.text).replace("\"","\'") msgid=str(msgid).replace("\n"," ") element.text=msgid msgIdList.append(msgid) else: save_locale(element.attrib['{http://www.w3.org/XML/1998/namespace}lang'],msgid,element.text) #Fixes the xml replacing " with ' and \n with spaces if xmlFix: _debug(("Attempting to fix the xmlFile %s")%(xmlFile)) xml.write(path+'/'+xmlFile) except Exception as e: print(str(e)) print (xmlFile) #def generate_tmp_po def save_locale(locale,msgid,msgstr=None): poString=str(msgstr).replace("\"","\'") poString=poString.replace("\n"," ") if "valencia" in locale or "qcv" in locale: locale="ca_ES.UTF-8@valencia" if locale in poDict.keys(): poDict[locale].update({msgid:poString}) else: poDict[locale]={msgid:poString} localeSet.append(locale) #def save_locale def print_locale(outputFile): global suffix for msgId in msgIdList: #Open locale po and append the info for localization in localeSet: f=open(outputFile+"_"+localization+".po"+suffix,'a') if f.tell()==0: f.write("msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=utf-8\"\n") f.write("msgid \""+ msgId+"\"\n") if msgId in poDict[localization]: f.write("msgstr \""+poDict[localization][msgId]+"\"\n") else: f.write("msgstr \"\"\n") f.close #def print_locale def get_locale_from_po(poFile): locale=os.path.basename(poFile) locale=locale.replace('catalogue_','') locale=locale.replace('.po','') if "valencia" in locale or "qcv" in locale: locale="ca_ES.UTF-8@valencia" _debug("Locale set to "+locale) return(locale) #def get_locale_from_po def load_po(poFile,locale): global poDict global fileEncoding fileEncoding=PO.detect_encoding(poFile) poDict={} _debug(("Loading po %s with %s encoding")%(poFile,fileEncoding)) po = PO.pofile(poFile,encoding=fileEncoding) for entry in po: if entry.msgid in poDict.keys(): poDict[entry.msgid].update({locale:entry.msgstr}) else: poDict[entry.msgid]={locale:entry.msgstr} #def load_po def get_translations(msgid): translationDict={} _debug("Searching translations for " + msgid) if msgid in poDict.keys(): translationDict=poDict[msgid].copy() _debug("Get "+str(translationDict)) return translationDict #def get_translations def merge_translations(candidatePaths,locale): global suffix for path in candidatePaths: dirContent = os.listdir(path) for xmlFile in dirContent: if not xmlFile.endswith(".xml"): continue xml = ET.parse(path+'/'+xmlFile) xmlRoot=xml.getroot() tagList=['name','summary','description'] for tag in tagList: for element in xmlRoot.iter(tag): if tag=='description': #1st loop, get the msgid for xmlDesc in element: if not (xmlDesc.attrib): translationDict=get_translated_msgid(xmlDesc) break #2dn loop, modify translations for descriptions for xmlDesc in element: if not (xmlDesc.attrib): continue modify_translation(xmlDesc,translationDict) #Generate new translations for locale,translation in translationDict.items(): add_new_translation(element,'p',translation) else: if not (element.attrib): translationDict=get_translated_msgid(element) else: modify_translation(element,translationDict) #Generate new translations for locale,translation in translationDict.items(): if tag=='description': continue add_new_translation(xmlRoot,tag,translation) xml.write(path+'/'+xmlFile+suffix,encoding=fileEncoding,xml_declaration=True) def get_translated_msgid(element): msgid=str(element.text).replace("\"","\'") msgid=str(msgid).replace("\n"," ") translationDict=get_translations(msgid) return translationDict #def get_translated_msgid def modify_translation(element,translationDict): _debug(("Modifying %s translation")%(locale)) translation=element.text localeTrans=element.attrib['{http://www.w3.org/XML/1998/namespace}lang'] if localeTrans in translationDict.keys(): translation=translationDict[localeTrans] if translation: element.text=translation del translationDict[localeTrans] return (translation) #def modify_translation def add_new_translation(element,tag,text): if text!='': newDesc=ET.SubElement(element, tag) newDesc.tail="\n" newDesc.set('xml:lang',locale) newDesc.text=text _debug(("Added new tag %s with %s translation")%(tag,locale)) #def add_new_translation #### MAIN PROGRAM #### dbg=0 xmlFix=0 suffix='' fileEncoding='' mode='' wrkDir='' poFile='' parse_arguments() print(("Analyzing %s for action %s") % (wrkDir,mode)) poDict={} pathList=get_package_dirs(wrkDir) if mode=='export': localeSet=[] msgIdList=[] outputFile="catalogue" generate_tmp_po(pathList) localeSet=set(localeSet) print_locale(outputFile) print("Po files generated for "+str(localeSet)) else: locale=get_locale_from_po(poFile) load_po(poFile,locale) merge_translations(pathList,locale) print("Updated metadata xml in "+str(pathList))