1 """Internationalization and localization support.
3 This module provides internationalization (I18N) and localization (L10N)
4 support for your Python programs by providing an interface to the GNU gettext
5 message catalog library.
7 I18N refers to the operation by which a program is made aware of multiple
8 languages. L10N refers to the adaptation of your program, once
9 internationalized, to the local language and cultural habits.
49 from errno
import ENOENT
51 __all__ = [
"bindtextdomain",
"textdomain",
"gettext",
"dgettext",
52 "find",
"translation",
"install",
"Catalog"]
54 _default_localedir = os.path.join(sys.prefix,
'share',
'locale')
58 def _expand_lang(locale):
59 from locale
import normalize
61 COMPONENT_CODESET = 1 << 0
62 COMPONENT_TERRITORY = 1 << 1
63 COMPONENT_MODIFIER = 1 << 2
66 pos = locale.find(
'@')
68 modifier = locale[pos:]
70 mask |= COMPONENT_MODIFIER
73 pos = locale.find(
'.')
75 codeset = locale[pos:]
77 mask |= COMPONENT_CODESET
80 pos = locale.find(
'_')
82 territory = locale[pos:]
84 mask |= COMPONENT_TERRITORY
89 for i
in range(mask+1):
92 if i & COMPONENT_TERRITORY: val += territory
93 if i & COMPONENT_CODESET: val += codeset
94 if i & COMPONENT_MODIFIER: val += modifier
108 def _parse(self, fp):
130 LE_MAGIC = 0x950412de
131 BE_MAGIC = 0xde120495
133 def _parse(self, fp):
134 """Override this method to support alternative .mo formats."""
138 unpack = struct.unpack
139 filename = getattr(fp,
'name',
'')
146 magic = unpack(
'<i', buf[:4])[0] & MASK
148 version, msgcount, masteridx, transidx = unpack(
'<4i', buf[4:20])
151 version, msgcount, masteridx, transidx = unpack(
'>4i', buf[4:20])
154 raise IOError(0,
'Bad magic number', filename)
161 for i
in xrange(0, msgcount):
162 mlen, moff = unpack(ii, buf[masteridx:masteridx+8])
164 mend = moff + (mlen & MASK)
165 tlen, toff = unpack(ii, buf[transidx:transidx+8])
167 tend = toff + (tlen & MASK)
168 if mend < buflen
and tend < buflen:
169 tmsg = buf[toff:tend]
170 catalog[buf[moff:mend]] = tmsg
172 raise IOError(0,
'File is corrupt', filename)
174 if mlen == 0
and tmsg.lower().startswith(
'project-id-version:'):
176 for item
in tmsg.split(
'\n'):
180 k, v = item.split(
':', 1)
181 k = k.strip().
lower()
184 if k ==
'content-type':
185 self.
_charset = v.split(
'charset=')[1]
191 return self._catalog.get(message, message)
194 tmsg = self._catalog.get(message, message)
200 def find(domain, localedir=None, languages=None):
202 if localedir
is None:
203 localedir = _default_localedir
204 if languages
is None:
206 for envar
in (
'LANGUAGE',
'LC_ALL',
'LC_MESSAGES',
'LANG'):
207 val = os.environ.get(envar)
209 languages = val.split(
':')
211 if 'C' not in languages:
212 languages.append(
'C')
215 for lang
in languages:
216 for nelang
in _expand_lang(lang):
217 if nelang
not in nelangs:
218 nelangs.append(nelang)
223 mofile = os.path.join(localedir, lang,
'LC_MESSAGES',
'%s.mo' % domain)
224 if os.path.exists(mofile):
233 def translation(domain, localedir=None, languages=None,
234 class_=
None, fallback=0):
236 class_ = GNUTranslations
237 mofile =
find(domain, localedir, languages)
241 raise IOError(ENOENT,
'No translation file found for domain', domain)
242 key = os.path.abspath(mofile)
246 t = _translations.get(key)
248 t = _translations.setdefault(key,
class_(
open(mofile,
'rb')))
253 def install(domain, localedir=None, unicode=0):
261 _current_domain =
'messages'
265 global _current_domain
266 if domain
is not None:
267 _current_domain = domain
268 return _current_domain
273 if localedir
is not None:
274 _localedirs[domain] = localedir
275 return _localedirs.get(domain, _default_localedir)
280 t =
translation(domain, _localedirs.get(domain,
None))
283 return t.gettext(message)
287 return dgettext(_current_domain, message)
304 Catalog = translation