OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python2.4 | |
2 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 '''Support for "policy_templates.json" format used by the policy template | |
7 generator as a source for generating ADM,ADMX,etc files.''' | |
8 | |
9 import types | |
10 import pprint | |
11 import re | |
12 | |
13 from grit.gather import regexp | |
14 from grit import util | |
15 from grit import tclib | |
16 from xml.dom import minidom | |
17 | |
18 | |
19 class PolicyJson(regexp.RegexpGatherer): | |
20 '''Collects and translates the following strings from policy_templates.json: | |
21 - captions,descriptions and labels of policies | |
22 - captions of enumeration items | |
23 - misc strings from the 'messages' section | |
24 Translatable strings may have untranslateable placeholders with the same | |
25 format that is used in .grd files. | |
26 ''' | |
27 | |
28 def __init__(self, text): | |
29 if util.IsExtraVerbose(): | |
30 print text | |
31 regexp.RegexpGatherer.__init__(self, text) | |
32 | |
33 def _ParsePlaceholder(self, placeholder, msg): | |
34 '''Extracts a placeholder from a DOM node and adds it to a tclib Message. | |
35 | |
36 Args: | |
37 placeholder: A DOM node of the form: | |
38 <ph name="PLACEHOLDER_NAME">Placeholder text<ex>Example value</ex></ph> | |
39 msg: The placeholder is added to this message. | |
40 ''' | |
41 text = [] | |
42 example_text = [] | |
43 for node1 in placeholder.childNodes: | |
44 if (node1.nodeType == minidom.Node.TEXT_NODE): | |
45 text.append(node1.data) | |
46 elif (node1.nodeType == minidom.Node.ELEMENT_NODE and | |
47 node1.tagName == 'ex'): | |
48 for node2 in node1.childNodes: | |
49 example_text.append(node2.toxml()) | |
50 else: | |
51 raise Exception('Unexpected element inside a placeholder: ' + | |
52 node2.toxml()) | |
53 if example_text == []: | |
54 # In such cases the original text is okay for an example. | |
55 example_text = text | |
56 msg.AppendPlaceholder(tclib.Placeholder( | |
57 placeholder.attributes['name'].value, | |
58 ''.join(text).strip(), | |
59 ''.join(example_text).strip())) | |
60 | |
61 def _ParseMessage(self, string, desc): | |
62 '''Parses a given string and adds it to the output as a translatable chunk | |
63 with a given description. | |
64 | |
65 Args: | |
66 string: The message string to parse. | |
67 desc: The description of the message (for the translators). | |
68 ''' | |
69 msg = tclib.Message(description=desc) | |
70 xml = '<msg>' + string + '</msg>' | |
71 node = minidom.parseString(xml).childNodes[0] | |
72 for child in node.childNodes: | |
73 if child.nodeType == minidom.Node.TEXT_NODE: | |
74 msg.AppendText(child.data) | |
75 elif child.nodeType == minidom.Node.ELEMENT_NODE: | |
76 if child.tagName == 'ph': | |
77 self._ParsePlaceholder(child, msg) | |
78 else: | |
79 raise Exception("Not implemented.") | |
80 else: | |
81 raise Exception("Not implemented.") | |
82 self.skeleton_.append(self.uberclique.MakeClique(msg)) | |
Jói
2011/01/10 21:16:50
Don't you get into problems with JSON escaping whe
gfeher
2011/01/11 20:09:11
The output seems fine. I've diffed all the outputt
Jói
2011/01/11 20:24:57
OK.
gfeher
2011/01/12 12:18:33
I probably won't need to override Escape.
| |
83 | |
84 def _ParseNode(self, node): | |
85 '''Traverses the subtree of a DOM node, and register a tclib message for | |
86 all the <message> nodes. | |
87 ''' | |
88 att_text = [] | |
89 if node.attributes: | |
90 items = node.attributes.items() | |
91 items.sort() | |
92 for key, value in items: | |
93 att_text.append(' %s=\"%s\"' % (key, value)) | |
94 self._AddNontranslateableChunk("<%s%s>" % | |
95 (node.tagName, ''.join(att_text))) | |
96 if node.tagName == 'message': | |
97 msg = tclib.Message(description=node.attributes['desc']) | |
98 for child in node.childNodes: | |
99 if child.nodeType == minidom.Node.TEXT_NODE: | |
100 if msg == None: | |
101 self._AddNontranslateableChunk(child.data) | |
102 else: | |
103 msg.AppendText(child.data) | |
104 elif child.nodeType == minidom.Node.ELEMENT_NODE: | |
105 if child.tagName == 'ph': | |
106 self._ParsePlaceholder(child, msg) | |
107 else: | |
108 assert False | |
109 self.skeleton_.append(self.uberclique.MakeClique(msg)) | |
110 else: | |
111 for child in node.childNodes: | |
112 if child.nodeType == minidom.Node.TEXT_NODE: | |
113 self._AddNontranslateableChunk(child.data) | |
114 elif node.nodeType == minidom.Node.ELEMENT_NODE: | |
115 self._ParseNode(child) | |
116 | |
117 self._AddNontranslateableChunk("</%s>" % node.tagName) | |
118 | |
119 def _AddIndentedNontranslateableChunk(self, depth, string): | |
120 '''Adds a nontranslateable chunk of text to the internally stored output. | |
121 | |
122 Args: | |
123 depth: The number of double spaces to prepend to the next argument string. | |
124 string: The chunk of text to add. | |
125 ''' | |
126 result = [] | |
127 while depth > 0: | |
128 result.append(' ') | |
129 depth = depth - 1 | |
130 result.append(string) | |
131 self._AddNontranslateableChunk(''.join(result)) | |
132 | |
133 def _GetDescription(self, item, item_type, parent_item, key): | |
134 '''Creates a description for a translatable message. The description gives | |
135 some context for the person who will translate this message. | |
136 | |
137 Args: | |
138 item: A policy or an enumeration item. | |
139 item_type: 'enum_item' | 'policy' | |
140 parent_item: The owner of item. (A policy of type group or enum.) | |
141 key: The name of the key to parse. | |
142 depth: The level of indentation. | |
143 ''' | |
144 key_map = { | |
145 'desc': 'Description', | |
146 'caption': 'Caption', | |
147 'label': 'Label', | |
148 } | |
149 if type == 'policy': | |
150 return '%s of the policy named %s' % (key_map[key], item['name']) | |
151 elif type == 'enum_item': | |
152 return ('%s of the option named %s in policy %s' % | |
153 (key_map[key], item['name'], parent_item['name'])) | |
154 | |
155 def _AddPolicyKey(self, item, item_type, parent_item, key, depth): | |
156 '''Given a policy/enumeration item and a key, adds that key and its value | |
157 into the output. | |
158 E.g.: | |
159 'example_value': 123 | |
160 If key indicates that the value is a translatable string, then it is parsed | |
161 as a translatable string. | |
162 | |
163 Args: | |
164 item: A policy or an enumeration item. | |
165 item_type: 'enum_item' | 'policy' | |
166 parent_item: The owner of item. (A policy of type group or enum.) | |
167 key: The name of the key to parse. | |
168 depth: The level of indentation. | |
169 ''' | |
170 self._AddIndentedNontranslateableChunk(depth, "'%s': " % key) | |
171 if key in ('desc', 'caption', 'label'): | |
172 self._AddNontranslateableChunk("'''") | |
173 self._ParseMessage( | |
174 item[key], | |
175 self._GetDescription(item, item_type, parent_item, key)) | |
176 self._AddNontranslateableChunk("''',\n") | |
177 else: | |
178 str_val = item[key] | |
179 if type(str_val) == types.StringType: | |
180 str_val = "'%s'" % str_val | |
181 else: | |
182 str_val = str(str_val) | |
183 self._AddNontranslateableChunk(str_val + ',\n') | |
184 | |
185 def _AddItems(self, items, item_type, parent_item, depth): | |
186 '''Parses and adds a list of items from the JSON file. Items can be policies | |
187 or parts of an enum policy. | |
188 | |
189 Args: | |
190 items: Either a list of policies or a list of dictionaries. | |
191 item_type: 'enum_item' | 'policy' | |
192 parent_item: If items contains a list of policies, then this is the policy | |
193 group that owns them. If items contains a list of enumeration items, | |
194 then this is the enum policy that holds them. | |
195 depth: Indicates the depth of our position in the JSON hierarchy. Used to | |
196 add nice line-indent to the output. | |
197 ''' | |
198 for item1 in items: | |
199 self._AddIndentedNontranslateableChunk(depth, "{\n") | |
200 for key in item1.keys(): | |
201 if key == 'items': | |
202 self._AddIndentedNontranslateableChunk(depth + 1, "'items': [\n") | |
203 self._AddItems(item1['items'], 'enum_item', item1, depth + 2) | |
204 self._AddIndentedNontranslateableChunk(depth + 1, "],\n") | |
205 if key == 'policies': | |
206 self._AddIndentedNontranslateableChunk(depth + 1, "'policies': [\n") | |
207 self._AddItems(item1['policies'], 'policy', item1, depth + 2) | |
208 self._AddIndentedNontranslateableChunk(depth + 1, "],\n") | |
209 else: | |
210 self._AddPolicyKey(item1, item_type, parent_item, key, depth + 1) | |
211 self._AddIndentedNontranslateableChunk(depth, "},\n") | |
212 | |
213 def _AddMessages(self): | |
214 '''Processed and adds the 'messages' section to the output.''' | |
215 self._AddNontranslateableChunk(" 'messages': {\n") | |
216 for name, message in self.data['messages'].iteritems(): | |
217 self._AddNontranslateableChunk(" '%s': {\n" % name) | |
Jói
2011/01/10 21:16:50
use two space indent
gfeher
2011/01/11 20:09:11
Done.
| |
218 self._AddNontranslateableChunk(" 'text': '''") | |
219 self._ParseMessage(message['text'], message['desc']) | |
220 self._AddNontranslateableChunk("'''\n") | |
221 self._AddNontranslateableChunk(" },\n") | |
222 self._AddNontranslateableChunk(" },\n") | |
223 | |
224 def _AddPlaceholders(self): | |
225 '''Adds the 'placeholders' section to the output.''' | |
226 self._AddNontranslateableChunk(" 'placeholders':\n") | |
227 self._AddNontranslateableChunk( | |
228 pprint.pformat(self.data['placeholders'], indent=2, depth=2)) | |
229 | |
230 # Although we use the RegexpGatherer base class, we do not use the | |
Jói
2011/01/10 21:16:50
This is confusing, since the inheritance implies a
gfeher
2011/01/11 20:09:11
I agree.
Jói
2011/01/11 20:24:57
I would recommend extracting the base class as dis
gfeher
2011/01/12 12:18:33
Done.
| |
231 # _RegExpParse method of that class to implement Parse(). Instead, we | |
232 # parse using a DOM parser. | |
233 def Parse(self): | |
234 if (self.have_parsed_): | |
Jói
2011/01/10 21:16:50
looks like you are forgetting to set self.have_par
gfeher
2011/01/11 20:09:11
Done. This was copied from muppet_strings.py, shou
Jói
2011/01/11 20:24:57
Yes please.
gfeher
2011/01/12 12:18:33
Done.
| |
235 return | |
236 | |
237 self.data = eval(self.text_) | |
238 | |
239 self._AddNontranslateableChunk('{\n') | |
240 self._AddNontranslateableChunk(" 'policy_definitions': [\n") | |
241 self._AddItems(self.data['policy_definitions'], 'policy', None, 2) | |
242 self._AddNontranslateableChunk(" ],\n") | |
243 self._AddMessages() | |
244 self._AddPlaceholders() | |
245 self._AddNontranslateableChunk('\n}') | |
246 | |
247 | |
248 def FromFile(filename_or_stream, extkey=None, encoding='cp1252'): | |
249 if isinstance(filename_or_stream, types.StringTypes): | |
250 if util.IsVerbose(): | |
251 print "PolicyJson reading file %s, encoding %s" % ( | |
252 filename_or_stream, encoding) | |
253 filename_or_stream = \ | |
254 util.WrapInputStream(file(filename_or_stream, 'r'), encoding) | |
255 return PolicyJson(filename_or_stream.read()) | |
256 FromFile = staticmethod(FromFile) | |
OLD | NEW |