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

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

Issue 7994004: Initial source commit to grit-i18n project. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 9 years, 2 months 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')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright (c) 2011 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 skeleton_gatherer
14 from grit import util
15 from grit import tclib
16 from xml.dom import minidom
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 __init__(self, text):
29 if util.IsExtraVerbose():
30 print text
31 skeleton_gatherer.SkeletonGatherer.__init__(self)
32 self.text_ = text
33
34 def _ParsePlaceholder(self, placeholder, msg):
35 '''Extracts a placeholder from a DOM node and adds it to a tclib Message.
36
37 Args:
38 placeholder: A DOM node of the form:
39 <ph name="PLACEHOLDER_NAME">Placeholder text<ex>Example value</ex></ph>
40 msg: The placeholder is added to this message.
41 '''
42 text = []
43 example_text = []
44 for node1 in placeholder.childNodes:
45 if (node1.nodeType == minidom.Node.TEXT_NODE):
46 text.append(node1.data)
47 elif (node1.nodeType == minidom.Node.ELEMENT_NODE and
48 node1.tagName == 'ex'):
49 for node2 in node1.childNodes:
50 example_text.append(node2.toxml())
51 else:
52 raise Exception('Unexpected element inside a placeholder: ' +
53 node2.toxml())
54 if example_text == []:
55 # In such cases the original text is okay for an example.
56 example_text = text
57 msg.AppendPlaceholder(tclib.Placeholder(
58 placeholder.attributes['name'].value,
59 ''.join(text).strip(),
60 ''.join(example_text).strip()))
61
62 def _ParseMessage(self, string, desc):
63 '''Parses a given string and adds it to the output as a translatable chunk
64 with a given description.
65
66 Args:
67 string: The message string to parse.
68 desc: The description of the message (for the translators).
69 '''
70 msg = tclib.Message(description=desc)
71 xml = '<msg>' + string + '</msg>'
72 node = minidom.parseString(xml).childNodes[0]
73 for child in node.childNodes:
74 if child.nodeType == minidom.Node.TEXT_NODE:
75 msg.AppendText(child.data)
76 elif child.nodeType == minidom.Node.ELEMENT_NODE:
77 if child.tagName == 'ph':
78 self._ParsePlaceholder(child, msg)
79 else:
80 raise Exception("Not implemented.")
81 else:
82 raise Exception("Not implemented.")
83 self.skeleton_.append(self.uberclique.MakeClique(msg))
84
85 def _ParseNode(self, node):
86 '''Traverses the subtree of a DOM node, and register a tclib message for
87 all the <message> nodes.
88 '''
89 att_text = []
90 if node.attributes:
91 items = node.attributes.items()
92 items.sort()
93 for key, value in items:
94 att_text.append(' %s=\"%s\"' % (key, value))
95 self._AddNontranslateableChunk("<%s%s>" %
96 (node.tagName, ''.join(att_text)))
97 if node.tagName == 'message':
98 msg = tclib.Message(description=node.attributes['desc'])
99 for child in node.childNodes:
100 if child.nodeType == minidom.Node.TEXT_NODE:
101 if msg == None:
102 self._AddNontranslateableChunk(child.data)
103 else:
104 msg.AppendText(child.data)
105 elif child.nodeType == minidom.Node.ELEMENT_NODE:
106 if child.tagName == 'ph':
107 self._ParsePlaceholder(child, msg)
108 else:
109 assert False
110 self.skeleton_.append(self.uberclique.MakeClique(msg))
111 else:
112 for child in node.childNodes:
113 if child.nodeType == minidom.Node.TEXT_NODE:
114 self._AddNontranslateableChunk(child.data)
115 elif node.nodeType == minidom.Node.ELEMENT_NODE:
116 self._ParseNode(child)
117
118 self._AddNontranslateableChunk("</%s>" % node.tagName)
119
120 def _AddIndentedNontranslateableChunk(self, depth, string):
121 '''Adds a nontranslateable chunk of text to the internally stored output.
122
123 Args:
124 depth: The number of double spaces to prepend to the next argument string.
125 string: The chunk of text to add.
126 '''
127 result = []
128 while depth > 0:
129 result.append(' ')
130 depth = depth - 1
131 result.append(string)
132 self._AddNontranslateableChunk(''.join(result))
133
134 def _GetDescription(self, item, item_type, parent_item, key):
135 '''Creates a description for a translatable message. The description gives
136 some context for the person who will translate this message.
137
138 Args:
139 item: A policy or an enumeration item.
140 item_type: 'enum_item' | 'policy'
141 parent_item: The owner of item. (A policy of type group or enum.)
142 key: The name of the key to parse.
143 depth: The level of indentation.
144 '''
145 key_map = {
146 'desc': 'Description',
147 'caption': 'Caption',
148 'label': 'Label',
149 }
150 if item_type == 'policy':
151 return '%s of the policy named %s' % (key_map[key], item['name'])
152 elif item_type == 'enum_item':
153 return ('%s of the option named %s in policy %s' %
154 (key_map[key], item['name'], parent_item['name']))
155 else:
156 raise Exception('Unexpected type %s' % item_type)
157
158 def _AddPolicyKey(self, item, item_type, parent_item, key, depth):
159 '''Given a policy/enumeration item and a key, adds that key and its value
160 into the output.
161 E.g.:
162 'example_value': 123
163 If key indicates that the value is a translatable string, then it is parsed
164 as a translatable string.
165
166 Args:
167 item: A policy or an enumeration item.
168 item_type: 'enum_item' | 'policy'
169 parent_item: The owner of item. (A policy of type group or enum.)
170 key: The name of the key to parse.
171 depth: The level of indentation.
172 '''
173 self._AddIndentedNontranslateableChunk(depth, "'%s': " % key)
174 if key in ('desc', 'caption', 'label'):
175 self._AddNontranslateableChunk("'''")
176 self._ParseMessage(
177 item[key],
178 self._GetDescription(item, item_type, parent_item, key))
179 self._AddNontranslateableChunk("''',\n")
180 else:
181 str_val = item[key]
182 if type(str_val) == types.StringType:
183 str_val = "'%s'" % self.Escape(str_val)
184 else:
185 str_val = str(str_val)
186 self._AddNontranslateableChunk(str_val + ',\n')
187
188 def _AddItems(self, items, item_type, parent_item, depth):
189 '''Parses and adds a list of items from the JSON file. Items can be policies
190 or parts of an enum policy.
191
192 Args:
193 items: Either a list of policies or a list of dictionaries.
194 item_type: 'enum_item' | 'policy'
195 parent_item: If items contains a list of policies, then this is the policy
196 group that owns them. If items contains a list of enumeration items,
197 then this is the enum policy that holds them.
198 depth: Indicates the depth of our position in the JSON hierarchy. Used to
199 add nice line-indent to the output.
200 '''
201 for item1 in items:
202 self._AddIndentedNontranslateableChunk(depth, "{\n")
203 for key in item1.keys():
204 if key == 'items':
205 self._AddIndentedNontranslateableChunk(depth + 1, "'items': [\n")
206 self._AddItems(item1['items'], 'enum_item', item1, depth + 2)
207 self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
208 elif key == 'policies':
209 self._AddIndentedNontranslateableChunk(depth + 1, "'policies': [\n")
210 self._AddItems(item1['policies'], 'policy', item1, depth + 2)
211 self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
212 else:
213 self._AddPolicyKey(item1, item_type, parent_item, key, depth + 1)
214 self._AddIndentedNontranslateableChunk(depth, "},\n")
215
216 def _AddMessages(self):
217 '''Processed and adds the 'messages' section to the output.'''
218 self._AddNontranslateableChunk(" 'messages': {\n")
219 for name, message in self.data['messages'].iteritems():
220 self._AddNontranslateableChunk(" '%s': {\n" % name)
221 self._AddNontranslateableChunk(" 'text': '''")
222 self._ParseMessage(message['text'], message['desc'])
223 self._AddNontranslateableChunk("'''\n")
224 self._AddNontranslateableChunk(" },\n")
225 self._AddNontranslateableChunk(" },\n")
226
227 # Although we use the RegexpGatherer base class, we do not use the
228 # _RegExpParse method of that class to implement Parse(). Instead, we
229 # parse using a DOM parser.
230 def Parse(self):
231 if (self.have_parsed_):
232 return
233 self.have_parsed_ = True
234
235 self.data = eval(self.text_)
236
237 self._AddNontranslateableChunk('{\n')
238 self._AddNontranslateableChunk(" 'policy_definitions': [\n")
239 self._AddItems(self.data['policy_definitions'], 'policy', None, 2)
240 self._AddNontranslateableChunk(" ],\n")
241 self._AddMessages()
242 self._AddNontranslateableChunk('\n}')
243
244 def Escape(self, text):
245 # \ -> \\
246 # ' -> \'
247 # " -> \"
248 return text.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
249
250 def FromFile(filename_or_stream, extkey=None, encoding='cp1252'):
251 if isinstance(filename_or_stream, types.StringTypes):
252 if util.IsVerbose():
253 print "PolicyJson reading file %s, encoding %s" % (
254 filename_or_stream, encoding)
255 filename_or_stream = \
256 util.WrapInputStream(file(filename_or_stream, 'r'), encoding)
257 return PolicyJson(filename_or_stream.read())
258 FromFile = staticmethod(FromFile)
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