Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/humanize/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,14 @@ def ordinal(value: NumberOrString, gender: str = "male") -> str:
return str(value)
gender = "male" if gender == "male" else "female"
digit = 0 if value % 100 in (11, 12, 13) else value % 10
return f"{value}{P_(f'{digit} ({gender})', _ORDINAL_SUFFIXES[digit])}"
suffix = P_(f"{digit} ({gender})", _ORDINAL_SUFFIXES[digit])
# Most languages append a suffix to the number ("2nd"), but some position the
# number inside the ordinal word (e.g. Romanian "al 2-lea", "a 2-a"). A
# translation can opt into that by including a "%s"/"%d" placeholder for the
# number; without one, the classic "<number><suffix>" form is kept.
if "%s" in suffix or "%d" in suffix:
return suffix % value
return f"{value}{suffix}"


def intcomma(value: NumberOrString, ndigits: int | None = None) -> str:
Expand Down
30 changes: 30 additions & 0 deletions tests/test_i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,36 @@ def test_ordinal_genders(
humanize.i18n.deactivate()


def test_ordinal_interpolated_number(monkeypatch: pytest.MonkeyPatch) -> None:
"""Ordinals whose translation carries a placeholder position the number
themselves, e.g. Romanian "al 2-lea" / "a 2-a". Regression test for #270.
"""
from humanize import number

def fake_pgettext(msgctxt: str, message: str) -> str:
return "a %s-a" if "female" in msgctxt else "al %s-lea"

monkeypatch.setattr(number, "P_", fake_pgettext)
assert humanize.ordinal(2) == "al 2-lea"
assert humanize.ordinal(23) == "al 23-lea"
assert humanize.ordinal(2, gender="female") == "a 2-a"

# A %d placeholder is accepted as well.
monkeypatch.setattr(number, "P_", lambda ctx, msg: "%d-lea")
assert humanize.ordinal(7) == "7-lea"


def test_ordinal_suffix_unchanged(monkeypatch: pytest.MonkeyPatch) -> None:
"""A translation without a placeholder keeps the classic <number><suffix>
form, so existing locales are unaffected by the interpolation support.
"""
from humanize import number

monkeypatch.setattr(number, "P_", lambda ctx, msg: "º")
assert humanize.ordinal(5) == "5º"
assert humanize.ordinal(21) == "21º"


def test_default_locale_path_defined__spec__() -> None:
i18n = importlib.import_module("humanize.i18n")
assert i18n._get_default_locale_path() is not None
Expand Down