277 lines
7.6 KiB
Python
277 lines
7.6 KiB
Python
import os
|
|
import gettext
|
|
import bisect
|
|
from locale import getdefaultlocale
|
|
from copy import copy, deepcopy
|
|
|
|
import six
|
|
|
|
from .compat import MutableMapping
|
|
|
|
|
|
class Trans(object):
|
|
|
|
def __init__(self):
|
|
self.registry = {}
|
|
self.current = None
|
|
self.set(getdefaultlocale()[0])
|
|
|
|
def __getitem__(self, language):
|
|
if language:
|
|
try:
|
|
return self.registry[language]
|
|
except KeyError:
|
|
self.registry[language] = gettext.translation(
|
|
'l18n',
|
|
os.path.join(os.path.dirname(__file__), 'locale'),
|
|
languages=[language],
|
|
fallback=True
|
|
)
|
|
return self.registry[language]
|
|
else:
|
|
return None
|
|
|
|
def set(self, language):
|
|
self.current = self[language]
|
|
|
|
def gettext(self, s):
|
|
try:
|
|
return self.current.gettext(s)
|
|
except AttributeError:
|
|
return s
|
|
|
|
if six.PY2:
|
|
def ugettext(self, s):
|
|
try:
|
|
return self.current.ugettext(s)
|
|
except AttributeError:
|
|
return s
|
|
|
|
|
|
_trans = Trans()
|
|
|
|
|
|
def set_language(language=None):
|
|
_trans.set(language)
|
|
|
|
|
|
if six.PY2:
|
|
def translate(s, utf8=True, trans=_trans):
|
|
if trans:
|
|
if utf8:
|
|
return trans.ugettext(s)
|
|
return trans.gettext(s)
|
|
else:
|
|
return s
|
|
else:
|
|
def translate(s, utf8=True, trans=_trans):
|
|
if trans:
|
|
t = trans.gettext(s)
|
|
if utf8:
|
|
return t
|
|
return t.encode()
|
|
else:
|
|
return s
|
|
|
|
|
|
class L18NLazyObject(object):
|
|
|
|
def _value(self, utf8=True):
|
|
raise NotImplementedError
|
|
|
|
def __str__(self):
|
|
return self._value(utf8=six.PY3)
|
|
|
|
def __bytes__(self):
|
|
return self._value(utf8=False)
|
|
|
|
def __unicode__(self):
|
|
return self._value(utf8=True)
|
|
|
|
|
|
class L18NLazyString(L18NLazyObject):
|
|
|
|
def __init__(self, s):
|
|
self._str = s
|
|
|
|
def __copy__(self):
|
|
return self.__class__(self._str)
|
|
|
|
def __deepcopy__(self, memo):
|
|
result = self.__copy__()
|
|
memo[id(self)] = result
|
|
return result
|
|
|
|
def _value(self, utf8=True):
|
|
return translate(self._str, utf8)
|
|
|
|
def __repr__(self):
|
|
return 'L18NLazyString <%s>' % repr(self._str)
|
|
|
|
def __getattr__(self, name):
|
|
# fallback to call the value's attribute in case it's not found in
|
|
# L18NLazyString
|
|
return getattr(self._value(), name)
|
|
|
|
|
|
class L18NLazyStringsList(L18NLazyObject):
|
|
|
|
def __init__(self, sep='/', *s):
|
|
# we assume that the separator and the strings have the same encoding
|
|
# (text_type)
|
|
self._sep = sep
|
|
self._strings = s
|
|
|
|
def __copy__(self):
|
|
return self.__class__(self._sep, *self._strings)
|
|
|
|
def __deepcopy__(self, memo):
|
|
result = self.__copy__()
|
|
memo[id(self)] = result
|
|
return result
|
|
|
|
def _value(self, utf8=True):
|
|
sep = self._sep
|
|
if utf8 and isinstance(sep, six.binary_type):
|
|
sep = sep.decode(encoding='utf-8')
|
|
elif not utf8 and isinstance(sep, str):
|
|
sep = sep.encode(encoding='utf-8')
|
|
return sep.join([translate(s, utf8)
|
|
for s in self._strings])
|
|
|
|
def __repr__(self):
|
|
return 'L18NLazyStringsList <%s>' % self._sep.join([
|
|
repr(s) for s in self._strings
|
|
])
|
|
|
|
def __getattr__(self, name):
|
|
# fallback to call the value's attribute in case it's not found in
|
|
# L18NLazyStringsList
|
|
return getattr(self._value(), name)
|
|
|
|
|
|
class L18NBaseMap(MutableMapping):
|
|
"""
|
|
Generic dictionary that returns lazy string or lazy string lists
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.store = dict(*args, **kwargs)
|
|
self.sorted = {}
|
|
|
|
def __copy__(self):
|
|
result = self.__class__()
|
|
result.store = self.store
|
|
result.sorted = self.sorted
|
|
return result
|
|
|
|
def __deepcopy__(self, memo):
|
|
result = self.__class__()
|
|
memo[id(self)] = result
|
|
result.store = deepcopy(self.store, memo)
|
|
result.sorted = deepcopy(self.sorted, memo)
|
|
return result
|
|
|
|
def __getitem__(self, key):
|
|
raise NotImplementedError
|
|
|
|
def __setitem__(self, key, value):
|
|
self.store[key] = value
|
|
for locale, (keys, values) in six.iteritems(self.sorted):
|
|
tr = translate(value, trans=_trans[locale])
|
|
i = bisect.bisect_left(values, tr)
|
|
keys.insert(i, key)
|
|
values.insert(i, tr)
|
|
|
|
def __delitem__(self, key):
|
|
del self.store[key]
|
|
for keys, values in self.sorted.values():
|
|
i = keys.index(key)
|
|
del keys[i]
|
|
del values[i]
|
|
|
|
def __iter__(self):
|
|
loc = _trans.current._info['language'] if _trans.current else None
|
|
try:
|
|
return iter(self.sorted[loc][0])
|
|
except KeyError:
|
|
keys = []
|
|
values = []
|
|
# we can't use iteritems here, as we need to call __getitem__
|
|
# via self[key]
|
|
for key in iter(self.store):
|
|
value = str(self[key])
|
|
i = bisect.bisect_left(values, value)
|
|
keys.insert(i, key)
|
|
values.insert(i, value)
|
|
self.sorted[loc] = (keys, values)
|
|
return iter(keys)
|
|
|
|
def __len__(self):
|
|
return len(self.store)
|
|
|
|
def subset(self, keys):
|
|
"""
|
|
Generates a subset of the current map (e.g. to retrieve only tzs in
|
|
common_timezones from the tz_cities or tz_fullnames maps)
|
|
"""
|
|
sub = self.__class__()
|
|
|
|
self_keys = set(self.store.keys())
|
|
subset_keys = self_keys.intersection(keys)
|
|
removed_keys = self_keys.difference(subset_keys)
|
|
|
|
sub.store = {k: self.store[k] for k in subset_keys}
|
|
for loc, sorted_items in six.iteritems(self.sorted):
|
|
loc_keys = copy(self.sorted[loc][0])
|
|
loc_values = copy(self.sorted[loc][1])
|
|
for k in removed_keys:
|
|
i = loc_keys.index(k)
|
|
del loc_keys[i]
|
|
del loc_values[i]
|
|
sub.sorted[loc] = (loc_keys, loc_values)
|
|
return sub
|
|
|
|
|
|
class L18NMap(L18NBaseMap):
|
|
|
|
def __getitem__(self, key):
|
|
return L18NLazyString(self.store[key])
|
|
|
|
|
|
class L18NListMap(L18NBaseMap):
|
|
|
|
def __init__(self, sep='/', aux=None, *args, **kwargs):
|
|
self._sep = sep
|
|
self._aux = aux
|
|
super(L18NListMap, self).__init__(*args, **kwargs)
|
|
|
|
def __copy__(self):
|
|
result = super(L18NListMap, self).__copy__()
|
|
result._sep = self._sep
|
|
result._aux = self._aux
|
|
return result
|
|
|
|
def __deepcopy__(self, memo):
|
|
result = super(L18NListMap, self).__deepcopy__(memo)
|
|
result._sep = self._sep
|
|
result._aux = None if self._aux is None else deepcopy(self._aux, memo)
|
|
return result
|
|
|
|
def __getitem__(self, key):
|
|
strs = key.split(self._sep)
|
|
strs[-1] = key
|
|
lst = []
|
|
for s in strs:
|
|
try:
|
|
lst.append(self.store[s])
|
|
except KeyError:
|
|
lst.append(self._aux[s])
|
|
return L18NLazyStringsList(self._sep, *lst)
|
|
|
|
def subset(self, keys):
|
|
sub = super(L18NListMap, self).subset(keys)
|
|
sub._sep = self._sep
|
|
sub._aux = deepcopy(self._aux)
|
|
return sub
|