Skip to content
Open
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
22 changes: 14 additions & 8 deletions babel/lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,22 @@ def format_list(
"standard-narrow": ["standard-short", "standard"],
"standard-short": ["standard"],
"unit": ["unit-short", "standard"],
"unit-narrow": ["unit-short", "unit", "standard"],
"unit-narrow": ["unit-short", "standard"],
"unit-short": ["standard"],
}


def _resolve_list_style(locale: Locale, style: str):
for style in (style, *(_style_fallbacks.get(style, []))): # noqa: B020
if style in locale.list_patterns:
return locale.list_patterns[style]
raise ValueError(
f"Locale {locale} does not support list formatting style {style!r} "
f"(supported are {sorted(locale.list_patterns)})",
)
# A style may define only some of its parts, so missing parts are
# filled in from the fallback styles, in order.
patterns = {}
for fallback_style in (style, *(_style_fallbacks.get(style, []))):
if fallback_style in locale.list_patterns:
for part, pattern in locale.list_patterns[fallback_style].items():
patterns.setdefault(part, pattern)
if not patterns:
raise ValueError(
f"Locale {locale} does not support list formatting style {style!r} "
f"(supported are {sorted(locale.list_patterns)})",
)
return patterns
37 changes: 36 additions & 1 deletion tests/test_lists.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from babel import lists, units
from babel import Locale, lists, units


@pytest.mark.parametrize(
Expand Down Expand Up @@ -43,3 +43,38 @@ def test_issue_1098():
def test_lists_default_locale_deprecation():
with pytest.warns(DeprecationWarning):
_ = lists.DEFAULT_LOCALE


@pytest.mark.parametrize(
('list', 'style', 'locale', 'expected'),
[
# Styles defining only some parts get the missing ones from fallback styles.
(['A', 'B', 'C'], 'unit', 'de', 'A, B und C'),
(['A', 'B', 'C'], 'standard-narrow', 'fr', 'A, B, C'),
(['A', 'B', 'C', 'D'], 'standard-narrow', 'fr', 'A, B, C, D'),
(['A', 'B', 'C'], 'unit', 'hy', 'A, B և C'),
(['A', 'B', 'C'], 'unit-narrow', 'is', 'A B og C'),
(['A', 'B', 'C'], 'unit', 'ar_OM', 'A، وB، وC'),
# The nearest fallback style wins: 'end' from standard-short, 'start' from standard.
(['A', 'B', 'C'], 'standard-narrow', 'wo_SN', 'A, B, & C'),
# Two items with no '2' part use the fallback '2' pattern.
(['A', 'B'], 'standard-short', 'hi', 'A और B'),
(['A', 'B'], 'standard-narrow', 'lo', 'A ແລະ B'),
(['A', 'B'], 'unit-narrow', 'wo_SN', 'A, B'),
(['A', 'B'], 'unit-narrow', 'sat', 'A, B'),
(['A', 'B'], 'unit', 'ar_OM', 'A وB'),
# Parts defined by the requested style take precedence.
(['A', 'B', 'C'], 'standard-narrow', 'lo', 'A, B, C'),
(['A', 'B'], 'unit-short', 'de', 'A, B'),
],
)
def test_format_list_partial_style_fallback(list, style, locale, expected):
assert lists.format_list(list, style=style, locale=locale) == expected


def test_format_list_does_not_modify_locale_patterns():
locale = Locale.parse('fr')
before = {style: dict(parts) for style, parts in locale.list_patterns.items()}
lists.format_list(['A', 'B', 'C', 'D'], style='standard-narrow', locale=locale)
after = {style: dict(parts) for style, parts in locale.list_patterns.items()}
assert after == before