Loading django/contrib/humanize/templatetags/humanize.py +12 −4 Original line number Diff line number Diff line Loading @@ -8,11 +8,13 @@ from django.conf import settings from django.template import defaultfilters from django.utils.encoding import force_text from django.utils.formats import number_format from django.utils.safestring import mark_safe from django.utils.translation import pgettext, ungettext, ugettext as _ from django.utils.timezone import is_aware, utc register = template.Library() @register.filter(is_safe=True) def ordinal(value): """ Loading @@ -25,8 +27,10 @@ def ordinal(value): return value suffixes = (_('th'), _('st'), _('nd'), _('rd'), _('th'), _('th'), _('th'), _('th'), _('th'), _('th')) if value % 100 in (11, 12, 13): # special case return "%d%s" % (value, suffixes[0]) return "%d%s" % (value, suffixes[value % 10]) return mark_safe("%d%s" % (value, suffixes[0])) # Mark value safe so i18n does not break with <sup> or <sub> see #19988 return mark_safe("%d%s" % (value, suffixes[value % 10])) @register.filter(is_safe=True) def intcomma(value, use_l10n=True): Loading Loading @@ -97,6 +101,7 @@ intword_converters = ( )), ) @register.filter(is_safe=False) def intword(value): """ Loading Loading @@ -130,6 +135,7 @@ def intword(value): return _check_for_i18n(new_value, *converters(new_value)) return value @register.filter(is_safe=True) def apnumber(value): """ Loading @@ -144,6 +150,7 @@ def apnumber(value): return value return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1] # Perform the comparison in the default time zone when USE_TZ = True # (unless a specific time zone has been applied with the |timezone filter). @register.filter(expects_localtime=True) Loading Loading @@ -172,6 +179,7 @@ def naturalday(value, arg=None): return _('yesterday') return defaultfilters.date(value, arg) # This filter doesn't require expects_localtime=True because it deals properly # with both naive and aware datetimes. Therefore avoid the cost of conversion. @register.filter Loading django/contrib/humanize/tests.py +16 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ from i18n import TransRealMixin now = datetime.datetime(2012, 3, 9, 22, 30) class MockDateTime(datetime.datetime): @classmethod def now(self, tz=None): Loading @@ -40,11 +41,11 @@ class MockDateTime(datetime.datetime): class HumanizeTests(TransRealMixin, TestCase): def humanize_tester(self, test_list, result_list, method): def humanize_tester(self, test_list, result_list, method, normalize_result_func=escape): for test_content, result in zip(test_list, result_list): t = Template('{%% load humanize %%}{{ test_content|%s }}' % method) rendered = t.render(Context(locals())).strip() self.assertEqual(rendered, escape(result), self.assertEqual(rendered, normalize_result_func(result), msg="%s test failed, produced '%s', should've produced '%s'" % (method, rendered, result)) def test_ordinal(self): Loading @@ -58,6 +59,19 @@ class HumanizeTests(TransRealMixin, TestCase): with translation.override('en'): self.humanize_tester(test_list, result_list, 'ordinal') def test_i18n_html_ordinal(self): """Allow html in output on i18n strings""" test_list = ('1', '2', '3', '4', '11', '12', '13', '101', '102', '103', '111', 'something else', None) result_list = ('1<sup>er</sup>', '2<sup>e</sup>', '3<sup>e</sup>', '4<sup>e</sup>', '11<sup>e</sup>', '12<sup>e</sup>', '13<sup>e</sup>', '101<sup>er</sup>', '102<sup>e</sup>', '103<sup>e</sup>', '111<sup>e</sup>', 'something else', 'None') with translation.override('fr-fr'): self.humanize_tester(test_list, result_list, 'ordinal', lambda x: x) def test_intcomma(self): test_list = (100, 1000, 10123, 10311, 1000000, 1234567.25, '100', '1000', '10123', '10311', '1000000', '1234567.1234567', Decimal('1234567.1234567'), Loading Loading
django/contrib/humanize/templatetags/humanize.py +12 −4 Original line number Diff line number Diff line Loading @@ -8,11 +8,13 @@ from django.conf import settings from django.template import defaultfilters from django.utils.encoding import force_text from django.utils.formats import number_format from django.utils.safestring import mark_safe from django.utils.translation import pgettext, ungettext, ugettext as _ from django.utils.timezone import is_aware, utc register = template.Library() @register.filter(is_safe=True) def ordinal(value): """ Loading @@ -25,8 +27,10 @@ def ordinal(value): return value suffixes = (_('th'), _('st'), _('nd'), _('rd'), _('th'), _('th'), _('th'), _('th'), _('th'), _('th')) if value % 100 in (11, 12, 13): # special case return "%d%s" % (value, suffixes[0]) return "%d%s" % (value, suffixes[value % 10]) return mark_safe("%d%s" % (value, suffixes[0])) # Mark value safe so i18n does not break with <sup> or <sub> see #19988 return mark_safe("%d%s" % (value, suffixes[value % 10])) @register.filter(is_safe=True) def intcomma(value, use_l10n=True): Loading Loading @@ -97,6 +101,7 @@ intword_converters = ( )), ) @register.filter(is_safe=False) def intword(value): """ Loading Loading @@ -130,6 +135,7 @@ def intword(value): return _check_for_i18n(new_value, *converters(new_value)) return value @register.filter(is_safe=True) def apnumber(value): """ Loading @@ -144,6 +150,7 @@ def apnumber(value): return value return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1] # Perform the comparison in the default time zone when USE_TZ = True # (unless a specific time zone has been applied with the |timezone filter). @register.filter(expects_localtime=True) Loading Loading @@ -172,6 +179,7 @@ def naturalday(value, arg=None): return _('yesterday') return defaultfilters.date(value, arg) # This filter doesn't require expects_localtime=True because it deals properly # with both naive and aware datetimes. Therefore avoid the cost of conversion. @register.filter Loading
django/contrib/humanize/tests.py +16 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ from i18n import TransRealMixin now = datetime.datetime(2012, 3, 9, 22, 30) class MockDateTime(datetime.datetime): @classmethod def now(self, tz=None): Loading @@ -40,11 +41,11 @@ class MockDateTime(datetime.datetime): class HumanizeTests(TransRealMixin, TestCase): def humanize_tester(self, test_list, result_list, method): def humanize_tester(self, test_list, result_list, method, normalize_result_func=escape): for test_content, result in zip(test_list, result_list): t = Template('{%% load humanize %%}{{ test_content|%s }}' % method) rendered = t.render(Context(locals())).strip() self.assertEqual(rendered, escape(result), self.assertEqual(rendered, normalize_result_func(result), msg="%s test failed, produced '%s', should've produced '%s'" % (method, rendered, result)) def test_ordinal(self): Loading @@ -58,6 +59,19 @@ class HumanizeTests(TransRealMixin, TestCase): with translation.override('en'): self.humanize_tester(test_list, result_list, 'ordinal') def test_i18n_html_ordinal(self): """Allow html in output on i18n strings""" test_list = ('1', '2', '3', '4', '11', '12', '13', '101', '102', '103', '111', 'something else', None) result_list = ('1<sup>er</sup>', '2<sup>e</sup>', '3<sup>e</sup>', '4<sup>e</sup>', '11<sup>e</sup>', '12<sup>e</sup>', '13<sup>e</sup>', '101<sup>er</sup>', '102<sup>e</sup>', '103<sup>e</sup>', '111<sup>e</sup>', 'something else', 'None') with translation.override('fr-fr'): self.humanize_tester(test_list, result_list, 'ordinal', lambda x: x) def test_intcomma(self): test_list = (100, 1000, 10123, 10311, 1000000, 1234567.25, '100', '1000', '10123', '10311', '1000000', '1234567.1234567', Decimal('1234567.1234567'), Loading