13257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel"""Internationalization and localization support. 23257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 33257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielThis module provides internationalization (I18N) and localization (L10N) 43257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielsupport for your Python programs by providing an interface to the GNU gettext 53257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielmessage catalog library. 63257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 73257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielI18N refers to the operation by which a program is made aware of multiple 83257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniellanguages. L10N refers to the adaptation of your program, once 93257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielinternationalized, to the local language and cultural habits. 103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel""" 123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# This module represents the integration of work, contributions, feedback, and 143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# suggestions from the following people: 153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Martin von Loewis, who wrote the initial implementation of the underlying 173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# C-based libintlmodule (later renamed _gettext), along with a skeletal 183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# gettext.py implementation. 193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Peter Funk, who wrote fintl.py, a fairly complete wrapper around intlmodule, 213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# which also included a pure-Python implementation to read .mo files if 223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# intlmodule wasn't available. 233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# James Henstridge, who also wrote a gettext.py module, which has some 253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# interesting, but currently unsupported experimental features: the notion of 263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a Catalog class and instances, and the ability to add to a catalog file via 273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a Python API. 283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Barry Warsaw integrated these modules, wrote the .install() API and code, 303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# and conformed all C and Python code to Python's coding standards. 313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Francois Pinard and Marc-Andre Lemburg also contributed valuably to this 333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# module. 343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# J. David Ibanez implemented plural forms. Bruno Haible fixed some bugs. 363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# TODO: 383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# - Lazy loading of .mo files. Currently the entire catalog is loaded into 393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# memory, but that's probably bad for large translated programs. Instead, 403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# the lexical sort of original strings in GNU .mo files should be exploited 413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# to do binary searches and lazy initializations. Or you might want to use 423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# the undocumented double-hash algorithm for .mo files with hash tables, but 433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# you'll need to study the GNU gettext code to do this. 443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# - Support Solaris .mo file formats. Unfortunately, we've been unable to 463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# find this format documented anywhere. 473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielimport locale, copy, os, re, struct, sys 503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielfrom errno import ENOENT 513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel__all__ = ['NullTranslations', 'GNUTranslations', 'Catalog', 543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 'find', 'translation', 'install', 'textdomain', 'bindtextdomain', 553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 'bind_textdomain_codeset', 563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 'dgettext', 'dngettext', 'gettext', 'lgettext', 'ldgettext', 573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 'ldngettext', 'lngettext', 'ngettext', 583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel ] 593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_default_localedir = os.path.join(sys.prefix, 'share', 'locale') 613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef test(condition, true, false): 643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel """ 653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel Implements the C expression: 663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel condition ? true : false 683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel Required to correctly interpret plural forms. 703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel """ 713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if condition: 723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return true 733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return false 753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef c2py(plural): 783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel """Gets a C expression as used in PO files for plural forms and returns a 793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel Python lambda function that implements an equivalent expression. 803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel """ 813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Security check, allow only the "n" identifier 823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel from cStringIO import StringIO 843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except ImportError: 853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel from StringIO import StringIO 863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel import token, tokenize 873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tokens = tokenize.generate_tokens(StringIO(plural).readline) 883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel danger = [x for x in tokens if x[0] == token.NAME and x[1] != 'n'] 903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except tokenize.TokenError: 913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel raise ValueError, \ 923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 'plural forms expression error, maybe unbalanced parenthesis' 933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if danger: 953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel raise ValueError, 'plural forms expression could be dangerous' 963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Replace some C operators by their Python equivalents 983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel plural = plural.replace('&&', ' and ') 993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel plural = plural.replace('||', ' or ') 1003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel expr = re.compile(r'\!([^=])') 1023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel plural = expr.sub(' not \\1', plural) 1033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Regular expression and replacement function used to transform 1053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # "a?b:c" to "test(a,b,c)". 1063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel expr = re.compile(r'(.*?)\?(.*?):(.*)') 1073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def repl(x): 1083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return "test(%s, %s, %s)" % (x.group(1), x.group(2), 1093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel expr.sub(repl, x.group(3))) 1103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Code to transform the plural expression, taking care of parentheses 1123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel stack = [''] 1133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for c in plural: 1143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if c == '(': 1153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel stack.append('') 1163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel elif c == ')': 1173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if len(stack) == 1: 1183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Actually, we never reach this code, because unbalanced 1193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # parentheses get caught in the security check at the 1203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # beginning. 1213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel raise ValueError, 'unbalanced parenthesis in plural form' 1223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel s = expr.sub(repl, stack.pop()) 1233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel stack[-1] += '(%s)' % s 1243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 1253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel stack[-1] += c 1263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel plural = expr.sub(repl, stack.pop()) 1273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return eval('lambda n: int(%s)' % plural) 1293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef _expand_lang(locale): 1333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel from locale import normalize 1343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel locale = normalize(locale) 1353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel COMPONENT_CODESET = 1 << 0 1363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel COMPONENT_TERRITORY = 1 << 1 1373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel COMPONENT_MODIFIER = 1 << 2 1383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # split up the locale into its base components 1393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mask = 0 1403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel pos = locale.find('@') 1413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if pos >= 0: 1423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel modifier = locale[pos:] 1433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel locale = locale[:pos] 1443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mask |= COMPONENT_MODIFIER 1453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 1463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel modifier = '' 1473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel pos = locale.find('.') 1483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if pos >= 0: 1493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel codeset = locale[pos:] 1503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel locale = locale[:pos] 1513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mask |= COMPONENT_CODESET 1523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 1533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel codeset = '' 1543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel pos = locale.find('_') 1553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if pos >= 0: 1563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel territory = locale[pos:] 1573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel locale = locale[:pos] 1583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mask |= COMPONENT_TERRITORY 1593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 1603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel territory = '' 1613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel language = locale 1623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel ret = [] 1633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for i in range(mask+1): 1643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if not (i & ~mask): # if all components for this combo exist ... 1653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel val = language 1663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if i & COMPONENT_TERRITORY: val += territory 1673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if i & COMPONENT_CODESET: val += codeset 1683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if i & COMPONENT_MODIFIER: val += modifier 1693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel ret.append(val) 1703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel ret.reverse() 1713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return ret 1723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass NullTranslations: 1763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def __init__(self, fp=None): 1773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._info = {} 1783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._charset = None 1793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._output_charset = None 1803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._fallback = None 1813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if fp is not None: 1823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._parse(fp) 1833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def _parse(self, fp): 1853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel pass 1863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def add_fallback(self, fallback): 1883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 1893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._fallback.add_fallback(fallback) 1903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 1913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._fallback = fallback 1923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def gettext(self, message): 1943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 1953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.gettext(message) 1963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return message 1973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 1983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def lgettext(self, message): 1993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 2003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.lgettext(message) 2013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return message 2023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def ngettext(self, msgid1, msgid2, n): 2043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 2053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.ngettext(msgid1, msgid2, n) 2063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 2073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid1 2083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 2093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid2 2103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def lngettext(self, msgid1, msgid2, n): 2123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 2133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.lngettext(msgid1, msgid2, n) 2143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 2153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid1 2163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 2173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid2 2183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def ugettext(self, message): 2203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 2213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.ugettext(message) 2223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return unicode(message) 2233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def ungettext(self, msgid1, msgid2, n): 2253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 2263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.ungettext(msgid1, msgid2, n) 2273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 2283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return unicode(msgid1) 2293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 2303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return unicode(msgid2) 2313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def info(self): 2333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._info 2343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def charset(self): 2363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._charset 2373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def output_charset(self): 2393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._output_charset 2403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def set_output_charset(self, charset): 2423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._output_charset = charset 2433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def install(self, unicode=False, names=None): 2453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel import __builtin__ 2463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext 2473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if hasattr(names, "__contains__"): 2483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if "gettext" in names: 2493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel __builtin__.__dict__['gettext'] = __builtin__.__dict__['_'] 2503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if "ngettext" in names: 2513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel __builtin__.__dict__['ngettext'] = (unicode and self.ungettext 2523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel or self.ngettext) 2533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if "lgettext" in names: 2543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel __builtin__.__dict__['lgettext'] = self.lgettext 2553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if "lngettext" in names: 2563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel __builtin__.__dict__['lngettext'] = self.lngettext 2573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass GNUTranslations(NullTranslations): 2603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Magic number of .mo files 2613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel LE_MAGIC = 0x950412deL 2623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel BE_MAGIC = 0xde120495L 2633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 2643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def _parse(self, fp): 2653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel """Override this method to support alternative .mo formats.""" 2663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel unpack = struct.unpack 2673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel filename = getattr(fp, 'name', '') 2683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Parse the .mo file header, which consists of 5 little endian 32 2693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # bit words. 2703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._catalog = catalog = {} 2713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self.plural = lambda n: int(n != 1) # germanic plural by default 2723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel buf = fp.read() 2733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel buflen = len(buf) 2743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Are we big endian or little endian? 2753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel magic = unpack('<I', buf[:4])[0] 2763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if magic == self.LE_MAGIC: 2773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel version, msgcount, masteridx, transidx = unpack('<4I', buf[4:20]) 2783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel ii = '<II' 2793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel elif magic == self.BE_MAGIC: 2803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel version, msgcount, masteridx, transidx = unpack('>4I', buf[4:20]) 2813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel ii = '>II' 2823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 2833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel raise IOError(0, 'Bad magic number', filename) 2843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Now put all messages from the .mo file buffer into the catalog 2853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # dictionary. 2863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for i in xrange(0, msgcount): 2873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mlen, moff = unpack(ii, buf[masteridx:masteridx+8]) 2883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mend = moff + mlen 2893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tlen, toff = unpack(ii, buf[transidx:transidx+8]) 2903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tend = toff + tlen 2913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if mend < buflen and tend < buflen: 2923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel msg = buf[moff:mend] 2933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = buf[toff:tend] 2943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 2953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel raise IOError(0, 'File is corrupt', filename) 2963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # See if we're looking at GNU .mo conventions for metadata 2973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if mlen == 0: 2983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Catalog description 2993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel lastk = None 3003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for item in tmsg.splitlines(): 3013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel item = item.strip() 3023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if not item: 3033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel continue 3043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel k = v = None 3053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if ':' in item: 3063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel k, v = item.split(':', 1) 3073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel k = k.strip().lower() 3083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel v = v.strip() 3093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._info[k] = v 3103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel lastk = k 3113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel elif lastk: 3123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._info[lastk] += '\n' + item 3133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if k == 'content-type': 3143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self._charset = v.split('charset=')[1] 3153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel elif k == 'plural-forms': 3163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel v = v.split(';') 3173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel plural = v[1].split('plural=')[1] 3183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel self.plural = c2py(plural) 3193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Note: we unconditionally convert both msgids and msgstrs to 3203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Unicode using the character encoding specified in the charset 3213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # parameter of the Content-Type header. The gettext documentation 3223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # strongly encourages msgids to be us-ascii, but some applications 3233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # require alternative encodings (e.g. Zope's ZCML and ZPT). For 3243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # traditional gettext applications, the msgid conversion will 3253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # cause no problems since us-ascii should always be a subset of 3263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # the charset encoding. We may want to fall back to 8-bit msgids 3273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # if the Unicode conversion fails. 3283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if '\x00' in msg: 3293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Plural forms 3303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel msgid1, msgid2 = msg.split('\x00') 3313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = tmsg.split('\x00') 3323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._charset: 3333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel msgid1 = unicode(msgid1, self._charset) 3343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = [unicode(x, self._charset) for x in tmsg] 3353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for i in range(len(tmsg)): 3363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel catalog[(msgid1, i)] = tmsg[i] 3373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 3383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._charset: 3393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel msg = unicode(msg, self._charset) 3403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = unicode(tmsg, self._charset) 3413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel catalog[msg] = tmsg 3423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # advance to next entry in the seek tables 3433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel masteridx += 8 3443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel transidx += 8 3453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 3463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def gettext(self, message): 3473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel missing = object() 3483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = self._catalog.get(message, missing) 3493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if tmsg is missing: 3503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 3513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.gettext(message) 3523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return message 3533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Encode the Unicode tmsg back to an 8-bit string, if possible 3543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._output_charset: 3553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(self._output_charset) 3563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel elif self._charset: 3573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(self._charset) 3583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg 3593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 3603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def lgettext(self, message): 3613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel missing = object() 3623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = self._catalog.get(message, missing) 3633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if tmsg is missing: 3643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 3653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.lgettext(message) 3663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return message 3673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._output_charset: 3683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(self._output_charset) 3693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(locale.getpreferredencoding()) 3703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 3713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def ngettext(self, msgid1, msgid2, n): 3723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 3733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = self._catalog[(msgid1, self.plural(n))] 3743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._output_charset: 3753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(self._output_charset) 3763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel elif self._charset: 3773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(self._charset) 3783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg 3793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except KeyError: 3803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 3813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.ngettext(msgid1, msgid2, n) 3823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 3833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid1 3843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 3853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid2 3863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 3873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def lngettext(self, msgid1, msgid2, n): 3883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 3893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = self._catalog[(msgid1, self.plural(n))] 3903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._output_charset: 3913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(self._output_charset) 3923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg.encode(locale.getpreferredencoding()) 3933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except KeyError: 3943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 3953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.lngettext(msgid1, msgid2, n) 3963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 3973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid1 3983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 3993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid2 4003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def ugettext(self, message): 4023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel missing = object() 4033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = self._catalog.get(message, missing) 4043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if tmsg is missing: 4053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 4063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.ugettext(message) 4073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return unicode(message) 4083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg 4093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel def ungettext(self, msgid1, msgid2, n): 4113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 4123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = self._catalog[(msgid1, self.plural(n))] 4133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except KeyError: 4143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if self._fallback: 4153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return self._fallback.ungettext(msgid1, msgid2, n) 4163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 4173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = unicode(msgid1) 4183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 4193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel tmsg = unicode(msgid2) 4203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return tmsg 4213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# Locate a .mo file using the gettext strategy 4243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef find(domain, localedir=None, languages=None, all=0): 4253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Get some reasonable defaults for arguments that were not supplied 4263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if localedir is None: 4273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel localedir = _default_localedir 4283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if languages is None: 4293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel languages = [] 4303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): 4313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel val = os.environ.get(envar) 4323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if val: 4333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel languages = val.split(':') 4343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel break 4353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if 'C' not in languages: 4363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel languages.append('C') 4373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # now normalize and expand the languages 4383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel nelangs = [] 4393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for lang in languages: 4403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for nelang in _expand_lang(lang): 4413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if nelang not in nelangs: 4423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel nelangs.append(nelang) 4433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # select a language 4443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if all: 4453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel result = [] 4463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 4473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel result = None 4483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for lang in nelangs: 4493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if lang == 'C': 4503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel break 4513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain) 4523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if os.path.exists(mofile): 4533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if all: 4543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel result.append(mofile) 4553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 4563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return mofile 4573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return result 4583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a mapping between absolute .mo file path and Translation object 4623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_translations = {} 4633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef translation(domain, localedir=None, languages=None, 4653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel class_=None, fallback=False, codeset=None): 4663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if class_ is None: 4673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel class_ = GNUTranslations 4683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel mofiles = find(domain, localedir, languages, all=1) 4693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if not mofiles: 4703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if fallback: 4713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return NullTranslations() 4723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel raise IOError(ENOENT, 'No translation file found for domain', domain) 4733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Avoid opening, reading, and parsing the .mo file after it's been done 4743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # once. 4753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel result = None 4763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel for mofile in mofiles: 4773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel key = (class_, os.path.abspath(mofile)) 4783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = _translations.get(key) 4793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if t is None: 4803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel with open(mofile, 'rb') as fp: 4813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = _translations.setdefault(key, class_(fp)) 4823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # Copy the translation object to allow setting fallbacks and 4833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # output charset. All other instance data is shared with the 4843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel # cached object. 4853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = copy.copy(t) 4863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if codeset: 4873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t.set_output_charset(codeset) 4883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if result is None: 4893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel result = t 4903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 4913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel result.add_fallback(t) 4923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return result 4933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef install(domain, localedir=None, unicode=False, codeset=None, names=None): 4963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = translation(domain, localedir, fallback=True, codeset=codeset) 4973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t.install(unicode, names) 4983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 4993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a mapping b/w domains and locale directories 5023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_localedirs = {} 5033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# a mapping b/w domains and codesets 5043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_localecodesets = {} 5053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# current global domain, `messages' used for compatibility w/ GNU gettext 5063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel_current_domain = 'messages' 5073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef textdomain(domain=None): 5103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel global _current_domain 5113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if domain is not None: 5123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel _current_domain = domain 5133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return _current_domain 5143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef bindtextdomain(domain, localedir=None): 5173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel global _localedirs 5183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if localedir is not None: 5193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel _localedirs[domain] = localedir 5203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return _localedirs.get(domain, _default_localedir) 5213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef bind_textdomain_codeset(domain, codeset=None): 5243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel global _localecodesets 5253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if codeset is not None: 5263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel _localecodesets[domain] = codeset 5273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return _localecodesets.get(domain) 5283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef dgettext(domain, message): 5313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 5323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = translation(domain, _localedirs.get(domain, None), 5333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel codeset=_localecodesets.get(domain)) 5343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except IOError: 5353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return message 5363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return t.gettext(message) 5373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef ldgettext(domain, message): 5393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 5403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = translation(domain, _localedirs.get(domain, None), 5413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel codeset=_localecodesets.get(domain)) 5423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except IOError: 5433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return message 5443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return t.lgettext(message) 5453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef dngettext(domain, msgid1, msgid2, n): 5473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 5483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = translation(domain, _localedirs.get(domain, None), 5493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel codeset=_localecodesets.get(domain)) 5503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except IOError: 5513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 5523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid1 5533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 5543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid2 5553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return t.ngettext(msgid1, msgid2, n) 5563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef ldngettext(domain, msgid1, msgid2, n): 5583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel try: 5593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel t = translation(domain, _localedirs.get(domain, None), 5603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel codeset=_localecodesets.get(domain)) 5613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel except IOError: 5623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel if n == 1: 5633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid1 5643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel else: 5653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return msgid2 5663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return t.lngettext(msgid1, msgid2, n) 5673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef gettext(message): 5693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return dgettext(_current_domain, message) 5703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef lgettext(message): 5723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return ldgettext(_current_domain, message) 5733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef ngettext(msgid1, msgid2, n): 5753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return dngettext(_current_domain, msgid1, msgid2, n) 5763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef lngettext(msgid1, msgid2, n): 5783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel return ldngettext(_current_domain, msgid1, msgid2, n) 5793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# dcgettext() has been deemed unnecessary and is not implemented. 5813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# James Henstridge's Catalog constructor from GNOME gettext. Documented usage 5833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# was: 5843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# 5853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# import gettext 5863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR) 5873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# _ = cat.gettext 5883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# print _('Hello World') 5893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# The resulting catalog object currently don't support access through a 5913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# dictionary API, which was supported (but apparently unused) in GNOME 5923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# gettext. 5933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel 5943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielCatalog = translation 595