"""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 <
[email protected]>
(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()