Skip to content

Commit

Permalink
Make functions private and deprecate public aliases in i18n module
Browse files Browse the repository at this point in the history
Prior to this change, the `Unit` enum and the `abs_timedelta` and
`date_and_delta` functions were listed as public functions in the
documentation but were not available from the root package:

    >>> import humanize
    >>> humanize.gettext
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
    AttributeError: module 'humanize' has no attribute 'gettext'
    >>> humanize.ngettext
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
    AttributeError: module 'humanize' has no attribute 'ngettext'

This change makes these members private.

In order to preserve backwards compatibility after this change, we
provide aliases for these members which emit deprecation warnings.
  • Loading branch information
samueljsb committed Nov 29, 2021
1 parent 2482af7 commit 2fdcdb3
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 30 deletions.
141 changes: 132 additions & 9 deletions src/humanize/i18n.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Activate, get and deactivate translations."""
import gettext as gettext_module
import os.path
import warnings
from threading import local

__all__ = ["activate", "deactivate", "gettext", "ngettext", "thousands_separator"]
Expand Down Expand Up @@ -66,7 +67,7 @@ def deactivate():
_CURRENT.locale = None


def gettext(message):
def _gettext(message):
"""Get translation.
Args:
Expand All @@ -78,7 +79,27 @@ def gettext(message):
return get_translation().gettext(message)


def pgettext(msgctxt, message):
def gettext(message):
"""Get translation.
Args:
message (str): Text to translate.
Returns:
str: Translated text.
WARNING: This function has been deprecated. It is still available as the private
member `_gettext`.
"""
warnings.warn(
"`gettext` has been deprecated. "
"It is still available as the private member `_gettext`.",
DeprecationWarning,
)
return _gettext(message)


def _pgettext(msgctxt, message):
"""Fetches a particular translation.
It works with `msgctxt` .po modifiers and allows duplicate keys with different
Expand All @@ -103,8 +124,32 @@ def pgettext(msgctxt, message):
return message if translation == key else translation


def ngettext(message, plural, num):
"""Plural version of gettext.
def pgettext(msgctxt, message):
"""Fetches a particular translation.
It works with `msgctxt` .po modifiers and allows duplicate keys with different
translations.
Args:
msgctxt (str): Context of the translation.
message (str): Text to translate.
Returns:
str: Translated text.
WARNING: This function has been deprecated. It is still available as the private
member `_pgettext`.
"""
warnings.warn(
"`pgettext` has been deprecated. "
"It is still available as the private member `_pgettext`.",
DeprecationWarning,
)
return _pgettext(msgctxt, message)


def _ngettext(message, plural, num):
"""Plural version of _gettext.
Args:
message (str): Singular text to translate.
Expand All @@ -118,14 +163,37 @@ def ngettext(message, plural, num):
return get_translation().ngettext(message, plural, num)


def gettext_noop(message):
def ngettext(msgctxt, message):
"""Plural version of gettext.
Args:
message (str): Singular text to translate.
plural (str): Plural text to translate.
num (str): The number (e.g. item count) to determine translation for the
respective grammatical number.
Returns:
str: Translated text.
WARNING: This function has been deprecated. It is still available as the private
member `_ngettext`.
"""
warnings.warn(
"`ngettext` has been deprecated. "
"It is still available as the private member `_ngettext`.",
DeprecationWarning,
)
return _ngettext(msgctxt, message)


def _gettext_noop(message):
"""Mark a string as a translation string without translating it.
Example usage:
```python
CONSTANTS = [gettext_noop('first'), gettext_noop('second')]
CONSTANTS = [_gettext_noop('first'), _gettext_noop('second')]
def num_name(n):
return gettext(CONSTANTS[n])
return _gettext(CONSTANTS[n])
```
Args:
Expand All @@ -137,14 +205,41 @@ def num_name(n):
return message


def ngettext_noop(singular, plural):
def gettext_noop(message):
"""Mark a string as a translation string without translating it.
Example usage:
```python
CONSTANTS = [_gettext_noop('first'), _gettext_noop('second')]
def num_name(n):
return _gettext(CONSTANTS[n])
```
Args:
message (str): Text to translate in the future.
Returns:
str: Original text, unchanged.
WARNING: This function has been deprecated. It is still available as the private
member `_gettext_noop`.
"""
warnings.warn(
"`gettext_noop` has been deprecated. "
"It is still available as the private member `_gettext_noop`.",
DeprecationWarning,
)
return _gettext_noop(message)


def _ngettext_noop(singular, plural):
"""Mark two strings as pluralized translations without translating them.
Example usage:
```python
CONSTANTS = [ngettext_noop('first', 'firsts'), ngettext_noop('second', 'seconds')]
def num_name(n):
return ngettext(*CONSTANTS[n])
return _ngettext(*CONSTANTS[n])
```
Args:
Expand All @@ -157,6 +252,34 @@ def num_name(n):
return (singular, plural)


def ngettext_noop(singular, plural):
"""Mark two strings as pluralized translations without translating them.
Example usage:
```python
CONSTANTS = [ngettext_noop('first', 'firsts'), ngettext_noop('second', 'seconds')]
def num_name(n):
return _ngettext(*CONSTANTS[n])
```
Args:
singular (str): Singular text to translate in the future.
plural (str): Plural text to translate in the future.
Returns:
tuple: Original text, unchanged.
WARNING: This function has been deprecated. It is still available as the private
member `_ngettext_noop`.
"""
warnings.warn(
"`ngettext_noop` has been deprecated. "
"It is still available as the private member `_ngettext_noop`.",
DeprecationWarning,
)
return _ngettext_noop(singular, plural)


def thousands_separator() -> str:
"""Return the thousands separator for a locale, default to comma.
Expand Down
12 changes: 6 additions & 6 deletions src/humanize/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import re
from fractions import Fraction

