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 from translate.storage import base
29 import re
30
32 """escape a given .rc string into a valid Python string"""
33 pystring = re.sub('"\s*\\\\\n\s*"', "", string)
34 pystring = re.sub("\\\\\\\n", "", pystring)
35 pystring = re.sub("\\\\n", "\n", pystring)
36 pystring = re.sub("\\\\t", "\t", pystring)
37 pystring = re.sub("\\\\\\\\", "\\\\", pystring)
38 return pystring
39
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
60 """Sets the source AND the target to be equal"""
61 self._rich_source = None
62 self._value = source or ""
63
66
67 source = property(getsource, setsource)
68
70 """Note: this also sets the .source attribute!"""
71 self._rich_target = None
72 self.source = target
73
76 target = property(gettarget, settarget)
77
84
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
94
95 - def addnote(self, text, origin=None, position="append"):
96 self.comments.append(note)
97
99 return '\n'.join(self.comments)
100
103
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
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
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
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("/*"):
188
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
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
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
230 """convert the units back to lines"""
231 return "".join(self.blocks)
232