1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
34 """escape a given .rc string into a valid Python string"""
35 pystring = re.sub('"\s*\\\\\n\s*"', "", string)
36 pystring = re.sub("\\\\\\\n", "", pystring)
37 pystring = re.sub("\\\\n", "\n", pystring)
38 pystring = re.sub("\\\\t", "\t", pystring)
39 pystring = re.sub("\\\\\\\\", "\\\\", pystring)
40 return pystring
41
42
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"):
63
65 """Sets the source AND the target to be equal"""
66 self._rich_source = None
67 self._value = source or ""
68
71
72 source = property(getsource, setsource)
73
78
81 target = property(gettarget, settarget)
82
89
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
99
100 - def addnote(self, text, origin=None, position="append"):
101 self.comments.append(note)
102
104 return '\n'.join(self.comments)
105
108
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
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
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
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("/*"):
195
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
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
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
237 """convert the units back to lines"""
238 return "".join(self.blocks)
239