- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / installer / util / prebuild / create_string_rc.py
1 #!/usr/bin/env python
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
4 # found in the LICENSE file.
5
6 """This script generates an rc file and header (setup_strings.{rc,h}) to be
7 included in setup.exe. The rc file includes translations for strings pulled
8 from generated_resource.grd and the localized .xtb files.
9
10 The header file includes IDs for each string, but also has values to allow
11 getting a string based on a language offset.  For example, the header file
12 looks like this:
13
14 #define IDS_L10N_OFFSET_AR 0
15 #define IDS_L10N_OFFSET_BG 1
16 #define IDS_L10N_OFFSET_CA 2
17 ...
18 #define IDS_L10N_OFFSET_ZH_TW 41
19
20 #define IDS_MY_STRING_AR 1600
21 #define IDS_MY_STRING_BG 1601
22 ...
23 #define IDS_MY_STRING_BASE IDS_MY_STRING_AR
24
25 This allows us to lookup an an ID for a string by adding IDS_MY_STRING_BASE and
26 IDS_L10N_OFFSET_* for the language we are interested in.
27 """
28
29 import glob
30 import os
31 import sys
32 from xml.dom import minidom
33
34 # We are expected to use ../../../../third_party/python_24/python.exe
35 from google import path_utils
36
37 # Quick hack to fix the path.
38 sys.path.append(os.path.abspath('../../tools/grit'))
39 sys.path.append(os.path.abspath('../tools/grit'))
40 from grit.extern import tclib
41
42 # The IDs of strings we want to import from generated_resources.grd and include
43 # in setup.exe's resources.
44 kStringIds = [
45   'IDS_PRODUCT_NAME',
46   'IDS_SXS_SHORTCUT_NAME',
47   'IDS_PRODUCT_APP_LAUNCHER_NAME',
48   'IDS_PRODUCT_BINARIES_NAME',
49   'IDS_PRODUCT_DESCRIPTION',
50   'IDS_PRODUCT_FRAME_NAME',
51   'IDS_UNINSTALL_CHROME',
52   'IDS_ABOUT_VERSION_COMPANY_NAME',
53   'IDS_INSTALL_HIGHER_VERSION',
54   'IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER',
55   'IDS_INSTALL_HIGHER_VERSION_CF',
56   'IDS_INSTALL_HIGHER_VERSION_CB_CF',
57   'IDS_INSTALL_SYSTEM_LEVEL_EXISTS',
58   'IDS_INSTALL_FAILED',
59   'IDS_SAME_VERSION_REPAIR_FAILED',
60   'IDS_SAME_VERSION_REPAIR_FAILED_CF',
61   'IDS_SETUP_PATCH_FAILED',
62   'IDS_INSTALL_OS_NOT_SUPPORTED',
63   'IDS_INSTALL_OS_ERROR',
64   'IDS_INSTALL_TEMP_DIR_FAILED',
65   'IDS_INSTALL_UNCOMPRESSION_FAILED',
66   'IDS_INSTALL_INVALID_ARCHIVE',
67   'IDS_INSTALL_INSUFFICIENT_RIGHTS',
68   'IDS_INSTALL_NO_PRODUCTS_TO_UPDATE',
69   'IDS_UNINSTALL_COMPLETE',
70   'IDS_INSTALL_DIR_IN_USE',
71   'IDS_INSTALL_NON_MULTI_INSTALLATION_EXISTS',
72   'IDS_INSTALL_MULTI_INSTALLATION_EXISTS',
73   'IDS_INSTALL_READY_MODE_REQUIRES_CHROME',
74   'IDS_INSTALL_INCONSISTENT_UPDATE_POLICY',
75   'IDS_OEM_MAIN_SHORTCUT_NAME',
76   'IDS_SHORTCUT_TOOLTIP',
77   'IDS_SHORTCUT_NEW_WINDOW',
78   'IDS_APP_LAUNCHER_PRODUCT_DESCRIPTION',
79   'IDS_APP_LAUNCHER_SHORTCUT_TOOLTIP',
80   'IDS_UNINSTALL_APP_LAUNCHER',
81   'IDS_APP_LIST_SHORTCUT_NAME',
82   'IDS_APP_LIST_SHORTCUT_NAME_CANARY',
83 ]
84
85 # The ID of the first resource string.
86 kFirstResourceID = 1600
87
88
89 class TranslationStruct:
90   """A helper struct that holds information about a single translation."""
91   def __init__(self, resource_id_str, language, translation):
92     self.resource_id_str = resource_id_str
93     self.language = language
94     self.translation = translation
95
96   def __cmp__(self, other):
97     """Allow TranslationStructs to be sorted by id."""
98     id_result = cmp(self.resource_id_str, other.resource_id_str)
99     return cmp(self.language, other.language) if id_result == 0 else id_result
100
101
102 def CollectTranslatedStrings(branding):
103   """Collects all the translations for all the strings specified by kStringIds.
104   Returns a list of tuples of (string_id, language, translated string). The
105   list is sorted by language codes."""
106   strings_file = 'app/chromium_strings.grd'
107   translation_files = 'chromium_strings*.xtb'
108   if branding == 'Chrome':
109     strings_file = 'app/google_chrome_strings.grd'
110     translation_files = 'google_chrome_strings*.xtb'
111   kGeneratedResourcesPath = os.path.join(path_utils.ScriptDir(), '..', '..',
112                                          '..', strings_file)
113   kTranslationDirectory = os.path.join(path_utils.ScriptDir(), '..', '..',
114                                        '..', 'app', 'resources')
115   kTranslationFiles = glob.glob(os.path.join(kTranslationDirectory,
116                                              translation_files))
117
118   # Get the strings out of generated_resources.grd.
119   dom = minidom.parse(kGeneratedResourcesPath)
120   # message_nodes is a list of message dom nodes corresponding to the string
121   # ids we care about.  We want to make sure that this list is in the same
122   # order as kStringIds so we can associate them together.
123   message_nodes = []
124   all_message_nodes = dom.getElementsByTagName('message')
125   for string_id in kStringIds:
126     message_nodes.append([x for x in all_message_nodes if
127                           x.getAttribute('name') == string_id][0])
128   message_texts = [node.firstChild.nodeValue.strip() for node in message_nodes]
129
130   # Generate the message ID of the string to correlate it with its translations
131   # in the xtb files.
132   translation_ids = [tclib.GenerateMessageId(text) for text in message_texts]
133
134   # Manually put _EN_US in the list of translated strings because it doesn't
135   # have a .xtb file.
136   translated_strings = []
137   for string_id, message_text in zip(kStringIds, message_texts):
138     translated_strings.append(TranslationStruct(string_id,
139                                                 'EN_US',
140                                                 message_text))
141
142   # Gather the translated strings from the .xtb files.  If an .xtb file doesn't
143   # have the string we want, use the en-US string.
144   for xtb_filename in kTranslationFiles:
145     dom = minidom.parse(xtb_filename)
146     language = dom.documentElement.getAttribute('lang')
147     language = language.replace('-', '_').upper()
148     translation_nodes = {}
149     for translation_node in dom.getElementsByTagName('translation'):
150       translation_id = translation_node.getAttribute('id')
151       if translation_id in translation_ids:
152         translation_nodes[translation_id] = (translation_node.firstChild
153                                                              .nodeValue
154                                                              .strip())
155     for i, string_id in enumerate(kStringIds):
156       translated_string = translation_nodes.get(translation_ids[i],
157                                                 message_texts[i])
158       translated_strings.append(TranslationStruct(string_id,
159                                                   language,
160                                                   translated_string))
161
162   translated_strings.sort()
163   return translated_strings
164
165
166 def WriteRCFile(translated_strings, out_filename):
167   """Writes a resource (rc) file with all the language strings provided in
168   |translated_strings|."""
169   kHeaderText = (
170     u'#include "%s.h"\n\n'
171     u'STRINGTABLE\n'
172     u'BEGIN\n'
173   ) % os.path.basename(out_filename)
174   kFooterText = (
175     u'END\n'
176   )
177   lines = [kHeaderText]
178   for translation_struct in translated_strings:
179     # Escape special characters for the rc file.
180     translation = (translation_struct.translation.replace('"', '""')
181                                                  .replace('\t', '\\t')
182                                                  .replace('\n', '\\n'))
183     lines.append(u'  %s "%s"\n' % (translation_struct.resource_id_str + '_'
184                                        + translation_struct.language,
185                                    translation))
186   lines.append(kFooterText)
187   outfile = open(out_filename + '.rc', 'wb')
188   outfile.write(''.join(lines).encode('utf-16'))
189   outfile.close()
190
191
192 def WriteHeaderFile(translated_strings, out_filename):
193   """Writes a .h file with resource ids.  This file can be included by the
194   executable to refer to identifiers."""
195   lines = []
196   do_languages_lines = ['\n#define DO_LANGUAGES']
197   installer_string_mapping_lines = ['\n#define DO_INSTALLER_STRING_MAPPING']
198
199   # Write the values for how the languages ids are offset.
200   seen_languages = set()
201   offset_id = 0
202   for translation_struct in translated_strings:
203     lang = translation_struct.language
204     if lang not in seen_languages:
205       seen_languages.add(lang)
206       lines.append('#define IDS_L10N_OFFSET_%s %s' % (lang, offset_id))
207       do_languages_lines.append('  HANDLE_LANGUAGE(%s, IDS_L10N_OFFSET_%s)'
208                                 % (lang.replace('_', '-').lower(), lang))
209       offset_id += 1
210     else:
211       break
212
213   # Write the resource ids themselves.
214   resource_id = kFirstResourceID
215   for translation_struct in translated_strings:
216     lines.append('#define %s %s' % (translation_struct.resource_id_str + '_'
217                                         + translation_struct.language,
218                                     resource_id))
219     resource_id += 1
220
221   # Write out base ID values.
222   for string_id in kStringIds:
223     lines.append('#define %s_BASE %s_%s' % (string_id,
224                                             string_id,
225                                             translated_strings[0].language))
226     installer_string_mapping_lines.append('  HANDLE_STRING(%s_BASE, %s)'
227                                           % (string_id, string_id))
228
229   outfile = open(out_filename, 'wb')
230   outfile.write('\n'.join(lines))
231   outfile.write('\n#ifndef RC_INVOKED')
232   outfile.write(' \\\n'.join(do_languages_lines))
233   outfile.write(' \\\n'.join(installer_string_mapping_lines))
234   # .rc files must end in a new line
235   outfile.write('\n#endif  // ndef RC_INVOKED\n')
236   outfile.close()
237
238
239 def main(argv):
240   # TODO: Use optparse to parse command line flags.
241   if len(argv) < 2:
242     print 'Usage:\n  %s <output_directory> [branding]' % argv[0]
243     return 1
244   branding = ''
245   if (len(sys.argv) > 2):
246     branding = argv[2]
247   translated_strings = CollectTranslatedStrings(branding)
248   kFilebase = os.path.join(argv[1], 'installer_util_strings')
249   WriteRCFile(translated_strings, kFilebase)
250   WriteHeaderFile(translated_strings, kFilebase + '.h')
251   return 0
252
253
254 if '__main__' == __name__:
255   sys.exit(main(sys.argv))