]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Implement long/narrow -> short fallback in babel.units (#1225) master
authortrillian <just.so.you.can.email.me@gmail.com>
Sat, 11 Apr 2026 15:50:58 +0000 (18:50 +0300)
committerGitHub <noreply@github.com>
Sat, 11 Apr 2026 15:50:58 +0000 (15:50 +0000)
Also implemented fallback from the correct pluralization to "other", which was also missing.

Fixes #1217.

babel/units.py
tests/test_units.py

index 88ebb909c20cb2116f14cd1ea4680bf93dd56df3..2c3462422a23fc02405981e3cb0a2c357aae1cd5 100644 (file)
@@ -121,7 +121,7 @@ def format_unit(
 
     .. versionadded:: 2.2.0
 
-    :param value: the value to format. If this is a string, no number formatting will be attempted.
+    :param value: the value to format. If this is a string, no number formatting will be attempted and the number is assumed to be singular for the purposes of unit formatting.
     :param measurement_unit: the code of a measurement unit.
                              Known units can be found in the CLDR Unit Validity XML file:
                              https://unicode.org/repos/cldr/tags/latest/common/validity/unit.xml
@@ -137,7 +137,6 @@ def format_unit(
     q_unit = _find_unit_pattern(measurement_unit, locale=locale)
     if not q_unit:
         raise UnknownUnitError(unit=measurement_unit, locale=locale)
-    unit_patterns = locale._data["unit_patterns"][q_unit].get(length, {})
 
     if isinstance(value, str):  # Assume the value is a preformatted singular.
         formatted_value = value
@@ -151,8 +150,21 @@ def format_unit(
         )
         plural_form = locale.plural_form(value)
 
-    if plural_form in unit_patterns:
-        return unit_patterns[plural_form].format(formatted_value)
+    unit_patterns = locale._data["unit_patterns"][q_unit]
+
+    # We do not support `<alias>` tags at all while ingesting CLDR data,
+    # so these aliases specified in `root.xml` are hard-coded here:
+    # <unitLength type="long"><alias source="locale" path="../unitLength[@type='short']"/></unitLength>
+    # <unitLength type="narrow"><alias source="locale" path="../unitLength[@type='short']"/></unitLength>
+    lengths_to_check = [length, "short"] if length in ("long", "narrow") else [length]
+
+    for real_length in lengths_to_check:
+        length_patterns = unit_patterns.get(real_length, {})
+        # Fall back from the correct plural form to "other"
+        # (this is specified in LDML "Lateral Inheritance")
+        pat = length_patterns.get(plural_form) or length_patterns.get("other")
+        if pat:
+            return pat.format(formatted_value)
 
     # Fall back to a somewhat bad representation.
     # nb: This is marked as no-cover, as the current CLDR seemingly has no way for this to happen.
index 7c6ad6b4dcb8970f572a922ab7841139c873c07d..27b7535fe67020f86dff191b4a0395741dbc6e00 100644 (file)
@@ -17,3 +17,15 @@ from babel.units import format_unit
 ])
 def test_new_cldr46_units(unit, count, expected):
     assert format_unit(count, unit, locale='cs_CZ') == expected
+
+
+@pytest.mark.parametrize('count, unit, locale, length, expected', [
+    (1, 'duration-month', 'et', 'long', '1 kuu'),
+    (1, 'duration-minute', 'et', 'narrow', '1 min'),
+    (2, 'duration-minute', 'et', 'narrow', '2 min'),
+    (2, 'digital-byte', 'et', 'long', '2 baiti'),
+    (1, 'duration-day', 'it', 'long', '1 giorno'),
+    (1, 'duration-day', 'it', 'short', '1 giorno'),
+])
+def test_issue_1217(count, unit, locale, length, expected):
+    assert format_unit(count, unit, length, locale=locale) == expected