1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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
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"""
59
61 """Sets the source AND the target to be equal"""
62 self._value = source or ""
63
66
67 source = property(getsource, setsource)
68
70 """Note: this also sets the .source attribute!"""
71 self.source = target
72
75 target = property(gettarget, settarget)
76
83
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
93
94 - def addnote(self, note, origin=None):
95 self.comments.append(note)
96
98 return '\n'.join(self.comments)
99
102
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
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
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
159 if block.startswith("STRINGTABLE"):
160
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
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("/*"):
177
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
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
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
219 """convert the units back to lines"""
220 return "".join(self.blocks)
221