Hotel/env/lib/python3.5/site-packages/django/contrib/humanize/templatetags/humanize.py

293 lines
12 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
from datetime import date, datetime
from decimal import Decimal
from django import template
from django.conf import settings
from django.template import defaultfilters
from django.utils.formats import number_format
from django.utils.safestring import mark_safe
from django.utils.timezone import is_aware, utc
from django.utils.translation import (
gettext as _, ngettext, npgettext_lazy, pgettext,
)
register = template.Library()
@register.filter(is_safe=True)
def ordinal(value):
"""
Convert an integer to its ordinal as a string. 1 is '1st', 2 is '2nd',
3 is '3rd', etc. Works for any integer.
"""
try:
value = int(value)
except (TypeError, ValueError):
return value
if value % 100 in (11, 12, 13):
# Translators: Ordinal format for 11 (11th), 12 (12th), and 13 (13th).
value = pgettext('ordinal 11, 12, 13', '{}th').format(value)
else:
templates = (
# Translators: Ordinal format when value ends with 0, e.g. 80th.
pgettext('ordinal 0', '{}th'),
# Translators: Ordinal format when value ends with 1, e.g. 81st, except 11.
pgettext('ordinal 1', '{}st'),
# Translators: Ordinal format when value ends with 2, e.g. 82nd, except 12.
pgettext('ordinal 2', '{}nd'),
# Translators: Ordinal format when value ends with 3, e.g. 83th, except 13.
pgettext('ordinal 3', '{}rd'),
# Translators: Ordinal format when value ends with 4, e.g. 84th.
pgettext('ordinal 4', '{}th'),
# Translators: Ordinal format when value ends with 5, e.g. 85th.
pgettext('ordinal 5', '{}th'),
# Translators: Ordinal format when value ends with 6, e.g. 86th.
pgettext('ordinal 6', '{}th'),
# Translators: Ordinal format when value ends with 7, e.g. 87th.
pgettext('ordinal 7', '{}th'),
# Translators: Ordinal format when value ends with 8, e.g. 88th.
pgettext('ordinal 8', '{}th'),
# Translators: Ordinal format when value ends with 9, e.g. 89th.
pgettext('ordinal 9', '{}th'),
)
value = templates[value % 10].format(value)
# Mark value safe so i18n does not break with <sup> or <sub> see #19988
return mark_safe(value)
@register.filter(is_safe=True)
def intcomma(value, use_l10n=True):
"""
Convert an integer to a string containing commas every three digits.
For example, 3000 becomes '3,000' and 45000 becomes '45,000'.
"""
if settings.USE_L10N and use_l10n:
try:
if not isinstance(value, (float, Decimal)):
value = int(value)
except (TypeError, ValueError):
return intcomma(value, False)
else:
return number_format(value, force_grouping=True)
orig = str(value)
new = re.sub(r"^(-?\d+)(\d{3})", r'\g<1>,\g<2>', orig)
if orig == new:
return new
else:
return intcomma(new, use_l10n)
# A tuple of standard large number to their converters
intword_converters = (
(6, lambda number: (
ngettext('%(value).1f million', '%(value).1f million', number),
ngettext('%(value)s million', '%(value)s million', number),
)),
(9, lambda number: (
ngettext('%(value).1f billion', '%(value).1f billion', number),
ngettext('%(value)s billion', '%(value)s billion', number),
)),
(12, lambda number: (
ngettext('%(value).1f trillion', '%(value).1f trillion', number),
ngettext('%(value)s trillion', '%(value)s trillion', number),
)),
(15, lambda number: (
ngettext('%(value).1f quadrillion', '%(value).1f quadrillion', number),
ngettext('%(value)s quadrillion', '%(value)s quadrillion', number),
)),
(18, lambda number: (
ngettext('%(value).1f quintillion', '%(value).1f quintillion', number),
ngettext('%(value)s quintillion', '%(value)s quintillion', number),
)),
(21, lambda number: (
ngettext('%(value).1f sextillion', '%(value).1f sextillion', number),
ngettext('%(value)s sextillion', '%(value)s sextillion', number),
)),
(24, lambda number: (
ngettext('%(value).1f septillion', '%(value).1f septillion', number),
ngettext('%(value)s septillion', '%(value)s septillion', number),
)),
(27, lambda number: (
ngettext('%(value).1f octillion', '%(value).1f octillion', number),
ngettext('%(value)s octillion', '%(value)s octillion', number),
)),
(30, lambda number: (
ngettext('%(value).1f nonillion', '%(value).1f nonillion', number),
ngettext('%(value)s nonillion', '%(value)s nonillion', number),
)),
(33, lambda number: (
ngettext('%(value).1f decillion', '%(value).1f decillion', number),
ngettext('%(value)s decillion', '%(value)s decillion', number),
)),
(100, lambda number: (
ngettext('%(value).1f googol', '%(value).1f googol', number),
ngettext('%(value)s googol', '%(value)s googol', number),
)),
)
@register.filter(is_safe=False)
def intword(value):
"""
Convert a large integer to a friendly text representation. Works best
for numbers over 1 million. For example, 1000000 becomes '1.0 million',
1200000 becomes '1.2 million' and '1200000000' becomes '1.2 billion'.
"""
try:
value = int(value)
except (TypeError, ValueError):
return value
if value < 1000000:
return value
def _check_for_i18n(value, float_formatted, string_formatted):
"""
Use the i18n enabled defaultfilters.floatformat if possible
"""
if settings.USE_L10N:
value = defaultfilters.floatformat(value, 1)
template = string_formatted
else:
template = float_formatted
return template % {'value': value}
for exponent, converters in intword_converters:
large_number = 10 ** exponent
if value < large_number * 1000:
new_value = value / large_number
return _check_for_i18n(new_value, *converters(new_value))
return value
@register.filter(is_safe=True)
def apnumber(value):
"""
For numbers 1-9, return the number spelled out. Otherwise, return the
number. This follows Associated Press style.
"""
try:
value = int(value)
except (TypeError, ValueError):
return value
if not 0 < value < 10:
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)
def naturalday(value, arg=None):
"""
For date values that are tomorrow, today or yesterday compared to
present day return representing string. Otherwise, return a string
formatted according to settings.DATE_FORMAT.
"""
try:
tzinfo = getattr(value, 'tzinfo', None)
value = date(value.year, value.month, value.day)
except AttributeError:
# Passed value wasn't a date object
return value
except ValueError:
# Date arguments out of range
return value
today = datetime.now(tzinfo).date()
delta = value - today
if delta.days == 0:
return _('today')
elif delta.days == 1:
return _('tomorrow')
elif delta.days == -1:
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
def naturaltime(value):
"""
For date and time values show how many seconds, minutes, or hours ago
compared to current timestamp return representing string.
"""
if not isinstance(value, date): # datetime is a subclass of date
return value
now = datetime.now(utc if is_aware(value) else None)
if value < now:
delta = now - value
if delta.days != 0:
# Translators: delta will contain a string like '2 months' or '1 month, 2 weeks'
return _('%(delta)s ago') % {'delta': defaultfilters.timesince(value, now, time_strings={
# Translators: 'naturaltime-past' strings will be included in
# '%(delta)s ago'
'year': npgettext_lazy('naturaltime-past', '%d year', '%d years'),
'month': npgettext_lazy('naturaltime-past', '%d month', '%d months'),
'week': npgettext_lazy('naturaltime-past', '%d week', '%d weeks'),
'day': npgettext_lazy('naturaltime-past', '%d day', '%d days'),
'hour': npgettext_lazy('naturaltime-past', '%d hour', '%d hours'),
'minute': npgettext_lazy('naturaltime-past', '%d minute', '%d minutes')
})}
elif delta.seconds == 0:
return _('now')
elif delta.seconds < 60:
return ngettext(
# Translators: please keep a non-breaking space (U+00A0)
# between count and time unit.
'a second ago', '%(count)s seconds ago', delta.seconds
) % {'count': delta.seconds}
elif delta.seconds // 60 < 60:
count = delta.seconds // 60
return ngettext(
# Translators: please keep a non-breaking space (U+00A0)
# between count and time unit.
'a minute ago', '%(count)s minutes ago', count
) % {'count': count}
else:
count = delta.seconds // 60 // 60
return ngettext(
# Translators: please keep a non-breaking space (U+00A0)
# between count and time unit.
'an hour ago', '%(count)s hours ago', count
) % {'count': count}
else:
delta = value - now
if delta.days != 0:
# Translators: delta will contain a string like '2 months' or '1 month, 2 weeks'
return _('%(delta)s from now') % {'delta': defaultfilters.timeuntil(value, now, time_strings={
# Translators: 'naturaltime-future' strings will be included in
# '%(delta)s from now'
'year': npgettext_lazy('naturaltime-future', '%d year', '%d years'),
'month': npgettext_lazy('naturaltime-future', '%d month', '%d months'),
'week': npgettext_lazy('naturaltime-future', '%d week', '%d weeks'),
'day': npgettext_lazy('naturaltime-future', '%d day', '%d days'),
'hour': npgettext_lazy('naturaltime-future', '%d hour', '%d hours'),
'minute': npgettext_lazy('naturaltime-future', '%d minute', '%d minutes')
})}
elif delta.seconds == 0:
return _('now')
elif delta.seconds < 60:
return ngettext(
# Translators: please keep a non-breaking space (U+00A0)
# between count and time unit.
'a second from now', '%(count)s seconds from now', delta.seconds
) % {'count': delta.seconds}
elif delta.seconds // 60 < 60:
count = delta.seconds // 60
return ngettext(
# Translators: please keep a non-breaking space (U+00A0)
# between count and time unit.
'a minute from now', '%(count)s minutes from now', count
) % {'count': count}
else:
count = delta.seconds // 60 // 60
return ngettext(
# Translators: please keep a non-breaking space (U+00A0)
# between count and time unit.
'an hour from now', '%(count)s hours from now', count
) % {'count': count}