Skip to content

Commit 9bbe285

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents e450f3a + 726af40 commit 9bbe285

File tree

8 files changed

+122
-38
lines changed

8 files changed

+122
-38
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.10.0
2+
current_version = 0.11.1
33
commit = True
44
tag = True
55

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Flask Babel - 0.10.0
1+
# Flask Babel - 0.11.1
22

33
[![Build Status](https://travis-ci.org/python-babel/flask-babel.svg?branch=master)](https://travis-ci.org/python-babel/flask-babel)
44
[![PyPI](https://img.shields.io/pypi/v/flask-babel.svg?maxAge=2592000)](https://pypi.python.org/pypi/Flask-Babel)

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
# built documents.
5050
#
5151
# The short X.Y version.
52-
version = '0.10.0'
52+
version = '0.11.1'
5353
# The full version, including alpha/beta/rc tags.
54-
release = '0.10.0'
54+
release = '0.11.1'
5555

5656
# The language for content autogenerated by Sphinx. Refer to documentation
5757
# for a list of supported languages.

flask_babel/__init__.py

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@
1111
from __future__ import absolute_import
1212
import os
1313

14-
# this is a workaround for a snow leopard bug that babel does not
15-
# work around :)
16-
if os.environ.get('LC_CTYPE', '').lower() == 'utf-8':
17-
os.environ['LC_CTYPE'] = 'en_US.utf-8'
18-
1914
from datetime import datetime
2015
from contextlib import contextmanager
2116
from flask import current_app, request
@@ -30,7 +25,8 @@
3025
timezone = pytz.timezone
3126
UTC = pytz.UTC
3227

33-
from flask_babel._compat import string_types, text_type
28+
from flask_babel._compat import string_types
29+
from flask_babel.speaklater import LazyString
3430

3531

3632
class Babel(object):
@@ -128,7 +124,7 @@ def localeselector(self, f):
128124
time. If `None` is returned, the locale falls back to the one from
129125
the configuration.
130126
131-
This has to return the locale as string (eg: ``'de_AT'``, ''`en_US`'')
127+
This has to return the locale as string (eg: ``'de_AT'``, ``'en_US'``)
132128
"""
133129
assert self.locale_selector_func is None, \
134130
'a localeselector function is already registered'
@@ -217,19 +213,27 @@ def get_translations():
217213
"""
218214
ctx = _get_current_context()
219215

216+
if ctx is None:
217+
return support.NullTranslations()
218+
220219
translations = getattr(ctx, 'babel_translations', None)
221220
if translations is None:
222221
translations = support.Translations()
223222

224223
babel = current_app.extensions['babel']
225224
for dirname in babel.translation_directories:
226-
translations.merge(
227-
support.Translations.load(
225+
catalog = support.Translations.load(
228226
dirname,
229227
[get_locale()],
230228
babel.domain
231229
)
232-
)
230+
translations.merge(catalog)
231+
# FIXME: Workaround for merge() being really, really stupid. It
232+
# does not copy _info, plural(), or any other instance variables
233+
# populated by GNUTranslations. We probably want to stop using
234+
# `support.Translations.merge` entirely.
235+
if hasattr(catalog, 'plural'):
236+
translations.plural = catalog.plural
233237

234238
ctx.babel_translations = translations
235239

@@ -437,10 +441,9 @@ def format_time(time=None, format=None, rebase=True):
437441

438442

439443
def format_timedelta(datetime_or_timedelta, granularity='second',
440-
add_direction=False):
444+
add_direction=False, threshold=0.85):
441445
"""Format the elapsed time from the given date to now or the given
442-
timedelta. This currently requires an unreleased development
443-
version of Babel.
446+
timedelta.
444447
445448
This function is also available in the template context as filter
446449
named `timedeltaformat`.
@@ -450,6 +453,7 @@ def format_timedelta(datetime_or_timedelta, granularity='second',
450453
return dates.format_timedelta(
451454
datetime_or_timedelta,
452455
granularity,
456+
threshold=threshold,
453457
add_direction=add_direction,
454458
locale=get_locale()
455459
)
@@ -601,21 +605,6 @@ def npgettext(context, singular, plural, num, **variables):
601605
return s if not variables else s % variables
602606

603607

604-
def make_json_lazy_string(func, *args, **kwargs):
605-
"""Like :method:`speaklater.make_lazy_string` but returns a subclass
606-
that provides an :method:`__html__` method. That method is used by
607-
:class:`flask.json.JSONEncoder` to serialize objects of unrecognized
608-
types.
609-
"""
610-
from speaklater import _LazyString
611-
612-
class JsonLazyString(_LazyString):
613-
def __html__(self):
614-
return text_type(self)
615-
616-
return JsonLazyString(func, args, kwargs)
617-
618-
619608
def lazy_gettext(string, **variables):
620609
"""Like :func:`gettext` but the string returned is lazy which means
621610
it will be translated when it is used as an actual string.
@@ -628,7 +617,7 @@ def lazy_gettext(string, **variables):
628617
def index():
629618
return unicode(hello)
630619
"""
631-
return make_json_lazy_string(gettext, string, **variables)
620+
return LazyString(gettext, string, **variables)
632621

633622

634623
def lazy_pgettext(context, string, **variables):
@@ -637,7 +626,7 @@ def lazy_pgettext(context, string, **variables):
637626
638627
.. versionadded:: 0.7
639628
"""
640-
return make_json_lazy_string(pgettext, context, string, **variables)
629+
return LazyString(pgettext, context, string, **variables)
641630

642631

643632
def _get_current_context():

flask_babel/speaklater.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# -*- coding: utf-8 -*-
2+
from flask_babel._compat import text_type
3+
4+
5+
class LazyString(object):
6+
def __init__(self, func, *args, **kwargs):
7+
self._func = func
8+
self._args = args
9+
self._kwargs = kwargs
10+
11+
def __getattr__(self, attr):
12+
if attr == "__setstate__":
13+
raise AttributeError(attr)
14+
string = text_type(self)
15+
if hasattr(string, attr):
16+
return getattr(string, attr)
17+
raise AttributeError(attr)
18+
19+
def __repr__(self):
20+
return "l'{0}'".format(text_type(self))
21+
22+
def __str__(self):
23+
return text_type(self._func(*self._args, **self._kwargs))
24+
25+
def __len__(self):
26+
return len(text_type(self))
27+
28+
def __getitem__(self, key):
29+
return text_type(self)[key]
30+
31+
def __iter__(self):
32+
return iter(text_type(self))
33+
34+
def __contains__(self, item):
35+
return item in text_type(self)
36+
37+
def __add__(self, other):
38+
return text_type(self) + other
39+
40+
def __radd__(self, other):
41+
return other + text_type(self)
42+
43+
def __mul__(self, other):
44+
return text_type(self) * other
45+
46+
def __rmul__(self, other):
47+
return other * text_type(self)
48+
49+
def __lt__(self, other):
50+
return text_type(self) < other
51+
52+
def __le__(self, other):
53+
return text_type(self) <= other
54+
55+
def __eq__(self, other):
56+
return text_type(self) == other
57+
58+
def __ne__(self, other):
59+
return text_type(self) != other
60+
61+
def __gt__(self, other):
62+
return text_type(self) > other
63+
64+
def __ge__(self, other):
65+
return text_type(self) >= other
66+
67+
def __html__(self):
68+
return text_type(self)
69+
70+
def __hash__(self):
71+
return hash(text_type(self))
72+
73+
def __mod__(self, other):
74+
return text_type(self) % other
75+
76+
def __rmod__(self, other):
77+
return other + text_type(self)

setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
setup(
2222
name='Flask-Babel',
23-
version='0.10.0',
23+
version='0.11.1',
2424
url='http://github.com/python-babel/flask-babel',
2525
license='BSD',
2626
author='Armin Ronacher',
@@ -33,7 +33,6 @@
3333
install_requires=[
3434
'Flask',
3535
'Babel>=2.3',
36-
'speaklater>=1.2',
3736
'Jinja2>=2.5'
3837
],
3938
classifiers=[

tests/tests.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import os
66
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
77

8+
import pickle
9+
810
import unittest
911
from decimal import Decimal
1012
import flask
11-
from datetime import datetime
13+
from datetime import datetime, timedelta
1214
import flask_babel as babel
1315
from flask_babel import gettext, ngettext, lazy_gettext, get_translations
1416
from babel.support import NullTranslations
@@ -76,18 +78,35 @@ def test_different_domain(self):
7678

7779
assert gettext(u'Good bye') == 'Auf Wiedersehen'
7880

81+
def test_lazy_old_style_formatting(self):
82+
lazy_string = lazy_gettext(u'Hello %(name)s')
83+
assert lazy_string % {u'name': u'test'} == u'Hello test'
84+
85+
lazy_string = lazy_gettext(u'test')
86+
assert u'Hello %s' % lazy_string == u'Hello test'
87+
88+
def test_lazy_pickling(self):
89+
lazy_string = lazy_gettext(u'Foo')
90+
pickled = pickle.dumps(lazy_string)
91+
unpickled = pickle.loads(pickled)
92+
93+
assert unpickled == lazy_string
94+
7995

8096
class DateFormattingTestCase(unittest.TestCase):
8197

8298
def test_basics(self):
8399
app = flask.Flask(__name__)
84100
babel.Babel(app)
85101
d = datetime(2010, 4, 12, 13, 46)
102+
delta = timedelta(days=6)
86103

87104
with app.test_request_context():
88105
assert babel.format_datetime(d) == 'Apr 12, 2010, 1:46:00 PM'
89106
assert babel.format_date(d) == 'Apr 12, 2010'
90107
assert babel.format_time(d) == '1:46:00 PM'
108+
assert babel.format_timedelta(delta) == '1 week'
109+
assert babel.format_timedelta(delta, threshold=1) == '6 days'
91110

92111
with app.test_request_context():
93112
app.config['BABEL_DEFAULT_TIMEZONE'] = 'Europe/Vienna'

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = py26, py27, py33
2+
envlist = py26, py27, py33, py34, py35
33

44
[testenv]
55
deps = pytz>=2013a

0 commit comments

Comments
 (0)