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

Unified 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, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « grit/gather/muppet_strings_unittest.py ('k') | grit/gather/policy_json_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: grit/gather/policy_json.py
===================================================================
--- grit/gather/policy_json.py (revision 0)
+++ grit/gather/policy_json.py (revision 0)
@@ -0,0 +1,258 @@
+#!/usr/bin/python2.4
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Support for "policy_templates.json" format used by the policy template
+generator as a source for generating ADM,ADMX,etc files.'''
+
+import types
+import pprint
+import re
+
+from grit.gather import skeleton_gatherer
+from grit import util
+from grit import tclib
+from xml.dom import minidom
+
+
+class PolicyJson(skeleton_gatherer.SkeletonGatherer):
+ '''Collects and translates the following strings from policy_templates.json:
+ - captions,descriptions and labels of policies
+ - captions of enumeration items
+ - misc strings from the 'messages' section
+ Translatable strings may have untranslateable placeholders with the same
+ format that is used in .grd files.
+ '''
+
+ def __init__(self, text):
+ if util.IsExtraVerbose():
+ print text
+ skeleton_gatherer.SkeletonGatherer.__init__(self)
+ self.text_ = text
+
+ def _ParsePlaceholder(self, placeholder, msg):
+ '''Extracts a placeholder from a DOM node and adds it to a tclib Message.
+
+ Args:
+ placeholder: A DOM node of the form:
+ <ph name="PLACEHOLDER_NAME">Placeholder text<ex>Example value</ex></ph>
+ msg: The placeholder is added to this message.
+ '''
+ text = []
+ example_text = []
+ for node1 in placeholder.childNodes:
+ if (node1.nodeType == minidom.Node.TEXT_NODE):
+ text.append(node1.data)
+ elif (node1.nodeType == minidom.Node.ELEMENT_NODE and
+ node1.tagName == 'ex'):
+ for node2 in node1.childNodes:
+ example_text.append(node2.toxml())
+ else:
+ raise Exception('Unexpected element inside a placeholder: ' +
+ node2.toxml())
+ if example_text == []:
+ # In such cases the original text is okay for an example.
+ example_text = text
+ msg.AppendPlaceholder(tclib.Placeholder(
+ placeholder.attributes['name'].value,
+ ''.join(text).strip(),
+ ''.join(example_text).strip()))
+
+ def _ParseMessage(self, string, desc):
+ '''Parses a given string and adds it to the output as a translatable chunk
+ with a given description.
+
+ Args:
+ string: The message string to parse.
+ desc: The description of the message (for the translators).
+ '''
+ msg = tclib.Message(description=desc)
+ xml = '<msg>' + string + '</msg>'
+ node = minidom.parseString(xml).childNodes[0]
+ for child in node.childNodes:
+ if child.nodeType == minidom.Node.TEXT_NODE:
+ msg.AppendText(child.data)
+ elif child.nodeType == minidom.Node.ELEMENT_NODE:
+ if child.tagName == 'ph':
+ self._ParsePlaceholder(child, msg)
+ else:
+ raise Exception("Not implemented.")
+ else:
+ raise Exception("Not implemented.")
+ self.skeleton_.append(self.uberclique.MakeClique(msg))
+
+ def _ParseNode(self, node):
+ '''Traverses the subtree of a DOM node, and register a tclib message for
+ all the <message> nodes.
+ '''
+ att_text = []
+ if node.attributes:
+ items = node.attributes.items()
+ items.sort()
+ for key, value in items:
+ att_text.append(' %s=\"%s\"' % (key, value))
+ self._AddNontranslateableChunk("<%s%s>" %
+ (node.tagName, ''.join(att_text)))
+ if node.tagName == 'message':
+ msg = tclib.Message(description=node.attributes['desc'])
+ for child in node.childNodes:
+ if child.nodeType == minidom.Node.TEXT_NODE:
+ if msg == None:
+ self._AddNontranslateableChunk(child.data)
+ else:
+ msg.AppendText(child.data)
+ elif child.nodeType == minidom.Node.ELEMENT_NODE:
+ if child.tagName == 'ph':
+ self._ParsePlaceholder(child, msg)
+ else:
+ assert False
+ self.skeleton_.append(self.uberclique.MakeClique(msg))
+ else:
+ for child in node.childNodes:
+ if child.nodeType == minidom.Node.TEXT_NODE:
+ self._AddNontranslateableChunk(child.data)
+ elif node.nodeType == minidom.Node.ELEMENT_NODE:
+ self._ParseNode(child)
+
+ self._AddNontranslateableChunk("</%s>" % node.tagName)
+
+ def _AddIndentedNontranslateableChunk(self, depth, string):
+ '''Adds a nontranslateable chunk of text to the internally stored output.
+
+ Args:
+ depth: The number of double spaces to prepend to the next argument string.
+ string: The chunk of text to add.
+ '''
+ result = []
+ while depth > 0:
+ result.append(' ')
+ depth = depth - 1
+ result.append(string)
+ self._AddNontranslateableChunk(''.join(result))
+
+ def _GetDescription(self, item, item_type, parent_item, key):
+ '''Creates a description for a translatable message. The description gives
+ some context for the person who will translate this message.
+
+ Args:
+ item: A policy or an enumeration item.
+ item_type: 'enum_item' | 'policy'
+ parent_item: The owner of item. (A policy of type group or enum.)
+ key: The name of the key to parse.
+ depth: The level of indentation.
+ '''
+ key_map = {
+ 'desc': 'Description',
+ 'caption': 'Caption',
+ 'label': 'Label',
+ }
+ if item_type == 'policy':
+ return '%s of the policy named %s' % (key_map[key], item['name'])
+ elif item_type == 'enum_item':
+ return ('%s of the option named %s in policy %s' %
+ (key_map[key], item['name'], parent_item['name']))
+ else:
+ raise Exception('Unexpected type %s' % item_type)
+
+ def _AddPolicyKey(self, item, item_type, parent_item, key, depth):
+ '''Given a policy/enumeration item and a key, adds that key and its value
+ into the output.
+ E.g.:
+ 'example_value': 123
+ If key indicates that the value is a translatable string, then it is parsed
+ as a translatable string.
+
+ Args:
+ item: A policy or an enumeration item.
+ item_type: 'enum_item' | 'policy'
+ parent_item: The owner of item. (A policy of type group or enum.)
+ key: The name of the key to parse.
+ depth: The level of indentation.
+ '''
+ self._AddIndentedNontranslateableChunk(depth, "'%s': " % key)
+ if key in ('desc', 'caption', 'label'):
+ self._AddNontranslateableChunk("'''")
+ self._ParseMessage(
+ item[key],
+ self._GetDescription(item, item_type, parent_item, key))
+ self._AddNontranslateableChunk("''',\n")
+ else:
+ str_val = item[key]
+ if type(str_val) == types.StringType:
+ str_val = "'%s'" % self.Escape(str_val)
+ else:
+ str_val = str(str_val)
+ self._AddNontranslateableChunk(str_val + ',\n')
+
+ def _AddItems(self, items, item_type, parent_item, depth):
+ '''Parses and adds a list of items from the JSON file. Items can be policies
+ or parts of an enum policy.
+
+ Args:
+ items: Either a list of policies or a list of dictionaries.
+ item_type: 'enum_item' | 'policy'
+ parent_item: If items contains a list of policies, then this is the policy
+ group that owns them. If items contains a list of enumeration items,
+ then this is the enum policy that holds them.
+ depth: Indicates the depth of our position in the JSON hierarchy. Used to
+ add nice line-indent to the output.
+ '''
+ for item1 in items:
+ self._AddIndentedNontranslateableChunk(depth, "{\n")
+ for key in item1.keys():
+ if key == 'items':
+ self._AddIndentedNontranslateableChunk(depth + 1, "'items': [\n")
+ self._AddItems(item1['items'], 'enum_item', item1, depth + 2)
+ self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
+ elif key == 'policies':
+ self._AddIndentedNontranslateableChunk(depth + 1, "'policies': [\n")
+ self._AddItems(item1['policies'], 'policy', item1, depth + 2)
+ self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
+ else:
+ self._AddPolicyKey(item1, item_type, parent_item, key, depth + 1)
+ self._AddIndentedNontranslateableChunk(depth, "},\n")
+
+ def _AddMessages(self):
+ '''Processed and adds the 'messages' section to the output.'''
+ self._AddNontranslateableChunk(" 'messages': {\n")
+ for name, message in self.data['messages'].iteritems():
+ self._AddNontranslateableChunk(" '%s': {\n" % name)
+ self._AddNontranslateableChunk(" 'text': '''")
+ self._ParseMessage(message['text'], message['desc'])
+ self._AddNontranslateableChunk("'''\n")
+ self._AddNontranslateableChunk(" },\n")
+ self._AddNontranslateableChunk(" },\n")
+
+ # Although we use the RegexpGatherer base class, we do not use the
+ # _RegExpParse method of that class to implement Parse(). Instead, we
+ # parse using a DOM parser.
+ def Parse(self):
+ if (self.have_parsed_):
+ return
+ self.have_parsed_ = True
+
+ self.data = eval(self.text_)
+
+ self._AddNontranslateableChunk('{\n')
+ self._AddNontranslateableChunk(" 'policy_definitions': [\n")
+ self._AddItems(self.data['policy_definitions'], 'policy', None, 2)
+ self._AddNontranslateableChunk(" ],\n")
+ self._AddMessages()
+ self._AddNontranslateableChunk('\n}')
+
+ def Escape(self, text):
+ # \ -> \\
+ # ' -> \'
+ # " -> \"
+ return text.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
+
+ def FromFile(filename_or_stream, extkey=None, encoding='cp1252'):
+ if isinstance(filename_or_stream, types.StringTypes):
+ if util.IsVerbose():
+ print "PolicyJson reading file %s, encoding %s" % (
+ filename_or_stream, encoding)
+ filename_or_stream = \
+ util.WrapInputStream(file(filename_or_stream, 'r'), encoding)
+ return PolicyJson(filename_or_stream.read())
+ FromFile = staticmethod(FromFile)
Property changes on: grit/gather/policy_json.py
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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