2626DEFAULT_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+ )
0 commit comments