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 Zuza Software Foundation
      5  #
      6  # This file is part of translate.
      7  #
      8  # translate 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  # translate 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 translate; if not, write to the Free Software
     20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     21
     22  """classes that hold units of .rc files (rcunit) or entire files
     23  (rcfile) these files are used in translating Windows Resources
     24
     25  @note: This implementation is based mostly on observing WINE .rc files,
     26  these should mimic other non-WINE .rc files.
     27  """
     28
     29  from translate.storage import base
     30  import re
     31  import sys
     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 -def escape_to_rc(string):
     43      """escape a given Python string into a valid .rc string"""
     44      rcstring = re.sub("\\\\", "\\\\\\\\", string)
     45      rcstring = re.sub("\t", "\\\\t", rcstring)
     46      rcstring = re.sub("\n", "\\\\n", rcstring)
     47      return rcstring
     48
     49 -class rcunit(base.TranslationUnit):
     50      """a unit of an rc file"""
     51 -    def __init__(self, source=""):
     52          """construct a blank rcunit"""
     53          super(rcunit, self).__init__(source)
     54          self.name = ""
     55          self._value = ""
     56          self.comments = []
     57          self.source = source
     58          self.match = None
     59
     60 -    def setsource(self, source):
     61          """Sets the source AND the target to be equal"""
     62          self._value = source or ""
     63
     64 -    def getsource(self):
     65          return self._value
     66
     67      source = property(getsource, setsource)
     68
     69 -    def settarget(self, target):
     70          """Note: this also sets the .source attribute!"""
     71          self.source = target
     72
     73 -    def gettarget(self):
     74          return self.source
     75      target = property(gettarget, settarget)
     76
     77 -    def __str__(self):
     78          """convert to a string. double check that unicode is handled somehow here"""
     79          source = self.getoutput()
     80          if isinstance(source, unicode):
     81              return source.encode(getattr(self, "encoding", "UTF-8"))
     82          return source
     83
     84 -    def getoutput(self):
     85          """convert the element back into formatted lines for a .rc file"""
     86          if self.isblank():
     87              return "".join(self.comments + ["\n"])
     88          else:
     89              return "".join(self.comments + ["%s=%s\n" % (self.name, self.value)])
     90
     91 -    def getlocations(self):
     92          return [self.name]
     93
     94 -    def addnote(self, note, origin=None):
     95          self.comments.append(note)
     96
     97 -    def getnotes(self, origin=None):
     98          return '\n'.join(self.comments)
     99
    100 -    def removenotes(self):
    101          self.comments = []
    102
    103 -    def isblank(self):
    104          """returns whether this is a blank element, containing only comments..."""
    105          return not (self.name or self.value)
    106
    107 -class rcfile(base.TranslationStore):
    108      """this class represents a .rc file, made up of rcunits"""
    109      UnitClass = rcunit
    110 -    def __init__(self, inputfile=None):
    111          """construct an rcfile, optionally reading in from inputfile"""
    112          super(rcfile, self).__init__(unitclass = self.UnitClass)
    113          self.filename = getattr(inputfile, 'name', '')
    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          languagesection = False
    154
    155          self.blocks = BLOCKS_RE.findall(rcsrc)
    156
    157          for blocknum, block in enumerate(self.blocks):
    158              #print block.split("\n")[0]
    159              if block.startswith("STRINGTABLE"):
    160                  #print "stringtable:\n %s------\n" % block
    161                  for match in STRINGTABLE_RE.finditer(block):
    162                      if not match.groupdict()['value']:
    163                          continue
    164                      newunit = rcunit(escape_to_python(match.groupdict()['value']))
    165                      newunit.name = "STRINGTABLE." + match.groupdict()['name']
    166                      newunit.match = match
    167                      self.addunit(newunit)
    168              if block.startswith("LANGUAGE"):
    169                  #print "language"
    170                  if not languagesection:
    171                      languagesection = True
    172                  else:
    173                      print >> sys.stderr, "Can only process one language section"
    174                      self.blocks = self.blocks[:blocknum]
    175                      break
    176              if block.startswith("/*"):  # Comments
    177                  #print "comment"
    178                  pass
    179              if re.match("[0-9A-Z_]+\s+DIALOG", block) is not None:
    180                  dialog = re.match("(?P<dialogname>[0-9A-Z_]+)\s+(?P<dialogtype>DIALOGEX|DIALOG)", block).groupdict()
    181                  dialogname = dialog["dialogname"]
    182                  dialogtype = dialog["dialogtype"]
    183                  #print "dialog: %s" % dialogname
    184                  for match in DIALOG_RE.finditer(block):
    185                      if not match.groupdict()['value']:
    186                          continue
    187                      type = match.groupdict()['type']
    188                      value = match.groupdict()['value']
    189                      name = match.groupdict()['name']
    190                      newunit = rcunit(escape_to_python(value))
    191                      if type == "CAPTION" or type == "Caption":
    192                          newunit.name = "%s.%s.%s" % (dialogtype, dialogname, type)
    193                      elif name == "-1":
    194                          newunit.name = "%s.%s.%s.%s" % (dialogtype, dialogname, type, value.replace(" ", "_"))
    195                      else:
    196                          newunit.name = "%s.%s.%s.%s" % (dialogtype, dialogname, type, name)
    197                      newunit.match = match
    198                      self.addunit(newunit)
    199              if re.match("[0-9A-Z_]+\s+MENU", block) is not None:
    200                  menuname = re.match("(?P<menuname>[0-9A-Z_]+)\s+MENU", block).groupdict()["menuname"]
    201                  #print "menu: %s" % menuname
    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 == "POPUP":
    210                          newunit.name = "MENU.%s.%s" % (menuname, type)
    211                      elif name == "-1":
    212                          newunit.name = "MENU.%s.%s.%s" % (menuname, type, value.replace(" ", "_"))
    213                      else:
    214                          newunit.name = "MENU.%s.%s.%s" % (menuname, type, name)
    215                      newunit.match = match
    216                      self.addunit(newunit)
    217
    218 -    def __str__(self):
    219          """convert the units back to lines"""
    220          return "".join(self.blocks)
    221