1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22  """Handles converting of files between formats (used by translate.convert tools)""" 
 23   
 24  import os.path 
 25  from translate.misc import optrecurse 
 26   
 27  optparse = optrecurse.optparse 
 28  try: 
 29      from cStringIO import StringIO 
 30  except ImportError: 
 31      from StringIO import StringIO 
 32   
 34      """a specialized Option Parser for convertor tools...""" 
 35 -    def __init__(self, formats, usetemplates=False, usepots=False, allowmissingtemplate=False, description=None): 
  42   
 44          """adds an option to include / exclude fuzzy translations""" 
 45          fuzzyhelp = "use translations marked fuzzy" 
 46          nofuzzyhelp = "don't use translations marked fuzzy" 
 47          if default: 
 48              fuzzyhelp += " (default)" 
 49          else: 
 50              nofuzzyhelp += " (default)" 
 51          self.add_option("", "--fuzzy", dest="includefuzzy", action="store_true", default=default, help=fuzzyhelp) 
 52          self.add_option("", "--nofuzzy", dest="includefuzzy", action="store_false", default=default, help=nofuzzyhelp) 
 53          self.passthrough.append("includefuzzy") 
  54   
 56          """adds an option to say what to do with duplicate strings""" 
 57          self.add_option("", "--duplicates", dest="duplicatestyle", default=default, 
 58              type="choice", choices=["msgid_comment", "msgctxt", "merge", "keep", "msgid_comment_all"], 
 59              help="what to do with duplicate strings (identical source text): merge, msgid_comment, msgctxt, keep, msgid_comment_all (default: '%s')" % default, metavar="DUPLICATESTYLE") 
 60          self.passthrough.append("duplicatestyle") 
  61   
 63          """adds an option to say how to split the po/pot files""" 
 64          self.add_option("", "--multifile", dest="multifilestyle", default=default, 
 65              type="choice", choices=["single", "toplevel", "onefile"], 
 66              help="how to split po/pot files (single, toplevel or onefile)", metavar="MULTIFILESTYLE") 
 67          self.passthrough.append("multifilestyle") 
  68   
 79   
 90   
 97   
 99          """filters output options, processing relevant switches in options""" 
100          if self.usepots and options.pot: 
101              outputoptions = {} 
102              for (inputformat, templateformat), (outputformat, convertor) in self.outputoptions.iteritems(): 
103                  inputformat = self.potifyformat(inputformat) 
104                  templateformat = self.potifyformat(templateformat) 
105                  outputformat = self.potifyformat(outputformat) 
106                  outputoptions[(inputformat, templateformat)] = (outputformat, convertor) 
107              return outputoptions 
108          else: 
109              return self.outputoptions 
 110   
112          """sets the -P/--pot option depending on input/output formats etc""" 
113          if self.usepots: 
114              potoption = optparse.Option("-P", "--pot", \ 
115                      action="store_true", dest="pot", default=False, \ 
116                      help="output PO Templates (.pot) rather than PO files (.po)") 
117              self.define_option(potoption) 
 118   
120          """verifies that the options are valid (required options are present, etc)""" 
121          pass 
 122   
123 -    def run(self, argv=None): 
  131   
136   
137 -def copytemplate(inputfile, outputfile, templatefile, **kwargs): 
 138      """copies the template file to the output file""" 
139      outputfile.write(templatefile.read()) 
140      return True 
 141   
143      """an object that knows how to replace strings in files""" 
144 -    def __init__(self, searchstring, replacestring): 
 145          self.searchstring = searchstring 
146          self.replacestring = replacestring 
 147   
149          """actually replace the text""" 
150          if self.searchstring is not None and self.replacestring is not None: 
151              return text.replace(self.searchstring, self.replacestring) 
152          else: 
153              return text 
 154   
159   
161          """copies the template file to the output file, searching and replacing""" 
162          outputfile.write(self.doreplace(templatefile.read())) 
163          return True 
  164   
165   
166   
167   
168   
169   
170   
171   
172   
173   
174   
175   
176   
178      """ConvertOptionParser that can handle recursing into single archive files. 
179      archiveformats maps extension to class. if the extension doesn't matter, it can be None. 
180      if the extension is only valid for input/output/template, it can be given as (extension, filepurpose)""" 
181 -    def __init__(self, formats, usetemplates=False, usepots=False, description=None, archiveformats=None): 
 182          if archiveformats is None: 