from .i18n import gettext as _
from .i18n import ngettext
from .i18n import ngettext_noop as NS_
from .i18n import pgettext as P_
from .i18n import _gettext as _
from .i18n import _ngettext
from .i18n import _ngettext_noop as NS_
from .i18n import _pgettext as P_
from .i18n import thousands_separator


Expand Down Expand Up @@ -201,12 +201,12 @@ def intword(value, format="%.1f"):
chopped = value / float(powers[ordinal])
singular, plural = human_powers[ordinal]
return (
" ".join([format, ngettext(singular, plural, math.ceil(chopped))])
" ".join([format, _ngettext(singular, plural, math.ceil(chopped))])
) % chopped
else:
singular, plural = human_powers[ordinal - 1]
return (
" ".join([format, ngettext(singular, plural, math.ceil(chopped))])
" ".join([format, _ngettext(singular, plural, math.ceil(chopped))])
) % chopped
return str(value)

Expand Down
30 changes: 15 additions & 15 deletions src/humanize/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from enum import Enum, EnumMeta
from functools import total_ordering

from .i18n import gettext as _
from .i18n import ngettext
from .i18n import _gettext as _
from .i18n import _ngettext

__all__ = [
"naturaldelta",
Expand Down Expand Up @@ -209,7 +209,7 @@ def naturaldelta(
if seconds == 0:
if minimum_unit == _Unit.MICROSECONDS and delta.microseconds < 1000:
return (
ngettext("%d microsecond", "%d microseconds", delta.microseconds)
_ngettext("%d microsecond", "%d microseconds", delta.microseconds)
% delta.microseconds
)
elif minimum_unit == _Unit.MILLISECONDS or (
Expand All @@ -218,52 +218,52 @@ def naturaldelta(
):
milliseconds = delta.microseconds / 1000
return (
ngettext("%d millisecond", "%d milliseconds", milliseconds)
_ngettext("%d millisecond", "%d milliseconds", milliseconds)
% milliseconds
)
return _("a moment")
elif seconds == 1:
return _("a second")
elif seconds < 60:
return ngettext("%d second", "%d seconds", seconds) % seconds
return _ngettext("%d second", "%d seconds", seconds) % seconds
elif 60 <= seconds < 120:
return _("a minute")
elif 120 <= seconds < 3600:
minutes = seconds // 60
return ngettext("%d minute", "%d minutes", minutes) % minutes
return _ngettext("%d minute", "%d minutes", minutes) % minutes
elif 3600 <= seconds < 3600 * 2:
return _("an hour")
elif 3600 < seconds:
hours = seconds // 3600
return ngettext("%d hour", "%d hours", hours) % hours
return _ngettext("%d hour", "%d hours", hours) % hours
elif years == 0:
if days == 1:
return _("a day")
if not use_months:
return ngettext("%d day", "%d days", days) % days
return _ngettext("%d day", "%d days", days) % days
else:
if not months:
return ngettext("%d day", "%d days", days) % days
return _ngettext("%d day", "%d days", days) % days
elif months == 1:
return _("a month")
else:
return ngettext("%d month", "%d months", months) % months
return _ngettext("%d month", "%d months", months) % months
elif years == 1:
if not months and not days:
return _("a year")
elif not months:
return ngettext("1 year, %d day", "1 year, %d days", days) % days
return _ngettext("1 year, %d day", "1 year, %d days", days) % days
elif use_months:
if months == 1:
return _("1 year, 1 month")
else:
return (
ngettext("1 year, %d month", "1 year, %d months", months) % months
_ngettext("1 year, %d month", "1 year, %d months", months) % months
)
else:
return ngettext("1 year, %d day", "1 year, %d days", days) % days
return _ngettext("1 year, %d day", "1 year, %d days", days) % days
else:
return ngettext("%d year", "%d years", years) % years
return _ngettext("%d year", "%d years", years) % years


def naturaltime(
Expand Down Expand Up @@ -601,7 +601,7 @@ def precisedelta(value, minimum_unit="seconds", suppress=(), format="%0.2f") ->
for unit, fmt in zip(reversed(_Unit), fmts):
singular_txt, plural_txt, value = fmt
if value > 0 or (not texts and unit == min_unit):
fmt_txt = ngettext(singular_txt, plural_txt, value)
fmt_txt = _ngettext(singular_txt, plural_txt, value)
if unit == min_unit and math.modf(value)[0] > 0:
fmt_txt = fmt_txt.replace("%d", format)

Expand Down

0 comments on commit 2fdcdb3

Please sign in to comment.