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