183              self.archiveformats = {} 
184          else: 
185              self.archiveformats = archiveformats 
186          self.archiveoptions = {} 
187          ConvertOptionParser.__init__(self, formats, usetemplates, usepots, description=description) 
 188   
190          """allows setting options that will always be passed to openarchive""" 
191          self.archiveoptions = kwargs 
 192   
193 -    def isrecursive(self, fileoption, filepurpose='input'): 
 197   
198 -    def isarchive(self, fileoption, filepurpose='input'): 
 199          """returns whether the file option is an archive file""" 
200          if not isinstance(fileoption, (str, unicode)): 
201              return False 
202          mustexist = (filepurpose != 'output') 
203          if mustexist and not os.path.isfile(fileoption): 
204              return False 
205          fileext = self.splitext(fileoption)[1] 
206           
207          return self.getarchiveclass(fileext, filepurpose, os.path.isdir(fileoption)) is not None 
 208   
210          """returns the archiveclass for the given fileext and filepurpose""" 
211          archiveclass = self.archiveformats.get(fileext, None) 
212          if archiveclass is not None: 
213              return archiveclass 
214          archiveclass = self.archiveformats.get((fileext, filepurpose), None) 
215          if archiveclass is not None: 
216              return archiveclass 
217          if not isdir: 
218              archiveclass = self.archiveformats.get(None, None) 
219              if archiveclass is not None: 
220                  return archiveclass 
221              archiveclass = self.archiveformats.get((None, filepurpose), None) 
222              if archiveclass is not None: 
223                  return archiveclass 
224          return None 
 225   
226 -    def openarchive(self, archivefilename, filepurpose, **kwargs): 
 227          """creates an archive object for the given file""" 
228          archiveext = self.splitext(archivefilename)[1] 
229          archiveclass = self.getarchiveclass(archiveext, filepurpose, os.path.isdir(archivefilename)) 
230          archiveoptions = self.archiveoptions.copy() 
231          archiveoptions.update(kwargs) 
232          return archiveclass(archivefilename, **archiveoptions) 
 233   
241   
243          """recurse through archive files and convert files""" 
244          inputfiles = [] 
245          for inputpath in options.inputarchive: 
246              if self.isexcluded(options, inputpath): 
247                  continue 
248              top, name = os.path.split(inputpath) 
249              if not self.isvalidinputname(options, name): 
250                  continue 
251              inputfiles.append(inputpath) 
252          return inputfiles 
 253   
260   
267   
278   
280          """gets the absolute path to a template file""" 
281          if templatepath is not None and self.usetemplates and options.template: 
282              if self.isarchive(options.template, 'template'): 
283                  return templatepath 
284              elif not options.recursivetemplate: 
285                  return templatepath 
286              else: 
287                  return os.path.join(options.template, templatepath) 
288          else: 
289              return None 
 290   
298   
300          """gets the absolute path to an output file""" 
301          if self.isarchive(options.output, 'output'): 
302              return outputpath 
303          elif options.recursiveoutput and options.output: 
304              return os.path.join(options.output, outputpath) 
305          else: 
306              return outputpath 
 307   
312   
323   
325          """opens the templatearchive if not already open""" 
326          if not self.usetemplates: 
327              return 
328          if options.template and self.isarchive(options.template, 'template') and not hasattr(options, "templatearchive"): 
329              options.templatearchive = self.openarchive(options.template, 'template') 
 330   
335   
347   
348 -    def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath): 
 349          """run an invidividual conversion""" 
350          if self.isarchive(options.output, 'output'): 
351              inputfile = self.openinputfile(options, fullinputpath) 
352               
353              templatefile = self.opentemplatefile(options, fulltemplatepath) 
354              outputfile = self.openoutputfile(options, fulloutputpath) 
355              passthroughoptions = self.getpassthroughoptions(options) 
356              if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions): 
357                  if not outputfile.isatty(): 
358                      outputfile.close() 
359                  return True 
360              else: 
361                  if fulloutputpath and os.path.isfile(fulloutputpath): 
362                      outputfile.close() 
363                      os.unlink(fulloutputpath) 
364                  return False 
365          else: 
366              return super(ArchiveConvertOptionParser, self).processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath) 
  367   
368 -def main(argv=None): 
 369      parser = ArchiveConvertOptionParser({}, description=__doc__) 
370      parser.run(argv) 
 371