Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. #!/usr/bin/python
  2.  
  3. #
  4. # Usage:
  5. #     gen_xmlpool.py /path/to/t_option.h localedir lang lang lang ...
  6. #
  7. # For each given language, this script expects to find a .mo file at
  8. # `{localedir}/{language}/LC_MESSAGES/options.mo`.
  9. #
  10.  
  11. import sys
  12. import gettext
  13. import re
  14.  
  15. # Path to t_options.h
  16. template_header_path = sys.argv[1]
  17.  
  18. localedir = sys.argv[2]
  19.  
  20. # List of supported languages
  21. languages = sys.argv[3:]
  22.  
  23. # Escape special characters in C strings
  24. def escapeCString (s):
  25.     escapeSeqs = {'\a' : '\\a', '\b' : '\\b', '\f' : '\\f', '\n' : '\\n',
  26.                   '\r' : '\\r', '\t' : '\\t', '\v' : '\\v', '\\' : '\\\\'}
  27.     # " -> '' is a hack. Quotes (") aren't possible in XML attributes.
  28.     # Better use Unicode characters for typographic quotes in option
  29.     # descriptions and translations.
  30.     i = 0
  31.     r = ''
  32.     while i < len(s):
  33.         # Special case: escape double quote with \u201c or \u201d, depending
  34.         # on whether it's an open or close quote. This is needed because plain
  35.         # double quotes are not possible in XML attributes.
  36.         if s[i] == '"':
  37.             if i == len(s)-1 or s[i+1].isspace():
  38.                 # close quote
  39.                 q = u'\u201c'
  40.             else:
  41.                 # open quote
  42.                 q = u'\u201d'
  43.             r = r + q
  44.         elif escapeSeqs.has_key(s[i]):
  45.             r = r + escapeSeqs[s[i]]
  46.         else:
  47.             r = r + s[i]
  48.         i = i + 1
  49.     return r
  50.  
  51. # Expand escape sequences in C strings (needed for gettext lookup)
  52. def expandCString (s):
  53.     escapeSeqs = {'a' : '\a', 'b' : '\b', 'f' : '\f', 'n' : '\n',
  54.                   'r' : '\r', 't' : '\t', 'v' : '\v',
  55.                   '"' : '"', '\\' : '\\'}
  56.     i = 0
  57.     escape = False
  58.     hexa = False
  59.     octa = False
  60.     num = 0
  61.     digits = 0
  62.     r = ''
  63.     while i < len(s):
  64.         if not escape:
  65.             if s[i] == '\\':
  66.                 escape = True
  67.             else:
  68.                 r = r + s[i]
  69.         elif hexa:
  70.             if (s[i] >= '0' and s[i] <= '9') or \
  71.                (s[i] >= 'a' and s[i] <= 'f') or \
  72.                (s[i] >= 'A' and s[i] <= 'F'):
  73.                 num = num * 16 + int(s[i],16)
  74.                 digits = digits + 1
  75.             else:
  76.                 digits = 2
  77.             if digits >= 2:
  78.                 hexa = False
  79.                 escape = False
  80.                 r = r + chr(num)
  81.         elif octa:
  82.             if s[i] >= '0' and s[i] <= '7':
  83.                 num = num * 8 + int(s[i],8)
  84.                 digits = digits + 1
  85.             else:
  86.                 digits = 3
  87.             if digits >= 3:
  88.                 octa = False
  89.                 escape = False
  90.                 r = r + chr(num)
  91.         else:
  92.             if escapeSeqs.has_key(s[i]):
  93.                 r = r + escapeSeqs[s[i]]
  94.                 escape = False
  95.             elif s[i] >= '0' and s[i] <= '7':
  96.                 octa = True
  97.                 num = int(s[i],8)
  98.                 if num <= 3:
  99.                     digits = 1
  100.                 else:
  101.                     digits = 2
  102.             elif s[i] == 'x' or s[i] == 'X':
  103.                 hexa = True
  104.                 num = 0
  105.                 digits = 0
  106.             else:
  107.                 r = r + s[i]
  108.                 escape = False
  109.         i = i + 1
  110.     return r
  111.  
  112. # Expand matches. The first match is always a DESC or DESC_BEGIN match.
  113. # Subsequent matches are ENUM matches.
  114. #
  115. # DESC, DESC_BEGIN format: \1 \2=<lang> \3 \4=gettext(" \5=<text> \6=") \7
  116. # ENUM format:             \1 \2=gettext(" \3=<text> \4=") \5
  117. def expandMatches (matches, translations, end=None):
  118.     assert len(matches) > 0
  119.     nTranslations = len(translations)
  120.     i = 0
  121.     # Expand the description+enums for all translations
  122.     for lang,trans in translations:
  123.         i = i + 1
  124.         # Make sure that all but the last line of a simple description
  125.         # are extended with a backslash.
  126.         suffix = ''
  127.         if len(matches) == 1 and i < len(translations) and \
  128.                not matches[0].expand (r'\7').endswith('\\'):
  129.             suffix = ' \\'
  130.         # Expand the description line. Need to use ugettext in order to allow
  131.         # non-ascii unicode chars in the original English descriptions.
  132.         text = escapeCString (trans.ugettext (unicode (expandCString (
  133.             matches[0].expand (r'\5')), "utf-8"))).encode("utf-8")
  134.         print matches[0].expand (r'\1' + lang + r'\3"' + text + r'"\7') + suffix
  135.         # Expand any subsequent enum lines
  136.         for match in matches[1:]:
  137.             text = escapeCString (trans.ugettext (unicode (expandCString (
  138.                 match.expand (r'\3')), "utf-8"))).encode("utf-8")
  139.             print match.expand (r'\1"' + text + r'"\5')
  140.  
  141.         # Expand description end
  142.         if end:
  143.             print end,
  144.  
  145. # Compile a list of translation classes to all supported languages.
  146. # The first translation is always a NullTranslations.
  147. translations = [("en", gettext.NullTranslations())]
  148. for lang in languages:
  149.     try:
  150.         trans = gettext.translation ("options", localedir, [lang])
  151.     except IOError:
  152.         sys.stderr.write ("Warning: language '%s' not found.\n" % lang)
  153.         continue
  154.     translations.append ((lang, trans))
  155.  
  156. # Regular expressions:
  157. reLibintl_h  = re.compile (r'#\s*include\s*<libintl.h>')
  158. reDESC       = re.compile (r'(\s*DRI_CONF_DESC\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
  159. reDESC_BEGIN = re.compile (r'(\s*DRI_CONF_DESC_BEGIN\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
  160. reENUM       = re.compile (r'(\s*DRI_CONF_ENUM\s*\([^,]+,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
  161. reDESC_END   = re.compile (r'\s*DRI_CONF_DESC_END')
  162.  
  163. # Print a header
  164. print \
  165. "/***********************************************************************\n" \
  166. " ***        THIS FILE IS GENERATED AUTOMATICALLY. DON'T EDIT!        ***\n" \
  167. " ***********************************************************************/"
  168.  
  169. # Process the options template and generate options.h with all
  170. # translations.
  171. template = file (template_header_path, "r")
  172. descMatches = []
  173. for line in template:
  174.     if len(descMatches) > 0:
  175.         matchENUM     = reENUM    .match (line)
  176.         matchDESC_END = reDESC_END.match (line)
  177.         if matchENUM:
  178.             descMatches.append (matchENUM)
  179.         elif matchDESC_END:
  180.             expandMatches (descMatches, translations, line)
  181.             descMatches = []
  182.         else:
  183.             sys.stderr.write (
  184.                 "Warning: unexpected line inside description dropped:\n%s\n" \
  185.                 % line)
  186.         continue
  187.     if reLibintl_h.search (line):
  188.         # Ignore (comment out) #include <libintl.h>
  189.         print "/* %s * commented out by gen_xmlpool.py */" % line
  190.         continue
  191.     matchDESC       = reDESC      .match (line)
  192.     matchDESC_BEGIN = reDESC_BEGIN.match (line)
  193.     if matchDESC:
  194.         assert len(descMatches) == 0
  195.         expandMatches ([matchDESC], translations)
  196.     elif matchDESC_BEGIN:
  197.         assert len(descMatches) == 0
  198.         descMatches = [matchDESC_BEGIN]
  199.     else:
  200.         print line,
  201.  
  202. if len(descMatches) > 0:
  203.     sys.stderr.write ("Warning: unterminated description at end of file.\n")
  204.     expandMatches (descMatches, translations)
  205.