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