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