diff options
author | Aarni Koskela <akx@iki.fi> | 2018-01-19 13:39:07 +0200 |
---|---|---|
committer | Aarni Koskela <akx@iki.fi> | 2018-01-19 17:44:26 +0200 |
commit | bd2ac889827f55fc6d48b3e63fa38e3c090f8892 (patch) | |
tree | a1bc5de9f712463cad5d95c9acd03a92041b1223 | |
parent | ff7e3d2e74ff2c271294ae850f5fa139ce981488 (diff) | |
download | babel-bd2ac889827f55fc6d48b3e63fa38e3c090f8892.tar.gz |
Lists: add support for various list styles other than the default
This was inspired by the CLDR 32 release notes:
> New “disjunctive” list style (eg “a, b, or c”)
-rw-r--r-- | babel/core.py | 6 | ||||
-rw-r--r-- | babel/lists.py | 55 | ||||
-rwxr-xr-x | scripts/import_cldr.py | 10 | ||||
-rw-r--r-- | tests/test_lists.py | 7 |
4 files changed, 63 insertions, 15 deletions
diff --git a/babel/core.py b/babel/core.py index 5a9091f..5a1a912 100644 --- a/babel/core.py +++ b/babel/core.py @@ -857,11 +857,11 @@ class Locale(object): .. note:: The format of the value returned may change between Babel versions. - >>> Locale('en').list_patterns['start'] + >>> Locale('en').list_patterns['standard']['start'] u'{0}, {1}' - >>> Locale('en').list_patterns['end'] + >>> Locale('en').list_patterns['standard']['end'] u'{0}, and {1}' - >>> Locale('en_GB').list_patterns['end'] + >>> Locale('en_GB').list_patterns['standard']['end'] u'{0} and {1}' """ return self._data['list_patterns'] diff --git a/babel/lists.py b/babel/lists.py index 82e5590..3294379 100644 --- a/babel/lists.py +++ b/babel/lists.py @@ -11,7 +11,7 @@ * ``LC_ALL``, and * ``LANG`` - :copyright: (c) 2015 by the Babel Team. + :copyright: (c) 2015, 2018 by the Babel Team. :license: BSD, see LICENSE for more details. """ @@ -20,16 +20,46 @@ from babel.core import Locale, default_locale DEFAULT_LOCALE = default_locale() -def format_list(lst, locale=DEFAULT_LOCALE): +def format_list(lst, style='standard', locale=DEFAULT_LOCALE): """ Format the items in `lst` as a list. - >>> format_list(['apples', 'oranges', 'pears'], 'en') + >>> format_list(['apples', 'oranges', 'pears'], locale='en') u'apples, oranges, and pears' - >>> format_list(['apples', 'oranges', 'pears'], 'zh') + >>> format_list(['apples', 'oranges', 'pears'], locale='zh') u'apples\u3001oranges\u548cpears' + >>> format_list(['omena', 'peruna', 'aplari'], style='or', locale='fi') + u'omena, peruna tai aplari' + + These styles are defined, but not all are necessarily available in all locales. + The following text is verbatim from the Unicode TR35-49 spec [1]. + + * standard: + A typical 'and' list for arbitrary placeholders. + eg. "January, February, and March" + * standard-short: + A short version of a 'and' list, suitable for use with short or abbreviated placeholder values. + eg. "Jan., Feb., and Mar." + * or: + A typical 'or' list for arbitrary placeholders. + eg. "January, February, or March" + * or-short: + A short version of an 'or' list. + eg. "Jan., Feb., or Mar." + * unit: + A list suitable for wide units. + eg. "3 feet, 7 inches" + * unit-short: + A list suitable for short units + eg. "3 ft, 7 in" + * unit-narrow: + A list suitable for narrow units, where space on the screen is very limited. + eg. "3′ 7″" + + [1]: https://www.unicode.org/reports/tr35/tr35-49/tr35-general.html#ListPatterns :param lst: a sequence of items to format in to a list + :param style: the style to format the list with. See above for description. :param locale: the locale """ locale = Locale.parse(locale) @@ -37,12 +67,21 @@ def format_list(lst, locale=DEFAULT_LOCALE): return '' if len(lst) == 1: return lst[0] + + if style not in locale.list_patterns: + raise ValueError('Locale %s does not support list formatting style %r (supported are %s)' % ( + locale, + style, + list(sorted(locale.list_patterns)), + )) + patterns = locale.list_patterns[style] + if len(lst) == 2: - return locale.list_patterns['2'].format(*lst) + return patterns['2'].format(*lst) - result = locale.list_patterns['start'].format(lst[0], lst[1]) + result = patterns['start'].format(lst[0], lst[1]) for elem in lst[2:-1]: - result = locale.list_patterns['middle'].format(result, elem) - result = locale.list_patterns['end'].format(result, lst[-1]) + result = patterns['middle'].format(result, elem) + result = patterns['end'].format(result, lst[-1]) return result diff --git a/scripts/import_cldr.py b/scripts/import_cldr.py index 94fe85e..d50e146 100755 --- a/scripts/import_cldr.py +++ b/scripts/import_cldr.py @@ -397,7 +397,7 @@ def _process_local_datas(sup, srcdir, destdir, force=False, dump_json=False): data["day_period_rules"] = day_period_rules[locale_id] parse_locale_display_names(data, tree) - + parse_list_patterns(data, tree) parse_dates(data, tree, sup, regions, territory) for calendar in tree.findall('.//calendars/calendar'): @@ -478,12 +478,14 @@ def parse_locale_display_names(data, tree): scripts = data.setdefault('scripts', {}) for elem in tree.findall('.//scripts/script'): _import_type_text(scripts, elem) + + +def parse_list_patterns(data, tree): list_patterns = data.setdefault('list_patterns', {}) for listType in tree.findall('.//listPatterns/listPattern'): - if 'type' in listType.attrib: - continue + by_type = list_patterns.setdefault(listType.attrib.get('type', 'standard'), {}) for listPattern in listType.findall('listPatternPart'): - list_patterns[listPattern.attrib['type']] = _text(listPattern) + by_type[listPattern.attrib['type']] = _text(listPattern) def parse_dates(data, tree, sup, regions, territory): diff --git a/tests/test_lists.py b/tests/test_lists.py index bd297ec..e843a63 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -1,4 +1,6 @@ # coding=utf-8 +import pytest + from babel import lists @@ -12,3 +14,8 @@ def test_format_list(): (['string1', 'string2', 'string3', 'string4'], 'ne', u'string1,string2, string3 र string4'), ]: assert lists.format_list(list, locale=locale) == expected + + +def test_format_list_error(): + with pytest.raises(ValueError): + lists.format_list(['a', 'b', 'c'], style='orange', locale='en') |