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

Side by Side Diff: remoting/tools/localize.py

Issue 18323017: Localized Chromoting Host on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 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
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2013 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 """
7 localize.py -- Generates an output file from the given template replacing
8 variables and localizing strings.
9
10 The script uses Jinja2 template processing library (src/third_party/jinja2).
11 Variables available to the templates:
12 - |languages| - the list of languages passed on the command line. ('-l').
13 - Each KEY=VALUE define ('-d') can be accesses as |KEY|.
14 - |official_build| is set to '1' when CHROME_BUILD_TYPE environment variable
15 is set to "_official".
16
17 Filters:
18 - GetCodepage - returns the code page for the given language.
19 - GetCodepageDecimal same as GetCodepage, but returns a decimal value.
20 - GetLangId - returns Win32 LANGID.
21 - GetPrimaryLanguage - returns a named Win32 constant specifing the primary
22 language ID.
23 - GetSublanguage - returns a named Win32 constant specifing the sublanguage
24 ID.
25
26 Globals:
27 - SelectLanguage(language) - allows to select the language to the used by
28 {% trans %}{% endtrans %} statements.
29
30 """
31
32 import io
33 import json
34 from optparse import OptionParser
35 import os
36 import sys
37 from string import Template
38
39 '''
40 See https://code.google.com/p/grit-i18n/source/browse/trunk/grit/format/rc.py
41 (revision r79)
42
43 This dictionary defines the language lookup table, which is used for replacing
44 the GRIT expand variables for language info in Product Version resource. The key
45 is the language ISO country code, and the value specifies the corresponding
46 locale identifier, code page, primary language and sublanguage.
47
48 LCID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
49 Codepage resource: http://www.science.co.il/language/locale-codes.asp
50 Language ID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
51
52 There is no appropriate sublang for Spanish (Latin America) [es-419], so we
53 use Mexico. SUBLANG_DEFAULT would incorrectly map to Spain. Unlike other
54 Latin American countries, Mexican Spanish is supported by VERSIONINFO:
55 http://msdn.microsoft.com/en-us/library/aa381058.aspx
56
57 '''
58
59 _LANGUAGE_LANGID = {
60 # Language neutral LCID, unicode(1200) code page.
61 'neutral' : [ '0000', '04b0', 'LANG_NEUTRAL', 'SUBLANG_NEUTRAL' ],
62 # LANG_USER_DEFAULT LCID, unicode(1200) code page.
63 'userdefault' : [ '0400', '04b0', 'LANG_NEUTRAL', 'SUBLANG_DEFAULT' ],
64 'ar' : [ '0401', '04e8', 'LANG_ARABIC', 'SUBLANG_DEFAULT' ],
garykac 2013/07/09 00:34:12 Is there an order to this list? I expected it to
alexeypa (please no reviews) 2013/07/09 01:05:23 Done.
65 'fi' : [ '040b', '04e4', 'LANG_FINNISH', 'SUBLANG_DEFAULT' ],
66 'ko' : [ '0412', '03b5', 'LANG_KOREAN', 'SUBLANG_KOREAN' ],
67 'es' : [ '040a', '04e4', 'LANG_SPANISH', 'SUBLANG_SPANISH_MODERN' ],
68 'bg' : [ '0402', '04e3', 'LANG_BULGARIAN', 'SUBLANG_DEFAULT' ],
69 # No codepage for filipino, use unicode(1200).
70 'fil' : [ '0464', '04e4', '100', 'SUBLANG_DEFAULT' ],
71 'fr' : [ '040c', '04e4', 'LANG_FRENCH', 'SUBLANG_FRENCH' ],
72 'lv' : [ '0426', '04e9', 'LANG_LATVIAN', 'SUBLANG_DEFAULT' ],
73 'sv' : [ '041d', '04e4', 'LANG_SWEDISH', 'SUBLANG_SWEDISH' ],
74 'ca' : [ '0403', '04e4', 'LANG_CATALAN', 'SUBLANG_DEFAULT' ],
75 'de' : [ '0407', '04e4', 'LANG_GERMAN', 'SUBLANG_GERMAN' ],
76 'lt' : [ '0427', '04e9', 'LANG_LITHUANIAN', 'SUBLANG_LITHUANIAN' ],
77 'zh-CN' : [ '0804', '03a8', 'LANG_CHINESE', 'SUBLANG_CHINESE_SIMPLIFIED' ],
78 'zh-TW' : [ '0404', '03b6', 'LANG_CHINESE', 'SUBLANG_CHINESE_TRADITIONAL' ],
79 'zh-HK' : [ '0c04', '03b6', 'LANG_CHINESE', 'SUBLANG_CHINESE_HONGKONG' ],
80 'el' : [ '0408', '04e5', 'LANG_GREEK', 'SUBLANG_DEFAULT' ],
81 'nb' : [ '0414', '04e4', 'LANG_NORWEGIAN', 'SUBLANG_DEFAULT' ],
82 'no' : [ '0414', '04e4', 'LANG_NORWEGIAN', 'SUBLANG_DEFAULT' ],
83 'th' : [ '041e', '036a', 'LANG_THAI', 'SUBLANG_DEFAULT' ],
84 'he' : [ '040d', '04e7', 'LANG_HEBREW', 'SUBLANG_DEFAULT' ],
85 'iw' : [ '040d', '04e7', 'LANG_HEBREW', 'SUBLANG_DEFAULT' ],
86 'pl' : [ '0415', '04e2', 'LANG_POLISH', 'SUBLANG_DEFAULT' ],
87 'tr' : [ '041f', '04e6', 'LANG_TURKISH', 'SUBLANG_DEFAULT' ],
88 'hr' : [ '041a', '04e4', 'LANG_CROATIAN', 'SUBLANG_DEFAULT' ],
89 # No codepage for Hindi, use unicode(1200).
90 'hi' : [ '0439', '04b0', 'LANG_HINDI', 'SUBLANG_DEFAULT' ],
91 'pt-PT' : [ '0816', '04e4', 'LANG_PORTUGUESE', 'SUBLANG_PORTUGUESE' ],
92 'pt-BR' : [ '0416', '04e4', 'LANG_PORTUGUESE', 'SUBLANG_DEFAULT' ],
93 'uk' : [ '0422', '04e3', 'LANG_UKRAINIAN', 'SUBLANG_DEFAULT' ],
94 'cs' : [ '0405', '04e2', 'LANG_CZECH', 'SUBLANG_DEFAULT' ],
95 'hu' : [ '040e', '04e2', 'LANG_HUNGARIAN', 'SUBLANG_DEFAULT' ],
96 'ro' : [ '0418', '04e2', 'LANG_ROMANIAN', 'SUBLANG_DEFAULT' ],
97 # No codepage for Urdu, use unicode(1200).
98 'ur' : [ '0420', '04b0', 'LANG_URDU', 'SUBLANG_DEFAULT' ],
99 'da' : [ '0406', '04e4', 'LANG_DANISH', 'SUBLANG_DEFAULT' ],
100 'is' : [ '040f', '04e4', 'LANG_ICELANDIC', 'SUBLANG_DEFAULT' ],
101 'ru' : [ '0419', '04e3', 'LANG_RUSSIAN', 'SUBLANG_DEFAULT' ],
102 'vi' : [ '042a', '04ea', 'LANG_VIETNAMESE', 'SUBLANG_DEFAULT' ],
103 'nl' : [ '0413', '04e4', 'LANG_DUTCH', 'SUBLANG_DEFAULT' ],
104 'id' : [ '0421', '04e4', 'LANG_INDONESIAN', 'SUBLANG_DEFAULT' ],
105 'sr' : [ '081a', '04e2', 'LANG_SERBIAN', 'SUBLANG_SERBIAN_CYRILLIC' ],
106 'en-GB' : [ '0809', '040e', 'LANG_ENGLISH', 'SUBLANG_ENGLISH_UK' ],
107 'it' : [ '0410', '04e4', 'LANG_ITALIAN', 'SUBLANG_DEFAULT' ],
108 'sk' : [ '041b', '04e2', 'LANG_SLOVAK', 'SUBLANG_DEFAULT' ],
109 'et' : [ '0425', '04e9', 'LANG_ESTONIAN', 'SUBLANG_DEFAULT' ],
110 'ja' : [ '0411', '03a4', 'LANG_JAPANESE', 'SUBLANG_DEFAULT' ],
111 'sl' : [ '0424', '04e2', 'LANG_SLOVENIAN', 'SUBLANG_DEFAULT' ],
112 'en' : [ '0409', '04b0', 'LANG_ENGLISH', 'SUBLANG_ENGLISH_US' ],
113 # LCID for Mexico; Windows does not support L.A. LCID.
114 'es-419' : [ '080a', '04e4', 'LANG_SPANISH', 'SUBLANG_SPANISH_MEXICAN' ],
115 # No codepage for Bengali, use unicode(1200).
116 'bn' : [ '0445', '04b0', 'LANG_BENGALI', 'SUBLANG_DEFAULT' ],
117 'fa' : [ '0429', '04e8', 'LANG_PERSIAN', 'SUBLANG_DEFAULT' ],
118 # No codepage for Gujarati, use unicode(1200).
119 'gu' : [ '0447', '04b0', 'LANG_GUJARATI', 'SUBLANG_DEFAULT' ],
120 # No codepage for Kannada, use unicode(1200).
121 'kn' : [ '044b', '04b0', 'LANG_KANNADA', 'SUBLANG_DEFAULT' ],
122 # Malay (Malaysia) [ms-MY]
123 'ms' : [ '043e', '04e4', 'LANG_MALAY', 'SUBLANG_DEFAULT' ],
124 # No codepage for Malayalam, use unicode(1200).
125 'ml' : [ '044c', '04b0', 'LANG_MALAYALAM', 'SUBLANG_DEFAULT' ],
126 # No codepage for Marathi, use unicode(1200).
127 'mr' : [ '044e', '04b0', 'LANG_MARATHI', 'SUBLANG_DEFAULT' ],
128 # No codepage for Oriya , use unicode(1200).
129 'or' : [ '0448', '04b0', 'LANG_ORIYA', 'SUBLANG_DEFAULT' ],
130 # No codepage for Tamil, use unicode(1200).
131 'ta' : [ '0449', '04b0', 'LANG_TAMIL', 'SUBLANG_DEFAULT' ],
132 # No codepage for Telugu, use unicode(1200).
133 'te' : [ '044a', '04b0', 'LANG_TELUGU', 'SUBLANG_DEFAULT' ],
134 # No codepage for Amharic, use unicode(1200). >= Vista.
135 'am' : [ '045e', '04b0', 'LANG_AMHARIC', 'SUBLANG_DEFAULT' ],
136 'sw' : [ '0441', '04e4', 'LANG_SWAHILI', 'SUBLANG_DEFAULT' ],
137 'af' : [ '0436', '04e4', 'LANG_AFRIKAANS', 'SUBLANG_DEFAULT' ],
138 'eu' : [ '042d', '04e4', 'LANG_BASQUE', 'SUBLANG_DEFAULT' ],
139 'fr-CA' : [ '0c0c', '04e4', 'LANG_FRENCH', 'SUBLANG_FRENCH_CANADIAN' ],
140 'gl' : [ '0456', '04e4', 'LANG_GALICIAN', 'SUBLANG_DEFAULT' ],
141 # No codepage for Zulu, use unicode(1200).
142 'zu' : [ '0435', '04b0', 'LANG_ZULU', 'SUBLANG_DEFAULT' ],
143 'pa' : [ '0446', '04b0', 'LANG_PUNJABI', 'SUBLANG_PUNJABI_INDIA' ],
144 'sa' : [ '044f', '04b0', 'LANG_SANSKRIT', 'SUBLANG_SANSKRIT_INDIA' ],
145 'si' : [ '045b', '04b0', 'LANG_SINHALESE', 'SUBLANG_SINHALESE_SRI_LANKA' ],
146 'ne' : [ '0461', '04b0', 'LANG_NEPALI', 'SUBLANG_NEPALI_NEPAL' ],
147 'ti' : [ '0873', '04b0', 'LANG_TIGRIGNA', 'SUBLANG_TIGRIGNA_ERITREA' ],
148 'fake-bidi' : [ '040d', '04e7', 'LANG_HEBREW', 'SUBLANG_DEFAULT' ],
149 }
150
151 # Right-To-Left languages
152 _RTL_LANGUAGES = (
153 'ar', # Arabic
154 'fa', # Farsi
155 'iw', # Hebrew
156 'ks', # Kashmiri
157 'ku', # Kurdish
158 'ps', # Pashto
159 'ur', # Urdu
160 'yi', # Yiddish
161 )
162
163
164 def GetCodepage(language):
165 """ Returns the codepage for the given |language|. """
166 langid = _LANGUAGE_LANGID[language]
167 return langid[1]
168
169
170 def GetCodepageDecimal(language):
171 """ Returns the codepage for the given |language| as a decimal value. """
172 langid = _LANGUAGE_LANGID[language]
173 return str(int(langid[1], 16))
174
175
176 def GetLangId(language):
177 """ Returns the language id for the given |language|. """
178 langid = _LANGUAGE_LANGID[language]
179 return langid[0]
180
181
182 def GetPrimaryLanguage(language):
183 """ Returns the primary language ID for the given |language|. """
184 langid = _LANGUAGE_LANGID[language]
185 return langid[2]
186
187
188 def GetSublanguage(language):
189 """ Returns the sublanguage ID for the given |language|. """
190 langid = _LANGUAGE_LANGID[language]
191 return langid[3]
192
193
194 def IsRtlLanguage(language):
195 return language in _RTL_LANGUAGES;
196
197
198 def NormalizeLanguageCode(language):
199 return language.replace('_', '-', 1)
200
201
202 def ReadValuesFromFile(values_dict, file_name):
203 """
204 Reads KEYWORD=VALUE settings from the specified file.
205
206 Everything to the left of the first '=' is the keyword,
207 everything to the right is the value. No stripping of
208 white space, so beware.
209
210 The file must exist, otherwise you get the Python exception from open().
211 """
212 for line in open(file_name, 'r').readlines():
213 key, val = line.rstrip('\r\n').split('=', 1)
214 values_dict[key] = val
215
216
217 def ReadMessagesFromFile(file_name):
218 """
219 Reads messages from a 'chrome_messages_json' file.
220
221 The file must exist, otherwise you get the Python exception from open().
222 """
223 messages_file = io.open(file_name, encoding='utf-8-sig')
224 messages = json.load(messages_file)
225 messages_file.close()
226
227 values = {}
228 for key in messages.keys():
229 values[key] = unicode(messages[key]['message']);
230 return values
231
232
233 def WriteIfChanged(file_name, contents, encoding='utf-16'):
234 """
235 Writes the specified contents to the specified file_name
236 iff the contents are different than the current contents.
237 """
238 try:
239 target = io.open(file_name, 'r')
240 old_contents = target.read()
241 except EnvironmentError:
242 pass
243 except UnicodeDecodeError:
244 target.close()
245 os.unlink(file_name)
246 else:
247 if contents == old_contents:
248 return
249 target.close()
250 os.unlink(file_name)
251 io.open(file_name, 'w', encoding=encoding).write(contents)
252
253
254 class MessageMap:
255 """ Provides a dictionary of localized messages for each language."""
256 def __init__(self, languages, messages_path):
257 self.language = None
258 self.message_map = {}
259
260 # Populate the message map
261 if messages_path:
262 for language in languages:
263 file_name = os.path.join(messages_path,
264 language.replace('-', '_', 1),
265 'messages.json')
266 self.message_map[language] = ReadMessagesFromFile(file_name)
267
268 def GetText(self, message):
269 """ Returns a localized message for the current language. """
270 return self.message_map[self.language][message]
271
272 def SelectLanguage(self, language):
273 """ Selects the language to be used when retrieving localized messages. """
274 self.language = language
275
276 def MakeSelectLanguage(self):
277 """ Returns a function that can be used to select the current language. """
278 return lambda language: self.SelectLanguage(language)
279
280 def MakeGetText(self):
281 """ Returns a function that can be used to retrieve a localized message. """
282 return lambda message: self.GetText(message)
283
284
285 def Localize(source, target, options):
286 # Load jinja2 library.
287 if options.jinja2:
288 jinja2_path = os.path.normpath(options.jinja2)
289 else:
290 jinja2_path = os.path.normpath(os.path.join(os.path.abspath(__file__),
291 '../../../third_party/jinja2'))
292 sys.path.append(os.path.split(jinja2_path)[0])
293 from jinja2 import Environment, FileSystemLoader
294
295 # Create jinja2 environment.
296 (template_path, template_name) = os.path.split(source)
297 env = Environment(loader=FileSystemLoader(template_path),
298 extensions=['jinja2.ext.do', 'jinja2.ext.i18n'])
299
300 # Register custom filters.
301 env.filters['GetCodepage'] = GetCodepage
302 env.filters['GetCodepageDecimal'] = GetCodepageDecimal
303 env.filters['GetLangId'] = GetLangId
304 env.filters['GetPrimaryLanguage'] = GetPrimaryLanguage
305 env.filters['GetSublanguage'] = GetSublanguage
306
307 # Set the list of languages to use
308 languages = map(NormalizeLanguageCode, options.languages)
309 context = { 'languages' : languages }
310 env.globals['IsRtlLanguage'] = IsRtlLanguage
311
312 # Load the localized messages and register the message map with jinja2.i18n
313 # extension.
314 message_map = MessageMap(languages, options.messages_path)
315 env.globals['SelectLanguage'] = message_map.MakeSelectLanguage()
316 env.install_gettext_callables(message_map.MakeGetText(),
317 message_map.MakeGetText());
318
319 # Add OFFICIAL_BUILD variable the same way chrome/tools/build/version.py
320 # does.
321 if os.environ.get('CHROME_BUILD_TYPE') == '_official':
322 context['official_build'] = '1'
323 else:
324 context['official_build'] = '0'
325
326 # Add all variables defined in the command line.
327 if options.define:
328 for define in options.define:
329 context.update(dict([define.split('=', 1)]));
330
331 # Read KEYWORD=VALUE variables from file.
332 if options.input:
333 for file_name in options.input:
334 ReadValuesFromFile(context, file_name)
335
336 template = env.get_template(template_name)
337 WriteIfChanged(target, template.render(context), options.encoding);
338 return 0;
339
340
341 def main():
342 usage = "Usage: localize [options] <input> <output>"
343 parser = OptionParser(usage=usage)
344 parser.add_option(
345 '-d', '--define', dest='define', action='append', type='string',
346 help='define a variable (VAR=VALUE).')
347 parser.add_option(
348 '-i', '--input', dest='input', action='append', type='string',
349 help='read variables from INPUT.')
350 parser.add_option(
351 '-l', '--language', dest='languages', action='append', type='string',
352 help='add LANGUAGE to the list of languages to use.')
353 parser.add_option(
354 '--encoding', dest='encoding', type='string', default='utf-16',
355 help="set the encoding of <output>. 'utf-16' is the default.")
356 parser.add_option(
357 '--jinja2', dest='jinja2', type='string',
358 help="specifies path to the jinja2 library.")
359 parser.add_option(
360 '--messages_path', dest='messages_path', type='string',
361 help="set path to localized messages.")
362
363 options, args = parser.parse_args()
364 if len(args) != 2:
365 parser.error('Two positional arguments (<input> and <output>) are expected')
366 if not options.languages:
367 parser.error('At least one language must be specified')
368 if not options.messages_path:
369 parser.error('--messages_path is required')
370
371 return Localize(args[0], args[1], options)
372
373 if __name__ == '__main__':
374 sys.exit(main())
375
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698