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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: grit/format/rc.py
===================================================================
--- grit/format/rc.py (revision 0)
+++ grit/format/rc.py (revision 0)
@@ -0,0 +1,445 @@
+#!/usr/bin/python2.4
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Support for formatting an RC file for compilation.
+'''
+
+import os
+import types
+import re
+
+from grit import util
+from grit.format import interface
+
+# Matches all different types of linebreaks.
+_LINEBREAKS = re.compile('\r\n|\n|\r')
+
+'''
+This dictionary defines the langauge charset pair lookup table, which is used
+for replacing the GRIT expand variables for language info in Product Version
+resource. The key is the language ISO country code, and the value
+is the language and character-set pair, which is a hexadecimal string
+consisting of the concatenation of the language and character-set identifiers.
+The first 4 digit of the value is the hex value of LCID, the remaining
+4 digits is the hex value of character-set id(code page)of the language.
+
+We have defined three GRIT expand_variables to be used in the version resource
+file to set the language info. Here is an example how they should be used in
+the VS_VERSION_INFO section of the resource file to allow GRIT to localize
+the language info correctly according to product locale.
+
+VS_VERSION_INFO VERSIONINFO
+...
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "[GRITVERLANGCHARSETHEX]"
+ BEGIN
+ ...
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", [GRITVERLANGID], [GRITVERCHARSETID]
+ END
+END
+
+'''
+
+_LANGUAGE_CHARSET_PAIR = {
+ 'ar' : '040104e8',
+ 'fi' : '040b04e4',
+ 'ko' : '041203b5',
+ 'es' : '040a04e4',
+ 'bg' : '040204e3',
+ 'fr' : '040c04e4',
+ 'lv' : '042604e9',
+ 'sv' : '041d04e4',
+ 'ca' : '040304e4',
+ 'de' : '040704e4',
+ 'lt' : '042704e9',
+ # no lcid for tl(Tagalog), use default custom locale
+ 'tl' : '0c0004b0',
+ 'zh-CN' : '080403a8',
+ 'el' : '040804e5',
+ 'no' : '041404e4',
+ 'th' : '041e036a',
+ 'zh-TW' : '040403b6',
+ 'iw' : '040d04e7',
+ 'pl' : '041504e2',
+ 'tr' : '041f04e6',
+ 'hr' : '041a04e4',
+ # no codepage for hindi, use unicode(1200)
+ 'hi' : '043904b0',
+ 'pt-BR' : '041604e4',
+ 'uk' : '042204e3',
+ 'cs' : '040504e2',
+ 'hu' : '040e04e2',
+ 'ro' : '041804e2',
+ # no codepage for urdu, use unicode(1200)
+ 'ur' : '042004b0',
+ 'da' : '040604e4',
+ 'is' : '040f04e4',
+ 'ru' : '041904e3',
+ 'vi' : '042a04ea',
+ 'nl' : '041304e4',
+ 'id' : '042104e4',
+ 'sr' : '081a04e2',
+ 'en-GB' : '0809040e',
+ 'it' : '041004e4',
+ 'sk' : '041b04e2',
+ 'et' : '042504e9',
+ 'ja' : '041103a4',
+ 'sl' : '042404e2',
+ 'en' : '040904b0',
+ 'fake_bidi' : '040d04e7',
+}
+
+_LANGUAGE_DIRECTIVE_PAIR = {
+ 'ar' : 'LANG_ARABIC, SUBLANG_DEFAULT',
+ 'fi' : 'LANG_FINNISH, SUBLANG_DEFAULT',
+ 'ko' : 'LANG_KOREAN, SUBLANG_KOREAN',
+ 'es' : 'LANG_SPANISH, SUBLANG_SPANISH_MODERN',
+ 'bg' : 'LANG_BULGARIAN, SUBLANG_DEFAULT',
+ 'fr' : 'LANG_FRENCH, SUBLANG_FRENCH',
+ 'lv' : 'LANG_LATVIAN, SUBLANG_DEFAULT',
+ 'sv' : 'LANG_SWEDISH, SUBLANG_SWEDISH',
+ 'ca' : 'LANG_CATALAN, SUBLANG_DEFAULT',
+ 'de' : 'LANG_GERMAN, SUBLANG_GERMAN',
+ 'lt' : 'LANG_LITHUANIAN, SUBLANG_LITHUANIAN',
+ 'tl' : 'LANG_NEUTRAL, SUBLANG_DEFAULT',
+ 'zh-CN' : 'LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED',
+ 'el' : 'LANG_GREEK, SUBLANG_DEFAULT',
+ 'no' : 'LANG_NORWEGIAN, SUBLANG_DEFAULT',
+ 'th' : 'LANG_THAI, SUBLANG_DEFAULT',
+ 'zh-TW' : 'LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL',
+ 'iw' : 'LANG_HEBREW, SUBLANG_DEFAULT',
+ 'pl' : 'LANG_POLISH, SUBLANG_DEFAULT',
+ 'tr' : 'LANG_TURKISH, SUBLANG_DEFAULT',
+ 'hr' : 'LANG_CROATIAN, SUBLANG_DEFAULT',
+ 'hi' : 'LANG_HINDI, SUBLANG_DEFAULT',
+ 'pt-BR' : 'LANG_PORTUGUESE, SUBLANG_DEFAULT',
+ 'uk' : 'LANG_UKRAINIAN, SUBLANG_DEFAULT',
+ 'cs' : 'LANG_CZECH, SUBLANG_DEFAULT',
+ 'hu' : 'LANG_HUNGARIAN, SUBLANG_DEFAULT',
+ 'ro' : 'LANG_ROMANIAN, SUBLANG_DEFAULT',
+ 'ur' : 'LANG_URDU, SUBLANG_DEFAULT',
+ 'da' : 'LANG_DANISH, SUBLANG_DEFAULT',
+ 'is' : 'LANG_ICELANDIC, SUBLANG_DEFAULT',
+ 'ru' : 'LANG_RUSSIAN, SUBLANG_DEFAULT',
+ 'vi' : 'LANG_VIETNAMESE, SUBLANG_DEFAULT',
+ 'nl' : 'LANG_DUTCH, SUBLANG_DEFAULT',
+ 'id' : 'LANG_INDONESIAN, SUBLANG_DEFAULT',
+ 'sr' : 'LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC',
+ 'en-GB' : 'LANG_ENGLISH, SUBLANG_ENGLISH_UK',
+ 'it' : 'LANG_ITALIAN, SUBLANG_DEFAULT',
+ 'sk' : 'LANG_SLOVAK, SUBLANG_DEFAULT',
+ 'et' : 'LANG_ESTONIAN, SUBLANG_DEFAULT',
+ 'ja' : 'LANG_JAPANESE, SUBLANG_DEFAULT',
+ 'sl' : 'LANG_SLOVENIAN, SUBLANG_DEFAULT',
+ 'en' : 'LANG_ENGLISH, SUBLANG_ENGLISH_US',
+ 'fake_bidi' : 'LANG_HEBREW, SUBLANG_DEFAULT',
+}
+
+def GetLangCharsetPair(language) :
+ if _LANGUAGE_CHARSET_PAIR.has_key(language) :
+ return _LANGUAGE_CHARSET_PAIR[language]
+ else :
+ print 'Warning:GetLangCharsetPair() found undefined language %s' %(language)
+ return ''
+
+def GetLangDirectivePair(language) :
+ if _LANGUAGE_DIRECTIVE_PAIR.has_key(language) :
+ return _LANGUAGE_DIRECTIVE_PAIR[language]
+ else :
+ print 'Warning:GetLangDirectivePair() found undefined language %s' % (language)
+ return 'unknown language: see tools/grit/format/rc.py'
+
+def GetLangIdHex(language) :
+ if _LANGUAGE_CHARSET_PAIR.has_key(language) :
+ langcharset = _LANGUAGE_CHARSET_PAIR[language]
+ lang_id = '0x' + langcharset[0:4]
+ return lang_id
+ else :
+ print 'Warning:GetLangIdHex() found undefined language %s' %(language)
+ return ''
+
+
+def GetCharsetIdDecimal(language) :
+ if _LANGUAGE_CHARSET_PAIR.has_key(language) :
+ langcharset = _LANGUAGE_CHARSET_PAIR[language]
+ charset_decimal = int(langcharset[4:], 16)
+ return str(charset_decimal)
+ else :
+ print 'Warning:GetCharsetIdDecimal() found undefined language %s' %(language)
+ return ''
+
+
+def GetUnifiedLangCode(language) :
+ r = re.compile('([a-z]{1,2})_([a-z]{1,2})')
+ if r.match(language) :
+ underscore = language.find('_')
+ return language[0:underscore] + '-' + language[underscore + 1:].upper()
+ else :
+ return language
+
+
+def _MakeRelativePath(base_path, path_to_make_relative):
+ '''Returns a relative path such from the base_path to
+ the path_to_make_relative.
+
+ In other words, os.join(base_path,
+ MakeRelativePath(base_path, path_to_make_relative))
+ is the same location as path_to_make_relative.
+
+ Args:
+ base_path: the root path
+ path_to_make_relative: an absolute path that is on the same drive
+ as base_path
+ '''
+
+ def _GetPathAfterPrefix(prefix_path, path_with_prefix):
+ '''Gets the subpath within in prefix_path for the path_with_prefix
+ with no beginning or trailing path separators.
+
+ Args:
+ prefix_path: the base path
+ path_with_prefix: a path that starts with prefix_path
+ '''
+ assert path_with_prefix.startswith(prefix_path)
+ path_without_prefix = path_with_prefix[len(prefix_path):]
+ normalized_path = os.path.normpath(path_without_prefix.strip(os.path.sep))
+ if normalized_path == '.':
+ normalized_path = ''
+ return normalized_path
+
+ def _GetCommonBaseDirectory(*args):
+ '''Returns the common prefix directory for the given paths
+
+ Args:
+ The list of paths (at least one of which should be a directory)
+ '''
+ prefix = os.path.commonprefix(args)
+ # prefix is a character-by-character prefix (i.e. it does not end
+ # on a directory bound, so this code fixes that)
+
+ # if the prefix ends with the separator, then it is prefect.
+ if len(prefix) > 0 and prefix[-1] == os.path.sep:
+ return prefix
+
+ # We need to loop through all paths or else we can get
+ # tripped up by "c:\a" and "c:\abc". The common prefix
+ # is "c:\a" which is a directory and looks good with
+ # respect to the first directory but it is clear that
+ # isn't a common directory when the second path is
+ # examined.
+ for path in args:
+ assert len(path) >= len(prefix)
+ # If the prefix the same length as the path,
+ # then the prefix must be a directory (since one
+ # of the arguements should be a directory).
+ if path == prefix:
+ continue
+ # if the character after the prefix in the path
+ # is the separator, then the prefix appears to be a
+ # valid a directory as well for the given path
+ if path[len(prefix)] == os.path.sep:
+ continue
+ # Otherwise, the prefix is not a directory, so it needs
+ # to be shortened to be one
+ index_sep = prefix.rfind(os.path.sep)
+ # The use "index_sep + 1" because it includes the final sep
+ # and it handles the case when the index_sep is -1 as well
+ prefix = prefix[:index_sep + 1]
+ # At this point we backed up to a directory bound which is
+ # common to all paths, so we can quit going through all of
+ # the paths.
+ break
+ return prefix
+
+ prefix = _GetCommonBaseDirectory(base_path, path_to_make_relative)
+ # If the paths had no commonality at all, then return the absolute path
+ # because it is the best that can be done. If the path had to be relative
+ # then eventually this absolute path will be discovered (when a build breaks)
+ # and an appropriate fix can be made, but having this allows for the best
+ # backward compatibility with the absolute path behavior in the past.
+ if len(prefix) <= 0:
+ return path_to_make_relative
+ # Build a path from the base dir to the common prefix
+ remaining_base_path = _GetPathAfterPrefix(prefix, base_path)
+
+ # The follow handles two case: "" and "foo\\bar"
+ path_pieces = remaining_base_path.split(os.path.sep)
+ base_depth_from_prefix = len([d for d in path_pieces if len(d)])
+ base_to_prefix = (".." + os.path.sep) * base_depth_from_prefix
+
+ # Put add in the path from the prefix to the path_to_make_relative
+ remaining_other_path = _GetPathAfterPrefix(prefix, path_to_make_relative)
+ return base_to_prefix + remaining_other_path
+
+
+class TopLevel(interface.ItemFormatter):
+ '''Writes out the required preamble for RC files.'''
+ def Format(self, item, lang='en', begin_item=True, output_dir='.'):
+ assert isinstance(lang, types.StringTypes)
+ if not begin_item:
+ return ''
+ else:
+ # Find the location of the resource header file, so that we can include
+ # it.
+ resource_header = 'resource.h' # fall back to this
+ language_directive = ''
+ for output in item.GetRoot().GetOutputFiles():
+ if output.attrs['type'] == 'rc_header':
+ resource_header = os.path.abspath(output.GetOutputFilename())
+ resource_header = _MakeRelativePath(output_dir, resource_header)
+ if output.attrs['lang'] != lang:
+ continue
+ if output.attrs['language_section'] == '':
+ # If no language_section is requested, no directive is added
+ # (Used when the generated rc will be included from another rc
+ # file that will have the appropriate language directive)
+ language_directive = ''
+ elif output.attrs['language_section'] == 'neutral':
+ # If a neutral language section is requested (default), add a
+ # neutral language directive
+ language_directive = 'LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL'
+ elif output.attrs['language_section'] == 'lang':
+ language_directive = 'LANGUAGE %s' % GetLangDirectivePair(lang)
+ resource_header = resource_header.replace('\\', '\\\\')
+ return '''// Copyright (c) Google Inc. %d
+// All rights reserved.
+// This file is automatically generated by GRIT. Do not edit.
+
+#include "%s"
+#include <winresrc.h>
+#ifdef IDC_STATIC
+#undef IDC_STATIC
+#endif
+#define IDC_STATIC (-1)
+
+%s
+
+
+''' % (util.GetCurrentYear(), resource_header, language_directive)
+# end Format() function
+
+
+
+class StringTable(interface.ItemFormatter):
+ '''Surrounds a collection of string messages with the required begin and
+ end blocks to declare a string table.'''
+
+ def Format(self, item, lang='en', begin_item=True, output_dir='.'):
+ assert isinstance(lang, types.StringTypes)
+ if begin_item:
+ return 'STRINGTABLE\nBEGIN\n'
+ else:
+ return 'END\n\n'
+
+
+class Message(interface.ItemFormatter):
+ '''Writes out a single message to a string table.'''
+
+ def Format(self, item, lang='en', begin_item=True, output_dir='.'):
+ from grit.node import message
+ if not begin_item:
+ return ''
+
+ assert isinstance(lang, types.StringTypes)
+ assert isinstance(item, message.MessageNode)
+
+ message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
+ # Escape quotation marks (RC format uses doubling-up
+ message = message.replace('"', '""')
+ # Replace linebreaks with a \n escape
+ message = _LINEBREAKS.sub(r'\\n', message)
+
+ name_attr = item.GetTextualIds()[0]
+
+ return ' %-15s "%s"\n' % (name_attr, message)
+
+
+class RcSection(interface.ItemFormatter):
+ '''Writes out an .rc file section.'''
+
+ def Format(self, item, lang='en', begin_item=True, output_dir='.'):
+ if not begin_item:
+ return ''
+
+ assert isinstance(lang, types.StringTypes)
+ from grit.node import structure
+ assert isinstance(item, structure.StructureNode)
+
+ if item.IsExcludedFromRc():
+ return ''
+ else:
+ text = item.gatherer.Translate(
+ lang, skeleton_gatherer=item.GetSkeletonGatherer(),
+ pseudo_if_not_available=item.PseudoIsAllowed(),
+ fallback_to_english=item.ShouldFallbackToEnglish()) + '\n\n'
+
+ # Replace the language expand_variables in version rc info.
+ unified_lang_code = GetUnifiedLangCode(lang)
+ if text.find('[GRITVERLANGCHARSETHEX]') != -1:
+ text = text.replace('[GRITVERLANGCHARSETHEX]',
+ GetLangCharsetPair(unified_lang_code))
+ if text.find('[GRITVERLANGID]') != -1:
+ text = text.replace('[GRITVERLANGID]', GetLangIdHex(unified_lang_code))
+ if text.find('[GRITVERCHARSETID]') != -1:
+ text = text.replace('[GRITVERCHARSETID]',
+ GetCharsetIdDecimal(unified_lang_code))
+
+ return text
+
+
+class RcInclude(interface.ItemFormatter):
+ '''Writes out an item that is included in an .rc file (e.g. an ICON)'''
+
+ def __init__(self, type, filenameWithoutPath = 0, relative_path = 0,
+ flatten_html = 0):
+ '''Indicates to the instance what the type of the resource include is,
+ e.g. 'ICON' or 'HTML'. Case must be correct, i.e. if the type is all-caps
+ the parameter should be all-caps.
+
+ Args:
+ type: 'ICON'
+ '''
+ self.type_ = type
+ self.filenameWithoutPath = filenameWithoutPath
+ self.relative_path_ = relative_path
+ self.flatten_html = flatten_html
+
+ def Format(self, item, lang='en', begin_item=True, output_dir='.'):
+ if not begin_item:
+ return ''
+
+ assert isinstance(lang, types.StringTypes)
+ from grit.node import structure
+ from grit.node import include
+ assert isinstance(item, (structure.StructureNode, include.IncludeNode))
+ assert (isinstance(item, include.IncludeNode) or
+ item.attrs['type'] in ['tr_html', 'admin_template', 'txt', 'muppet'])
+
+ # By default, we use relative pathnames to included resources so that
+ # sharing the resulting .rc files is possible.
+ #
+ # The FileForLanguage() Function has the side effect of generating the file
+ # if needed (e.g. if it is an HTML file include).
+ filename = os.path.abspath(item.FileForLanguage(lang, output_dir))
+ if self.flatten_html:
+ filename = item.Flatten(output_dir)
+ elif self.filenameWithoutPath:
+ filename = os.path.basename(filename)
+ elif self.relative_path_:
+ filename = _MakeRelativePath(output_dir, filename)
+
+ filename = filename.replace('\\', '\\\\') # escape for the RC format
+
+ if isinstance(item, structure.StructureNode) and item.IsExcludedFromRc():
+ return ''
+ else:
+ return '%-18s %-18s "%s"\n' % (item.attrs['name'], self.type_, filename)
+
Property changes on: grit/format/rc.py
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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