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

Unified Diff: grit/tool/android2grd.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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « grit/tool/__init__.py ('k') | grit/tool/android2grd_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: grit/tool/android2grd.py
===================================================================
--- grit/tool/android2grd.py (revision 202)
+++ grit/tool/android2grd.py (working copy)
@@ -1,479 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 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.
-
-"""The 'grit android2grd' tool."""
-
-
-import getopt
-import os.path
-import StringIO
-from xml.dom import Node
-import xml.dom.minidom
-
-import grit.node.empty
-from grit.node import io
-from grit.node import message
-
-from grit.tool import interface
-
-from grit import grd_reader
-from grit import lazy_re
-from grit import tclib
-from grit import util
-
-
-# The name of a string in strings.xml
-_STRING_NAME = lazy_re.compile(r'[a-z0-9_]+\Z')
-
-# A string's character limit in strings.xml
-_CHAR_LIMIT = lazy_re.compile(r'\[CHAR-LIMIT=(\d+)\]')
-
-# Finds String.Format() style format specifiers such as "%-5.2f".
-_FORMAT_SPECIFIER = lazy_re.compile(
- '%'
- '([1-9][0-9]*\$|<)?' # argument_index
- '([-#+ 0,(]*)' # flags
- '([0-9]+)?' # width
- '(\.[0-9]+)?' # precision
- '([bBhHsScCdoxXeEfgGaAtT%n])') # conversion
-
-
-class Android2Grd(interface.Tool):
- """Tool for converting Android string.xml files into chrome Grd files.
-
-Usage: grit [global options] android2grd [OPTIONS] STRINGS_XML
-
-The Android2Grd tool will convert an Android strings.xml file (whose path is
-specified by STRINGS_XML) and create a chrome style grd file containing the
-relevant information.
-
-Because grd documents are much richer than strings.xml documents we supplement
-the information required by grds using OPTIONS with sensible defaults.
-
-OPTIONS may be any of the following:
-
- --name FILENAME Specify the base FILENAME. This should be without
- any file type suffix. By default
- "chrome_android_strings" will be used.
-
- --languages LANGUAGES Comma separated list of ISO language codes (e.g.
- en-US, en-GB, ru, zh-CN). These codes will be used
- to determine the names of resource and translations
- files that will be declared by the output grd file.
-
- --grd-dir GRD_DIR Specify where the resultant grd file
- (FILENAME.grd) should be output. By default this
- will be the present working directory.
-
- --header-dir HEADER_DIR Specify the location of the directory where grit
- generated C++ headers (whose name will be
- FILENAME.h) will be placed. Use an empty string to
- disable rc generation. Default: empty.
-
- --rc-dir RC_DIR Specify the directory where resource files will
- be located relative to grit build's output
- directory. Use an empty string to disable rc
- generation. Default: empty.
-
- --xml-dir XML_DIR Specify where to place localized strings.xml files
- relative to grit build's output directory. For each
- language xx a values-xx/strings.xml file will be
- generated. Use an empty string to disable
- strings.xml generation. Default: '.'.
-
- --xtb-dir XTB_DIR Specify where the xtb files containing translations
- will be located relative to the grd file. Default:
- '.'.
-"""
-
- _NAME_FLAG = 'name'
- _LANGUAGES_FLAG = 'languages'
- _GRD_DIR_FLAG = 'grd-dir'
- _RC_DIR_FLAG = 'rc-dir'
- _HEADER_DIR_FLAG = 'header-dir'
- _XTB_DIR_FLAG = 'xtb-dir'
- _XML_DIR_FLAG = 'xml-dir'
-
- def __init__(self):
- self.name = 'chrome_android_strings'
- self.languages = []
- self.grd_dir = '.'
- self.rc_dir = None
- self.xtb_dir = '.'
- self.xml_res_dir = '.'
- self.header_dir = None
-
- def ShortDescription(self):
- """Returns a short description of the Android2Grd tool.
-
- Overridden from grit.interface.Tool
-
- Returns:
- A string containing a short description of the android2grd tool.
- """
- return 'Converts Android string.xml files into Chrome grd files.'
-
- def ParseOptions(self, args):
- """Set this objects and return all non-option arguments."""
- flags = [
- Android2Grd._NAME_FLAG,
- Android2Grd._LANGUAGES_FLAG,
- Android2Grd._GRD_DIR_FLAG,
- Android2Grd._RC_DIR_FLAG,
- Android2Grd._HEADER_DIR_FLAG,
- Android2Grd._XTB_DIR_FLAG,
- Android2Grd._XML_DIR_FLAG, ]
- (opts, args) = getopt.getopt(args, None, ['%s=' % o for o in flags])
-
- for key, val in opts:
- # Get rid of the preceding hypens.
- k = key[2:]
- if k == Android2Grd._NAME_FLAG:
- self.name = val
- elif k == Android2Grd._LANGUAGES_FLAG:
- self.languages = val.split(',')
- elif k == Android2Grd._GRD_DIR_FLAG:
- self.grd_dir = val
- elif k == Android2Grd._RC_DIR_FLAG:
- self.rc_dir = val
- elif k == Android2Grd._HEADER_DIR_FLAG:
- self.header_dir = val
- elif k == Android2Grd._XTB_DIR_FLAG:
- self.xtb_dir = val
- elif k == Android2Grd._XML_DIR_FLAG:
- self.xml_res_dir = val
- return args
-
- def Run(self, opts, args):
- """Runs the Android2Grd tool.
-
- Inherited from grit.interface.Tool.
-
- Args:
- opts: List of string arguments that should be parsed.
- args: String containing the path of the strings.xml file to be converted.
- """
- args = self.ParseOptions(args)
- if len(args) != 1:
- print ('Tool requires one argument, the path to the Android '
- 'strings.xml resource file to be converted.')
- return 2
- self.SetOptions(opts)
-
- android_path = args[0]
-
- # Read and parse the Android strings.xml file.
- with open(android_path) as android_file:
- android_dom = xml.dom.minidom.parse(android_file)
-
- # Do the hard work -- convert the Android dom to grd file contents.
- grd_dom = self.AndroidDomToGrdDom(android_dom)
- grd_string = unicode(grd_dom)
-
- # Write the grd string to a file in grd_dir.
- grd_filename = self.name + '.grd'
- grd_path = os.path.join(self.grd_dir, grd_filename)
- with open(grd_path, 'w') as grd_file:
- grd_file.write(grd_string)
-
- def AndroidDomToGrdDom(self, android_dom):
- """Converts a strings.xml DOM into a DOM representing the contents of
- a grd file.
-
- Args:
- android_dom: A xml.dom.Document containing the contents of the Android
- string.xml document.
- Returns:
- The DOM for the grd xml document produced by converting the Android DOM.
- """
-
- # Start with a basic skeleton for the .grd file.
- root = grd_reader.Parse(StringIO.StringIO(
- '''<?xml version="1.0" encoding="UTF-8"?>
- <grit base_dir="." latest_public_release="0"
- current_release="1" source_lang_id="en">
- <outputs />
- <translations />
- <release allow_pseudo="false" seq="1">
- <messages fallback_to_english="true" />
- </release>
- </grit>'''), dir='.')
- outputs = root.children[0]
- translations = root.children[1]
- messages = root.children[2].children[0]
- assert (isinstance(messages, grit.node.empty.MessagesNode) and
- isinstance(translations, grit.node.empty.TranslationsNode) and
- isinstance(outputs, grit.node.empty.OutputsNode))
-
- if self.header_dir:
- cpp_header = self.__CreateCppHeaderOutputNode(outputs, self.header_dir)
- for lang in self.languages:
- # Create an output element for each language.
- if self.rc_dir:
- self.__CreateRcOutputNode(outputs, lang, self.rc_dir)
- if self.xml_res_dir:
- self.__CreateAndroidXmlOutputNode(outputs, lang, self.xml_res_dir)
- if lang != 'en':
- self.__CreateFileNode(translations, lang)
- # Convert all the strings.xml strings into grd messages.
- self.__CreateMessageNodes(messages, android_dom.documentElement)
-
- return root
-
- def __CreateMessageNodes(self, messages, resources):
- """Creates the <message> elements and adds them as children of <messages>.
-
- Args:
- messages: the <messages> element in the strings.xml dom.
- resources: the <resources> element in the grd dom.
- """
- # <string> elements contain the definition of the resource.
- # The description of a <string> element is contained within the comment
- # node element immediately preceeding the string element in question.
- description = ''
- for child in resources.childNodes:
- if child.nodeType == Node.COMMENT_NODE:
- # Remove leading/trailing whitespace; collapse consecutive whitespaces.
- description = ' '.join(child.data.split())
- elif child.nodeType == Node.ELEMENT_NODE:
- if child.tagName != 'string':
- print 'Warning: ignoring unknown tag <%s>' % child.tagName
- else:
- translatable = self.IsTranslatable(child)
- raw_name = child.getAttribute('name')
- if not _STRING_NAME.match(raw_name):
- print 'Error: illegal string name: %s' % raw_name
- grd_name = 'IDS_' + raw_name.upper()
- # Transform the <string> node contents into a tclib.Message, taking
- # care to handle whitespace transformations and escaped characters,
- # and coverting <xliff:g> placeholders into <ph> placeholders.
- msg = self.CreateTclibMessage(child)
- msg_node = self.__CreateMessageNode(messages, grd_name, description,
- msg, translatable)
- messages.AddChild(msg_node)
- # Reset the description once a message has been parsed.
- description = ''
-
- def CreateTclibMessage(self, android_string):
- """Transforms a <string/> element from strings.xml into a tclib.Message.
-
- Interprets whitespace, quotes, and escaped characters in the android_string
- according to Android's formatting and styling rules for strings. Also
- converts <xliff:g> placeholders into <ph> placeholders, e.g.:
-
- <xliff:g id="website" example="google.com">%s</xliff:g>
- becomes
- <ph name="website"><ex>google.com</ex>%s</ph>
-
- Returns:
- The tclib.Message.
- """
- msg = tclib.Message()
- current_text = '' # Accumulated text that hasn't yet been added to msg.
- nodes = android_string.childNodes
-
- for i, node in enumerate(nodes):
- # Handle text nodes.
- if node.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
- current_text += node.data
-
- # Handle <xliff:g> and other tags.
- elif node.nodeType == Node.ELEMENT_NODE:
- if node.tagName == 'xliff:g':
- assert node.hasAttribute('id'), 'missing id: ' + node.data()
- placeholder_id = node.getAttribute('id')
- placeholder_text = self.__FormatPlaceholderText(node)
- placeholder_example = node.getAttribute('example')
- if not placeholder_example:
- print ('Info: placeholder does not contain an example: %s' %
- node.toxml())
- placeholder_example = placeholder_id.upper()
- msg.AppendPlaceholder(tclib.Placeholder(placeholder_id,
- placeholder_text, placeholder_example))
- else:
- print ('Warning: removing tag <%s> which must be inside a '
- 'placeholder: %s' % (node.tagName, node.toxml()))
- msg.AppendText(self.__FormatPlaceholderText(node))
-
- # Handle other nodes.
- elif node.nodeType != Node.COMMENT_NODE:
- assert False, 'Unknown node type: %s' % node.nodeType
-
- is_last_node = (i == len(nodes) - 1)
- if (current_text and
- (is_last_node or nodes[i + 1].nodeType == Node.ELEMENT_NODE)):
- # For messages containing just text and comments (no xml tags) Android
- # strips leading and trailing whitespace. We mimic that behavior.
- if not msg.GetContent() and is_last_node:
- current_text = current_text.strip()
- msg.AppendText(self.__FormatAndroidString(current_text))
- current_text = ''
-
- return msg
-
- def __FormatAndroidString(self, android_string, inside_placeholder=False):
- r"""Returns android_string formatted for a .grd file.
-
- * Collapses consecutive whitespaces, except when inside double-quotes.
- * Replaces \\, \n, \t, \", \' with \, newline, tab, ", '.
- """
- backslash_map = {'\\' : '\\', 'n' : '\n', 't' : '\t', '"' : '"', "'" : "'"}
- is_quoted_section = False # True when we're inside double quotes.
- is_backslash_sequence = False # True after seeing an unescaped backslash.
- prev_char = ''
- output = []
- for c in android_string:
- if is_backslash_sequence:
- # Unescape \\, \n, \t, \", and \'.
- assert c in backslash_map, 'Illegal escape sequence: \\%s' % c
- output.append(backslash_map[c])
- is_backslash_sequence = False
- elif c == '\\':
- is_backslash_sequence = True
- elif c.isspace() and not is_quoted_section:
- # Turn whitespace into ' ' and collapse consecutive whitespaces.
- if not prev_char.isspace():
- output.append(' ')
- elif c == '"':
- is_quoted_section = not is_quoted_section
- else:
- output.append(c)
- prev_char = c
- output = ''.join(output)
-
- if is_quoted_section:
- print 'Warning: unbalanced quotes in string: %s' % android_string
-
- if is_backslash_sequence:
- print 'Warning: trailing backslash in string: %s' % android_string
-
- # Check for format specifiers outside of placeholder tags.
- if not inside_placeholder:
- format_specifier = _FORMAT_SPECIFIER.search(output)
- if format_specifier:
- print ('Warning: format specifiers are not inside a placeholder '
- '<xliff:g/> tag: %s' % output)
-
- return output
-
- def __FormatPlaceholderText(self, placeholder_node):
- """Returns the text inside of an <xliff:g> placeholder node."""
- text = []
- for childNode in placeholder_node.childNodes:
- if childNode.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
- text.append(childNode.data)
- elif childNode.nodeType != Node.COMMENT_NODE:
- assert False, 'Unknown node type in ' + placeholder_node.toxml()
- return self.__FormatAndroidString(''.join(text), inside_placeholder=True)
-
- def __CreateMessageNode(self, messages_node, grd_name, description, msg,
- translatable):
- """Creates and initializes a <message> element.
-
- Message elements correspond to Android <string> elements in that they
- declare a string resource along with a programmatic id.
- """
- if not description:
- print 'Warning: no description for %s' % grd_name
- # Check that we actually fit within the character limit we've specified.
- match = _CHAR_LIMIT.search(description)
- if match:
- char_limit = int(match.group(1))
- msg_content = msg.GetRealContent()
- if len(msg_content) > char_limit:
- print ('Warning: char-limit for %s is %d, but length is %d: %s' %
- (grd_name, char_limit, len(msg_content), msg_content))
- return message.MessageNode.Construct(parent=messages_node,
- name=grd_name,
- message=msg,
- desc=description,
- translateable=translatable)
-
- def __CreateFileNode(self, translations_node, lang):
- """Creates and initializes the <file> elements.
-
- File elements provide information on the location of translation files
- (xtbs)
- """
- xtb_file = os.path.normpath(os.path.join(
- self.xtb_dir, '%s_%s.xtb' % (self.name, lang)))
- fnode = io.FileNode()
- fnode.StartParsing(u'file', translations_node)
- fnode.HandleAttribute('path', xtb_file)
- fnode.HandleAttribute('lang', lang)
- fnode.EndParsing()
- translations_node.AddChild(fnode)
- return fnode
-
- def __CreateCppHeaderOutputNode(self, outputs_node, header_dir):
- """Creates the <output> element corresponding to the generated c header."""
- header_file_name = os.path.join(header_dir, self.name + '.h')
- header_node = io.OutputNode()
- header_node.StartParsing(u'output', outputs_node)
- header_node.HandleAttribute('filename', header_file_name)
- header_node.HandleAttribute('type', 'rc_header')
- emit_node = io.EmitNode()
- emit_node.StartParsing(u'emit', header_node)
- emit_node.HandleAttribute('emit_type', 'prepend')
- emit_node.EndParsing()
- header_node.AddChild(emit_node)
- header_node.EndParsing()
- outputs_node.AddChild(header_node)
- return header_node
-
- def __CreateRcOutputNode(self, outputs_node, lang, rc_dir):
- """Creates the <output> element corresponding to various rc file output."""
- rc_file_name = self.name + '_' + lang + ".rc"
- rc_path = os.path.join(rc_dir, rc_file_name)
- node = io.OutputNode()
- node.StartParsing(u'output', outputs_node)
- node.HandleAttribute('filename', rc_path)
- node.HandleAttribute('lang', lang)
- node.HandleAttribute('type', 'rc_all')
- node.EndParsing()
- outputs_node.AddChild(node)
- return node
-
- def __CreateAndroidXmlOutputNode(self, outputs_node, locale, xml_res_dir):
- """Creates the <output> element corresponding to various rc file output."""
- # Need to check to see if the locale has a region, e.g. the GB in en-GB.
- # When a locale has a region Android expects the region to be prefixed
- # with an 'r'. For example for en-GB Android expects a values-en-rGB
- # directory. Also, Android expects nb, tl, in, iw, ji as the language
- # codes for Norwegian, Tagalog/Filipino, Indonesian, Hebrew, and Yiddish:
- # http://developer.android.com/reference/java/util/Locale.html
- if locale == 'es-419':
- android_locale = 'es-rUS'
- else:
- android_lang, dash, region = locale.partition('-')
- lang_map = {'no': 'nb', 'fil': 'tl', 'id': 'in', 'he': 'iw', 'yi': 'ji'}
- android_lang = lang_map.get(android_lang, android_lang)
- android_locale = android_lang + ('-r' + region if region else '')
- values = 'values-' + android_locale if android_locale != 'en' else 'values'
- xml_path = os.path.normpath(os.path.join(
- xml_res_dir, values, 'strings.xml'))
-
- node = io.OutputNode()
- node.StartParsing(u'output', outputs_node)
- node.HandleAttribute('filename', xml_path)
- node.HandleAttribute('lang', locale)
- node.HandleAttribute('type', 'android')
- node.EndParsing()
- outputs_node.AddChild(node)
- return node
-
- def IsTranslatable(self, android_string):
- """Determines if a <string> element is a candidate for translation.
-
- A <string> element is by default translatable unless otherwise marked.
- """
- if android_string.hasAttribute('translatable'):
- value = android_string.getAttribute('translatable').lower()
- if value not in ('true', 'false'):
- print 'Warning: translatable attribute has invalid value: %s' % value
- return value == 'true'
- else:
- return True
-
« no previous file with comments | « grit/tool/__init__.py ('k') | grit/tool/android2grd_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698