diff options
Diffstat (limited to 'translate-toolkit-1.5.1/translate/convert/convert.py')
-rw-r--r-- | translate-toolkit-1.5.1/translate/convert/convert.py | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/translate-toolkit-1.5.1/translate/convert/convert.py b/translate-toolkit-1.5.1/translate/convert/convert.py new file mode 100644 index 0000000..9796031 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/convert.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""Handles converting of files between formats (used by translate.convert tools)""" + +import os.path +from translate.misc import optrecurse +# don't import optparse ourselves, get the version from optrecurse +optparse = optrecurse.optparse +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +class ConvertOptionParser(optrecurse.RecursiveOptionParser, object): + """a specialized Option Parser for convertor tools...""" + def __init__(self, formats, usetemplates=False, usepots=False, allowmissingtemplate=False, description=None): + """construct the specialized Option Parser""" + optrecurse.RecursiveOptionParser.__init__(self, formats, usetemplates, + allowmissingtemplate=allowmissingtemplate, description=description) + self.usepots = usepots + self.setpotoption() + self.set_usage() + + def add_fuzzy_option(self, default=False): + """adds an option to include / exclude fuzzy translations""" + fuzzyhelp = "use translations marked fuzzy" + nofuzzyhelp = "don't use translations marked fuzzy" + if default: + fuzzyhelp += " (default)" + else: + nofuzzyhelp += " (default)" + self.add_option("", "--fuzzy", dest="includefuzzy", action="store_true", default=default, help=fuzzyhelp) + self.add_option("", "--nofuzzy", dest="includefuzzy", action="store_false", default=default, help=nofuzzyhelp) + self.passthrough.append("includefuzzy") + + def add_duplicates_option(self, default="msgctxt"): + """adds an option to say what to do with duplicate strings""" + self.add_option("", "--duplicates", dest="duplicatestyle", default=default, + type="choice", choices=["msgctxt", "merge"], + help="what to do with duplicate strings (identical source text): merge, msgctxt (default: '%s')" % default, metavar="DUPLICATESTYLE") + self.passthrough.append("duplicatestyle") + + def add_multifile_option(self, default="single"): + """adds an option to say how to split the po/pot files""" + self.add_option("", "--multifile", dest="multifilestyle", default=default, + type="choice", choices=["single", "toplevel", "onefile"], + help="how to split po/pot files (single, toplevel or onefile)", metavar="MULTIFILESTYLE") + self.passthrough.append("multifilestyle") + + def potifyformat(self, fileformat): + """converts a .po to a .pot where required""" + if fileformat is None: + return fileformat + elif fileformat == "po": + return "pot" + elif fileformat.endswith(os.extsep + "po"): + return fileformat + "t" + else: + return fileformat + + def getformathelp(self, formats): + """make a nice help string for describing formats...""" + # include implicit pot options... + helpformats = [] + for fileformat in formats: + helpformats.append(fileformat) + potformat = self.potifyformat(fileformat) + if potformat != fileformat: + helpformats.append(potformat) + return super(ConvertOptionParser, self).getformathelp(helpformats) + + def filterinputformats(self, options): + """filters input formats, processing relevant switches in options""" + if self.usepots and options.pot: + return [self.potifyformat(inputformat) for inputformat in self.inputformats] + else: + return self.inputformats + + def filteroutputoptions(self, options): + """filters output options, processing relevant switches in options""" + if self.usepots and options.pot: + outputoptions = {} + for (inputformat, templateformat), (outputformat, convertor) in self.outputoptions.iteritems(): + inputformat = self.potifyformat(inputformat) + templateformat = self.potifyformat(templateformat) + outputformat = self.potifyformat(outputformat) + outputoptions[(inputformat, templateformat)] = (outputformat, convertor) + return outputoptions + else: + return self.outputoptions + + def setpotoption(self): + """sets the -P/--pot option depending on input/output formats etc""" + if self.usepots: + potoption = optparse.Option("-P", "--pot", \ + action="store_true", dest="pot", default=False, \ + help="output PO Templates (.pot) rather than PO files (.po)") + self.define_option(potoption) + + def verifyoptions(self, options): + """verifies that the options are valid (required options are present, etc)""" + pass + + def run(self, argv=None): + """parses the command line options and runs the conversion""" + (options, args) = self.parse_args(argv) + options.inputformats = self.filterinputformats(options) + options.outputoptions = self.filteroutputoptions(options) + self.usepsyco(options) + self.verifyoptions(options) + self.recursiveprocess(options) + +def copyinput(inputfile, outputfile, templatefile, **kwargs): + """copies the input file to the output file""" + outputfile.write(inputfile.read()) + return True + +def copytemplate(inputfile, outputfile, templatefile, **kwargs): + """copies the template file to the output file""" + outputfile.write(templatefile.read()) + return True + +class Replacer: + """an object that knows how to replace strings in files""" + def __init__(self, searchstring, replacestring): + self.searchstring = searchstring + self.replacestring = replacestring + + def doreplace(self, text): + """actually replace the text""" + if self.searchstring is not None and self.replacestring is not None: + return text.replace(self.searchstring, self.replacestring) + else: + return text + + def searchreplaceinput(self, inputfile, outputfile, templatefile, **kwargs): + """copies the input file to the output file, searching and replacing""" + outputfile.write(self.doreplace(inputfile.read())) + return True + + def searchreplacetemplate(self, inputfile, outputfile, templatefile, **kwargs): + """copies the template file to the output file, searching and replacing""" + outputfile.write(self.doreplace(templatefile.read())) + return True + +# archive files need to know how to: +# - openarchive: creates an archive object for the archivefilename +# * requires a constructor that takes the filename +# - iterarchivefile: iterate through the names in the archivefile +# * requires the default iterator to do this +# - archivefileexists: check if a given pathname exists inside the archivefile +# * uses the in operator - requires __contains__ (or will use __iter__ by default) +# - openarchiveinputfile: returns an open input file from the archive, given the path +# * requires an archivefile.openinputfile method that takes the pathname +# - openarchiveoutputfile: returns an open output file from the archive, given the path +# * requires an archivefile.openoutputfile method that takes the pathname + +class ArchiveConvertOptionParser(ConvertOptionParser): + """ConvertOptionParser that can handle recursing into single archive files. + archiveformats maps extension to class. if the extension doesn't matter, it can be None. + if the extension is only valid for input/output/template, it can be given as (extension, filepurpose)""" + def __init__(self, formats, usetemplates=False, usepots=False, description=None, archiveformats=None): + if archiveformats is None: + self.archiveformats = {} + else: + self.archiveformats = archiveformats + self.archiveoptions = {} + ConvertOptionParser.__init__(self, formats, usetemplates, usepots, description=description) + + def setarchiveoptions(self, **kwargs): + """allows setting options that will always be passed to openarchive""" + self.archiveoptions = kwargs + + def isrecursive(self, fileoption, filepurpose='input'): + """checks if fileoption is a recursive file""" + if self.isarchive(fileoption, filepurpose): return True + return super(ArchiveConvertOptionParser, self).isrecursive(fileoption, filepurpose) + + def isarchive(self, fileoption, filepurpose='input'): + """returns whether the file option is an archive file""" + if not isinstance(fileoption, (str, unicode)): + return False + mustexist = (filepurpose != 'output') + if mustexist and not os.path.isfile(fileoption): + return False + fileext = self.splitext(fileoption)[1] + # if None is in the archive formats, then treat all non-directory inputs as archives + return self.getarchiveclass(fileext, filepurpose, os.path.isdir(fileoption)) is not None + + def getarchiveclass(self, fileext, filepurpose, isdir=False): + """returns the archiveclass for the given fileext and filepurpose""" + archiveclass = self.archiveformats.get(fileext, None) + if archiveclass is not None: + return archiveclass + archiveclass = self.archiveformats.get((fileext, filepurpose), None) + if archiveclass is not None: + return archiveclass + if not isdir: + archiveclass = self.archiveformats.get(None, None) + if archiveclass is not None: + return archiveclass + archiveclass = self.archiveformats.get((None, filepurpose), None) + if archiveclass is not None: + return archiveclass + return None + + def openarchive(self, archivefilename, filepurpose, **kwargs): + """creates an archive object for the given file""" + archiveext = self.splitext(archivefilename)[1] + archiveclass = self.getarchiveclass(archiveext, filepurpose, os.path.isdir(archivefilename)) + archiveoptions = self.archiveoptions.copy() + archiveoptions.update(kwargs) + return archiveclass(archivefilename, **archiveoptions) + + def recurseinputfiles(self, options): + """recurse through archive file / directories and return files to be converted""" + if self.isarchive(options.input, 'input'): + options.inputarchive = self.openarchive(options.input, 'input') + return self.recursearchivefiles(options) + else: + return super(ArchiveConvertOptionParser, self).recurseinputfiles(options) + + def recursearchivefiles(self, options): + """recurse through archive files and convert files""" + inputfiles = [] + for inputpath in options.inputarchive: + if self.isexcluded(options, inputpath): + continue + top, name = os.path.split(inputpath) + if not self.isvalidinputname(options, name): + continue + inputfiles.append(inputpath) + return inputfiles + + def openinputfile(self, options, fullinputpath): + """opens the input file""" + if self.isarchive(options.input, 'input'): + return options.inputarchive.openinputfile(fullinputpath) + else: + return super(ArchiveConvertOptionParser, self).openinputfile(options, fullinputpath) + + def getfullinputpath(self, options, inputpath): + """gets the absolute path to an input file""" + if self.isarchive(options.input, 'input'): + return inputpath + else: + return os.path.join(options.input, inputpath) + + def opentemplatefile(self, options, fulltemplatepath): + """opens the template file (if required)""" + if fulltemplatepath is not None: + if options.recursivetemplate and self.isarchive(options.template, 'template'): + # TODO: deal with different names in input/template archives + if fulltemplatepath in options.templatearchive: + return options.templatearchive.openinputfile(fulltemplatepath) + else: + self.warning("missing template file %s" % fulltemplatepath) + return super(ArchiveConvertOptionParser, self).opentemplatefile(options, fulltemplatepath) + + def getfulltemplatepath(self, options, templatepath): + """gets the absolute path to a template file""" + if templatepath is not None and self.usetemplates and options.template: + if self.isarchive(options.template, 'template'): + return templatepath + elif not options.recursivetemplate: + return templatepath + else: + return os.path.join(options.template, templatepath) + else: + return None + + def templateexists(self, options, templatepath): + """returns whether the given template exists...""" + if templatepath is not None: + if self.isarchive(options.template, 'template'): + # TODO: deal with different names in input/template archives + return templatepath in options.templatearchive + return super(ArchiveConvertOptionParser, self).templateexists(options, templatepath) + + def getfulloutputpath(self, options, outputpath): + """gets the absolute path to an output file""" + if self.isarchive(options.output, 'output'): + return outputpath + elif options.recursiveoutput and options.output: + return os.path.join(options.output, outputpath) + else: + return outputpath + + def checkoutputsubdir(self, options, subdir): + """checks to see if subdir under options.output needs to be created, creates if neccessary""" + if not self.isarchive(options.output, 'output'): + super(ArchiveConvertOptionParser, self).checkoutputsubdir(options, subdir) + + def openoutputfile(self, options, fulloutputpath): + """opens the output file""" + if self.isarchive(options.output, 'output'): + outputstream = options.outputarchive.openoutputfile(fulloutputpath) + if outputstream is None: + self.warning("Could not find where to put %s in output archive; writing to tmp" % fulloutputpath) + return StringIO() + return outputstream + else: + return super(ArchiveConvertOptionParser, self).openoutputfile(options, fulloutputpath) + + def inittemplatearchive(self, options): + """opens the templatearchive if not already open""" + if not self.usetemplates: + return + if options.template and self.isarchive(options.template, 'template') and not hasattr(options, "templatearchive"): + options.templatearchive = self.openarchive(options.template, 'template') + + def initoutputarchive(self, options): + """creates an outputarchive if required""" + if options.output and self.isarchive(options.output, 'output'): + options.outputarchive = self.openarchive(options.output, 'output', mode="w") + + def recursiveprocess(self, options): + """recurse through directories and convert files""" + if hasattr(options, "multifilestyle"): + self.setarchiveoptions(multifilestyle=options.multifilestyle) + for filetype in ("input", "output", "template"): + allowoption = "allowrecursive%s" % filetype + if options.multifilestyle == "onefile" and getattr(options, allowoption, True): + setattr(options, allowoption, False) + self.inittemplatearchive(options) + self.initoutputarchive(options) + return super(ArchiveConvertOptionParser, self).recursiveprocess(options) + + def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath): + """run an invidividual conversion""" + if self.isarchive(options.output, 'output'): + inputfile = self.openinputfile(options, fullinputpath) + # TODO: handle writing back to same archive as input/template + templatefile = self.opentemplatefile(options, fulltemplatepath) + outputfile = self.openoutputfile(options, fulloutputpath) + passthroughoptions = self.getpassthroughoptions(options) + if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions): + if not outputfile.isatty(): + outputfile.close() + return True + else: + if fulloutputpath and os.path.isfile(fulloutputpath): + outputfile.close() + os.unlink(fulloutputpath) + return False + else: + return super(ArchiveConvertOptionParser, self).processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath) + +def main(argv=None): + parser = ArchiveConvertOptionParser({}, description=__doc__) + parser.run(argv) + |