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

Side by Side Diff: chrome/installer/util/prebuild/create_string_rc.py

Issue 2791593002: Allow installer::GetLocalizedString to return mode-specific strings. (Closed)
Patch Set: manzagop review comments Created 3 years, 8 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
« no previous file with comments | « chrome/installer/util/l10n_string_util_unittest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 """Generates .h and .rc files for installer strings. Run "python 6 """Generates .h and .rc files for installer strings. Run "python
7 create_string_rc.py" for usage details. 7 create_string_rc.py" for usage details.
8 8
9 This script generates an rc file and header (NAME.{rc,h}) to be included in 9 This script generates an rc file and header (NAME.{rc,h}) to be included in
10 setup.exe. The rc file includes translations for strings pulled from the given 10 setup.exe. The rc file includes translations for strings pulled from the given
(...skipping 12 matching lines...) Expand all
23 #define IDS_MY_STRING_AR 1600 23 #define IDS_MY_STRING_AR 1600
24 #define IDS_MY_STRING_BG 1601 24 #define IDS_MY_STRING_BG 1601
25 ... 25 ...
26 #define IDS_MY_STRING_BASE IDS_MY_STRING_AR 26 #define IDS_MY_STRING_BASE IDS_MY_STRING_AR
27 27
28 This allows us to lookup an an ID for a string by adding IDS_MY_STRING_BASE and 28 This allows us to lookup an an ID for a string by adding IDS_MY_STRING_BASE and
29 IDS_L10N_OFFSET_* for the language we are interested in. 29 IDS_L10N_OFFSET_* for the language we are interested in.
30 """ 30 """
31 31
32 import argparse 32 import argparse
33 import exceptions
33 import glob 34 import glob
34 import io 35 import io
35 import os 36 import os
36 import sys 37 import sys
37 from xml import sax 38 from xml import sax
38 39
39 BASEDIR = os.path.dirname(os.path.abspath(__file__)) 40 BASEDIR = os.path.dirname(os.path.abspath(__file__))
40 sys.path.append(os.path.join(BASEDIR, '../../../../tools/grit')) 41 sys.path.append(os.path.join(BASEDIR, '../../../../tools/grit'))
41 sys.path.append(os.path.join(BASEDIR, '../../../../tools/python')) 42 sys.path.append(os.path.join(BASEDIR, '../../../../tools/python'))
42 43
43 from grit.extern import tclib 44 from grit.extern import tclib
44 45
45 # The IDs of strings we want to import from the .grd files and include in 46 # The IDs of strings we want to import from the .grd files and include in
46 # setup.exe's resources. 47 # setup.exe's resources.
47 STRING_IDS = [ 48 STRING_IDS = [
48 'IDS_PRODUCT_NAME',
49 'IDS_SXS_SHORTCUT_NAME',
50 'IDS_PRODUCT_DESCRIPTION',
51 'IDS_ABOUT_VERSION_COMPANY_NAME', 49 'IDS_ABOUT_VERSION_COMPANY_NAME',
50 'IDS_APP_SHORTCUTS_SUBDIR_NAME',
51 'IDS_APP_SHORTCUTS_SUBDIR_NAME_CANARY',
52 'IDS_INBOUND_MDNS_RULE_DESCRIPTION',
53 'IDS_INBOUND_MDNS_RULE_DESCRIPTION_CANARY',
54 'IDS_INBOUND_MDNS_RULE_NAME',
55 'IDS_INBOUND_MDNS_RULE_NAME_CANARY',
56 'IDS_INSTALL_EXISTING_VERSION_LAUNCHED',
57 'IDS_INSTALL_FAILED',
52 'IDS_INSTALL_HIGHER_VERSION', 58 'IDS_INSTALL_HIGHER_VERSION',
53 'IDS_INSTALL_FAILED', 59 'IDS_INSTALL_INSUFFICIENT_RIGHTS',
54 'IDS_SAME_VERSION_REPAIR_FAILED', 60 'IDS_INSTALL_INVALID_ARCHIVE',
55 'IDS_SETUP_PATCH_FAILED', 61 'IDS_INSTALL_OS_ERROR',
56 'IDS_INSTALL_OS_NOT_SUPPORTED', 62 'IDS_INSTALL_OS_NOT_SUPPORTED',
57 'IDS_INSTALL_OS_ERROR',
58 'IDS_INSTALL_SINGLETON_ACQUISITION_FAILED', 63 'IDS_INSTALL_SINGLETON_ACQUISITION_FAILED',
59 'IDS_INSTALL_TEMP_DIR_FAILED', 64 'IDS_INSTALL_TEMP_DIR_FAILED',
60 'IDS_INSTALL_UNCOMPRESSION_FAILED', 65 'IDS_INSTALL_UNCOMPRESSION_FAILED',
61 'IDS_INSTALL_INVALID_ARCHIVE', 66 'IDS_PRODUCT_DESCRIPTION',
62 'IDS_INSTALL_INSUFFICIENT_RIGHTS', 67 'IDS_PRODUCT_NAME',
68 'IDS_SAME_VERSION_REPAIR_FAILED',
69 'IDS_SETUP_PATCH_FAILED',
70 'IDS_SHORTCUT_NEW_WINDOW',
63 'IDS_SHORTCUT_TOOLTIP', 71 'IDS_SHORTCUT_TOOLTIP',
64 'IDS_SHORTCUT_NEW_WINDOW', 72 'IDS_SXS_SHORTCUT_NAME',
65 'IDS_APP_SHORTCUTS_SUBDIR_NAME',
66 'IDS_APP_SHORTCUTS_SUBDIR_NAME_CANARY',
67 'IDS_INBOUND_MDNS_RULE_NAME',
68 'IDS_INBOUND_MDNS_RULE_NAME_CANARY',
69 'IDS_INBOUND_MDNS_RULE_DESCRIPTION',
70 'IDS_INBOUND_MDNS_RULE_DESCRIPTION_CANARY',
71 'IDS_INSTALL_EXISTING_VERSION_LAUNCHED',
72 ] 73 ]
73 74
75 # Certain strings are conditional on a brand's install mode (see
76 # chrome/install_static/install_modes.h for details). This allows
77 # installer::GetLocalizedString to return a resource specific to the current
78 # install mode at runtime (e.g., "Google Chrome SxS" as IDS_SHORTCUT_NAME for
79 # the localized shortcut name for Google Chrome's canary channel). This mapping
80 # provides brand- and mode-specific string ids for a given input id as described
81 # here:
82 # {
83 # resource_id_1: { # A resource ID for use with GetLocalizedString.
84 # brand: [ # 'google_chrome', for example.
85 # string_id_1, # Strings listed in order of the brand's modes, as
86 # string_id_2, # specified in install_static::InstallConstantIndex.
87 # ...
88 # string_id_N,
89 # ],
90 # },
91 # resource_id_2: ...
92 # }
93 # 'resource_id_1' names an existing string ID. All calls to
94 # installer::GetLocalizedString with this string ID will map to the
95 # mode-specific string.
96 #
97 # Note: Update the test expectations in GetBaseMessageIdForMode.GoogleStringIds
98 # when adding to/modifying this structure.
99 MODE_SPECIFIC_STRINGS = {
100 'IDS_APP_SHORTCUTS_SUBDIR_NAME': {
101 'google_chrome': [
102 'IDS_APP_SHORTCUTS_SUBDIR_NAME',
103 'IDS_APP_SHORTCUTS_SUBDIR_NAME_CANARY',
104 ],
105 'chromium': [
106 'IDS_APP_SHORTCUTS_SUBDIR_NAME',
107 ],
108 },
109 'IDS_INBOUND_MDNS_RULE_DESCRIPTION': {
110 'google_chrome': [
111 'IDS_INBOUND_MDNS_RULE_DESCRIPTION',
112 'IDS_INBOUND_MDNS_RULE_DESCRIPTION_CANARY',
113 ],
114 'chromium': [
115 'IDS_INBOUND_MDNS_RULE_DESCRIPTION',
116 ],
117 },
118 'IDS_INBOUND_MDNS_RULE_NAME': {
119 'google_chrome': [
120 'IDS_INBOUND_MDNS_RULE_NAME',
121 'IDS_INBOUND_MDNS_RULE_NAME_CANARY',
122 ],
123 'chromium': [
124 'IDS_INBOUND_MDNS_RULE_NAME',
125 ],
126 },
127 'IDS_PRODUCT_NAME': {
manzagop (departed) 2017/04/03 13:06:33 May be a good idea to also call attention here to
grt (UTC plus 2) 2017/04/03 13:31:30 Done. I also added a bit more text to the comment
128 'google_chrome': [
129 'IDS_PRODUCT_NAME',
130 'IDS_SXS_SHORTCUT_NAME',
131 ],
132 'chromium': [
133 'IDS_PRODUCT_NAME',
134 ],
135 },
136 }
137 # Note: Update the test expectations in GetBaseMessageIdForMode.GoogleStringIds
138 # when adding to/modifying the above structure.
139
74 # The ID of the first resource string. 140 # The ID of the first resource string.
75 FIRST_RESOURCE_ID = 1600 141 FIRST_RESOURCE_ID = 1600
76 142
77 143
78 class GrdHandler(sax.handler.ContentHandler): 144 class GrdHandler(sax.handler.ContentHandler):
79 """Extracts selected strings from a .grd file. 145 """Extracts selected strings from a .grd file.
80 146
81 Attributes: 147 Attributes:
82 messages: A dict mapping string identifiers to their corresponding messages. 148 messages: A dict mapping string identifiers to their corresponding messages.
83 """ 149 """
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 translated_string = ''.join(self.__text_scraps).strip() 274 translated_string = ''.join(self.__text_scraps).strip()
209 for string_id in self.__string_ids: 275 for string_id in self.__string_ids:
210 self.translations[string_id] = translated_string 276 self.translations[string_id] = translated_string
211 self.__string_ids = None 277 self.__string_ids = None
212 self.__text_scraps = [] 278 self.__text_scraps = []
213 self.__characters_callback = None 279 self.__characters_callback = None
214 280
215 281
216 class StringRcMaker(object): 282 class StringRcMaker(object):
217 """Makes .h and .rc files containing strings and translations.""" 283 """Makes .h and .rc files containing strings and translations."""
218 def __init__(self, name, inputs, outdir): 284 def __init__(self, name, inputs, outdir, brand):
219 """Constructs a maker. 285 """Constructs a maker.
220 286
221 Args: 287 Args:
222 name: The base name of the generated files (e.g., 288 name: The base name of the generated files (e.g.,
223 'installer_util_strings'). 289 'installer_util_strings').
224 inputs: A list of (grd_file, xtb_dir) pairs containing the source data. 290 inputs: A list of (grd_file, xtb_dir) pairs containing the source data.
225 outdir: The directory into which the files will be generated. 291 outdir: The directory into which the files will be generated.
226 """ 292 """
227 self.name = name 293 self.name = name
228 self.inputs = inputs 294 self.inputs = inputs
229 self.outdir = outdir 295 self.outdir = outdir
296 self.brand = brand
230 297
231 def MakeFiles(self): 298 def MakeFiles(self):
232 translated_strings = self.__ReadSourceAndTranslatedStrings() 299 translated_strings = self.__ReadSourceAndTranslatedStrings()
233 self.__WriteRCFile(translated_strings) 300 self.__WriteRCFile(translated_strings)
234 self.__WriteHeaderFile(translated_strings) 301 self.__WriteHeaderFile(translated_strings)
235 302
236 class __TranslationData(object): 303 class __TranslationData(object):
237 """A container of information about a single translation.""" 304 """A container of information about a single translation."""
238 def __init__(self, resource_id_str, language, translation): 305 def __init__(self, resource_id_str, language, translation):
239 self.resource_id_str = resource_id_str 306 self.resource_id_str = resource_id_str
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 escaped_text)) 407 escaped_text))
341 outfile.write(FOOTER_TEXT) 408 outfile.write(FOOTER_TEXT)
342 409
343 def __WriteHeaderFile(self, translated_strings): 410 def __WriteHeaderFile(self, translated_strings):
344 """Writes a .h file with resource ids.""" 411 """Writes a .h file with resource ids."""
345 # TODO(grt): Stream the lines to the file rather than building this giant 412 # TODO(grt): Stream the lines to the file rather than building this giant
346 # list of lines first. 413 # list of lines first.
347 lines = [] 414 lines = []
348 do_languages_lines = ['\n#define DO_LANGUAGES'] 415 do_languages_lines = ['\n#define DO_LANGUAGES']
349 installer_string_mapping_lines = ['\n#define DO_INSTALLER_STRING_MAPPING'] 416 installer_string_mapping_lines = ['\n#define DO_INSTALLER_STRING_MAPPING']
417 do_mode_strings_lines = ['\n#define DO_MODE_STRINGS']
350 418
351 # Write the values for how the languages ids are offset. 419 # Write the values for how the languages ids are offset.
352 seen_languages = set() 420 seen_languages = set()
353 offset_id = 0 421 offset_id = 0
354 for translation_data in translated_strings: 422 for translation_data in translated_strings:
355 lang = translation_data.language 423 lang = translation_data.language
356 if lang not in seen_languages: 424 if lang not in seen_languages:
357 seen_languages.add(lang) 425 seen_languages.add(lang)
358 lines.append('#define IDS_L10N_OFFSET_%s %s' % (lang, offset_id)) 426 lines.append('#define IDS_L10N_OFFSET_%s %s' % (lang, offset_id))
359 do_languages_lines.append(' HANDLE_LANGUAGE(%s, IDS_L10N_OFFSET_%s)' 427 do_languages_lines.append(' HANDLE_LANGUAGE(%s, IDS_L10N_OFFSET_%s)'
360 % (lang.replace('_', '-').lower(), lang)) 428 % (lang.replace('_', '-').lower(), lang))
361 offset_id += 1 429 offset_id += 1
362 else: 430 else:
363 break 431 break
364 432
365 # Write the resource ids themselves. 433 # Write the resource ids themselves.
366 resource_id = FIRST_RESOURCE_ID 434 resource_id = FIRST_RESOURCE_ID
367 for translation_data in translated_strings: 435 for translation_data in translated_strings:
368 lines.append('#define %s %s' % (translation_data.resource_id_str + '_' + 436 lines.append('#define %s %s' % (translation_data.resource_id_str + '_' +
369 translation_data.language, 437 translation_data.language,
370 resource_id)) 438 resource_id))
371 resource_id += 1 439 resource_id += 1
372 440
441 # Handle mode-specific strings.
442 for string_id, brands in MODE_SPECIFIC_STRINGS.iteritems():
443 # Populate the DO_MODE_STRINGS macro.
444 brand_strings = brands.get(self.brand)
445 if not brand_strings:
446 raise exceptions.RuntimeError(
447 'No strings declared for brand \'%s\' in MODE_SPECIFIC_STRINGS for '
448 'message %s' % (self.brand, string_id))
449 do_mode_strings_lines.append(
450 ' HANDLE_MODE_STRING(%s_BASE, %s)'
451 % (string_id, ', '.join([ ('%s_BASE' % s) for s in brand_strings])))
452
373 # Write out base ID values. 453 # Write out base ID values.
374 for string_id in STRING_IDS: 454 for string_id in STRING_IDS:
375 lines.append('#define %s_BASE %s_%s' % (string_id, 455 lines.append('#define %s_BASE %s_%s' % (string_id,
376 string_id, 456 string_id,
377 translated_strings[0].language)) 457 translated_strings[0].language))
378 installer_string_mapping_lines.append(' HANDLE_STRING(%s_BASE, %s)' 458 installer_string_mapping_lines.append(' HANDLE_STRING(%s_BASE, %s)'
379 % (string_id, string_id)) 459 % (string_id, string_id))
380 460
381 with open(os.path.join(self.outdir, self.name + '.h'), 'wb') as outfile: 461 with open(os.path.join(self.outdir, self.name + '.h'), 'wb') as outfile:
382 outfile.write('\n'.join(lines)) 462 outfile.write('\n'.join(lines))
383 outfile.write('\n#ifndef RC_INVOKED') 463 outfile.write('\n#ifndef RC_INVOKED')
384 outfile.write(' \\\n'.join(do_languages_lines)) 464 outfile.write(' \\\n'.join(do_languages_lines))
385 outfile.write(' \\\n'.join(installer_string_mapping_lines)) 465 outfile.write(' \\\n'.join(installer_string_mapping_lines))
466 outfile.write(' \\\n'.join(do_mode_strings_lines))
386 # .rc files must end in a new line 467 # .rc files must end in a new line
387 outfile.write('\n#endif // ndef RC_INVOKED\n') 468 outfile.write('\n#endif // ndef RC_INVOKED\n')
388 469
389 470
390 def ParseCommandLine(): 471 def ParseCommandLine():
391 def GrdPathAndXtbDirPair(string): 472 def GrdPathAndXtbDirPair(string):
392 """Returns (grd_path, xtb_dir) given a colon-separated string of the same. 473 """Returns (grd_path, xtb_dir) given a colon-separated string of the same.
393 """ 474 """
394 parts = string.split(':') 475 parts = string.split(':')
395 if len(parts) is not 2: 476 if len(parts) is not 2:
396 raise argparse.ArgumentTypeError('%r is not grd_path:xtb_dir') 477 raise argparse.ArgumentTypeError('%r is not grd_path:xtb_dir')
397 return (parts[0], parts[1]) 478 return (parts[0], parts[1])
398 479
399 parser = argparse.ArgumentParser( 480 parser = argparse.ArgumentParser(
400 description='Generate .h and .rc files for installer strings.') 481 description='Generate .h and .rc files for installer strings.')
482 brands = [b for b in MODE_SPECIFIC_STRINGS.itervalues().next().iterkeys()]
483 parser.add_argument('-b',
484 choices=brands,
485 required=True,
486 help='identifier of the browser brand (e.g., chromium).',
487 dest='brand')
401 parser.add_argument('-i', action='append', 488 parser.add_argument('-i', action='append',
402 type=GrdPathAndXtbDirPair, 489 type=GrdPathAndXtbDirPair,
403 required=True, 490 required=True,
404 help='path to .grd file:relative path to .xtb dir', 491 help='path to .grd file:relative path to .xtb dir',
405 metavar='GRDFILE:XTBDIR', 492 metavar='GRDFILE:XTBDIR',
406 dest='inputs') 493 dest='inputs')
407 parser.add_argument('-o', 494 parser.add_argument('-o',
408 required=True, 495 required=True,
409 help='output directory for generated .rc and .h files', 496 help='output directory for generated .rc and .h files',
410 dest='outdir') 497 dest='outdir')
411 parser.add_argument('-n', 498 parser.add_argument('-n',
412 required=True, 499 required=True,
413 help='base name of generated .rc and .h files', 500 help='base name of generated .rc and .h files',
414 dest='name') 501 dest='name')
415 return parser.parse_args() 502 return parser.parse_args()
416 503
417 504
418 def main(): 505 def main():
419 args = ParseCommandLine() 506 args = ParseCommandLine()
420 StringRcMaker(args.name, args.inputs, args.outdir).MakeFiles() 507 StringRcMaker(args.name, args.inputs, args.outdir, args.brand).MakeFiles()
421 return 0 508 return 0
422 509
423 510
424 if '__main__' == __name__: 511 if '__main__' == __name__:
425 sys.exit(main()) 512 sys.exit(main())
OLDNEW
« no previous file with comments | « chrome/installer/util/l10n_string_util_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698