diff options
-rw-r--r-- | gettext_rh.py | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/gettext_rh.py b/gettext_rh.py new file mode 100644 index 000000000..4c7d38030 --- /dev/null +++ b/gettext_rh.py @@ -0,0 +1,368 @@ +"""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 + +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 + import os + for self.lang in lang: + if self.lang == 'C': + del os + return + catalog = "%s//%s/LC_MESSAGES/%s.mo" % ( + localedir, self.lang, domain) + if not os.access (catalog, os.R_OK) and os.access (catalog + ".gz", os.R_OK): + os.system ("/usr/bin/gunzip < %s.gz > %s " % (catalog, catalog)) + + try: + f = open(catalog, "rb") + buffer = f.read() + del f + if os.access (catalog + ".gz", os.R_OK): + try: + os.remove (catalog) + except: + pass + break + except IOError: + pass + else: + del os + return # assume C locale + + del os + 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() + |