translate.convert.po2oo

Package translate :: Package convert :: Module po2oo |
[hide private]
[frames] | no frames]

# Source Code for Module translate.convert.po2oo

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  #
      4  # Copyright 2004-2008,20010-2011 Zuza Software Foundation
      5  #
      6  # This file is part of translate.
      7  #
      8  # translate is free software; you can redistribute it and/or modify
      9  # it under the terms of the GNU General Public License as published by
     10  # the Free Software Foundation; either version 2 of the License, or
     11  # (at your option) any later version.
     12  #
     13  # translate is distributed in the hope that it will be useful,
     14  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  # GNU General Public License for more details.
     17  #
     18  # You should have received a copy of the GNU General Public License
     19  # along with translate; if not, write to the Free Software
     20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     21  #
     22
     23  """convert Gettext PO localization files to an OpenOffice.org (SDF) localization file
     24
     25  see: http://translate.sourceforge.net/wiki/toolkit/po2oo for examples and
     26  usage instructions
     27  """
     28
     29  import os
     30  import sys
     31  import time
     32
     33  from translate.storage import oo
     34  from translate.storage import factory
     35  from translate.filters import pofilter
     36  from translate.filters import checks
     37  from translate.filters import autocorrect
     38
     39
     40 -class reoo:
     41
     42 -    def __init__(self, templatefile, languages=None, timestamp=None, includefuzzy=False, long_keys=False, filteraction="exclude"):
     43          """construct a reoo converter for the specified languages (timestamp=0 means leave unchanged)"""
     44          # languages is a pair of language ids
     45          self.long_keys = long_keys
     46          self.readoo(templatefile)
     47          self.languages = languages
     48          self.filteraction = filteraction
     49          if timestamp is None:
     50              self.timestamp = time.strptime("2002-02-02 02:02:02", "%Y-%m-%d %H:%M:%S")
     51          else:
     52              self.timestamp = timestamp
     53          if self.timestamp:
     54              self.timestamp_str = time.strftime("%Y-%m-%d %H:%M:%S", self.timestamp)
     55          else:
     56              self.timestamp_str = None
     57          self.includefuzzy = includefuzzy
     58
     59 -    def makeindex(self):
     60          """makes an index of the oo keys that are used in the source file"""
     61          self.index = {}
     62          for ookey, theoo in self.o.ookeys.iteritems():
     63              sourcekey = oo.makekey(ookey, self.long_keys)
     64              self.index[sourcekey] = theoo
     65
     66 -    def readoo(self, of):
     67          """read in the oo from the file"""
     68          oosrc = of.read()
     69          self.o = oo.oofile()
     70          self.o.parse(oosrc)
     71          self.makeindex()
     72
     73 -    def handleunit(self, unit):
     74          # TODO: make this work for multiple columns in oo...
     75          locations = unit.getlocations()
     76          # technically our formats should just have one location for each entry...
     77          # but we handle multiple ones just to be safe...
     78          for location in locations:
     79              subkeypos = location.rfind('.')
     80              subkey = location[subkeypos+1:]
     81              key = location[:subkeypos]
     82              # this is just to handle our old system of using %s/%s:%s instead of %s/%s#%s
     83              key = key.replace(':', '#')
     84              # this is to handle using / instead of \ in the sourcefile...
     85              key = key.replace('\\', '/')
     86              key = oo.normalizefilename(key)
     87              if key in self.index:
     88                  # now we need to replace the definition of entity with msgstr
     89                  theoo = self.index[key] # find the oo
     90                  self.applytranslation(key, subkey, theoo, unit)
     91              else:
     92                  print >> sys.stderr, "couldn't find key %s from po in %d keys" % (key, len(self.index))
     93                  try:
     94                      sourceunitlines = str(unit)
     95                      if isinstance(sourceunitlines, unicode):
     96                          sourceunitlines = sourceunitlines.encode("utf-8")
     97                      print >> sys.stderr, sourceunitlines
     98                  except:
     99                      print >> sys.stderr, "error outputting source unit %r" % (str(unit),)
    100
    101 -    def applytranslation(self, key, subkey, theoo, unit):
    102          """applies the translation from the source unit to the oo unit"""
    103          if not self.includefuzzy and unit.isfuzzy():
    104              return
    105          makecopy = False
    106          if self.languages is None:
    107              part1 = theoo.lines[0]
    108              if len(theoo.lines) > 1:
    109                  part2 = theoo.lines[1]
    110              else:
    111                  makecopy = True
    112          else:
    113              part1 = theoo.languages[self.languages[0]]
    114              if self.languages[1] in theoo.languages:
    115                  part2 = theoo.languages[self.languages[1]]
    116              else:
    117                  makecopy = True
    118          if makecopy:
    119              part2 = oo.ooline(part1.getparts())
    120          unquotedid = unit.source
    121          unquotedstr = unit.target
    122          # If there is no translation, we don't want to add a line
    123          if len(unquotedstr) == 0:
    124              return
    125          if isinstance(unquotedstr, unicode):
    126              unquotedstr = unquotedstr.encode("UTF-8")
    127          # finally set the new definition in the oo, but not if its empty
    128          if len(unquotedstr) > 0:
    129              setattr(part2, subkey, unquotedstr)
    130          # set the modified time
    131          if self.timestamp_str:
    132              part2.timestamp = self.timestamp_str
    133          if self.languages:
    134              part2.languageid = self.languages[1]
    135          if makecopy:
    136              theoo.addline(part2)
    137
    138 -    def convertstore(self, sourcestore):
    139          # translate the strings
    140          for unit in sourcestore.units:
    141              # there may be more than one element due to msguniq merge
    142              if filter.validelement(unit, sourcestore.filename, self.filteraction):
    143                  self.handleunit(unit)
    144          # return the modified oo file object
    145          return self.o
    146
    147
    148 -def getmtime(filename):
    149      import stat
    150      return time.localtime(os.stat(filename)[stat.ST_MTIME])
    151
    152
    153 -class oocheckfilter(pofilter.pocheckfilter):
    154
    155 -    def validelement(self, unit, filename, filteraction):
    156          """Returns whether or not to use unit in conversion. (filename is just for error reporting)"""
    157          if filteraction == "none":
    158              return True
    159          filterresult = self.filterunit(unit)
    160          if filterresult:
    161              if filterresult != autocorrect:
    162                  for filtername, filtermessage in filterresult.iteritems():
    163                      location = unit.getlocations()[0].encode('utf-8')
    164                      if filtername in self.options.error:
    165                          print >> sys.stderr, "Error at %s::%s: %s" % (filename, location, filtermessage)
    166                          return not filteraction in ["exclude-all", "exclude-serious"]
    167                      if filtername in self.options.warning or self.options.alwayswarn:
    168                          print >> sys.stderr, "Warning at %s::%s: %s" % (filename, location, filtermessage)
    169                          return not filteraction in ["exclude-all"]
    170          return True
    171
    172
    173 -class oofilteroptions:
    174      error = ['variables', 'xmltags', 'escapes']
    175      warning = ['blank']
    176      #To only issue warnings for tests listed in warning, change the following to False:
    177      alwayswarn = True
    178      limitfilters = error + warning
    179      #To use all available tests, uncomment the following:
    180      #limitfilters = []
    181      #To exclude certain tests, list them in here:
    182      excludefilters = {}
    183      includefuzzy = False
    184      includereview = False
    185      autocorrect = False
    186
    187  options = oofilteroptions()
    188  filter = oocheckfilter(options, [checks.OpenOfficeChecker, checks.StandardUnitChecker], checks.openofficeconfig)
    189
    190
    191 -def convertoo(inputfile, outputfile, templatefile, sourcelanguage=None, targetlanguage=None, timestamp=None, includefuzzy=False, multifilestyle="single", skip_source=False, filteraction=None):
    192      inputstore = factory.getobject(inputfile)
    193      inputstore.filename = getattr(inputfile, 'name', '')
    194      if not targetlanguage:
    195          raise ValueError("You must specify the target language")
    196      if not sourcelanguage:
    197          if targetlanguage.isdigit():
    198              sourcelanguage = "01"
    199          else:
    200              sourcelanguage = "en-US"
    201      languages = (sourcelanguage, targetlanguage)
    202      if templatefile is None:
    203          raise ValueError("must have template file for oo files")
    204      else:
    205          convertor = reoo(templatefile, languages=languages, timestamp=timestamp, includefuzzy=includefuzzy, long_keys=multifilestyle != "single", filteraction=filteraction)
    206      outputstore = convertor.convertstore(inputstore)
    207      # TODO: check if we need to manually delete missing items
    208      outputfile.write(outputstore.__str__(skip_source, targetlanguage))
    209      return True
    210
    211
    212 -def main(argv=None):
    213      from translate.convert import convert
    214      formats = {("po", "oo"): ("oo", convertoo), ("xlf", "oo"): ("oo", convertoo), ("po", "sdf"): ("sdf", convertoo)}
    215      # always treat the input as an archive unless it is a directory
    216      archiveformats = {(None, "output"): oo.oomultifile, (None, "template"): oo.oomultifile}
    217      parser = convert.ArchiveConvertOptionParser(formats, usetemplates=True, description=__doc__, archiveformats=archiveformats)
    218      parser.add_option("-l", "--language", dest="targetlanguage", default=None,
    219              help="set target language code (e.g. af-ZA) [required]", metavar="LANG")
    220      parser.add_option("", "--source-language", dest="sourcelanguage", default=None,
    221              help="set source language code (default en-US)", metavar="LANG")
    222      parser.add_option("-T", "--keeptimestamp", dest="timestamp", default=None, action="store_const", const=0,
    223              help="don't change the timestamps of the strings")
    224      parser.add_option("", "--nonrecursiveoutput", dest="allowrecursiveoutput", default=True, action="store_false", help="don't treat the output oo as a recursive store")
    225      parser.add_option("", "--nonrecursivetemplate", dest="allowrecursivetemplate", default=True, action="store_false", help="don't treat the template oo as a recursive store")
    226      parser.add_option("", "--skipsource", dest="skip_source", default=False, action="store_true", help="don't output the source language, but fallback to it where needed")
    227      parser.add_option("", "--filteraction", dest="filteraction", default="none", metavar="ACTION",
    228              help="action on pofilter failure: none (default), warn, exclude-serious, exclude-all")
    229      parser.add_fuzzy_option()
    230      parser.add_multifile_option()
    231      parser.passthrough.append("sourcelanguage")
    232      parser.passthrough.append("targetlanguage")
    233      parser.passthrough.append("timestamp")
    234      parser.passthrough.append("skip_source")
    235      parser.passthrough.append("filteraction")
    236      parser.run(argv)
    237
    238
    239  if __name__ == '__main__':
    240      main()
    241