1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
|
"""This module allows python programs to use GNU gettext message catalogs.
Modified by Red Hat, Inc for use with anaconda installer.
Original file information follows:
Author: James Henstridge <james@daa.com.au>
(This is loosely based on gettext.pl in the GNU gettext distribution)
The best way to use it is like so:
import gettext
gettext.bindtextdomain(PACKAGE, LOCALEDIR)
gettext.textdomain(PACKAGE)
_ = gettext.gettext
print _('Hello World')
where PACKAGE is the domain for this package, and LOCALEDIR is usually
'$prefix/share/locale' where $prefix is the install prefix.
If you have more than one catalog to use, you can directly create catalog
objects. These objects are created as so:
import gettext
cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR)
_ = cat.gettext
print _('Hello World')
The catalog object can also be accessed as a dictionary (ie cat['hello']).
There are also some experimental features. You can add to the catalog, just
as you would with a normal dictionary. When you are finished, you can call
its save method, which will create a new .mo file containing all the
translations:
import gettext
cat = Catalog()
cat['Hello'] = 'konichiwa'
cat.save('./tmp.mo')
Once you have written an internationalized program, you can create a .po file
for it with "xgettext --keyword=_ fillename ...". Then do the translation and
compile it into a .mo file, ready for use with this module. Note that you
will have to use C style strings (ie. use double quotes) for proper string
extraction.
"""
import os, string, iutil, gzread
prefix = '/usr/local'
localedir = prefix + '/share/locale'
def _expandLang(str):
langs = [str]
# remove charset ...
if '.' in str:
langs.append(string.split(str, '.')[0])
# also add 2 character language code ...
if len(str) > 2:
langs.append(str[:2])
return langs
lang = []
for env in 'LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG':
if os.environ.has_key(env):
lang = string.split(os.environ[env], ':')
lang = map(_expandLang, lang)
lang = reduce(lambda a, b: a + b, lang)
break
if 'C' not in lang:
lang.append('C')
# remove duplicates
i = 0
while i < len(lang):
j = i + 1
while j < len(lang):
if lang[i] == lang[j]:
del lang[j]
else:
j = j + 1
i = i + 1
del i, j
if os.environ.has_key('PY_XGETTEXT'):
xgettext = os.environ['PY_XGETTEXT']
else:
xgettext = None
if iutil.getArch() == 'sparc':
_gettext_byteorder = 'msb'
else:
_gettext_byteorder = 'lsb'
del os, string, iutil
error = 'gettext.error'
def _lsbStrToInt(str):
return ord(str[0]) + \
(ord(str[1]) << 8) + \
(ord(str[2]) << 16) + \
(ord(str[3]) << 24)
def _intToLsbStr(int):
return chr(int & 0xff) + \
chr((int >> 8) & 0xff) + \
chr((int >> 16) & 0xff) + \
chr((int >> 24) & 0xff)
def _msbStrToInt(str):
return ord(str[3]) + \
(ord(str[2]) << 8) + \
(ord(str[1]) << 16) + \
(ord(str[0]) << 24)
def _intToMsbStr(int):
return chr((int >> 24) & 0xff) + \
chr((int >> 16) & 0xff) + \
chr((int >> 8) & 0xff) + \
chr(int & 0xff)
def _StrToInt(str):
if _gettext_byteorder == 'msb':
return _msbStrToInt(str)
else:
return _lsbStrToInt(str)
def _intToStr(int):
if _gettext_byteorder == 'msb':
return _intToMsbStr(str)
else:
return _intToLsbStr(str)
def _getpos(levels = 0):
"""Returns the position in the code where the function was called.
The function uses some knowledge about python stack frames."""
import sys
# get access to the stack frame by generating an exception.
try:
raise RuntimeError
except RuntimeError:
frame = sys.exc_traceback.tb_frame
frame = frame.f_back # caller's frame
while levels > 0:
frame = frame.f_back
levels = levels - 1
return (frame.f_globals['__name__'],
frame.f_code.co_name,
frame.f_lineno)
class Catalog:
def __init__(self, domain=None, localedir=localedir):
self.domain = domain
self.localedir = localedir
self.cat = {}
if not domain: return
for self.lang in lang:
if self.lang == 'C':
return
catalog = "%s/%s/LC_MESSAGES/%s.mo" % (
localedir, self.lang, domain)
try:
f = gzread.open(catalog)
buffer = f.read()
f.close()
del f
break
except IOError:
pass
else:
return # assume C locale
if _StrToInt(buffer[:4]) != 0x950412de:
# magic number doesn't match
raise error, 'Bad magic number in %s' % (catalog,)
self.revision = _StrToInt(buffer[4:8])
nstrings = _StrToInt(buffer[8:12])
origTabOffset = _StrToInt(buffer[12:16])
transTabOffset = _StrToInt(buffer[16:20])
for i in range(nstrings):
origLength = _StrToInt(buffer[origTabOffset:
origTabOffset+4])
origOffset = _StrToInt(buffer[origTabOffset+4:
origTabOffset+8])
origTabOffset = origTabOffset + 8
origStr = buffer[origOffset:origOffset+origLength]
transLength = _StrToInt(buffer[transTabOffset:
transTabOffset+4])
transOffset = _StrToInt(buffer[transTabOffset+4:
transTabOffset+8])
transTabOffset = transTabOffset + 8
transStr = buffer[transOffset:transOffset+transLength]
self.cat[origStr] = transStr
def gettext(self, string):
"""Get the translation of a given string"""
if self.cat.has_key(string):
return self.cat[string]
else:
return string
# allow catalog access as cat(str) and cat[str] and cat.gettext(str)
__getitem__ = gettext
__call__ = gettext
# this is experimental code for producing mo files from Catalog objects
def __setitem__(self, string, trans):
"""Set the translation of a given string"""
self.cat[string] = trans
def save(self, file):
"""Create a .mo file from a Catalog object"""
try:
f = open(file, "wb")
except IOError:
raise error, "can't open " + file + " for writing"
f.write(_intToStr(0x950412de)) # magic number
f.write(_intToStr(0)) # revision
f.write(_intToStr(len(self.cat))) # nstrings
oIndex = []; oData = ''
tIndex = []; tData = ''
for orig, trans in self.cat.items():
oIndex.append((len(orig), len(oData)))
oData = oData + orig + '\0'
tIndex.append((len(trans), len(tData)))
tData = tData + trans + '\0'
oIndexOfs = 20
tIndexOfs = oIndexOfs + 8 * len(oIndex)
oDataOfs = tIndexOfs + 8 * len(tIndex)
tDataOfs = oDataOfs + len(oData)
f.write(_intToStr(oIndexOfs))
f.write(_intToStr(tIndexOfs))
for length, offset in oIndex:
f.write(_intToStr(length))
f.write(_intToStr(offset + oDataOfs))
for length, offset in tIndex:
f.write(_intToStr(length))
f.write(_intToStr(offset + tDataOfs))
f.write(oData)
f.write(tData)
_cat = None
_cats = {}
if xgettext:
class Catalog:
def __init__(self, domain, localedir):
self.domain = domain
self.localedir = localedir
self._strings = {}
def gettext(self, string):
# there is always one level of redirection for calls
# to this function
pos = _getpos(2) # get this function's caller
if self._strings.has_key(string):
if pos not in self._strings[string]:
self._strings[string].append(pos)
else:
self._strings[string] = [pos]
return string
__getitem__ = gettext
__call__ = gettext
def __setitem__(self, item, data):
pass
def save(self, file):
pass
def output(self, fp):
import string
fp.write('# POT file for domain %s\n' % (self.domain,))
for str in self._strings.keys():
pos = map(lambda x: "%s(%s):%d" % x,
self._strings[str])
pos.sort()
length = 80
for p in pos:
if length + len(p) > 74:
fp.write('\n#:')
length = 2
fp.write(' ')
fp.write(p)
length = length + 1 + len(p)
fp.write('\n')
if '\n' in str:
fp.write('msgid ""\n')
lines = string.split(str, '\n')
lines = map(lambda x:
'"%s\\n"\n' % (x,),
lines[:-1]) + \
['"%s"\n' % (lines[-1],)]
fp.writelines(lines)
else:
fp.write('msgid "%s"\n' % (str,))
fp.write('msgstr ""\n')
import sys
if hasattr(sys, 'exitfunc'):
_exitchain = sys.exitfunc
else:
_exitchain = None
def exitfunc(dir=xgettext, _exitchain=_exitchain):
# actually output all the .pot files.
import os
for file in _cats.keys():
fp = open(os.path.join(dir, file + '.pot'), 'w')
cat = _cats[file]
cat.output(fp)
fp.close()
if _exitchain: _exitchain()
sys.exitfunc = exitfunc
del sys, exitfunc, _exitchain, xgettext
def bindtextdomain(domain, localedir=localedir):
global _cat
if not _cats.has_key(domain):
_cats[domain] = Catalog(domain, localedir)
if not _cat: _cat = _cats[domain]
def textdomain(domain):
global _cat
if not _cats.has_key(domain):
_cats[domain] = Catalog(domain)
_cat = _cats[domain]
def gettext(string):
if _cat == None: raise error, "No catalog loaded"
return _cat.gettext(string)
_ = gettext
def dgettext(domain, string):
if domain is None:
return gettext(string)
if not _cats.has_key(domain):
raise error, "Domain '" + domain + "' not loaded"
return _cats[domain].gettext(string)
def test():
import sys
global localedir
if len(sys.argv) not in (2, 3):
print "Usage: %s DOMAIN [LOCALEDIR]" % (sys.argv[0],)
sys.exit(1)
domain = sys.argv[1]
if len(sys.argv) == 3:
bindtextdomain(domain, sys.argv[2])
textdomain(domain)
info = gettext('') # this is where special info is often stored
if info:
print "Info for domain %s, lang %s." % (domain, _cat.lang)
print info
else:
print "No info given in mo file."
def getlangs():
global lang
return lang
def setlangs(newlang):
global lang
lang = newlang
if __name__ == '__main__':
test()
|