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"""
57
59 """Sets the source AND the target to be equal"""
60 self._value = source or ""
61
64
65 source = property(getsource, setsource)
66
68 """Note: this also sets the .source attribute!"""
69 self.source = target
70
73 target = property(gettarget, settarget)
74
81
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
91
92 - def addnote(self, text, origin=None, position="append"):
93 self.comments.append(note)
94
96 return '\n'.join(self.comments)
97
100
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
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
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
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("/*"):
184
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
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
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
226 """convert the units back to lines"""
227 return "".join(self.blocks)
228