1*b1cdbd2cSJim Jagielski#************************************************************** 2*b1cdbd2cSJim Jagielski# 3*b1cdbd2cSJim Jagielski# Licensed to the Apache Software Foundation (ASF) under one 4*b1cdbd2cSJim Jagielski# or more contributor license agreements. See the NOTICE file 5*b1cdbd2cSJim Jagielski# distributed with this work for additional information 6*b1cdbd2cSJim Jagielski# regarding copyright ownership. The ASF licenses this file 7*b1cdbd2cSJim Jagielski# to you under the Apache License, Version 2.0 (the 8*b1cdbd2cSJim Jagielski# "License"); you may not use this file except in compliance 9*b1cdbd2cSJim Jagielski# with the License. You may obtain a copy of the License at 10*b1cdbd2cSJim Jagielski# 11*b1cdbd2cSJim Jagielski# http://www.apache.org/licenses/LICENSE-2.0 12*b1cdbd2cSJim Jagielski# 13*b1cdbd2cSJim Jagielski# Unless required by applicable law or agreed to in writing, 14*b1cdbd2cSJim Jagielski# software distributed under the License is distributed on an 15*b1cdbd2cSJim Jagielski# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*b1cdbd2cSJim Jagielski# KIND, either express or implied. See the License for the 17*b1cdbd2cSJim Jagielski# specific language governing permissions and limitations 18*b1cdbd2cSJim Jagielski# under the License. 19*b1cdbd2cSJim Jagielski# 20*b1cdbd2cSJim Jagielski#************************************************************** 21*b1cdbd2cSJim Jagielski 22*b1cdbd2cSJim Jagielskifrom optparse import OptionParser 23*b1cdbd2cSJim Jagielskifrom sdf import SdfData 24*b1cdbd2cSJim Jagielskifrom pseudo import PseudoSet 25*b1cdbd2cSJim Jagielski 26*b1cdbd2cSJim Jagielskiimport sys 27*b1cdbd2cSJim Jagielskiimport os 28*b1cdbd2cSJim Jagielskiimport shutil 29*b1cdbd2cSJim Jagielski 30*b1cdbd2cSJim Jagielskiclass AbstractL10nTool: 31*b1cdbd2cSJim Jagielski _options = {} 32*b1cdbd2cSJim Jagielski _args = "" 33*b1cdbd2cSJim Jagielski _resource_type = "" 34*b1cdbd2cSJim Jagielski _source_language = "en-US" 35*b1cdbd2cSJim Jagielski 36*b1cdbd2cSJim Jagielski ##### Implement these abstract methods 37*b1cdbd2cSJim Jagielski 38*b1cdbd2cSJim Jagielski ##### Nameing scheme for the output files 39*b1cdbd2cSJim Jagielski def get_outputfile_format_str(self): 40*b1cdbd2cSJim Jagielski # filename,fileNoExt,language,extension,pathPrefix,pathPostFix,path 41*b1cdbd2cSJim Jagielski #return "{path}/{fileNoExt}_{language}.{extension}" 42*b1cdbd2cSJim Jagielski return self._options.pattern 43*b1cdbd2cSJim Jagielski 44*b1cdbd2cSJim Jagielski ################################# Merge single files ########################################### 45*b1cdbd2cSJim Jagielski 46*b1cdbd2cSJim Jagielski ##### Merge a single file 47*b1cdbd2cSJim Jagielski def merge_file(self, inputfilename, outputfilename, parsed_file_ref, lang, is_forced_lang, sdfdata): 48*b1cdbd2cSJim Jagielski pass 49*b1cdbd2cSJim Jagielski 50*b1cdbd2cSJim Jagielski ##### Helper for parse-once-use-often like parsing a xml file is needed implement it here 51*b1cdbd2cSJim Jagielski def parse_file(self, filename): 52*b1cdbd2cSJim Jagielski return None 53*b1cdbd2cSJim Jagielski 54*b1cdbd2cSJim Jagielski ################### Merge one big file containing all strings in all languages ################# 55*b1cdbd2cSJim Jagielski def merge_one_big_file(self, inputfile, outputfilename, parsed_file_ref, lang, sdfdata): 56*b1cdbd2cSJim Jagielski pass 57*b1cdbd2cSJim Jagielski 58*b1cdbd2cSJim Jagielski ################### Extract a single File ###################################################### 59*b1cdbd2cSJim Jagielski def extract_file(self, inputfile): 60*b1cdbd2cSJim Jagielski pass 61*b1cdbd2cSJim Jagielski 62*b1cdbd2cSJim Jagielski ################################################################################################ 63*b1cdbd2cSJim Jagielski 64*b1cdbd2cSJim Jagielski def format_outputfile(self, filename, language): 65*b1cdbd2cSJim Jagielski extension = filename[filename.rfind('.')+1:] 66*b1cdbd2cSJim Jagielski file = filename[:filename.rfind('.')] 67*b1cdbd2cSJim Jagielski # Python 2.3.x friendly 68*b1cdbd2cSJim Jagielski return self.get_outputfile_format_str().replace('[', '%(').replace(']',')s') % \ 69*b1cdbd2cSJim Jagielski { 'filename': filename, 'fileNoExt': file, 'language': language, 'extension': extension, 'path_prefix': self._options.path_prefix, 70*b1cdbd2cSJim Jagielski 'path_postfix': self._options.path_postfix, 'path': self.get_path() } 71*b1cdbd2cSJim Jagielski 72*b1cdbd2cSJim Jagielski #return self.get_outputfile_format_str().replace('[', '{').replace(']','}').format( 73*b1cdbd2cSJim Jagielski # filename=filename, fileNoExt=file, language=language, extension=extension, path_prefix=self._options.path_prefix, 74*b1cdbd2cSJim Jagielski # path_postfix=self._options.path_postfix, path=self.get_path()) 75*b1cdbd2cSJim Jagielski 76*b1cdbd2cSJim Jagielski def get_path(self): 77*b1cdbd2cSJim Jagielski if self._options.outputfile.find('/') == -1: 78*b1cdbd2cSJim Jagielski return "" 79*b1cdbd2cSJim Jagielski else: 80*b1cdbd2cSJim Jagielski return self._options.outputfile[:self._options.outputfile.rfind('/')] 81*b1cdbd2cSJim Jagielski 82*b1cdbd2cSJim Jagielski def merge(self, sdfdata): 83*b1cdbd2cSJim Jagielski langset,forcedset, foundset = PseudoSet(), PseudoSet() , PseudoSet() 84*b1cdbd2cSJim Jagielski 85*b1cdbd2cSJim Jagielski if self._options.languages: 86*b1cdbd2cSJim Jagielski langset = PseudoSet(self._options.languages) 87*b1cdbd2cSJim Jagielski if self._options.forcedlanguages: 88*b1cdbd2cSJim Jagielski forcedset = PseudoSet(self._options.forcedlanguages) 89*b1cdbd2cSJim Jagielski if sdfdata.get_languages_found_in_sdf(): 90*b1cdbd2cSJim Jagielski foundset = sdfdata.get_languages_found_in_sdf() 91*b1cdbd2cSJim Jagielski 92*b1cdbd2cSJim Jagielski if self.has_multi_inputfiles(): 93*b1cdbd2cSJim Jagielski filelist = self.read_inputfile_list() 94*b1cdbd2cSJim Jagielski else: 95*b1cdbd2cSJim Jagielski filelist = self._options.inputfile 96*b1cdbd2cSJim Jagielski 97*b1cdbd2cSJim Jagielski for inputfile in filelist: 98*b1cdbd2cSJim Jagielski ref = self.parse_file(inputfile) 99*b1cdbd2cSJim Jagielski # Don't write that files if there is no l10n present 100*b1cdbd2cSJim Jagielski if ((langset & foundset) - forcedset): # all langs given and found in sdf without enforced 101*b1cdbd2cSJim Jagielski [self.merge_file(inputfile,self.format_outputfile(inputfile, lang), ref, lang, False, sdfdata) for lang in ((langset & foundset) - forcedset)] 102*b1cdbd2cSJim Jagielski # Always write those files even if there is no l10n available 103*b1cdbd2cSJim Jagielski if forcedset: # all enforced langs 104*b1cdbd2cSJim Jagielski [self.merge_file(inputfile, self.format_outputfile(inputfile, lang), ref, lang, True, sdfdata) for lang in forcedset] 105*b1cdbd2cSJim Jagielski # In case a big file have to be written 106*b1cdbd2cSJim Jagielski if ((langset & foundset) | forcedset): # all langs given ,found in sdf and enforced ones 107*b1cdbd2cSJim Jagielski self.merge_one_big_file(inputfile, self.format_outputfile(inputfile, lang), ref, ((langset & foundset) | forcedset), sdfdata) 108*b1cdbd2cSJim Jagielski 109*b1cdbd2cSJim Jagielski def has_multi_inputfiles(self): 110*b1cdbd2cSJim Jagielski return self._options.inputfile[0] == '@' 111*b1cdbd2cSJim Jagielski 112*b1cdbd2cSJim Jagielski def copy_file(self, inputfilename, outputfilename): 113*b1cdbd2cSJim Jagielski try: 114*b1cdbd2cSJim Jagielski os.remove(outputfilename) 115*b1cdbd2cSJim Jagielski except: 116*b1cdbd2cSJim Jagielski pass 117*b1cdbd2cSJim Jagielski 118*b1cdbd2cSJim Jagielski try: 119*b1cdbd2cSJim Jagielski os.remove(outputfilename) 120*b1cdbd2cSJim Jagielski except: 121*b1cdbd2cSJim Jagielski pass 122*b1cdbd2cSJim Jagielski 123*b1cdbd2cSJim Jagielski try: 124*b1cdbd2cSJim Jagielski shutil.copy(inputfilename, outputfilename) 125*b1cdbd2cSJim Jagielski except IOError: 126*b1cdbd2cSJim Jagielski print("ERROR: Can not copy file '" + inputfilename + "' to " + "'" + outputfilename + "'") 127*b1cdbd2cSJim Jagielski sys.exit(-1) 128*b1cdbd2cSJim Jagielski 129*b1cdbd2cSJim Jagielski def extract(self): 130*b1cdbd2cSJim Jagielski try: 131*b1cdbd2cSJim Jagielski f = open(self._options.outputfile, "w+") 132*b1cdbd2cSJim Jagielski f.write(self.extract_file(self._options.inputfile)) 133*b1cdbd2cSJim Jagielski except IOError: 134*b1cdbd2cSJim Jagielski print("ERROR: Can not write file " + self._options.outputfile) 135*b1cdbd2cSJim Jagielski else: 136*b1cdbd2cSJim Jagielski f.close() 137*b1cdbd2cSJim Jagielski 138*b1cdbd2cSJim Jagielski # Parse the common options 139*b1cdbd2cSJim Jagielski def parse_options(self): 140*b1cdbd2cSJim Jagielski parser = OptionParser() 141*b1cdbd2cSJim Jagielski parser.add_option("-i", "--inputfile", dest="inputfile", metavar="FILE", help="resource file to read" ) 142*b1cdbd2cSJim Jagielski parser.add_option("-o", "--outputfile", dest="outputfile", metavar="FILE", help="extracted sdf or merged file" ) 143*b1cdbd2cSJim Jagielski parser.add_option("-m", "--inputsdffile", dest="input_sdf_file", metavar="FILE", help="merge this sdf file" ) 144*b1cdbd2cSJim Jagielski parser.add_option("-x", "--pathprefix", dest="path_prefix", metavar="PATH", help="" ) 145*b1cdbd2cSJim Jagielski parser.add_option("-y", "--pathpostfix", dest="path_postfix", metavar="PATH", help="" ) 146*b1cdbd2cSJim Jagielski parser.add_option("-p", "--projectname", dest="project_name", metavar="NAME", help="" ) 147*b1cdbd2cSJim Jagielski parser.add_option("-r", "--projectroot", dest="project_root", metavar="PATH", help="" ) 148*b1cdbd2cSJim Jagielski parser.add_option("-f", "--forcedlanguages", dest="forcedlanguages", metavar="ISOCODE[,ISOCODE]", help="Always merge those langs even if no l10n is available for those langs" ) 149*b1cdbd2cSJim Jagielski parser.add_option("-l", "--languages", dest="languages", metavar="ISOCODE[,ISOCODE]", help="Merge those langs if l10n is found for each") 150*b1cdbd2cSJim Jagielski parser.add_option("-s", "--pattern", dest="pattern", metavar="", help="" ) 151*b1cdbd2cSJim Jagielski parser.add_option("-q", "--quiet", action="store_true", dest="quietmode", help="",default=False) 152*b1cdbd2cSJim Jagielski (self._options, self.args) = parser.parse_args() 153*b1cdbd2cSJim Jagielski 154*b1cdbd2cSJim Jagielski # -l "de,pr,pt-BR" => [ "de" , "pt" , "pt-BR" ] 155*b1cdbd2cSJim Jagielski parse_complex_arg = lambda arg: arg.split(",") 156*b1cdbd2cSJim Jagielski 157*b1cdbd2cSJim Jagielski if self._options.forcedlanguages: 158*b1cdbd2cSJim Jagielski self._options.forcedlanguages = parse_complex_arg(self._options.forcedlanguages) 159*b1cdbd2cSJim Jagielski if self._options.languages: 160*b1cdbd2cSJim Jagielski self._options.languages = parse_complex_arg(self._options.languages) 161*b1cdbd2cSJim Jagielski self.test_options() 162*b1cdbd2cSJim Jagielski 163*b1cdbd2cSJim Jagielski def __init__(self): 164*b1cdbd2cSJim Jagielski self.parse_options() 165*b1cdbd2cSJim Jagielski if self._options.input_sdf_file != None and len(self._options.input_sdf_file): 166*b1cdbd2cSJim Jagielski sdfdata = SdfData(self._options.input_sdf_file) 167*b1cdbd2cSJim Jagielski sdfdata.read() 168*b1cdbd2cSJim Jagielski self.merge(sdfdata) 169*b1cdbd2cSJim Jagielski else: 170*b1cdbd2cSJim Jagielski self.extract() 171*b1cdbd2cSJim Jagielski 172*b1cdbd2cSJim Jagielski def make_dirs(self, filename): 173*b1cdbd2cSJim Jagielski dir = filename[:filename.rfind('/')] 174*b1cdbd2cSJim Jagielski if os.path.exists(dir): 175*b1cdbd2cSJim Jagielski if os.path.isfile(dir): 176*b1cdbd2cSJim Jagielski print("ERROR: There is a file '"+dir+"' where I want create a directory") 177*b1cdbd2cSJim Jagielski sys.exit(-1) 178*b1cdbd2cSJim Jagielski else: 179*b1cdbd2cSJim Jagielski return 180*b1cdbd2cSJim Jagielski else: 181*b1cdbd2cSJim Jagielski try: 182*b1cdbd2cSJim Jagielski os.makedirs(dir) 183*b1cdbd2cSJim Jagielski except IOError: 184*b1cdbd2cSJim Jagielski print("Error: Can not create dir " + dir) 185*b1cdbd2cSJim Jagielski sys.exit(-1) 186*b1cdbd2cSJim Jagielski 187*b1cdbd2cSJim Jagielski def test_options(self): 188*b1cdbd2cSJim Jagielski opt = self._options 189*b1cdbd2cSJim Jagielski is_valid = lambda x: x != None and len(x) > 0 190*b1cdbd2cSJim Jagielski return is_valid(opt.project_root) and is_valid(opt.project_name) and is_valid(opt.languages) and \ 191*b1cdbd2cSJim Jagielski ( is_valid(opt.inputfile) and (( is_valid(opt.path_prefix) and is_valid(opt.path_postfix) ) or is_valid(opt.outputfile)) and \ 192*b1cdbd2cSJim Jagielski ( ( is_valid(opt.input_sdf_file) and ( is_valid(opt.outputfile) or ( is_valid(opt.path_prefix) and is_valid(opt.path_postfix) ) or \ 193*b1cdbd2cSJim Jagielski ( is_valid(opt.inputfile) and is_valid(opt.outputFile)) )))) 194*b1cdbd2cSJim Jagielski print("Strange options ...") 195*b1cdbd2cSJim Jagielski sys.exit( -1 ) 196*b1cdbd2cSJim Jagielski 197*b1cdbd2cSJim Jagielski def read_inputfile_list(self): 198*b1cdbd2cSJim Jagielski if self.has_multi_inputfiles(): 199*b1cdbd2cSJim Jagielski lines = [] 200*b1cdbd2cSJim Jagielski try: 201*b1cdbd2cSJim Jagielski f = open(self._options.inputfile[1:], "r") 202*b1cdbd2cSJim Jagielski lines = [line.strip('\n') for line in f.readlines()] 203*b1cdbd2cSJim Jagielski except IOError: 204*b1cdbd2cSJim Jagielski print("ERROR: Can not read file list " + self._options.inputfile[2:]) 205*b1cdbd2cSJim Jagielski sys.exit(-1) 206*b1cdbd2cSJim Jagielski else: 207*b1cdbd2cSJim Jagielski f.close() 208*b1cdbd2cSJim Jagielski return lines 209*b1cdbd2cSJim Jagielski 210*b1cdbd2cSJim Jagielski def get_filename_string(self, inputfile): 211*b1cdbd2cSJim Jagielski absfile = os.path.realpath(os.path.abspath(inputfile)) 212*b1cdbd2cSJim Jagielski absroot = os.path.realpath(os.path.abspath(self._options.project_root)) 213*b1cdbd2cSJim Jagielski return absfile[len(absroot)+1:].replace('/','\\') 214