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