Skip to content

Commit 1a45aba

Browse files
committed
Support list format fallbacks
1 parent c5684f6 commit 1a45aba

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

babel/lists.py

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
DEFAULT_LOCALE = default_locale()
2727

2828

29-
def format_list(lst: Sequence[str],
30-
style: Literal['standard', 'standard-short', 'or', 'or-short', 'unit', 'unit-short', 'unit-narrow'] = 'standard',
31-
locale: Locale | str | None = DEFAULT_LOCALE) -> str:
29+
def format_list(
30+
lst: Sequence[str],
31+
style: Literal['standard', 'standard-short', 'or', 'or-short', 'unit', 'unit-short', 'unit-narrow'] = 'standard',
32+
locale: Locale | str | None = DEFAULT_LOCALE,
33+
) -> str:
3234
"""
3335
Format the items in `lst` as a list.
3436
@@ -39,7 +41,11 @@ def format_list(lst: Sequence[str],
3941
>>> format_list(['omena', 'peruna', 'aplari'], style='or', locale='fi')
4042
u'omena, peruna tai aplari'
4143
42-
These styles are defined, but not all are necessarily available in all locales.
44+
Not all styles are necessarily available in all locales.
45+
The function will attempt to fall back to replacement styles according to the rules
46+
set forth in the CLDR root XML file, and raise a ValueError if no suitable replacement
47+
can be found.
48+
4349
The following text is verbatim from the Unicode TR35-49 spec [1].
4450
4551
* standard:
@@ -76,14 +82,9 @@ def format_list(lst: Sequence[str],
7682
if len(lst) == 1:
7783
return lst[0]
7884

79-
if style not in locale.list_patterns:
80-
raise ValueError(
81-
f'Locale {locale} does not support list formatting style {style!r} '
82-
f'(supported are {sorted(locale.list_patterns)})',
83-
)
84-
patterns = locale.list_patterns[style]
85+
patterns = _resolve_list_style(locale, style)
8586

86-
if len(lst) == 2:
87+
if len(lst) == 2 and '2' in patterns:
8788
return patterns['2'].format(*lst)
8889

8990
result = patterns['start'].format(lst[0], lst[1])
@@ -92,3 +93,31 @@ def format_list(lst: Sequence[str],
9293
result = patterns['end'].format(result, lst[-1])
9394

9495
return result
96+
97+
98+
# Based on CLDR 45's root.xml file's `<alias>`es.
99+
# The root file defines both `standard` and `or`,
100+
# so they're always available.
101+
# TODO: It would likely be better to use the
102+
# babel.localedata.Alias mechanism for this,
103+
# but I'm not quite sure how it's supposed to
104+
# work with inheritance and data in the root.
105+
_style_fallbacks = {
106+
"or-narrow": ["or-short", "or"],
107+
"or-short": ["or"],
108+
"standard-narrow": ["standard-short", "standard"],
109+
"standard-short": ["standard"],
110+
"unit": ["unit-short", "standard"],
111+
"unit-narrow": ["unit-short", "unit", "standard"],
112+
"unit-short": ["standard"],
113+
}
114+
115+
116+
def _resolve_list_style(locale: Locale, style: str):
117+
for style in (style, *(_style_fallbacks.get(style, []))): # noqa: B020
118+
if style in locale.list_patterns:
119+
return locale.list_patterns[style]
120+
raise ValueError(
121+
f"Locale {locale} does not support list formatting style {style!r} "
122+
f"(supported are {sorted(locale.list_patterns)})",
123+
)

tests/test_lists.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from babel import lists
3+
from babel import lists, units
44

55

66
@pytest.mark.parametrize(('list', 'locale', 'expected'), [
@@ -18,3 +18,15 @@ def test_format_list(list, locale, expected):
1818
def test_format_list_error():
1919
with pytest.raises(ValueError):
2020
lists.format_list(['a', 'b', 'c'], style='orange', locale='en')
21+
22+
23+
def test_issue_1098():
24+
one_foot = units.format_unit(1, "length-foot", length="short", locale="zh_CN")
25+
five_inches = units.format_unit(5, "length-inch", length="short", locale="zh_CN")
26+
# zh-CN does not specify the "unit" style, so we fall back to "unit-short" style.
27+
assert (
28+
lists.format_list([one_foot, five_inches], style="unit", locale="zh_CN") ==
29+
lists.format_list([one_foot, five_inches], style="unit-short", locale="zh_CN") ==
30+
# Translation verified using Google Translate. It would add more spacing, but the glyphs are correct.
31+
"1英尺5英寸"
32+
)

0 commit comments

Comments
 (0)