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

# Source Code for Module translate.convert.pot2po

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  #
      4  # Copyright 2004-2009 Zuza Software Foundation
      5  #
      6  # This file is part of translate.
      7  #
      8  # This program 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  # This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
     20
     21  """Convert template files (like .pot or template .xlf files) translation files,
     22  preserving existing translations.
     23
     24  See: http://translate.sourceforge.net/wiki/toolkit/pot2po for examples and
     25  usage instructions.
     26  """
     27
     28  from translate.storage import factory
     29  from translate.search import match
     30  from translate.misc.multistring import multistring
     31  from translate.tools import pretranslate
     32  from translate.storage import poheader
     33
     34
     35 -def convertpot(input_file, output_file, template_file, tm=None, min_similarity=75, fuzzymatching=True, **kwargs):
     36      """Main conversion function"""
     37
     38      input_store = factory.getobject(input_file)
     39      template_store = None
     40      if template_file is not None:
     41          template_store = factory.getobject(template_file)
     42      output_store = convert_stores(input_store, template_store, tm, min_similarity, fuzzymatching, **kwargs)
     43      output_file.write(str(output_store))
     44      return 1
     45
     46 -def convert_stores(input_store, template_store, tm=None, min_similarity=75, fuzzymatching=True, **kwargs):
     47      """Actual conversion function, works on stores not files, returns
     48      a properly initialized pretranslated output store, with structure
     49      based on input_store, metadata based on template_store, migrates
     50      old translations from template_store and pretranslating from tm"""
     51
     52      #prepare for merging
     53      output_store = type(input_store)()
     54      #create fuzzy matchers to be used by pretranslate.pretranslate_unit
     55      matchers = []
     56      _prepare_merge(input_store, output_store, template_store)
     57      if fuzzymatching:
     58          if template_store:
     59              matcher = match.matcher(template_store, max_candidates=1, min_similarity=min_similarity, max_length=3000, usefuzzy=True)
     60              matcher.addpercentage = False
     61              matchers.append(matcher)
     62          if tm:
     63              matcher = pretranslate.memory(tm, max_candidates=1, min_similarity=min_similarity, max_length=1000)
     64              matcher.addpercentage = False
     65              matchers.append(matcher)
     66
     67      #initialize store
     68      _store_pre_merge(input_store, output_store, template_store)
     69
     70      # Do matching
     71      for input_unit in input_store.units:
     72          if input_unit.istranslatable():
     73              input_unit = pretranslate.pretranslate_unit(input_unit, template_store, matchers, mark_reused=True)
     74              _unit_post_merge(input_unit, input_store, output_store, template_store)
     75              output_store.addunit(input_unit)
     76
     77      #finalize store
     78      _store_post_merge(input_store, output_store, template_store)
     79
     80      return output_store
     81
     82
     83  ##dispatchers
     84 -def _prepare_merge(input_store, output_store, template_store, **kwargs):
     85      """Prepare stores & TM matchers before merging."""
     86      #dispatch to format specific functions
     87      prepare_merge_hook = "_prepare_merge_%s" % input_store.__class__.__name__
     88      if  globals().has_key(prepare_merge_hook):
     89          globals()[prepare_merge_hook](input_store, output_store, template_store, **kwargs)
     90
     91      #generate an index so we can search by source string and location later on
     92      input_store.makeindex()
     93      if template_store:
     94          template_store.makeindex()
     95
     96
     97 -def _store_pre_merge(input_store, output_store, template_store, **kwargs) :
     98      """Initialize the new file with things like headers and metadata."""
     99      #formats that implement poheader interface are a special case
    100      if isinstance(input_store, poheader.poheader):
    101          _do_poheaders(input_store, output_store, template_store)
    102
    103      #dispatch to format specific functions
    104      store_pre_merge_hook = "_store_pre_merge_%s" % input_store.__class__.__name__
    105      if  globals().has_key(store_pre_merge_hook):
    106          globals()[store_pre_merge_hook](input_store, output_store, template_store, **kwargs)
    107
    108
    109 -def _store_post_merge(input_store, output_store, template_store, **kwargs) :
    110      """Close file after merging all translations, used for adding
    111      statistics, obsolete messages and similar wrapup tasks."""
    112      #dispatch to format specific functions
    113      store_post_merge_hook = "_store_post_merge_%s" % input_store.__class__.__name__
    114      if  globals().has_key(store_post_merge_hook):
    115          globals()[store_post_merge_hook](input_store, output_store, template_store, **kwargs)
    116
    117 -def _unit_post_merge(input_unit, input_store, output_store, template_store, **kwargs):
    118      """Handle any unit level cleanup and situations not handled by the merge()
    119      function."""
    120      #dispatch to format specific functions
    121      unit_post_merge_hook = "_unit_post_merge_%s" % input_unit.__class__.__name__
    122      if  globals().has_key(unit_post_merge_hook):
    123          globals()[unit_post_merge_hook](input_unit, input_store, output_store, template_store, **kwargs)
    124
    125
    126  ##format specific functions
    127 -def _prepare_merge_pofile(input_store, output_store, template_store):
    128      """PO format specific template preparation logic."""
    129      #we need to revive obsolete units to be able to consider
    130      #their translation when matching
    131      if template_store:
    132          for unit in template_store.units:
    133              if unit.isobsolete():
    134                  unit.resurrect()
    135
    136
    137 -def _unit_post_merge_pounit(input_unit, input_store, output_store, template_store):
    138      """PO format specific plural string initializtion logic."""
    139      #FIXME: do we want to do that for poxliff also?
    140      if input_unit.hasplural() and len(input_unit.target) == 0:
    141          # untranslated plural unit; Let's ensure that we have the correct number of plural forms:
    142          nplurals, plural = output_store.getheaderplural()
    143          if nplurals and nplurals.isdigit() and nplurals != '2':
    144              input_unit.target = multistring([""]*int(nplurals))
    145
    146
    147 -def _store_post_merge_pofile(input_store, output_store, template_store):
    148      """PO format specific: adds newly obsoleted messages to end of store."""
    149      #Let's take care of obsoleted messages
    150      if template_store:
    151          newlyobsoleted = []
    152          for unit in template_store.units:
    153              if unit.isheader():
    154                  continue
    155              if unit.target and not (input_store.findunit(unit.source) or hasattr(unit, "reused")):
    156                  #not in .pot, make it obsolete
    157                  unit.makeobsolete()
    158                  newlyobsoleted.append(unit)
    159              elif unit.isobsolete():
    160                  output_store.addunit(unit)
    161          for unit in newlyobsoleted:
    162              output_store.addunit(unit)
    163
    164
    165 -def _do_poheaders(input_store, output_store, template_store):
    166      """Adds initialized PO headers to output store."""
    167      # header values
    168      charset = "UTF-8"
    169      encoding = "8bit"
    170      project_id_version = None
    171      pot_creation_date = None
    172      po_revision_date = None
    173      last_translator = None
    174      language_team = None
    175      mime_version = None
    176      plural_forms = None
    177      kwargs = {}
    178
    179      if template_store is not None and isinstance(template_store, poheader.poheader):
    180          templateheadervalues = template_store.parseheader()
    181          for key, value in templateheadervalues.iteritems():
    182              if key == "Project-Id-Version":
    183                  project_id_version = value
    184              elif key == "Last-Translator":
    185                  last_translator = value
    186              elif key == "Language-Team":
    187                  language_team = value
    188              elif key == "PO-Revision-Date":
    189                  po_revision_date = value
    190              elif key in ("POT-Creation-Date", "MIME-Version"):
    191                  # don't know how to handle these keys, or ignoring them
    192                  pass
    193              elif key == "Content-Type":
    194                  kwargs[key] = value
    195              elif key == "Content-Transfer-Encoding":
    196                  encoding = value
    197              elif key == "Plural-Forms":
    198                  plural_forms = value
    199              else:
    200                  kwargs[key] = value
    201
    202      inputheadervalues = input_store.parseheader()
    203      for key, value in inputheadervalues.iteritems():
    204          if key in ("Project-Id-Version", "Last-Translator", "Language-Team", "PO-Revision-Date", "Content-Type", "Content-Transfer-Encoding", "Plural-Forms"):
    205              # want to carry these from the template so we ignore them
    206              pass
    207          elif key == "POT-Creation-Date":
    208              pot_creation_date = value
    209          elif key == "MIME-Version":
    210              mime_version = value
    211          else:
    212              kwargs[key] = value
    213
    214      output_header = output_store.makeheader(charset=charset, encoding=encoding, project_id_version=project_id_version,
    215          pot_creation_date=pot_creation_date, po_revision_date=po_revision_date, last_translator=last_translator,
    216          language_team=language_team, mime_version=mime_version, plural_forms=plural_forms, **kwargs)
    217
    218      # Get the header comments and fuzziness state
    219      if template_store is not None and len(template_store.units) > 0:
    220          if template_store.units[0].isheader():
    221              if template_store.units[0].getnotes("translator"):
    222                  output_header.addnote(template_store.units[0].getnotes("translator"), "translator")
    223              if input_store.units[0].getnotes("developer"):
    224                  output_header.addnote(input_store.units[0].getnotes("developer"), "developer")
    225              output_header.markfuzzy(template_store.units[0].isfuzzy())
    226      elif len(input_store.units) > 0 and input_store.units[0].isheader():
    227          output_header.addnote(input_store.units[0].getnotes())
    228
    229      output_store.addunit(output_header)
    230
    231
    232 -def main(argv=None):
    233      from translate.convert import convert
    234      formats = {"pot": ("po", convertpot), ("pot", "po"): ("po", convertpot),
    235                 "xlf": ("xlf", convertpot), ("xlf", "xlf"): ("xlf", convertpot),
    236              }
    237      parser = convert.ConvertOptionParser(formats, usepots=True, usetemplates=True,
    238          allowmissingtemplate=True, description=__doc__)
    239      parser.add_option("", "--tm", dest="tm", default=None,
    240          help="The file to use as translation memory when fuzzy matching")
    241      parser.passthrough.append("tm")
    242      defaultsimilarity = 75
    243      parser.add_option("-s", "--similarity", dest="min_similarity", default=defaultsimilarity,
    244          type="float", help="The minimum similarity for inclusion (default: %d%%)" % defaultsimilarity)
    245      parser.passthrough.append("min_similarity")
    246      parser.add_option("--nofuzzymatching", dest="fuzzymatching", action="store_false",
    247          default=True, help="Disable fuzzy matching")
    248      parser.passthrough.append("fuzzymatching")
    249      parser.run(argv)
    250
    251
    252  if __name__ == '__main__':
    253      main()
    254