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