Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(317)

Side by Side Diff: grit/gather/policy_json.py

Issue 1442863002: Remove contents of grit's SVN repository. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « grit/gather/muppet_strings_unittest.py ('k') | grit/gather/policy_json_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 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 sys
11
12 from grit.gather import skeleton_gatherer
13 from grit import util
14 from grit import tclib
15 from xml.dom import minidom
16 from xml.parsers.expat import ExpatError
17
18
19 class PolicyJson(skeleton_gatherer.SkeletonGatherer):
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 _ParsePlaceholder(self, placeholder, msg):
29 '''Extracts a placeholder from a DOM node and adds it to a tclib Message.
30
31 Args:
32 placeholder: A DOM node of the form:
33 <ph name="PLACEHOLDER_NAME">Placeholder text<ex>Example value</ex></ph>
34 msg: The placeholder is added to this message.
35 '''
36 text = []
37 example_text = []
38 for node1 in placeholder.childNodes:
39 if (node1.nodeType == minidom.Node.TEXT_NODE):
40 text.append(node1.data)
41 elif (node1.nodeType == minidom.Node.ELEMENT_NODE and
42 node1.tagName == 'ex'):
43 for node2 in node1.childNodes:
44 example_text.append(node2.toxml())
45 else:
46 raise Exception('Unexpected element inside a placeholder: ' +
47 node2.toxml())
48 if example_text == []:
49 # In such cases the original text is okay for an example.
50 example_text = text
51 msg.AppendPlaceholder(tclib.Placeholder(
52 placeholder.attributes['name'].value,
53 ''.join(text).strip(),
54 ''.join(example_text).strip()))
55
56 def _ParseMessage(self, string, desc):
57 '''Parses a given string and adds it to the output as a translatable chunk
58 with a given description.
59
60 Args:
61 string: The message string to parse.
62 desc: The description of the message (for the translators).
63 '''
64 msg = tclib.Message(description=desc)
65 xml = '<msg>' + string + '</msg>'
66 try:
67 node = minidom.parseString(xml).childNodes[0]
68 except ExpatError:
69 reason = '''Input isn't valid XML (has < & > been escaped?): ''' + string
70 raise Exception, reason, sys.exc_info()[2]
71
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))
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 item_type == 'policy':
150 return '%s of the policy named %s' % (key_map[key], item['name'])
151 elif item_type == 'enum_item':
152 return ('%s of the option named %s in policy %s' %
153 (key_map[key], item['name'], parent_item['name']))
154 else:
155 raise Exception('Unexpected type %s' % item_type)
156
157 def _AddPolicyKey(self, item, item_type, parent_item, key, depth):
158 '''Given a policy/enumeration item and a key, adds that key and its value
159 into the output.
160 E.g.:
161 'example_value': 123
162 If key indicates that the value is a translatable string, then it is parsed
163 as a translatable string.
164
165 Args:
166 item: A policy or an enumeration item.
167 item_type: 'enum_item' | 'policy'
168 parent_item: The owner of item. (A policy of type group or enum.)
169 key: The name of the key to parse.
170 depth: The level of indentation.
171 '''
172 self._AddIndentedNontranslateableChunk(depth, "'%s': " % key)
173 if key in ('desc', 'caption', 'label'):
174 self._AddNontranslateableChunk("'''")
175 self._ParseMessage(
176 item[key],
177 self._GetDescription(item, item_type, parent_item, key))
178 self._AddNontranslateableChunk("''',\n")
179 else:
180 str_val = item[key]
181 if type(str_val) == types.StringType:
182 str_val = "'%s'" % self.Escape(str_val)
183 else:
184 str_val = str(str_val)
185 self._AddNontranslateableChunk(str_val + ',\n')
186
187 def _AddItems(self, items, item_type, parent_item, depth):
188 '''Parses and adds a list of items from the JSON file. Items can be policies
189 or parts of an enum policy.
190
191 Args:
192 items: Either a list of policies or a list of dictionaries.
193 item_type: 'enum_item' | 'policy'
194 parent_item: If items contains a list of policies, then this is the policy
195 group that owns them. If items contains a list of enumeration items,
196 then this is the enum policy that holds them.
197 depth: Indicates the depth of our position in the JSON hierarchy. Used to
198 add nice line-indent to the output.
199 '''
200 for item1 in items:
201 self._AddIndentedNontranslateableChunk(depth, "{\n")
202 for key in item1.keys():
203 if key == 'items':
204 self._AddIndentedNontranslateableChunk(depth + 1, "'items': [\n")
205 self._AddItems(item1['items'], 'enum_item', item1, depth + 2)
206 self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
207 elif key == 'policies':
208 self._AddIndentedNontranslateableChunk(depth + 1, "'policies': [\n")
209 self._AddItems(item1['policies'], 'policy', item1, depth + 2)
210 self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
211 else:
212 self._AddPolicyKey(item1, item_type, parent_item, key, depth + 1)
213 self._AddIndentedNontranslateableChunk(depth, "},\n")
214
215 def _AddMessages(self):
216 '''Processed and adds the 'messages' section to the output.'''
217 self._AddNontranslateableChunk(" 'messages': {\n")
218 for name, message in self.data['messages'].iteritems():
219 self._AddNontranslateableChunk(" '%s': {\n" % name)
220 self._AddNontranslateableChunk(" 'text': '''")
221 self._ParseMessage(message['text'], message['desc'])
222 self._AddNontranslateableChunk("'''\n")
223 self._AddNontranslateableChunk(" },\n")
224 self._AddNontranslateableChunk(" },\n")
225
226 # Although we use the RegexpGatherer base class, we do not use the
227 # _RegExpParse method of that class to implement Parse(). Instead, we
228 # parse using a DOM parser.
229 def Parse(self):
230 if self.have_parsed_:
231 return
232 self.have_parsed_ = True
233
234 self.text_ = self._LoadInputFile()
235 if util.IsExtraVerbose():
236 print self.text_
237
238 self.data = eval(self.text_)
239
240 self._AddNontranslateableChunk('{\n')
241 self._AddNontranslateableChunk(" 'policy_definitions': [\n")
242 self._AddItems(self.data['policy_definitions'], 'policy', None, 2)
243 self._AddNontranslateableChunk(" ],\n")
244 self._AddMessages()
245 self._AddNontranslateableChunk('\n}')
246
247 def Escape(self, text):
248 # \ -> \\
249 # ' -> \'
250 # " -> \"
251 return text.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
OLDNEW
« no previous file with comments | « grit/gather/muppet_strings_unittest.py ('k') | grit/gather/policy_json_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698