Created
December 8, 2014 17:33
-
-
Save davidrios/0725cb692c23871ade23 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
import gettext | |
import re | |
import threading | |
from datetime import datetime, time | |
import pytz | |
from babel import dates, numbers | |
_WRAPPED_GETTEXT_NAMES = ('gettext', 'ugettext', 'ngettext', 'ungettext') | |
_WRAPPED_BABEL_NAMES = { | |
'format_datetime': dates, | |
'format_date': dates, | |
'format_time': dates, | |
'format_timedelta': dates, | |
'parse_date': dates, | |
'format_currency': numbers, | |
'format_decimal': numbers, | |
'format_number': numbers, | |
'format_percent': numbers, | |
'format_scientific': numbers, | |
'parse_decimal': numbers, | |
'parse_grouping': numbers, | |
'parse_number': numbers, | |
} | |
class ThreadlocalLocale(object): | |
def __init__(self, app_name, mo_location, default_locale, default_timezone): | |
self._app_name = app_name | |
self._mo_location = mo_location | |
self._default_locale = default_locale | |
self._default_timezone = default_timezone | |
self._thread_local = threading.local() | |
self.set_locale(default_locale) | |
self.set_timezone(default_timezone) | |
gettext.install(True, localedir=None, unicode=1) | |
gettext.find(self._app_name, self._mo_location) | |
gettext.textdomain(self._app_name) | |
gettext.bind_textdomain_codeset(self._app_name, "UTF-8") | |
self._wrapped_funcs = {} | |
def _wrap_gettext_func(name): | |
def wrapped(*args, **kwargs): | |
return getattr(gettext.translation(self._app_name, self._mo_location, languages=[self._thread_local.locale], fallback=True), name)(*args, **kwargs) | |
return wrapped | |
for name in _WRAPPED_GETTEXT_NAMES: | |
self._wrapped_funcs[name] = _wrap_gettext_func(name) | |
def _wrap_babel_func(name, module): | |
if name in ('format_date', 'format_datetime', 'format_time'): | |
def wrapped(*args): | |
if isinstance(args[0], datetime): | |
args = list(args) | |
if args[0].tzinfo is None: | |
args[0] = pytz.utc.localize(args[0]) | |
args[0] = args[0].astimezone(self._thread_local.timezone) | |
return getattr(module, name)(*args, locale=self._thread_local.locale) | |
return wrapped | |
else: | |
def wrapped(*args): | |
return getattr(module, name)(*args, locale=self._thread_local.locale) | |
return wrapped | |
for name, module in _WRAPPED_BABEL_NAMES.items(): | |
self._wrapped_funcs[name] = _wrap_babel_func(name, module) | |
def set_locale(self, locale): | |
self._thread_local.locale = locale | |
def set_timezone(self, timezone): | |
if isinstance(timezone, basestring): | |
timezone = pytz.timezone(timezone) | |
if not (isinstance(timezone, pytz.tzinfo.DstTzInfo) or isinstance(timezone, pytz.tzinfo.StaticTzInfo)): | |
raise ValueError('must be a valid pytz timezone') | |
self._thread_local.timezone = timezone | |
def parse_time(self, string): | |
format = dates.get_time_format(locale=self._thread_local.locale).pattern.lower() | |
hour_idx = format.index('h') | |
if hour_idx < 0: | |
hour_idx = format.index('k') | |
min_idx = format.index('m') | |
sec_idx = format.index('s') | |
indexes = [(hour_idx, 'H'), (min_idx, 'M'), (sec_idx, 'S')] | |
indexes.sort() | |
indexes = dict([(item[1], idx) for idx, item in enumerate(indexes)]) | |
numbers = re.findall('(\d+)', string) | |
hour = int(numbers[indexes['H']]) | |
minute = int(numbers[indexes['M']]) | |
second = 0 | |
if len(numbers) > indexes['S']: | |
second = int(numbers[indexes['S']]) | |
return time(hour, minute, second) | |
def parse_datetime(self, val, separator=' '): | |
dstr, tstr = val.split(separator, 1) | |
date = self.parse_date(dstr) | |
time = self.parse_time(tstr) | |
dt = self._thread_local.timezone.localize(datetime( | |
date.year, date.month, date.day, time.hour, time.minute, time.second | |
)) | |
return dt.astimezone(pytz.utc).replace(tzinfo=None) | |
def format_decimal2(self, number): | |
return self.format_decimal(number, '#,##0.00') | |
def __getattr__(self, name): | |
if name not in _WRAPPED_GETTEXT_NAMES and name not in _WRAPPED_BABEL_NAMES: | |
raise TypeError('attribute "%s" not supported' % name) | |
return self._wrapped_funcs[name] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment