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