Home | History | Annotate | Download | only in build
      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 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 
     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   - 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 
     33 import io
     34 import json
     35 from optparse import OptionParser
     36 import os
     37 import sys
     38 from 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 '''
    402 This dictionary defines the language lookup table. The key is the language ISO
    403 country code, and the value specifies the corresponding code page, primary
    404 language and sublanguage.
    405 
    406 LCID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
    407 Codepage resource: http://www.science.co.il/language/locale-codes.asp
    408 Language ID resource: http://msdn.microsoft.com/en-us/library/ms776294.aspx
    409 
    410 There is no appropriate sublang for Spanish (Latin America) [es-419], so we
    411 use Mexico. SUBLANG_DEFAULT would incorrectly map to Spain. Unlike other
    412 Latin American countries, Mexican Spanish is supported by VERSIONINFO:
    413 http://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 
    510 def GetCodepage(language):
    511   """ Returns the codepage for the given |language|. """
    512   lang = _LANGUAGE_MAP[language]
    513   return "%04x" % lang[0]
    514 
    515 
    516 def 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 
    522 def 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 
    528 def 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 
    534 def GetSublanguage(language):
    535   """ Returns the sublanguage ID for the given |language|. """
    536   lang = _LANGUAGE_MAP[language]
    537   return _LANGUAGE_SUB[lang[2]]
    538 
    539 
    540 def IsRtlLanguage(language):
    541   return language in _RTL_LANGUAGES;
    542 
    543 
    544 def NormalizeLanguageCode(language):
    545   lang = language.replace('_', '-', 1)
    546   if lang == 'en-US':
    547     lang = 'en'
    548   return lang
    549 
    550 
    551 def GetDataPackageSuffix(language):
    552   lang = NormalizeLanguageCode(language)
    553   if lang == 'en':
    554     lang = 'en-US'
    555   return lang
    556 
    557 
    558 def GetJsonSuffix(language):
    559   return language.replace('-', '_', 1)
    560 
    561 
    562 def 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 
    577 def 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 
    593 def WriteIfChanged(file_name, contents, encoding='utf-16'):
    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 
    614 class 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.
    647 class GypTemplate(Template):
    648     delimiter = '@'
    649 
    650 
    651 def 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 chrome/tools/build/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 
    739 def 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-16',
    747       help="set the encoding of <output>. 'utf-16' 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 
    781 if __name__ == '__main__':
    782   sys.exit(DoMain(sys.argv[1:]))
    783 
    784