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, text, origin=None, position="append"):
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