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

Side by Side Diff: grit/format/rc.py

Issue 7994004: Initial source commit to grit-i18n project. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 9 years, 3 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
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright (c) 2011 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 '''Support for formatting an RC file for compilation.
7 '''
8
9 import os
10 import types
11 import re
12
13 from grit import util
14 from grit.format import interface
15
16 # Matches all different types of linebreaks.
17 _LINEBREAKS = re.compile('\r\n|\n|\r')
18
19 '''
20 This dictionary defines the langauge charset pair lookup table, which is used
21 for replacing the GRIT expand variables for language info in Product Version
22 resource. The key is the language ISO country code, and the value
23 is the language and character-set pair, which is a hexadecimal string
24 consisting of the concatenation of the language and character-set identifiers.
25 The first 4 digit of the value is the hex value of LCID, the remaining
26 4 digits is the hex value of character-set id(code page)of the language.
27
28 We have defined three GRIT expand_variables to be used in the version resource
29 file to set the language info. Here is an example how they should be used in
30 the VS_VERSION_INFO section of the resource file to allow GRIT to localize
31 the language info correctly according to product locale.
32
33 VS_VERSION_INFO VERSIONINFO
34 ...
35 BEGIN
36 BLOCK "StringFileInfo"
37 BEGIN
38 BLOCK "[GRITVERLANGCHARSETHEX]"
39 BEGIN
40 ...
41 END
42 END
43 BLOCK "VarFileInfo"
44 BEGIN
45 VALUE "Translation", [GRITVERLANGID], [GRITVERCHARSETID]
46 END
47 END
48
49 '''
50
51 _LANGUAGE_CHARSET_PAIR = {
52 'ar' : '040104e8',
53 'fi' : '040b04e4',
54 'ko' : '041203b5',
55 'es' : '040a04e4',
56 'bg' : '040204e3',
57 'fr' : '040c04e4',
58 'lv' : '042604e9',
59 'sv' : '041d04e4',
60 'ca' : '040304e4',
61 'de' : '040704e4',
62 'lt' : '042704e9',
63 # no lcid for tl(Tagalog), use default custom locale
64 'tl' : '0c0004b0',
65 'zh-CN' : '080403a8',
66 'el' : '040804e5',
67 'no' : '041404e4',
68 'th' : '041e036a',
69 'zh-TW' : '040403b6',
70 'iw' : '040d04e7',
71 'pl' : '041504e2',
72 'tr' : '041f04e6',
73 'hr' : '041a04e4',
74 # no codepage for hindi, use unicode(1200)
75 'hi' : '043904b0',
76 'pt-BR' : '041604e4',
77 'uk' : '042204e3',
78 'cs' : '040504e2',
79 'hu' : '040e04e2',
80 'ro' : '041804e2',
81 # no codepage for urdu, use unicode(1200)
82 'ur' : '042004b0',
83 'da' : '040604e4',
84 'is' : '040f04e4',
85 'ru' : '041904e3',
86 'vi' : '042a04ea',
87 'nl' : '041304e4',
88 'id' : '042104e4',
89 'sr' : '081a04e2',
90 'en-GB' : '0809040e',
91 'it' : '041004e4',
92 'sk' : '041b04e2',
93 'et' : '042504e9',
94 'ja' : '041103a4',
95 'sl' : '042404e2',
96 'en' : '040904b0',
97 'fake_bidi' : '040d04e7',
98 }
99
100 _LANGUAGE_DIRECTIVE_PAIR = {
101 'ar' : 'LANG_ARABIC, SUBLANG_DEFAULT',
102 'fi' : 'LANG_FINNISH, SUBLANG_DEFAULT',
103 'ko' : 'LANG_KOREAN, SUBLANG_KOREAN',
104 'es' : 'LANG_SPANISH, SUBLANG_SPANISH_MODERN',
105 'bg' : 'LANG_BULGARIAN, SUBLANG_DEFAULT',
106 'fr' : 'LANG_FRENCH, SUBLANG_FRENCH',
107 'lv' : 'LANG_LATVIAN, SUBLANG_DEFAULT',
108 'sv' : 'LANG_SWEDISH, SUBLANG_SWEDISH',
109 'ca' : 'LANG_CATALAN, SUBLANG_DEFAULT',
110 'de' : 'LANG_GERMAN, SUBLANG_GERMAN',
111 'lt' : 'LANG_LITHUANIAN, SUBLANG_LITHUANIAN',
112 'tl' : 'LANG_NEUTRAL, SUBLANG_DEFAULT',
113 'zh-CN' : 'LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED',
114 'el' : 'LANG_GREEK, SUBLANG_DEFAULT',
115 'no' : 'LANG_NORWEGIAN, SUBLANG_DEFAULT',
116 'th' : 'LANG_THAI, SUBLANG_DEFAULT',
117 'zh-TW' : 'LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL',
118 'iw' : 'LANG_HEBREW, SUBLANG_DEFAULT',
119 'pl' : 'LANG_POLISH, SUBLANG_DEFAULT',
120 'tr' : 'LANG_TURKISH, SUBLANG_DEFAULT',
121 'hr' : 'LANG_CROATIAN, SUBLANG_DEFAULT',
122 'hi' : 'LANG_HINDI, SUBLANG_DEFAULT',
123 'pt-BR' : 'LANG_PORTUGUESE, SUBLANG_DEFAULT',
124 'uk' : 'LANG_UKRAINIAN, SUBLANG_DEFAULT',
125 'cs' : 'LANG_CZECH, SUBLANG_DEFAULT',
126 'hu' : 'LANG_HUNGARIAN, SUBLANG_DEFAULT',
127 'ro' : 'LANG_ROMANIAN, SUBLANG_DEFAULT',
128 'ur' : 'LANG_URDU, SUBLANG_DEFAULT',
129 'da' : 'LANG_DANISH, SUBLANG_DEFAULT',
130 'is' : 'LANG_ICELANDIC, SUBLANG_DEFAULT',
131 'ru' : 'LANG_RUSSIAN, SUBLANG_DEFAULT',
132 'vi' : 'LANG_VIETNAMESE, SUBLANG_DEFAULT',
133 'nl' : 'LANG_DUTCH, SUBLANG_DEFAULT',
134 'id' : 'LANG_INDONESIAN, SUBLANG_DEFAULT',
135 'sr' : 'LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC',
136 'en-GB' : 'LANG_ENGLISH, SUBLANG_ENGLISH_UK',
137 'it' : 'LANG_ITALIAN, SUBLANG_DEFAULT',
138 'sk' : 'LANG_SLOVAK, SUBLANG_DEFAULT',
139 'et' : 'LANG_ESTONIAN, SUBLANG_DEFAULT',
140 'ja' : 'LANG_JAPANESE, SUBLANG_DEFAULT',
141 'sl' : 'LANG_SLOVENIAN, SUBLANG_DEFAULT',
142 'en' : 'LANG_ENGLISH, SUBLANG_ENGLISH_US',
143 'fake_bidi' : 'LANG_HEBREW, SUBLANG_DEFAULT',
144 }
145
146 def GetLangCharsetPair(language) :
147 if _LANGUAGE_CHARSET_PAIR.has_key(language) :
148 return _LANGUAGE_CHARSET_PAIR[language]
149 else :
150 print 'Warning:GetLangCharsetPair() found undefined language %s' %(language)
151 return ''
152
153 def GetLangDirectivePair(language) :
154 if _LANGUAGE_DIRECTIVE_PAIR.has_key(language) :
155 return _LANGUAGE_DIRECTIVE_PAIR[language]
156 else :
157 print 'Warning:GetLangDirectivePair() found undefined language %s' % (langua ge)
158 return 'unknown language: see tools/grit/format/rc.py'
159
160 def GetLangIdHex(language) :
161 if _LANGUAGE_CHARSET_PAIR.has_key(language) :
162 langcharset = _LANGUAGE_CHARSET_PAIR[language]
163 lang_id = '0x' + langcharset[0:4]
164 return lang_id
165 else :
166 print 'Warning:GetLangIdHex() found undefined language %s' %(language)
167 return ''
168
169
170 def GetCharsetIdDecimal(language) :
171 if _LANGUAGE_CHARSET_PAIR.has_key(language) :
172 langcharset = _LANGUAGE_CHARSET_PAIR[language]
173 charset_decimal = int(langcharset[4:], 16)
174 return str(charset_decimal)
175 else :
176 print 'Warning:GetCharsetIdDecimal() found undefined language %s' %(language )
177 return ''
178
179
180 def GetUnifiedLangCode(language) :
181 r = re.compile('([a-z]{1,2})_([a-z]{1,2})')
182 if r.match(language) :
183 underscore = language.find('_')
184 return language[0:underscore] + '-' + language[underscore + 1:].upper()
185 else :
186 return language
187
188
189 def _MakeRelativePath(base_path, path_to_make_relative):
190 '''Returns a relative path such from the base_path to
191 the path_to_make_relative.
192
193 In other words, os.join(base_path,
194 MakeRelativePath(base_path, path_to_make_relative))
195 is the same location as path_to_make_relative.
196
197 Args:
198 base_path: the root path
199 path_to_make_relative: an absolute path that is on the same drive
200 as base_path
201 '''
202
203 def _GetPathAfterPrefix(prefix_path, path_with_prefix):
204 '''Gets the subpath within in prefix_path for the path_with_prefix
205 with no beginning or trailing path separators.
206
207 Args:
208 prefix_path: the base path
209 path_with_prefix: a path that starts with prefix_path
210 '''
211 assert path_with_prefix.startswith(prefix_path)
212 path_without_prefix = path_with_prefix[len(prefix_path):]
213 normalized_path = os.path.normpath(path_without_prefix.strip(os.path.sep))
214 if normalized_path == '.':
215 normalized_path = ''
216 return normalized_path
217
218 def _GetCommonBaseDirectory(*args):
219 '''Returns the common prefix directory for the given paths
220
221 Args:
222 The list of paths (at least one of which should be a directory)
223 '''
224 prefix = os.path.commonprefix(args)
225 # prefix is a character-by-character prefix (i.e. it does not end
226 # on a directory bound, so this code fixes that)
227
228 # if the prefix ends with the separator, then it is prefect.
229 if len(prefix) > 0 and prefix[-1] == os.path.sep:
230 return prefix
231
232 # We need to loop through all paths or else we can get
233 # tripped up by "c:\a" and "c:\abc". The common prefix
234 # is "c:\a" which is a directory and looks good with
235 # respect to the first directory but it is clear that
236 # isn't a common directory when the second path is
237 # examined.
238 for path in args:
239 assert len(path) >= len(prefix)
240 # If the prefix the same length as the path,
241 # then the prefix must be a directory (since one
242 # of the arguements should be a directory).
243 if path == prefix:
244 continue
245 # if the character after the prefix in the path
246 # is the separator, then the prefix appears to be a
247 # valid a directory as well for the given path
248 if path[len(prefix)] == os.path.sep:
249 continue
250 # Otherwise, the prefix is not a directory, so it needs
251 # to be shortened to be one
252 index_sep = prefix.rfind(os.path.sep)
253 # The use "index_sep + 1" because it includes the final sep
254 # and it handles the case when the index_sep is -1 as well
255 prefix = prefix[:index_sep + 1]
256 # At this point we backed up to a directory bound which is
257 # common to all paths, so we can quit going through all of
258 # the paths.
259 break
260 return prefix
261
262 prefix = _GetCommonBaseDirectory(base_path, path_to_make_relative)
263 # If the paths had no commonality at all, then return the absolute path
264 # because it is the best that can be done. If the path had to be relative
265 # then eventually this absolute path will be discovered (when a build breaks)
266 # and an appropriate fix can be made, but having this allows for the best
267 # backward compatibility with the absolute path behavior in the past.
268 if len(prefix) <= 0:
269 return path_to_make_relative
270 # Build a path from the base dir to the common prefix
271 remaining_base_path = _GetPathAfterPrefix(prefix, base_path)
272
273 # The follow handles two case: "" and "foo\\bar"
274 path_pieces = remaining_base_path.split(os.path.sep)
275 base_depth_from_prefix = len([d for d in path_pieces if len(d)])
276 base_to_prefix = (".." + os.path.sep) * base_depth_from_prefix
277
278 # Put add in the path from the prefix to the path_to_make_relative
279 remaining_other_path = _GetPathAfterPrefix(prefix, path_to_make_relative)
280 return base_to_prefix + remaining_other_path
281
282
283 class TopLevel(interface.ItemFormatter):
284 '''Writes out the required preamble for RC files.'''
285 def Format(self, item, lang='en', begin_item=True, output_dir='.'):
286 assert isinstance(lang, types.StringTypes)
287 if not begin_item:
288 return ''
289 else:
290 # Find the location of the resource header file, so that we can include
291 # it.
292 resource_header = 'resource.h' # fall back to this
293 language_directive = ''
294 for output in item.GetRoot().GetOutputFiles():
295 if output.attrs['type'] == 'rc_header':
296 resource_header = os.path.abspath(output.GetOutputFilename())
297 resource_header = _MakeRelativePath(output_dir, resource_header)
298 if output.attrs['lang'] != lang:
299 continue
300 if output.attrs['language_section'] == '':
301 # If no language_section is requested, no directive is added
302 # (Used when the generated rc will be included from another rc
303 # file that will have the appropriate language directive)
304 language_directive = ''
305 elif output.attrs['language_section'] == 'neutral':
306 # If a neutral language section is requested (default), add a
307 # neutral language directive
308 language_directive = 'LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL'
309 elif output.attrs['language_section'] == 'lang':
310 language_directive = 'LANGUAGE %s' % GetLangDirectivePair(lang)
311 resource_header = resource_header.replace('\\', '\\\\')
312 return '''// Copyright (c) Google Inc. %d
313 // All rights reserved.
314 // This file is automatically generated by GRIT. Do not edit.
315
316 #include "%s"
317 #include <winresrc.h>
318 #ifdef IDC_STATIC
319 #undef IDC_STATIC
320 #endif
321 #define IDC_STATIC (-1)
322
323 %s
324
325
326 ''' % (util.GetCurrentYear(), resource_header, language_directive)
327 # end Format() function
328
329
330
331 class StringTable(interface.ItemFormatter):
332 '''Surrounds a collection of string messages with the required begin and
333 end blocks to declare a string table.'''
334
335 def Format(self, item, lang='en', begin_item=True, output_dir='.'):
336 assert isinstance(lang, types.StringTypes)
337 if begin_item:
338 return 'STRINGTABLE\nBEGIN\n'
339 else:
340 return 'END\n\n'
341
342
343 class Message(interface.ItemFormatter):
344 '''Writes out a single message to a string table.'''
345
346 def Format(self, item, lang='en', begin_item=True, output_dir='.'):
347 from grit.node import message
348 if not begin_item:
349 return ''
350
351 assert isinstance(lang, types.StringTypes)
352 assert isinstance(item, message.MessageNode)
353
354 message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
355 # Escape quotation marks (RC format uses doubling-up
356 message = message.replace('"', '""')
357 # Replace linebreaks with a \n escape
358 message = _LINEBREAKS.sub(r'\\n', message)
359
360 name_attr = item.GetTextualIds()[0]
361
362 return ' %-15s "%s"\n' % (name_attr, message)
363
364
365 class RcSection(interface.ItemFormatter):
366 '''Writes out an .rc file section.'''
367
368 def Format(self, item, lang='en', begin_item=True, output_dir='.'):
369 if not begin_item:
370 return ''
371
372 assert isinstance(lang, types.StringTypes)
373 from grit.node import structure
374 assert isinstance(item, structure.StructureNode)
375
376 if item.IsExcludedFromRc():
377 return ''
378 else:
379 text = item.gatherer.Translate(
380 lang, skeleton_gatherer=item.GetSkeletonGatherer(),
381 pseudo_if_not_available=item.PseudoIsAllowed(),
382 fallback_to_english=item.ShouldFallbackToEnglish()) + '\n\n'
383
384 # Replace the language expand_variables in version rc info.
385 unified_lang_code = GetUnifiedLangCode(lang)
386 if text.find('[GRITVERLANGCHARSETHEX]') != -1:
387 text = text.replace('[GRITVERLANGCHARSETHEX]',
388 GetLangCharsetPair(unified_lang_code))
389 if text.find('[GRITVERLANGID]') != -1:
390 text = text.replace('[GRITVERLANGID]', GetLangIdHex(unified_lang_code))
391 if text.find('[GRITVERCHARSETID]') != -1:
392 text = text.replace('[GRITVERCHARSETID]',
393 GetCharsetIdDecimal(unified_lang_code))
394
395 return text
396
397
398 class RcInclude(interface.ItemFormatter):
399 '''Writes out an item that is included in an .rc file (e.g. an ICON)'''
400
401 def __init__(self, type, filenameWithoutPath = 0, relative_path = 0,
402 flatten_html = 0):
403 '''Indicates to the instance what the type of the resource include is,
404 e.g. 'ICON' or 'HTML'. Case must be correct, i.e. if the type is all-caps
405 the parameter should be all-caps.
406
407 Args:
408 type: 'ICON'
409 '''
410 self.type_ = type
411 self.filenameWithoutPath = filenameWithoutPath
412 self.relative_path_ = relative_path
413 self.flatten_html = flatten_html
414
415 def Format(self, item, lang='en', begin_item=True, output_dir='.'):
416 if not begin_item:
417 return ''
418
419 assert isinstance(lang, types.StringTypes)
420 from grit.node import structure
421 from grit.node import include
422 assert isinstance(item, (structure.StructureNode, include.IncludeNode))
423 assert (isinstance(item, include.IncludeNode) or
424 item.attrs['type'] in ['tr_html', 'admin_template', 'txt', 'muppet'] )
425
426 # By default, we use relative pathnames to included resources so that
427 # sharing the resulting .rc files is possible.
428 #
429 # The FileForLanguage() Function has the side effect of generating the file
430 # if needed (e.g. if it is an HTML file include).
431 filename = os.path.abspath(item.FileForLanguage(lang, output_dir))
432 if self.flatten_html:
433 filename = item.Flatten(output_dir)
434 elif self.filenameWithoutPath:
435 filename = os.path.basename(filename)
436 elif self.relative_path_:
437 filename = _MakeRelativePath(output_dir, filename)
438
439 filename = filename.replace('\\', '\\\\') # escape for the RC format
440
441 if isinstance(item, structure.StructureNode) and item.IsExcludedFromRc():
442 return ''
443 else:
444 return '%-18s %-18s "%s"\n' % (item.attrs['name'], self.type_, filename)
445
OLDNEW
« no previous file with comments | « grit/format/policy_templates/writers/xml_writer_base_unittest.py ('k') | grit/format/rc_header.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698