• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""
7localize.py -- Generates an output file from the given template replacing
8variables and localizing strings.
9
10The script uses Jinja2 template processing library (src/third_party/jinja2).
11Variables available to the templates:
12  - |languages| - the list of languages passed on the command line. ('-l').
13  - Each NAME=VALUE define ('-d') can be accesses as {{ NAME }}.
14  - |official_build| is set to '1' when CHROME_BUILD_TYPE environment variable
15    is set to "_official".
16
17Filters:
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
26Globals:
27  - IsRtlLanguage(language) - returns True if the language is right-to-left.
28  - SelectLanguage(language) - allows to select the language to the used by
29    {% trans %}{% endtrans %} statements.
30
31"""
32
33import io
34import json
35from optparse import OptionParser
36import os
37import sys
38from string import Template
39
40
41# Win32 primary languages IDs.
42_LANGUAGE_PRIMARY = {
43  'LANG_NEUTRAL' : 0x00,
44  'LANG_INVARIANT' : 0x7f,
45  'LANG_AFRIKAANS' : 0x36,
46  'LANG_ALBANIAN' : 0x1c,
47  'LANG_ALSATIAN' : 0x84,
48  'LANG_AMHARIC' : 0x5e,
49  'LANG_ARABIC' : 0x01,
50  'LANG_ARMENIAN' : 0x2b,
51  'LANG_ASSAMESE' : 0x4d,
52  'LANG_AZERI' : 0x2c,
53  'LANG_BASHKIR' : 0x6d,
54  'LANG_BASQUE' : 0x2d,
55  'LANG_BELARUSIAN' : 0x23,
56  'LANG_BENGALI' : 0x45,
57  'LANG_BRETON' : 0x7e,
58  'LANG_BOSNIAN' : 0x1a,
59  'LANG_BULGARIAN' : 0x02,
60  'LANG_CATALAN' : 0x03,
61  'LANG_CHINESE' : 0x04,
62  'LANG_CORSICAN' : 0x83,
63  'LANG_CROATIAN' : 0x1a,
64  'LANG_CZECH' : 0x05,
65  'LANG_DANISH' : 0x06,
66  'LANG_DARI' : 0x8c,
67  'LANG_DIVEHI' : 0x65,
68  'LANG_DUTCH' : 0x13,
69  'LANG_ENGLISH' : 0x09,
70  'LANG_ESTONIAN' : 0x25,
71  'LANG_FAEROESE' : 0x38,
72  'LANG_FILIPINO' : 0x64,
73  'LANG_FINNISH' : 0x0b,
74  'LANG_FRENCH' : 0x0c,
75  'LANG_FRISIAN' : 0x62,
76  'LANG_GALICIAN' : 0x56,
77  'LANG_GEORGIAN' : 0x37,
78  'LANG_GERMAN' : 0x07,
79  'LANG_GREEK' : 0x08,
80  'LANG_GREENLANDIC' : 0x6f,
81  'LANG_GUJARATI' : 0x47,
82  'LANG_HAUSA' : 0x68,
83  'LANG_HEBREW' : 0x0d,
84  'LANG_HINDI' : 0x39,
85  'LANG_HUNGARIAN' : 0x0e,
86  'LANG_ICELANDIC' : 0x0f,
87  'LANG_IGBO' : 0x70,
88  'LANG_INDONESIAN' : 0x21,
89  'LANG_INUKTITUT' : 0x5d,
90  'LANG_IRISH' : 0x3c,
91  'LANG_ITALIAN' : 0x10,
92  'LANG_JAPANESE' : 0x11,
93  'LANG_KANNADA' : 0x4b,
94  'LANG_KASHMIRI' : 0x60,
95  'LANG_KAZAK' : 0x3f,
96  'LANG_KHMER' : 0x53,
97  'LANG_KICHE' : 0x86,
98  'LANG_KINYARWANDA' : 0x87,
99  'LANG_KONKANI' : 0x57,
100  'LANG_KOREAN' : 0x12,
101  'LANG_KYRGYZ' : 0x40,
102  'LANG_LAO' : 0x54,
103  'LANG_LATVIAN' : 0x26,
104  'LANG_LITHUANIAN' : 0x27,
105  'LANG_LOWER_SORBIAN' : 0x2e,
106  'LANG_LUXEMBOURGISH' : 0x6e,
107  'LANG_MACEDONIAN' : 0x2f,
108  'LANG_MALAY' : 0x3e,
109  'LANG_MALAYALAM' : 0x4c,
110  'LANG_MALTESE' : 0x3a,
111  'LANG_MANIPURI' : 0x58,
112  'LANG_MAORI' : 0x81,
113  'LANG_MAPUDUNGUN' : 0x7a,
114  'LANG_MARATHI' : 0x4e,
115  'LANG_MOHAWK' : 0x7c,
116  'LANG_MONGOLIAN' : 0x50,
117  'LANG_NEPALI' : 0x61,
118  'LANG_NORWEGIAN' : 0x14,
119  'LANG_OCCITAN' : 0x82,
120  'LANG_ORIYA' : 0x48,
121  'LANG_PASHTO' : 0x63,
122  'LANG_PERSIAN' : 0x29,
123  'LANG_POLISH' : 0x15,
124  'LANG_PORTUGUESE' : 0x16,
125  'LANG_PUNJABI' : 0x46,
126  'LANG_QUECHUA' : 0x6b,
127  'LANG_ROMANIAN' : 0x18,
128  'LANG_ROMANSH' : 0x17,
129  'LANG_RUSSIAN' : 0x19,
130  'LANG_SAMI' : 0x3b,
131  'LANG_SANSKRIT' : 0x4f,
132  'LANG_SCOTTISH_GAELIC' : 0x91,
133  'LANG_SERBIAN' : 0x1a,
134  'LANG_SINDHI' : 0x59,
135  'LANG_SINHALESE' : 0x5b,
136  'LANG_SLOVAK' : 0x1b,
137  'LANG_SLOVENIAN' : 0x24,
138  'LANG_SOTHO' : 0x6c,
139  'LANG_SPANISH' : 0x0a,
140  'LANG_SWAHILI' : 0x41,
141  'LANG_SWEDISH' : 0x1d,
142  'LANG_SYRIAC' : 0x5a,
143  'LANG_TAJIK' : 0x28,
144  'LANG_TAMAZIGHT' : 0x5f,
145  'LANG_TAMIL' : 0x49,
146  'LANG_TATAR' : 0x44,
147  'LANG_TELUGU' : 0x4a,
148  'LANG_THAI' : 0x1e,
149  'LANG_TIBETAN' : 0x51,
150  'LANG_TIGRIGNA' : 0x73,
151  'LANG_TSWANA' : 0x32,
152  'LANG_TURKISH' : 0x1f,
153  'LANG_TURKMEN' : 0x42,
154  'LANG_UIGHUR' : 0x80,
155  'LANG_UKRAINIAN' : 0x22,
156  'LANG_UPPER_SORBIAN' : 0x2e,
157  'LANG_URDU' : 0x20,
158  'LANG_UZBEK' : 0x43,
159  'LANG_VIETNAMESE' : 0x2a,
160  'LANG_WELSH' : 0x52,
161  'LANG_WOLOF' : 0x88,
162  'LANG_XHOSA' : 0x34,
163  'LANG_YAKUT' : 0x85,
164  'LANG_YI' : 0x78,
165  'LANG_YORUBA' : 0x6a,
166  'LANG_ZULU' : 0x35,
167}
168
169
170# Win32 sublanguage IDs.
171_LANGUAGE_SUB = {
172  'SUBLANG_NEUTRAL' : 0x00,
173  'SUBLANG_DEFAULT' : 0x01,
174  'SUBLANG_SYS_DEFAULT' : 0x02,
175  'SUBLANG_CUSTOM_DEFAULT' : 0x03,
176  'SUBLANG_CUSTOM_UNSPECIFIED' : 0x04,
177  'SUBLANG_UI_CUSTOM_DEFAULT' : 0x05,
178  'SUBLANG_AFRIKAANS_SOUTH_AFRICA' : 0x01,
179  'SUBLANG_ALBANIAN_ALBANIA' : 0x01,
180  'SUBLANG_ALSATIAN_FRANCE' : 0x01,
181  'SUBLANG_AMHARIC_ETHIOPIA' : 0x01,
182  'SUBLANG_ARABIC_SAUDI_ARABIA' : 0x01,
183  'SUBLANG_ARABIC_IRAQ' : 0x02,
184  'SUBLANG_ARABIC_EGYPT' : 0x03,
185  'SUBLANG_ARABIC_LIBYA' : 0x04,
186  'SUBLANG_ARABIC_ALGERIA' : 0x05,
187  'SUBLANG_ARABIC_MOROCCO' : 0x06,
188  'SUBLANG_ARABIC_TUNISIA' : 0x07,
189  'SUBLANG_ARABIC_OMAN' : 0x08,
190  'SUBLANG_ARABIC_YEMEN' : 0x09,
191  'SUBLANG_ARABIC_SYRIA' : 0x0a,
192  'SUBLANG_ARABIC_JORDAN' : 0x0b,
193  'SUBLANG_ARABIC_LEBANON' : 0x0c,
194  'SUBLANG_ARABIC_KUWAIT' : 0x0d,
195  'SUBLANG_ARABIC_UAE' : 0x0e,
196  'SUBLANG_ARABIC_BAHRAIN' : 0x0f,
197  'SUBLANG_ARABIC_QATAR' : 0x10,
198  'SUBLANG_ARMENIAN_ARMENIA' : 0x01,
199  'SUBLANG_ASSAMESE_INDIA' : 0x01,
200  'SUBLANG_AZERI_LATIN' : 0x01,
201  'SUBLANG_AZERI_CYRILLIC' : 0x02,
202  'SUBLANG_BASHKIR_RUSSIA' : 0x01,
203  'SUBLANG_BASQUE_BASQUE' : 0x01,
204  'SUBLANG_BELARUSIAN_BELARUS' : 0x01,
205  'SUBLANG_BENGALI_INDIA' : 0x01,
206  'SUBLANG_BENGALI_BANGLADESH' : 0x02,
207  'SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN' : 0x05,
208  'SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC' : 0x08,
209  'SUBLANG_BRETON_FRANCE' : 0x01,
210  'SUBLANG_BULGARIAN_BULGARIA' : 0x01,
211  'SUBLANG_CATALAN_CATALAN' : 0x01,
212  'SUBLANG_CHINESE_TRADITIONAL' : 0x01,
213  'SUBLANG_CHINESE_SIMPLIFIED' : 0x02,
214  'SUBLANG_CHINESE_HONGKONG' : 0x03,
215  'SUBLANG_CHINESE_SINGAPORE' : 0x04,
216  'SUBLANG_CHINESE_MACAU' : 0x05,
217  'SUBLANG_CORSICAN_FRANCE' : 0x01,
218  'SUBLANG_CZECH_CZECH_REPUBLIC' : 0x01,
219  'SUBLANG_CROATIAN_CROATIA' : 0x01,
220  'SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN' : 0x04,
221  'SUBLANG_DANISH_DENMARK' : 0x01,
222  'SUBLANG_DARI_AFGHANISTAN' : 0x01,
223  'SUBLANG_DIVEHI_MALDIVES' : 0x01,
224  'SUBLANG_DUTCH' : 0x01,
225  'SUBLANG_DUTCH_BELGIAN' : 0x02,
226  'SUBLANG_ENGLISH_US' : 0x01,
227  'SUBLANG_ENGLISH_UK' : 0x02,
228  'SUBLANG_ENGLISH_AUS' : 0x03,
229  'SUBLANG_ENGLISH_CAN' : 0x04,
230  'SUBLANG_ENGLISH_NZ' : 0x05,
231  'SUBLANG_ENGLISH_EIRE' : 0x06,
232  'SUBLANG_ENGLISH_SOUTH_AFRICA' : 0x07,
233  'SUBLANG_ENGLISH_JAMAICA' : 0x08,
234  'SUBLANG_ENGLISH_CARIBBEAN' : 0x09,
235  'SUBLANG_ENGLISH_BELIZE' : 0x0a,
236  'SUBLANG_ENGLISH_TRINIDAD' : 0x0b,
237  'SUBLANG_ENGLISH_ZIMBABWE' : 0x0c,
238  'SUBLANG_ENGLISH_PHILIPPINES' : 0x0d,
239  'SUBLANG_ENGLISH_INDIA' : 0x10,
240  'SUBLANG_ENGLISH_MALAYSIA' : 0x11,
241  'SUBLANG_ENGLISH_SINGAPORE' : 0x12,
242  'SUBLANG_ESTONIAN_ESTONIA' : 0x01,
243  'SUBLANG_FAEROESE_FAROE_ISLANDS' : 0x01,
244  'SUBLANG_FILIPINO_PHILIPPINES' : 0x01,
245  'SUBLANG_FINNISH_FINLAND' : 0x01,
246  'SUBLANG_FRENCH' : 0x01,
247  'SUBLANG_FRENCH_BELGIAN' : 0x02,
248  'SUBLANG_FRENCH_CANADIAN' : 0x03,
249  'SUBLANG_FRENCH_SWISS' : 0x04,
250  'SUBLANG_FRENCH_LUXEMBOURG' : 0x05,
251  'SUBLANG_FRENCH_MONACO' : 0x06,
252  'SUBLANG_FRISIAN_NETHERLANDS' : 0x01,
253  'SUBLANG_GALICIAN_GALICIAN' : 0x01,
254  'SUBLANG_GEORGIAN_GEORGIA' : 0x01,
255  'SUBLANG_GERMAN' : 0x01,
256  'SUBLANG_GERMAN_SWISS' : 0x02,
257  'SUBLANG_GERMAN_AUSTRIAN' : 0x03,
258  'SUBLANG_GERMAN_LUXEMBOURG' : 0x04,
259  'SUBLANG_GERMAN_LIECHTENSTEIN' : 0x05,
260  'SUBLANG_GREEK_GREECE' : 0x01,
261  'SUBLANG_GREENLANDIC_GREENLAND' : 0x01,
262  'SUBLANG_GUJARATI_INDIA' : 0x01,
263  'SUBLANG_HAUSA_NIGERIA_LATIN' : 0x01,
264  'SUBLANG_HEBREW_ISRAEL' : 0x01,
265  'SUBLANG_HINDI_INDIA' : 0x01,
266  'SUBLANG_HUNGARIAN_HUNGARY' : 0x01,
267  'SUBLANG_ICELANDIC_ICELAND' : 0x01,
268  'SUBLANG_IGBO_NIGERIA' : 0x01,
269  'SUBLANG_INDONESIAN_INDONESIA' : 0x01,
270  'SUBLANG_INUKTITUT_CANADA' : 0x01,
271  'SUBLANG_INUKTITUT_CANADA_LATIN' : 0x02,
272  'SUBLANG_IRISH_IRELAND' : 0x02,
273  'SUBLANG_ITALIAN' : 0x01,
274  'SUBLANG_ITALIAN_SWISS' : 0x02,
275  'SUBLANG_JAPANESE_JAPAN' : 0x01,
276  'SUBLANG_KANNADA_INDIA' : 0x01,
277  'SUBLANG_KASHMIRI_SASIA' : 0x02,
278  'SUBLANG_KASHMIRI_INDIA' : 0x02,
279  'SUBLANG_KAZAK_KAZAKHSTAN' : 0x01,
280  'SUBLANG_KHMER_CAMBODIA' : 0x01,
281  'SUBLANG_KICHE_GUATEMALA' : 0x01,
282  'SUBLANG_KINYARWANDA_RWANDA' : 0x01,
283  'SUBLANG_KONKANI_INDIA' : 0x01,
284  'SUBLANG_KOREAN' : 0x01,
285  'SUBLANG_KYRGYZ_KYRGYZSTAN' : 0x01,
286  'SUBLANG_LAO_LAO' : 0x01,
287  'SUBLANG_LATVIAN_LATVIA' : 0x01,
288  'SUBLANG_LITHUANIAN' : 0x01,
289  'SUBLANG_LOWER_SORBIAN_GERMANY' : 0x02,
290  'SUBLANG_LUXEMBOURGISH_LUXEMBOURG' : 0x01,
291  'SUBLANG_MACEDONIAN_MACEDONIA' : 0x01,
292  'SUBLANG_MALAY_MALAYSIA' : 0x01,
293  'SUBLANG_MALAY_BRUNEI_DARUSSALAM' : 0x02,
294  'SUBLANG_MALAYALAM_INDIA' : 0x01,
295  'SUBLANG_MALTESE_MALTA' : 0x01,
296  'SUBLANG_MAORI_NEW_ZEALAND' : 0x01,
297  'SUBLANG_MAPUDUNGUN_CHILE' : 0x01,
298  'SUBLANG_MARATHI_INDIA' : 0x01,
299  'SUBLANG_MOHAWK_MOHAWK' : 0x01,
300  'SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA' : 0x01,
301  'SUBLANG_MONGOLIAN_PRC' : 0x02,
302  'SUBLANG_NEPALI_INDIA' : 0x02,
303  'SUBLANG_NEPALI_NEPAL' : 0x01,
304  'SUBLANG_NORWEGIAN_BOKMAL' : 0x01,
305  'SUBLANG_NORWEGIAN_NYNORSK' : 0x02,
306  'SUBLANG_OCCITAN_FRANCE' : 0x01,
307  'SUBLANG_ORIYA_INDIA' : 0x01,
308  'SUBLANG_PASHTO_AFGHANISTAN' : 0x01,
309  'SUBLANG_PERSIAN_IRAN' : 0x01,
310  'SUBLANG_POLISH_POLAND' : 0x01,
311  'SUBLANG_PORTUGUESE' : 0x02,
312  'SUBLANG_PORTUGUESE_BRAZILIAN' : 0x01,
313  'SUBLANG_PUNJABI_INDIA' : 0x01,
314  'SUBLANG_QUECHUA_BOLIVIA' : 0x01,
315  'SUBLANG_QUECHUA_ECUADOR' : 0x02,
316  'SUBLANG_QUECHUA_PERU' : 0x03,
317  'SUBLANG_ROMANIAN_ROMANIA' : 0x01,
318  'SUBLANG_ROMANSH_SWITZERLAND' : 0x01,
319  'SUBLANG_RUSSIAN_RUSSIA' : 0x01,
320  'SUBLANG_SAMI_NORTHERN_NORWAY' : 0x01,
321  'SUBLANG_SAMI_NORTHERN_SWEDEN' : 0x02,
322  'SUBLANG_SAMI_NORTHERN_FINLAND' : 0x03,
323  'SUBLANG_SAMI_LULE_NORWAY' : 0x04,
324  'SUBLANG_SAMI_LULE_SWEDEN' : 0x05,
325  'SUBLANG_SAMI_SOUTHERN_NORWAY' : 0x06,
326  'SUBLANG_SAMI_SOUTHERN_SWEDEN' : 0x07,
327  'SUBLANG_SAMI_SKOLT_FINLAND' : 0x08,
328  'SUBLANG_SAMI_INARI_FINLAND' : 0x09,
329  'SUBLANG_SANSKRIT_INDIA' : 0x01,
330  'SUBLANG_SCOTTISH_GAELIC' : 0x01,
331  'SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN' : 0x06,
332  'SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC' : 0x07,
333  'SUBLANG_SERBIAN_MONTENEGRO_LATIN' : 0x0b,
334  'SUBLANG_SERBIAN_MONTENEGRO_CYRILLIC' : 0x0c,
335  'SUBLANG_SERBIAN_SERBIA_LATIN' : 0x09,
336  'SUBLANG_SERBIAN_SERBIA_CYRILLIC' : 0x0a,
337  'SUBLANG_SERBIAN_CROATIA' : 0x01,
338  'SUBLANG_SERBIAN_LATIN' : 0x02,
339  'SUBLANG_SERBIAN_CYRILLIC' : 0x03,
340  'SUBLANG_SINDHI_INDIA' : 0x01,
341  'SUBLANG_SINDHI_PAKISTAN' : 0x02,
342  'SUBLANG_SINDHI_AFGHANISTAN' : 0x02,
343  'SUBLANG_SINHALESE_SRI_LANKA' : 0x01,
344  'SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA' : 0x01,
345  'SUBLANG_SLOVAK_SLOVAKIA' : 0x01,
346  'SUBLANG_SLOVENIAN_SLOVENIA' : 0x01,
347  'SUBLANG_SPANISH' : 0x01,
348  'SUBLANG_SPANISH_MEXICAN' : 0x02,
349  'SUBLANG_SPANISH_MODERN' : 0x03,
350  'SUBLANG_SPANISH_GUATEMALA' : 0x04,
351  'SUBLANG_SPANISH_COSTA_RICA' : 0x05,
352  'SUBLANG_SPANISH_PANAMA' : 0x06,
353  'SUBLANG_SPANISH_DOMINICAN_REPUBLIC' : 0x07,
354  'SUBLANG_SPANISH_VENEZUELA' : 0x08,
355  'SUBLANG_SPANISH_COLOMBIA' : 0x09,
356  'SUBLANG_SPANISH_PERU' : 0x0a,
357  'SUBLANG_SPANISH_ARGENTINA' : 0x0b,
358  'SUBLANG_SPANISH_ECUADOR' : 0x0c,
359  'SUBLANG_SPANISH_CHILE' : 0x0d,
360  'SUBLANG_SPANISH_URUGUAY' : 0x0e,
361  'SUBLANG_SPANISH_PARAGUAY' : 0x0f,
362  'SUBLANG_SPANISH_BOLIVIA' : 0x10,
363  'SUBLANG_SPANISH_EL_SALVADOR' : 0x11,
364  'SUBLANG_SPANISH_HONDURAS' : 0x12,
365  'SUBLANG_SPANISH_NICARAGUA' : 0x13,
366  'SUBLANG_SPANISH_PUERTO_RICO' : 0x14,
367  'SUBLANG_SPANISH_US' : 0x15,
368  'SUBLANG_SWAHILI_KENYA' : 0x01,
369  'SUBLANG_SWEDISH' : 0x01,
370  'SUBLANG_SWEDISH_FINLAND' : 0x02,
371  'SUBLANG_SYRIAC_SYRIA' : 0x01,
372  'SUBLANG_TAJIK_TAJIKISTAN' : 0x01,
373  'SUBLANG_TAMAZIGHT_ALGERIA_LATIN' : 0x02,
374  'SUBLANG_TAMIL_INDIA' : 0x01,
375  'SUBLANG_TATAR_RUSSIA' : 0x01,
376  'SUBLANG_TELUGU_INDIA' : 0x01,
377  'SUBLANG_THAI_THAILAND' : 0x01,
378  'SUBLANG_TIBETAN_PRC' : 0x01,
379  'SUBLANG_TIGRIGNA_ERITREA' : 0x02,
380  'SUBLANG_TSWANA_SOUTH_AFRICA' : 0x01,
381  'SUBLANG_TURKISH_TURKEY' : 0x01,
382  'SUBLANG_TURKMEN_TURKMENISTAN' : 0x01,
383  'SUBLANG_UIGHUR_PRC' : 0x01,
384  'SUBLANG_UKRAINIAN_UKRAINE' : 0x01,
385  'SUBLANG_UPPER_SORBIAN_GERMANY' : 0x01,
386  'SUBLANG_URDU_PAKISTAN' : 0x01,
387  'SUBLANG_URDU_INDIA' : 0x02,
388  'SUBLANG_UZBEK_LATIN' : 0x01,
389  'SUBLANG_UZBEK_CYRILLIC' : 0x02,
390  'SUBLANG_VIETNAMESE_VIETNAM' : 0x01,
391  'SUBLANG_WELSH_UNITED_KINGDOM' : 0x01,
392  'SUBLANG_WOLOF_SENEGAL' : 0x01,
393  'SUBLANG_XHOSA_SOUTH_AFRICA' : 0x01,
394  'SUBLANG_YAKUT_RUSSIA' : 0x01,
395  'SUBLANG_YI_PRC' : 0x01,
396  'SUBLANG_YORUBA_NIGERIA' : 0x01,
397  'SUBLANG_ZULU_SOUTH_AFRICA' : 0x01,
398}
399
400
401'''
402This dictionary defines the language lookup table. The key is the language ISO
403country code, and the value specifies the corresponding code page, primary
404language and sublanguage.
405
406LCID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
407Codepage resource: http://www.science.co.il/language/locale-codes.asp
408Language ID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
409
410There is no appropriate sublang for Spanish (Latin America) [es-419], so we
411use Mexico. SUBLANG_DEFAULT would incorrectly map to Spain. Unlike other
412Latin American countries, Mexican Spanish is supported by VERSIONINFO:
413http://msdn.microsoft.com/en-us/library/aa381058.aspx
414
415'''
416_LANGUAGE_MAP = {
417  # Language neutral LCID, unicode(1200) code page.
418  'neutral' : [ 1200, 'LANG_NEUTRAL', 'SUBLANG_NEUTRAL' ],
419  # LANG_USER_DEFAULT LCID, unicode(1200) code page.
420  'userdefault' : [ 1200, 'LANG_NEUTRAL', 'SUBLANG_DEFAULT' ],
421  'fake-bidi' : [ 1255, 'LANG_HEBREW', 'SUBLANG_DEFAULT' ],
422  'af' : [ 1252, 'LANG_AFRIKAANS', 'SUBLANG_DEFAULT' ],
423  'am' : [ 1200, 'LANG_AMHARIC', 'SUBLANG_DEFAULT' ],
424  'ar' : [ 1256, 'LANG_ARABIC', 'SUBLANG_DEFAULT' ],
425  'bg' : [ 1251, 'LANG_BULGARIAN', 'SUBLANG_DEFAULT' ],
426  'bn' : [ 1200, 'LANG_BENGALI', 'SUBLANG_DEFAULT' ],
427  'ca' : [ 1252, 'LANG_CATALAN', 'SUBLANG_DEFAULT' ],
428  'cs' : [ 1250, 'LANG_CZECH', 'SUBLANG_DEFAULT' ],
429  'da' : [ 1252, 'LANG_DANISH', 'SUBLANG_DEFAULT' ],
430  'de' : [ 1252, 'LANG_GERMAN', 'SUBLANG_GERMAN' ],
431  'el' : [ 1253, 'LANG_GREEK', 'SUBLANG_DEFAULT' ],
432  'en' : [ 1200, 'LANG_ENGLISH', 'SUBLANG_ENGLISH_US' ],
433  'en-GB' : [ 1038, 'LANG_ENGLISH', 'SUBLANG_ENGLISH_UK' ],
434  'es' : [ 1252, 'LANG_SPANISH', 'SUBLANG_SPANISH_MODERN' ],
435  # LCID for Mexico; Windows does not support L.A. LCID.
436  'es-419' : [ 1252, 'LANG_SPANISH', 'SUBLANG_SPANISH_MEXICAN' ],
437  'et' : [ 1257, 'LANG_ESTONIAN', 'SUBLANG_DEFAULT' ],
438  'eu' : [ 1252, 'LANG_BASQUE', 'SUBLANG_DEFAULT' ],
439  'fa' : [ 1256, 'LANG_PERSIAN', 'SUBLANG_DEFAULT' ],
440  'fi' : [ 1252, 'LANG_FINNISH', 'SUBLANG_DEFAULT' ],
441  'fil' : [ 1252, 'LANG_FILIPINO', 'SUBLANG_DEFAULT' ],
442  'fr' : [ 1252, 'LANG_FRENCH', 'SUBLANG_FRENCH' ],
443  'fr-CA' : [ 1252, 'LANG_FRENCH', 'SUBLANG_FRENCH_CANADIAN' ],
444  'gl' : [ 1252, 'LANG_GALICIAN', 'SUBLANG_DEFAULT' ],
445  'gu' : [ 1200, 'LANG_GUJARATI', 'SUBLANG_DEFAULT' ],
446  'he' : [ 1255, 'LANG_HEBREW', 'SUBLANG_DEFAULT' ],
447  'hi' : [ 1200, 'LANG_HINDI', 'SUBLANG_DEFAULT' ],
448  'hr' : [ 1252, 'LANG_CROATIAN', 'SUBLANG_DEFAULT' ],
449  'hu' : [ 1250, 'LANG_HUNGARIAN', 'SUBLANG_DEFAULT' ],
450  'id' : [ 1252, 'LANG_INDONESIAN', 'SUBLANG_DEFAULT' ],
451  'is' : [ 1252, 'LANG_ICELANDIC', 'SUBLANG_DEFAULT' ],
452  'it' : [ 1252, 'LANG_ITALIAN', 'SUBLANG_DEFAULT' ],
453  'iw' : [ 1255, 'LANG_HEBREW', 'SUBLANG_DEFAULT' ],
454  'ja' : [ 932, 'LANG_JAPANESE', 'SUBLANG_DEFAULT' ],
455  'kn' : [ 1200, 'LANG_KANNADA', 'SUBLANG_DEFAULT' ],
456  'ko' : [ 949, 'LANG_KOREAN', 'SUBLANG_KOREAN' ],
457  'lt' : [ 1257, 'LANG_LITHUANIAN', 'SUBLANG_LITHUANIAN' ],
458  'lv' : [ 1257, 'LANG_LATVIAN', 'SUBLANG_DEFAULT' ],
459  'ml' : [ 1200, 'LANG_MALAYALAM', 'SUBLANG_DEFAULT' ],
460  'mr' : [ 1200, 'LANG_MARATHI', 'SUBLANG_DEFAULT' ],
461  # Malay (Malaysia) [ms-MY]
462  'ms' : [ 1252, 'LANG_MALAY', 'SUBLANG_DEFAULT' ],
463  'nb' : [ 1252, 'LANG_NORWEGIAN', 'SUBLANG_NORWEGIAN_BOKMAL' ],
464  'ne' : [ 1200, 'LANG_NEPALI', 'SUBLANG_NEPALI_NEPAL' ],
465  'nl' : [ 1252, 'LANG_DUTCH', 'SUBLANG_DEFAULT' ],
466  'nn' : [ 1252, 'LANG_NORWEGIAN', 'SUBLANG_NORWEGIAN_NYNORSK' ],
467  'no' : [ 1252, 'LANG_NORWEGIAN', 'SUBLANG_DEFAULT' ],
468  'or' : [ 1200, 'LANG_ORIYA', 'SUBLANG_DEFAULT' ],
469  'pa' : [ 1200, 'LANG_PUNJABI', 'SUBLANG_PUNJABI_INDIA' ],
470  'pl' : [ 1250, 'LANG_POLISH', 'SUBLANG_DEFAULT' ],
471  'pt-BR' : [ 1252, 'LANG_PORTUGUESE', 'SUBLANG_DEFAULT' ],
472  'pt-PT' : [ 1252, 'LANG_PORTUGUESE', 'SUBLANG_PORTUGUESE' ],
473  'ro' : [ 1250, 'LANG_ROMANIAN', 'SUBLANG_DEFAULT' ],
474  'ru' : [ 1251, 'LANG_RUSSIAN', 'SUBLANG_DEFAULT' ],
475  'sa' : [ 1200, 'LANG_SANSKRIT', 'SUBLANG_SANSKRIT_INDIA' ],
476  'si' : [ 1200, 'LANG_SINHALESE', 'SUBLANG_SINHALESE_SRI_LANKA' ],
477  'sk' : [ 1250, 'LANG_SLOVAK', 'SUBLANG_DEFAULT' ],
478  'sl' : [ 1250, 'LANG_SLOVENIAN', 'SUBLANG_DEFAULT' ],
479  'sr' : [ 1250, 'LANG_SERBIAN', 'SUBLANG_SERBIAN_LATIN' ],
480  'sv' : [ 1252, 'LANG_SWEDISH', 'SUBLANG_SWEDISH' ],
481  'sw' : [ 1252, 'LANG_SWAHILI', 'SUBLANG_DEFAULT' ],
482  'ta' : [ 1200, 'LANG_TAMIL', 'SUBLANG_DEFAULT' ],
483  'te' : [ 1200, 'LANG_TELUGU', 'SUBLANG_DEFAULT' ],
484  'th' : [ 874, 'LANG_THAI', 'SUBLANG_DEFAULT' ],
485  'ti' : [ 1200, 'LANG_TIGRIGNA', 'SUBLANG_TIGRIGNA_ERITREA' ],
486  'tr' : [ 1254, 'LANG_TURKISH', 'SUBLANG_DEFAULT' ],
487  'uk' : [ 1251, 'LANG_UKRAINIAN', 'SUBLANG_DEFAULT' ],
488  'ur' : [ 1200, 'LANG_URDU', 'SUBLANG_DEFAULT' ],
489  'vi' : [ 1258, 'LANG_VIETNAMESE', 'SUBLANG_DEFAULT' ],
490  'zh-CN' : [ 936, 'LANG_CHINESE', 'SUBLANG_CHINESE_SIMPLIFIED' ],
491  'zh-HK' : [ 950, 'LANG_CHINESE', 'SUBLANG_CHINESE_HONGKONG' ],
492  'zh-TW' : [ 950, 'LANG_CHINESE', 'SUBLANG_CHINESE_TRADITIONAL' ],
493  'zu' : [ 1200, 'LANG_ZULU', 'SUBLANG_DEFAULT' ],
494}
495
496
497# Right-To-Left languages
498_RTL_LANGUAGES = (
499  'ar',  # Arabic
500  'fa',  # Farsi
501  'iw',  # Hebrew
502  'ks',  # Kashmiri
503  'ku',  # Kurdish
504  'ps',  # Pashto
505  'ur',  # Urdu
506  'yi',  # Yiddish
507)
508
509
510def GetCodepage(language):
511  """ Returns the codepage for the given |language|. """
512  lang = _LANGUAGE_MAP[language]
513  return "%04x" % lang[0]
514
515
516def GetCodepageDecimal(language):
517  """ Returns the codepage for the given |language| as a decimal value. """
518  lang = _LANGUAGE_MAP[language]
519  return "%d" % lang[0]
520
521
522def GetLangId(language):
523  """ Returns the language id for the given |language|. """
524  lang = _LANGUAGE_MAP[language]
525  return "%04x" % (_LANGUAGE_PRIMARY[lang[1]] | (_LANGUAGE_SUB[lang[2]] << 10))
526
527
528def GetPrimaryLanguage(language):
529  """ Returns the primary language ID for the given |language|. """
530  lang = _LANGUAGE_MAP[language]
531  return _LANGUAGE_PRIMARY[lang[1]]
532
533
534def GetSublanguage(language):
535  """ Returns the sublanguage ID for the given |language|. """
536  lang = _LANGUAGE_MAP[language]
537  return _LANGUAGE_SUB[lang[2]]
538
539
540def IsRtlLanguage(language):
541  return language in _RTL_LANGUAGES;
542
543
544def NormalizeLanguageCode(language):
545  lang = language.replace('_', '-', 1)
546  if lang == 'en-US':
547    lang = 'en'
548  return lang
549
550
551def GetDataPackageSuffix(language):
552  lang = NormalizeLanguageCode(language)
553  if lang == 'en':
554    lang = 'en-US'
555  return lang
556
557
558def GetJsonSuffix(language):
559  return language.replace('-', '_', 1)
560
561
562def ReadValuesFromFile(values_dict, file_name):
563  """
564  Reads NAME=VALUE settings from the specified file.
565
566  Everything to the left of the first '=' is the keyword,
567  everything to the right is the value.  No stripping of
568  white space, so beware.
569
570  The file must exist, otherwise you get the Python exception from open().
571  """
572  for line in open(file_name, 'r').readlines():
573    key, val = line.rstrip('\r\n').split('=', 1)
574    values_dict[key] = val
575
576
577def ReadMessagesFromFile(file_name):
578  """
579  Reads messages from a 'chrome_messages_json' file.
580
581  The file must exist, otherwise you get the Python exception from open().
582  """
583  messages_file = io.open(file_name, encoding='utf-8-sig')
584  messages = json.load(messages_file)
585  messages_file.close()
586
587  values = {}
588  for key in messages.keys():
589    values[key] = unicode(messages[key]['message']);
590  return values
591
592
593def WriteIfChanged(file_name, contents, encoding):
594  """
595  Writes the specified contents to the specified file_name
596  iff the contents are different than the current contents.
597  """
598  try:
599    target = io.open(file_name, 'r')
600    old_contents = target.read()
601  except EnvironmentError:
602    pass
603  except UnicodeDecodeError:
604    target.close()
605    os.unlink(file_name)
606  else:
607    if contents == old_contents:
608      return
609    target.close()
610    os.unlink(file_name)
611  io.open(file_name, 'w', encoding=encoding).write(contents)
612
613
614class MessageMap:
615  """ Provides a dictionary of localized messages for each language."""
616  def __init__(self, languages, locale_dir):
617    self.language = None
618    self.message_map = {}
619
620    # Populate the message map
621    if locale_dir:
622      for language in languages:
623        file_name = os.path.join(locale_dir,
624                                 GetJsonSuffix(language),
625                                 'messages.json')
626        self.message_map[language] = ReadMessagesFromFile(file_name)
627
628  def GetText(self, message):
629    """ Returns a localized message for the current language. """
630    return self.message_map[self.language][message]
631
632  def SelectLanguage(self, language):
633    """ Selects the language to be used when retrieving localized messages. """
634    self.language = language
635
636  def MakeSelectLanguage(self):
637    """ Returns a function that can be used to select the current language. """
638    return lambda language: self.SelectLanguage(language)
639
640  def MakeGetText(self):
641    """ Returns a function that can be used to retrieve a localized message. """
642    return lambda message: self.GetText(message)
643
644
645# Use '@' as a delimiter for string templates instead of '$' to avoid unintended
646# expansion when passing the string from GYP.
647class GypTemplate(Template):
648    delimiter = '@'
649
650
651def Localize(source, locales, options):
652  # Set the list of languages to use.
653  languages = map(NormalizeLanguageCode, locales)
654  context = { 'languages' : languages }
655
656  # Load the localized messages.
657  message_map = MessageMap(languages, options.locale_dir)
658
659  # Add OFFICIAL_BUILD variable the same way build/util/version.py
660  # does.
661  if os.environ.get('CHROME_BUILD_TYPE') == '_official':
662    context['official_build'] = '1'
663  else:
664    context['official_build'] = '0'
665
666  # Add all variables defined in the command line.
667  if options.define:
668    for define in options.define:
669      context.update(dict([define.split('=', 1)]));
670
671  # Read NAME=VALUE variables from file.
672  if options.variables:
673    for file_name in options.variables:
674      ReadValuesFromFile(context, file_name)
675
676  env = None
677  template = None
678
679  if source:
680    # Load jinja2 library.
681    if options.jinja2:
682      jinja2_path = os.path.normpath(options.jinja2)
683    else:
684      jinja2_path = os.path.normpath(
685          os.path.join(os.path.abspath(__file__),
686                       '../../../../third_party/jinja2'))
687    sys.path.append(os.path.split(jinja2_path)[0])
688    from jinja2 import Environment, FileSystemLoader
689
690    # Create jinja2 environment.
691    (template_path, template_name) = os.path.split(source)
692    env = Environment(loader=FileSystemLoader(template_path),
693                      extensions=['jinja2.ext.do', 'jinja2.ext.i18n'])
694
695    # Register custom filters.
696    env.filters['GetCodepage'] = GetCodepage
697    env.filters['GetCodepageDecimal'] = GetCodepageDecimal
698    env.filters['GetLangId'] = GetLangId
699    env.filters['GetPrimaryLanguage'] = GetPrimaryLanguage
700    env.filters['GetSublanguage'] = GetSublanguage
701
702    # Register the message map with jinja2.i18n extension.
703    env.globals['IsRtlLanguage'] = IsRtlLanguage
704    env.globals['SelectLanguage'] = message_map.MakeSelectLanguage()
705    env.install_gettext_callables(message_map.MakeGetText(),
706                                  message_map.MakeGetText());
707
708    template = env.get_template(template_name)
709
710  # Generate a separate file per each locale if requested.
711  outputs = []
712  if options.locale_output:
713    target = GypTemplate(options.locale_output)
714    for lang in languages:
715      context['languages'] = [ lang ]
716      context['language'] = lang
717      context['pak_suffix'] = GetDataPackageSuffix(lang)
718      context['json_suffix'] = GetJsonSuffix(lang)
719      message_map.SelectLanguage(lang)
720
721      template_file_name = target.safe_substitute(context)
722      outputs.append(template_file_name)
723      if not options.print_only:
724        WriteIfChanged(template_file_name, template.render(context),
725                       options.encoding)
726  else:
727    outputs.append(options.output)
728    if not options.print_only:
729      WriteIfChanged(options.output, template.render(context), options.encoding)
730
731  if options.print_only:
732    # Quote each element so filename spaces don't mess up gyp's attempt to parse
733    # it into a list.
734    return " ".join(['"%s"' % x for x in outputs])
735
736  return
737
738
739def DoMain(argv):
740  usage = "Usage: localize [options] locales"
741  parser = OptionParser(usage=usage)
742  parser.add_option(
743      '-d', '--define', dest='define', action='append', type='string',
744      help='define a variable (NAME=VALUE).')
745  parser.add_option(
746      '--encoding', dest='encoding', type='string', default='utf-8',
747      help="set the encoding of <output>. 'utf-8' is the default.")
748  parser.add_option(
749      '--jinja2', dest='jinja2', type='string',
750      help="specifies path to the jinja2 library.")
751  parser.add_option(
752      '--locale_dir', dest='locale_dir', type='string',
753      help="set path to localized message files.")
754  parser.add_option(
755      '--locale_output', dest='locale_output',  type='string',
756      help='specify the per-locale output file name.')
757  parser.add_option(
758      '-o', '--output', dest='output', type='string',
759      help="specify the output file name.")
760  parser.add_option(
761      '--print_only', dest='print_only', action='store_true',
762      default=False, help='print the output file names only.')
763  parser.add_option(
764      '-t', '--template', dest='template', type='string',
765      help="specify the template file name.")
766  parser.add_option(
767      '--variables', dest='variables', action='append', type='string',
768      help='read variables (NAME=VALUE) from file.')
769
770  options, locales = parser.parse_args(argv)
771  if not locales:
772    parser.error('At least one locale must be specified')
773  if bool(options.output) == bool(options.locale_output):
774    parser.error(
775        'Either --output or --locale_output must be specified but not both')
776  if not options.template and not options.print_only:
777    parser.error('The template name is required unless --print_only is used')
778
779  return Localize(options.template, locales, options)
780
781if __name__ == '__main__':
782  sys.exit(DoMain(sys.argv[1:]))
783
784