OLD | NEW |
---|---|
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 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 | 89 |
90 | 90 |
91 # In most cases we only need a name attribute and string value. | 91 # In most cases we only need a name attribute and string value. |
92 _SIMPLE_TEMPLATE = u'<string name="%s">%s</string>\n' | 92 _SIMPLE_TEMPLATE = u'<string name="%s">%s</string>\n' |
93 | 93 |
94 | 94 |
95 # In a few cases a product attribute is needed. | 95 # In a few cases a product attribute is needed. |
96 _PRODUCT_TEMPLATE = u'<string name="%s" product="%s">%s</string>\n' | 96 _PRODUCT_TEMPLATE = u'<string name="%s" product="%s">%s</string>\n' |
97 | 97 |
98 | 98 |
99 # Some strings have a plural equivalent | |
100 _PLURALS_TEMPLATE = '<plurals name="%s">\n%s</plurals>\n' | |
101 _PLURALS_ITEM_TEMPLATE = ' <item quantity="%s">%s</item>\n' | |
102 | |
103 | |
99 def Format(root, lang='en', output_dir='.'): | 104 def Format(root, lang='en', output_dir='.'): |
105 import sys | |
106 sys.stderr.write('outputting to %s\n' % output_dir) | |
newt (away)
2015/08/03 23:06:40
Looks like debugging code. Remove this?
conleyo
2015/08/04 00:27:52
Done.
| |
100 yield ('<?xml version="1.0" encoding="utf-8"?>\n' | 107 yield ('<?xml version="1.0" encoding="utf-8"?>\n' |
101 '<resources ' | 108 '<resources ' |
102 'xmlns:android="http://schemas.android.com/apk/res/android">\n') | 109 'xmlns:android="http://schemas.android.com/apk/res/android">\n') |
103 | 110 |
104 tagged_only = _TAGGED_ONLY_DEFAULT | 111 tagged_only = _TAGGED_ONLY_DEFAULT |
105 if _TAGGED_ONLY_ENV_VAR in os.environ: | 112 if _TAGGED_ONLY_ENV_VAR in os.environ: |
106 tagged_only = os.environ[_TAGGED_ONLY_ENV_VAR].lower() | 113 tagged_only = os.environ[_TAGGED_ONLY_ENV_VAR].lower() |
107 if tagged_only == 'true': | 114 if tagged_only == 'true': |
108 tagged_only = True | 115 tagged_only = True |
109 elif tagged_only == 'false': | 116 elif tagged_only == 'false': |
(...skipping 14 matching lines...) Expand all Loading... | |
124 """Returns true if node should be outputted. | 131 """Returns true if node should be outputted. |
125 | 132 |
126 Args: | 133 Args: |
127 node: a Node from the grd dom | 134 node: a Node from the grd dom |
128 tagged_only: true, if only tagged messages should be outputted | 135 tagged_only: true, if only tagged messages should be outputted |
129 """ | 136 """ |
130 return (isinstance(node, message.MessageNode) and | 137 return (isinstance(node, message.MessageNode) and |
131 (not tagged_only or _EMIT_TAG in node.formatter_data)) | 138 (not tagged_only or _EMIT_TAG in node.formatter_data)) |
132 | 139 |
133 | 140 |
141 def _LoadPluralMessage(message): | |
newt (away)
2015/08/03 23:06:40
Please add a docstring comment. This method is far
conleyo
2015/08/04 00:27:52
Done.
| |
142 import re | |
newt (away)
2015/08/03 23:06:40
move import to top of file
conleyo
2015/08/04 00:27:52
Done.
| |
143 ret = {} | |
144 plural_match = re.match(r'\{[A-Z_]+,\s*plural,(.*)\}$', message, re.S) | |
newt (away)
2015/08/03 23:06:40
Pre-compile these patterns, since they're used so
conleyo
2015/08/04 00:27:52
Done.
| |
145 if not plural_match: | |
146 return None | |
147 body = plural_match.group(1).strip() | |
newt (away)
2015/08/03 23:06:40
To make this more readable, I'd suggest naming the
conleyo
2015/08/04 00:27:53
Done.
| |
148 for item_match in re.finditer(r'(.*)\s*\{(.*?)\}', body): | |
newt (away)
2015/08/03 23:06:40
I'd replace the initial .* with \S+ since you want
conleyo
2015/08/04 00:27:52
Done.
| |
149 quantity = item_match.group(1) | |
150 value = item_match.group(2).replace('#', '%d') | |
151 number_match = re.match(r'=(.*)', quantity) | |
newt (away)
2015/08/03 23:06:40
Explicit checks here would be better, e.g.
if q
conleyo
2015/08/04 00:27:52
Done.
| |
152 if number_match: | |
153 number = number_match.group(1).strip() | |
154 if number == '0': | |
155 ret['zero'] = value | |
156 elif number == '1': | |
157 ret['one'] = value | |
158 elif number == '2': | |
159 ret['two'] = value | |
160 else: | |
161 ret[number] = value | |
162 else: | |
163 ret['other'] = value | |
164 return ret | |
165 | |
166 | |
134 def _FormatMessage(item, lang): | 167 def _FormatMessage(item, lang): |
135 """Writes out a single string as a <resource/> element.""" | 168 """Writes out a single string as a <resource/> element.""" |
136 | 169 |
137 value = item.ws_at_start + item.Translate(lang) + item.ws_at_end | 170 value = item.ws_at_start + item.Translate(lang) + item.ws_at_end |
171 plurals = _LoadPluralMessage(value) | |
newt (away)
2015/08/03 23:06:40
How about calling this _FormatPluralMessage() and
conleyo
2015/08/04 00:27:52
Done.
| |
138 # Replace < > & with < > & to ensure we generate valid XML and | 172 # Replace < > & with < > & to ensure we generate valid XML and |
139 # replace ' " with \' \" to conform to Android's string formatting rules. | 173 # replace ' " with \' \" to conform to Android's string formatting rules. |
140 value = xml.sax.saxutils.escape(value, {"'": "\\'", '"': '\\"'}) | 174 value = xml.sax.saxutils.escape(value, {"'": "\\'", '"': '\\"'}) |
newt (away)
2015/08/03 23:06:40
these replacements need to happen even for plural
conleyo
2015/08/04 00:27:52
Done.
| |
141 # Wrap the string in double quotes to preserve whitespace. | 175 # Wrap the string in double quotes to preserve whitespace. |
142 value = '"' + value + '"' | 176 value = '"' + value + '"' |
143 | 177 |
144 mangled_name = item.GetTextualIds()[0] | 178 mangled_name = item.GetTextualIds()[0] |
145 match = _NAME_PATTERN.match(mangled_name) | 179 match = _NAME_PATTERN.match(mangled_name) |
146 if not match: | 180 if not match: |
147 raise Exception('Unexpected resource name: %s' % mangled_name) | 181 raise Exception('Unexpected resource name: %s' % mangled_name) |
148 name = match.group('name').lower() | 182 name = match.group('name').lower() |
149 product = match.group('product') | 183 product = match.group('product') |
150 | 184 |
151 # Override product or name with values in formatter_data, if any. | 185 # Override product or name with values in formatter_data, if any. |
152 product = item.formatter_data.get(_PRODUCT_TAG, product) | 186 product = item.formatter_data.get(_PRODUCT_TAG, product) |
153 name = item.formatter_data.get(_NAME_TAG, name) | 187 name = item.formatter_data.get(_NAME_TAG, name) |
154 | 188 |
155 if product: | 189 |
190 if plurals: | |
191 items = '' | |
192 for quantity, item_value in plurals.items(): | |
193 items += _PLURALS_ITEM_TEMPLATE % (quantity, item_value) | |
194 return _PLURALS_TEMPLATE % (name, items) | |
195 elif product: | |
156 return _PRODUCT_TEMPLATE % (name, product, value) | 196 return _PRODUCT_TEMPLATE % (name, product, value) |
157 else: | 197 else: |
158 return _SIMPLE_TEMPLATE % (name, value) | 198 return _SIMPLE_TEMPLATE % (name, value) |
OLD | NEW |