Package translate :: Package tools :: Module podebug

[hide private]
[frames] | no frames]

# Source Code for Module translate.tools.podebug

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  #
      4  # Copyright 2004-2006,2008-2009 Zuza Software Foundation
      5  #
      6  # This file is part of the Translate Toolkit.
      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  """Insert debug messages into XLIFF and Gettext PO localization files
     22
     23  See: http://translate.sourceforge.net/wiki/toolkit/podebug for examples and
     24  usage instructions.
     25  """
     26
     27  import os
     28  import re
     29
     30  from translate.misc import hash
     31  from translate.storage import factory
     32  from translate.storage.placeables import StringElem, general
     33  from translate.storage.placeables import parse as rich_parse
     34  from translate.convert import dtd2po
     35
     36
     37 -def add_prefix(prefix, stringelems):
     38      for stringelem in stringelems:
     39          for string in stringelem.flatten():
     40              if len(string.sub) > 0:
     41                  string.sub[0] = prefix + string.sub[0]
     42      return stringelems
     43
     44  podebug_parsers = general.parsers
     45  podebug_parsers.remove(general.CapsPlaceable.parse)
     46  podebug_parsers.remove(general.CamelCasePlaceable.parse)
     47
     48
     49 -class podebug:
     50
     51 -    def __init__(self, format=None, rewritestyle=None, ignoreoption=None):
     52          if format is None:
     53              self.format = ""
     54          else:
     55              self.format = format
     56          self.rewritefunc = getattr(self, "rewrite_%s" % rewritestyle, None)
     57          self.ignorefunc = getattr(self, "ignore_%s" % ignoreoption, None)
     58
     59 -    def apply_to_translatables(self, string, func):
     60          """Applies func to all translatable strings in string."""
     61          string.map(
     62              lambda e: e.apply_to_strings(func),
     63              lambda e: e.isleaf() and e.istranslatable)
     64
     65 -    def rewritelist(cls):
     66          return [rewrite.replace("rewrite_", "") for rewrite in dir(cls) if rewrite.startswith("rewrite_")]
     67      rewritelist = classmethod(rewritelist)
     68
     69 -    def _rewrite_prepend_append(self, string, prepend, append=None):
     70          if append is None:
     71              append = prepend
     72          if not isinstance(string, StringElem):
     73              string = StringElem(string)
     74          string.sub.insert(0, prepend)
     75          if unicode(string).endswith(u'\n'):
     76              # Try and remove the last character from the tree
     77              try:
     78                  lastnode = string.flatten()[-1]
     79                  if isinstance(lastnode.sub[-1], unicode):
     80                      lastnode.sub[-1] = lastnode.sub[-1].rstrip(u'\n')
     81              except IndexError:
     82                  pass
     83              string.sub.append(append + u'\n')
     84          else:
     85              string.sub.append(append)
     86          return string
     87
     88 -    def rewrite_xxx(self, string):
     89          return self._rewrite_prepend_append(string, u"xxx")
     90
     91 -    def rewrite_bracket(self, string):
     92          return self._rewrite_prepend_append(string, u"[", u"]")
     93
     94 -    def rewrite_en(self, string):
     95          if not isinstance(string, StringElem):
     96              string = StringElem(string)
     97          return string
     98
     99 -    def rewrite_blank(self, string):
    100          return StringElem(u"")
    101
    102 -    def rewrite_chef(self, string):
    103          """Rewrite using Mock Swedish as made famous by Monty Python"""
    104          if not isinstance(string, StringElem):
    105              string = StringElem(string)
    106          # From Dive into Python which itself got it elsewhere
    107          # http://www.renderx.com/demos/examples/diveintopython.pdf
    108          subs = (
    109                 (r'a([nu])', r'u\1'),
    110                 (r'A([nu])', r'U\1'),
    111                 (r'a\B', r'e'),
    112                 (r'A\B', r'E'),
    113                 (r'en\b', r'ee'),
    114                 (r'\Bew', r'oo'),
    115                 (r'\Be\b', r'e-a'),
    116                 (r'\be', r'i'),
    117                 (r'\bE', r'I'),
    118                 (r'\Bf', r'ff'),
    119                 (r'\Bir', r'ur'),
    120                 (r'(\w*?)i(\w*?)$', r'\1ee\2'),
    121                 (r'\bow', r'oo'),
    122                 (r'\bo', r'oo'),
    123                 (r'\bO', r'Oo'),
    124                 (r'the', r'zee'),
    125                 (r'The', r'Zee'),
    126                 (r'th\b', r't'),
    127                 (r'\Btion', r'shun'),
    128                 (r'\Bu', r'oo'),
    129                 (r'\BU', r'Oo'),
    130                 (r'v', r'f'),
    131                 (r'V', r'F'),
    132                 (r'w', r'w'),
    133                 (r'W', r'W'),
    134                 (r'([a-z])[.]', r'\1. Bork Bork Bork!'))
    135          for a, b in subs:
    136              self.apply_to_translatables(string, lambda s: re.sub(a, b, s))
    137          return string
    138
    139      REWRITE_UNICODE_MAP = u"ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ" + u"[\\]^_`" + u"ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ"
    140
    141 -    def rewrite_unicode(self, string):
    142          """Convert to Unicode characters that look like the source string"""
    143          if not isinstance(string, StringElem):
    144              string = StringElem(string)
    145
    146          def transpose(char):
    147              loc = ord(char)-65
    148              if loc < 0 or loc > 56:
    149                  return char
    150              return self.REWRITE_UNICODE_MAP[loc]
    151
    152          def transformer(s):
    153              return ''.join([transpose(c) for c in s])
    154          self.apply_to_translatables(string, transformer)
    155          return string
    156
    157      REWRITE_FLIPPED_MAP = u"¡„#$%⅋,()⁎+´-·/012Ɛᔭ59Ƚ86:;<=>?@" + \
    158              u"∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅＭX⅄Z" + u"[\\]ᵥ_," + \
    159              u"ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz"
    160          # Brackets should be swapped if the string will be reversed in memory.
    161          # If a right-to-left override is used, the brackets should be
    162          # unchanged.
    163          #Some alternatives:
    164          # D: ᗡ◖
    165          # K: Ж⋊Ӽ
    166          # @: Ҩ - Seems only related in Dejavu Sans
    167          # Q: Ὄ Ό Ὀ Ὃ Ὄ Ṑ Ò Ỏ
    168          # _: ‾ - left out for now for the sake of GTK accelerators
    169
    170 -    def rewrite_flipped(self, string):
    171          """Convert the string to look flipped upside down."""
    172          if not isinstance(string, StringElem):
    173              string = StringElem(string)
    174
    175          def transpose(char):
    176              loc = ord(char)-33
    177              if loc < 0 or loc > 89:
    178                  return char
    179              return self.REWRITE_FLIPPED_MAP[loc]
    180
    181          def transformer(s):
    182              return u"\u202e" + u''.join([transpose(c) for c in s])
    183              # To reverse instead of using the RTL override:
    184              #return u''.join(reversed([transpose(c) for c in s]))
    185          self.apply_to_translatables(string, transformer)
    186          return string
    187
    188 -    def ignorelist(cls):
    189          return [ignore.replace("ignore_", "") for ignore in dir(cls) if ignore.startswith("ignore_")]
    190      ignorelist = classmethod(ignorelist)
    191
    192 -    def ignore_openoffice(self, unit):
    193          for location in unit.getlocations():
    194              if location.startswith("Common.xcu#..Common.View.Localisation"):
    195                  return True
    196              elif location.startswith("profile.lng#STR_DIR_MENU_NEW_"):
    197                  return True
    198              elif location.startswith("profile.lng#STR_DIR_MENU_WIZARD_"):
    199                  return True
    200          return False
    201
    202 -    def ignore_mozilla(self, unit):
    203          locations = unit.getlocations()
    204          if len(locations) == 1 and locations[0].lower().endswith(".accesskey"):
    205              return True
    206          for location in locations:
    207              if dtd2po.is_css_entity(location):
    208                  return True
    209              if location in ["brandShortName", "brandFullName", "vendorShortName"]:
    210                  return True
    211              if location.lower().endswith(".commandkey") or location.endswith(".key"):
    212                  return True
    213          return False
    214
    215 -    def ignore_gtk(self, unit):
    216          if unit.source == "default:LTR":
    217              return True
    218          return False
    219
    220 -    def ignore_kde(self, unit):
    221          if unit.source == "LTR":
    222              return True
    223          return False
    224
    225 -    def convertunit(self, unit, prefix):
    226          if self.ignorefunc:
    227              if self.ignorefunc(unit):
    228                  return unit
    229          if prefix.find("@hash_placeholder@") != -1:
    230              if unit.getlocations():
    231                  hashable = unit.getlocations()[0]
    232              else:
    233                  hashable = unit.source
    234              prefix = prefix.replace("@hash_placeholder@", hash.md5_f(hashable).hexdigest()[:self.hash_len])
    235          rich_source = unit.rich_source
    236          if not isinstance(rich_source, StringElem):
    237              rich_source = [rich_parse(string, podebug_parsers) for string in rich_source]
    238          if self.rewritefunc:
    239              rewritten = [self.rewritefunc(string) for string in rich_source]
    240              if rewritten:
    241                  unit.rich_target = rewritten
    242          elif not unit.istranslated():
    243              unit.rich_target = unit.rich_source
    244          unit.rich_target = add_prefix(prefix, unit.rich_target)
    245          return unit
    246
    247 -    def convertstore(self, store):
    248          filename = self.shrinkfilename(store.filename)
    249          prefix = self.format
    250          for formatstr in re.findall("%[0-9c]*[sfFbBdh]", self.format):
    251              if formatstr.endswith("s"):
    252                  formatted = self.shrinkfilename(store.filename)
    253              elif formatstr.endswith("f"):
    254                  formatted = store.filename
    255                  formatted = os.path.splitext(formatted)[0]
    256              elif formatstr.endswith("F"):
    257                  formatted = store.filename
    258              elif formatstr.endswith("b"):
    259                  formatted = os.path.basename(store.filename)
    260                  formatted = os.path.splitext(formatted)[0]
    261              elif formatstr.endswith("B"):
    262                  formatted = os.path.basename(store.filename)
    263              elif formatstr.endswith("d"):
    264                  formatted = os.path.dirname(store.filename)
    265              elif formatstr.endswith("h"):
    266                  try:
    267                      self.hash_len = int(filter(str.isdigit, formatstr[1:-1]))
    268                  except ValueError:
    269                      self.hash_len = 4
    270                  formatted = "@hash_placeholder@"
    271              else:
    272                  continue
    273              formatoptions = formatstr[1:-1]
    274              if formatoptions and not formatstr.endswith("h"):
    275                  if "c" in formatoptions and formatted:
    276                      formatted = formatted[0] + filter(lambda x: x.lower() not in "aeiou", formatted[1:])
    277                  length = filter(str.isdigit, formatoptions)
    278                  if length:
    279                      formatted = formatted[:int(length)]
    280              prefix = prefix.replace(formatstr, formatted)
    281          for unit in store.units:
    282              if not unit.istranslatable():
    283                  continue
    284              unit = self.convertunit(unit, prefix)
    285          return store
    286
    287 -    def shrinkfilename(self, filename):
    288          if filename.startswith("." + os.sep):
    289              filename = filename.replace("." + os.sep, "", 1)
    290          dirname = os.path.dirname(filename)
    291          dirparts = dirname.split(os.sep)
    292          if not dirparts:
    293              dirshrunk = ""
    294          else:
    295              dirshrunk = dirparts[0][:4] + "-"
    296              if len(dirparts) > 1:
    297                  dirshrunk += "".join([dirpart[0] for dirpart in dirparts[1:]]) + "-"
    298          baseshrunk = os.path.basename(filename)[:4]
    299          if "." in baseshrunk:
    300              baseshrunk = baseshrunk[:baseshrunk.find(".")]
    301          return dirshrunk + baseshrunk
    302
    303
    304 -def convertpo(inputfile, outputfile, templatefile, format=None, rewritestyle=None, ignoreoption=None):
    305      """Reads in inputfile, changes it to have debug strings, writes to outputfile."""
    306      # note that templatefile is not used, but it is required by the converter...
    307      inputstore = factory.getobject(inputfile)
    308      if inputstore.isempty():
    309          return 0
    310      convertor = podebug(format=format, rewritestyle=rewritestyle, ignoreoption=ignoreoption)
    311      outputstore = convertor.convertstore(inputstore)
    312      outputfile.write(str(outputstore))
    313      return 1
    314
    315
    316 -def main():
    317      from translate.convert import convert
    318      formats = {"po": ("po", convertpo), "pot": ("po", convertpo), "xlf": ("xlf", convertpo)}
    319      parser = convert.ConvertOptionParser(formats, description=__doc__)
    320      # TODO: add documentation on format strings...
    321      parser.add_option("-f", "--format", dest="format", default="",
    322          help="specify format string")
    323      parser.add_option("", "--rewrite", dest="rewritestyle",
    324          type="choice", choices=podebug.rewritelist(), metavar="STYLE",
    325          help="the translation rewrite style: %s" % ", ".join(podebug.rewritelist()))
    326      parser.add_option("", "--ignore", dest="ignoreoption",
    327          type="choice", choices=podebug.ignorelist(), metavar="APPLICATION",
    328          help="apply tagging ignore rules for the given application: %s" % ", ".join(podebug.ignorelist()))
    329      parser.passthrough.append("format")
    330      parser.passthrough.append("rewritestyle")
    331      parser.passthrough.append("ignoreoption")
    332      parser.run()
    333
    334
    335  if __name__ == '__main__':
    336      main()
    337