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

Side by Side Diff: grit/format/android_xml.py

Issue 1258833004: Compile plural strings to android <plurals> elem (Closed) Base URL: https://chromium.googlesource.com/external/grit-i18n.git@master
Patch Set: Removed debugging output again again again (sorry) Created 5 years, 4 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
« no previous file with comments | « no previous file | grit/format/android_xml_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
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Produces localized strings.xml files for Android. 6 """Produces localized strings.xml files for Android.
7 7
8 In cases where an "android" type output file is requested in a grd, the classes 8 In cases where an "android" type output file is requested in a grd, the classes
9 in android_xml will process the messages and translations to produce a valid 9 in android_xml will process the messages and translations to produce a valid
10 strings.xml that is properly localized with the specified language. 10 strings.xml that is properly localized with the specified language.
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 <message name="IDS_FOO_DEFAULT" formatter_data="android_java_product=default 51 <message name="IDS_FOO_DEFAULT" formatter_data="android_java_product=default
52 android_java_name=foo">has card</message> 52 android_java_name=foo">has card</message>
53 53
54 would generate 54 would generate
55 55
56 <string name="foo" product="nosdcard">"no card"</string> 56 <string name="foo" product="nosdcard">"no card"</string>
57 <string name="foo" product="default">"has card"</string> 57 <string name="foo" product="default">"has card"</string>
58 """ 58 """
59 59
60 import os 60 import os
61 import re
61 import types 62 import types
62 import xml.sax.saxutils 63 import xml.sax.saxutils
63 64
64 from grit import lazy_re 65 from grit import lazy_re
65 from grit.node import message 66 from grit.node import message
66 67
67 68
68 # When this environmental variable has value "true", only tagged messages will 69 # When this environmental variable has value "true", only tagged messages will
69 # be outputted. 70 # be outputted.
70 _TAGGED_ONLY_ENV_VAR = 'ANDROID_JAVA_TAGGED_ONLY' 71 _TAGGED_ONLY_ENV_VAR = 'ANDROID_JAVA_TAGGED_ONLY'
(...skipping 18 matching lines...) Expand all
89 90
90 91
91 # In most cases we only need a name attribute and string value. 92 # In most cases we only need a name attribute and string value.
92 _SIMPLE_TEMPLATE = u'<string name="%s">%s</string>\n' 93 _SIMPLE_TEMPLATE = u'<string name="%s">%s</string>\n'
93 94
94 95
95 # In a few cases a product attribute is needed. 96 # In a few cases a product attribute is needed.
96 _PRODUCT_TEMPLATE = u'<string name="%s" product="%s">%s</string>\n' 97 _PRODUCT_TEMPLATE = u'<string name="%s" product="%s">%s</string>\n'
97 98
98 99
100 # Some strings have a plural equivalent
101 _PLURALS_TEMPLATE = '<plurals name="%s">\n%s</plurals>\n'
102 _PLURALS_ITEM_TEMPLATE = ' <item quantity="%s">%s</item>\n'
103 _PLURALS_PATTERN = lazy_re.compile(r'\{[A-Z_]+,\s*plural,(?P<items>.*)\}$', flag s=re.S)
104 _PLURALS_ITEM_PATTERN = lazy_re.compile(r'(?P<quantity>\S+)\s*\{(?P<value>.*?)\} ')
105 _PLURALS_QUANTITY_MAP = {
106 '=0': 'zero',
107 'zero': 'zero',
108 '=1': 'one',
109 'one': 'one',
110 '=2': 'two',
111 'two': 'two',
112 'few': 'few',
113 'many': 'many',
114 'other': 'other',
115 }
116
117
99 def Format(root, lang='en', output_dir='.'): 118 def Format(root, lang='en', output_dir='.'):
100 yield ('<?xml version="1.0" encoding="utf-8"?>\n' 119 yield ('<?xml version="1.0" encoding="utf-8"?>\n'
101 '<resources ' 120 '<resources '
102 'xmlns:android="http://schemas.android.com/apk/res/android">\n') 121 'xmlns:android="http://schemas.android.com/apk/res/android">\n')
103 122
104 tagged_only = _TAGGED_ONLY_DEFAULT 123 tagged_only = _TAGGED_ONLY_DEFAULT
105 if _TAGGED_ONLY_ENV_VAR in os.environ: 124 if _TAGGED_ONLY_ENV_VAR in os.environ:
106 tagged_only = os.environ[_TAGGED_ONLY_ENV_VAR].lower() 125 tagged_only = os.environ[_TAGGED_ONLY_ENV_VAR].lower()
107 if tagged_only == 'true': 126 if tagged_only == 'true':
108 tagged_only = True 127 tagged_only = True
(...skipping 15 matching lines...) Expand all
124 """Returns true if node should be outputted. 143 """Returns true if node should be outputted.
125 144
126 Args: 145 Args:
127 node: a Node from the grd dom 146 node: a Node from the grd dom
128 tagged_only: true, if only tagged messages should be outputted 147 tagged_only: true, if only tagged messages should be outputted
129 """ 148 """
130 return (isinstance(node, message.MessageNode) and 149 return (isinstance(node, message.MessageNode) and
131 (not tagged_only or _EMIT_TAG in node.formatter_data)) 150 (not tagged_only or _EMIT_TAG in node.formatter_data))
132 151
133 152
153 def _FormatPluralMessage(message):
154 """Compiles ICU plural syntax to the body of an Android <plurals> element.
155
156 1. In a .grd file, we can write a plural string like this:
157
158 <message name="IDS_THINGS">
159 {NUM_THINGS, plural,
160 =1 {1 thing}
161 other {# things}}
162 </message>
163
164 2. The Android equivalent looks like this:
165
166 <plurals name="things">
167 <item quantity="one">1 thing</item>
168 <item quantity="other">%d things</item>
169 </plurals>
170
171 This method takes the body of (1) and converts it to the body of (2).
172
173 If the message is *not* a plural string, this function returns `None`.
174 If the message includes quantities without an equivalent format in Android,
175 it raises an exception.
176 """
177 ret = {}
178 plural_match = _PLURALS_PATTERN.match(message)
179 if not plural_match:
180 return None
181 body_in = plural_match.group('items').strip()
182 lines = []
183 for item_match in _PLURALS_ITEM_PATTERN.finditer(body_in):
184 quantity_in = item_match.group('quantity')
185 quantity_out = _PLURALS_QUANTITY_MAP.get(quantity_in)
186 value_in = item_match.group('value')
187 value_out = '"' + value_in.replace('#', '%d') + '"'
188 if quantity_out:
189 lines.append(_PLURALS_ITEM_TEMPLATE % (quantity_out, value_out))
190 else:
191 raise Exception('Unsupported plural quantity for android '
192 'strings.xml: %s' % quantity_in)
193 return ''.join(lines)
194
195
134 def _FormatMessage(item, lang): 196 def _FormatMessage(item, lang):
135 """Writes out a single string as a <resource/> element.""" 197 """Writes out a single string as a <resource/> element."""
136 198
137 value = item.ws_at_start + item.Translate(lang) + item.ws_at_end 199 value = item.ws_at_start + item.Translate(lang) + item.ws_at_end
138 # Replace < > & with &lt; &gt; &amp; to ensure we generate valid XML and 200 # Replace < > & with &lt; &gt; &amp; to ensure we generate valid XML and
139 # replace ' " with \' \" to conform to Android's string formatting rules. 201 # replace ' " with \' \" to conform to Android's string formatting rules.
140 value = xml.sax.saxutils.escape(value, {"'": "\\'", '"': '\\"'}) 202 value = xml.sax.saxutils.escape(value, {"'": "\\'", '"': '\\"'})
203 plurals = _FormatPluralMessage(value)
141 # Wrap the string in double quotes to preserve whitespace. 204 # Wrap the string in double quotes to preserve whitespace.
142 value = '"' + value + '"' 205 value = '"' + value + '"'
143 206
144 mangled_name = item.GetTextualIds()[0] 207 mangled_name = item.GetTextualIds()[0]
145 match = _NAME_PATTERN.match(mangled_name) 208 match = _NAME_PATTERN.match(mangled_name)
146 if not match: 209 if not match:
147 raise Exception('Unexpected resource name: %s' % mangled_name) 210 raise Exception('Unexpected resource name: %s' % mangled_name)
148 name = match.group('name').lower() 211 name = match.group('name').lower()
149 product = match.group('product') 212 product = match.group('product')
150 213
151 # Override product or name with values in formatter_data, if any. 214 # Override product or name with values in formatter_data, if any.
152 product = item.formatter_data.get(_PRODUCT_TAG, product) 215 product = item.formatter_data.get(_PRODUCT_TAG, product)
153 name = item.formatter_data.get(_NAME_TAG, name) 216 name = item.formatter_data.get(_NAME_TAG, name)
154 217
155 if product: 218 if plurals:
219 return _PLURALS_TEMPLATE % (name, plurals)
220 elif product:
156 return _PRODUCT_TEMPLATE % (name, product, value) 221 return _PRODUCT_TEMPLATE % (name, product, value)
157 else: 222 else:
158 return _SIMPLE_TEMPLATE % (name, value) 223 return _SIMPLE_TEMPLATE % (name, value)
OLDNEW
« no previous file with comments | « no previous file | grit/format/android_xml_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698