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