Package translate :: Package storage :: Module rc |
[hide private]
[frames] | no frames]

# Source Code for Module translate.storage.rc

      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  """Classes that hold units of .rc files (rcunit) or entire files
     22  (rcfile) used in translating Windows Resources.
     23
     24  @note: This implementation is based mostly on observing WINE .rc files,
     25  these should mimic other non-WINE .rc files.
     26  """
     27
     28  import re
     29
     30  from translate.storage import base
     31
     32
     33 -def escape_to_python(string):
     34      """escape a given .rc string into a valid Python string"""
     35      pystring = re.sub('"\s*\\\\\n\s*"', "", string)   # xxx"\n"xxx line continuation
     36      pystring = re.sub("\\\\\\\n", "", pystring)       # backslash newline line continuation
     37      pystring = re.sub("\\\\n", "\n", pystring)        # Convert escaped newline to a real newline
     38      pystring = re.sub("\\\\t", "\t", pystring)        # Convert escape tab to a real tab
     39      pystring = re.sub("\\\\\\\\", "\\\\", pystring)   # Convert escape backslash to a real escaped backslash
     40      return pystring
     41
     42
     43 -def escape_to_rc(string):
     44      """Escape a given Python string into a valid .rc string."""
     45      rcstring = re.sub("\\\\", "\\\\\\\\", string)
     46      rcstring = re.sub("\t", "\\\\t", rcstring)
     47      rcstring = re.sub("\n", "\\\\n", rcstring)
     48      return rcstring
     49
     50
     51 -class rcunit(base.TranslationUnit):
     52      """A unit of an rc file"""
     53
     54 -    def __init__(self, source="", encoding="cp1252"):
     55          """Construct a blank rcunit."""
     56          super(rcunit, self).__init__(source)
     57          self.name = ""
     58          self._value = ""
     59          self.comments = []
     60          self.source = source
     61          self.match = None
     62          self.encoding = encoding
     63
     64 -    def setsource(self, source):
     65          """Sets the source AND the target to be equal"""
     66          self._rich_source = None
     67          self._value = source or ""
     68
     69 -    def getsource(self):
     70          return self._value
     71
     72      source = property(getsource, setsource)
     73
     74 -    def settarget(self, target):
     75          """Note: this also sets the .source attribute!"""
     76          self._rich_target = None
     77          self.source = target
     78
     79 -    def gettarget(self):
     80          return self.source
     81      target = property(gettarget, settarget)
     82
     83 -    def __str__(self):
     84          """Convert to a string. Double check that unicode is handled somehow here."""
     85          source = self.getoutput()
     86          if isinstance(source, unicode):
     87              return source.encode(getattr(self, "encoding", "UTF-8"))
     88          return source
     89
     90 -    def getoutput(self):
     91          """Convert the element back into formatted lines for a .rc file."""
     92          if self.isblank():
     93              return "".join(self.comments + ["\n"])
     94          else:
     95              return "".join(self.comments + ["%s=%s\n" % (self.name, self.value)])
     96
     97 -    def getlocations(self):
     98          return [self.name]
     99
    100 -    def addnote(self, text, origin=None, position="append"):
    101          self.comments.append(note)
    102
    103 -    def getnotes(self, origin=None):
    104          return '\n'.join(self.comments)
    105
    106 -    def removenotes(self):
    107          self.comments = []
    108
    109 -    def isblank(self):
    110          """Returns whether this is a blank element, containing only comments."""
    111          return not (self.name or self.value)
    112
    113
    114 -class rcfile(base.TranslationStore):
    115      """This class represents a .rc file, made up of rcunits."""
    116      UnitClass = rcunit
    117
    118 -    def __init__(self, inputfile=None, lang=None, sublang=None, encoding="cp1252"):
    119          """Construct an rcfile, optionally reading in from inputfile."""
    120          self.encoding = encoding
    121          super(rcfile, self).__init__(unitclass=self.UnitClass)
    122          self.filename = getattr(inputfile, 'name', '')
    123          self.lang = lang
    124          self.sublang = sublang
    125          if inputfile is not None:
    126              rcsrc = inputfile.read().decode(encoding)
    127              inputfile.close()
    128              self.parse(rcsrc)
    129
    130 -    def parse(self, rcsrc):
    131          """Read the source of a .rc file in and include them as units."""
    132          BLOCKS_RE = re.compile("""
    133                           (?:
    134                           LANGUAGE\s+[^\n]*|                              # Language details
    135                           /\*.*?\*/[^\n]*|                                      # Comments
    136                           (?:[0-9A-Z_]+\s+(?:MENU|DIALOG|DIALOGEX)|STRINGTABLE)\s  # Translatable section
    137                           .*?
    138                           (?:
    139                           BEGIN(?:\s*?POPUP.*?BEGIN.*?END\s*?)+?END|BEGIN.*?END|  # FIXME Need a much better approach to nesting menus
    140                           {(?:\s*?POPUP.*?{.*?}\s*?)+?}|{.*?})+[\n]|
    141                           \s*[\n]         # Whitespace
    142                           )
    143                           """, re.DOTALL + re.VERBOSE)
    144          STRINGTABLE_RE = re.compile("""
    145                           (?P<name>[0-9A-Za-z_]+?),?\s*
    146                           L?"(?P<value>.*?)"\s*[\n]
    147                           """, re.DOTALL + re.VERBOSE)
    148          DIALOG_RE = re.compile("""
    149                           (?P<type>AUTOCHECKBOX|AUTORADIOBUTTON|CAPTION|Caption|CHECKBOX|CTEXT|CONTROL|DEFPUSHBUTTON|
    150                           GROUPBOX|LTEXT|PUSHBUTTON|RADIOBUTTON|RTEXT)  # Translatable types
    151                           \s+
    152                           L?                                    # Unkown prefix see ./dlls/shlwapi/shlwapi_En.rc
    153                           "(?P<value>.*?)"                                      # String value
    154                           (?:\s*,\s*|[\n])                          # FIXME ./dlls/mshtml/En.rc ID_DWL_DIALOG.LTEXT.ID_DWL_STATUS
    155                           (?P<name>.*?|)\s*(?:/[*].*?[*]/|),
    156                           """, re.DOTALL + re.VERBOSE)
    157          MENU_RE = re.compile("""
    158                           (?P<type>POPUP|MENUITEM)
    159                           \s+
    160                           "(?P<value>.*?)"                                      # String value
    161                           (?:\s*,?\s*)?
    162                           (?P<name>[^\s]+).*?[\n]
    163                           """, re.DOTALL + re.VERBOSE)
    164
    165          processsection = False
    166          self.blocks = BLOCKS_RE.findall(rcsrc)
    167          for blocknum, block in enumerate(self.blocks):
    168              #print block.split("\n")[0]
    169              processblock = None
    170              if block.startswith("LANGUAGE"):
    171                  if self.lang == None or self.sublang == None or re.match("LANGUAGE\s+%s,\s*%s\s*$" % (self.lang, self.sublang), block) is not None:
    172                      processsection = True
    173                  else:
    174                      processsection = False
    175              else:
    176                  if re.match(".+LANGUAGE\s+[0-9A-Za-z_]+,\s*[0-9A-Za-z_]+\s*[\n]", block, re.DOTALL) is not None:
    177                      if re.match(".+LANGUAGE\s+%s,\s*%s\s*[\n]" % (self.lang, self.sublang), block, re.DOTALL) is not None:
    178                          processblock = True
    179                      else:
    180                          processblock = False
    181
    182              if not (processblock == True or (processsection == True and processblock != False)):
    183                  continue
    184
    185              if block.startswith("STRINGTABLE"):
    186                  #print "stringtable:\n %s------\n" % block
    187                  for match in STRINGTABLE_RE.finditer(block):
    188                      if not match.groupdict()['value']:
    189                          continue
    190                      newunit = rcunit(escape_to_python(match.groupdict()['value']))
    191                      newunit.name = "STRINGTABLE." + match.groupdict()['name']
    192                      newunit.match = match
    193                      self.addunit(newunit)
    194              if block.startswith("/*"):  # Comments
    195                  #print "comment"
    196                  pass
    197              if re.match("[0-9A-Z_]+\s+DIALOG", block) is not None:
    198                  dialog = re.match("(?P<dialogname>[0-9A-Z_]+)\s+(?P<dialogtype>DIALOGEX|DIALOG)", block).groupdict()
    199                  dialogname = dialog["dialogname"]
    200                  dialogtype = dialog["dialogtype"]
    201                  #print "dialog: %s" % dialogname
    202                  for match in DIALOG_RE.finditer(block):
    203                      if not match.groupdict()['value']:
    204                          continue
    205                      type = match.groupdict()['type']
    206                      value = match.groupdict()['value']
    207                      name = match.groupdict()['name']
    208                      newunit = rcunit(escape_to_python(value))
    209                      if type == "CAPTION" or type == "Caption":
    210                          newunit.name = "%s.%s.%s" % (dialogtype, dialogname, type)
    211                      elif name == "-1":
    212                          newunit.name = "%s.%s.%s.%s" % (dialogtype, dialogname, type, value.replace(" ", "_"))
    213                      else:
    214                          newunit.name = "%s.%s.%s.%s" % (dialogtype, dialogname, type, name)
    215                      newunit.match = match
    216                      self.addunit(newunit)
    217              if re.match("[0-9A-Z_]+\s+MENU", block) is not None:
    218                  menuname = re.match("(?P<menuname>[0-9A-Z_]+)\s+MENU", block).groupdict()["menuname"]
    219                  #print "menu: %s" % menuname
    220                  for match in MENU_RE.finditer(block):
    221                      if not match.groupdict()['value']:
    222                          continue
    223                      type = match.groupdict()['type']
    224                      value = match.groupdict()['value']
    225                      name = match.groupdict()['name']
    226                      newunit = rcunit(escape_to_python(value))
    227                      if type == "POPUP":
    228                          newunit.name = "MENU.%s.%s" % (menuname, type)
    229                      elif name == "-1":
    230                          newunit.name = "MENU.%s.%s.%s" % (menuname, type, value.replace(" ", "_"))
    231                      else:
    232                          newunit.name = "MENU.%s.%s.%s" % (menuname, type, name)
    233                      newunit.match = match
    234                      self.addunit(newunit)
    235
    236 -    def __str__(self):
    237          """convert the units back to lines"""
    238          return "".join(self.blocks)
    239