from __future__ import annotations import re from typing import TYPE_CHECKING, Literal, Optional, Sequence from babel import lists from odoo.tools.misc import babel_locale_parse, get_lang if TYPE_CHECKING: import odoo.api XPG_LOCALE_RE = re.compile( r"""^ ([a-z]+) # language (_[A-Z\d]+)? # maybe _territory # no support for .codeset (we don't use that in Odoo) (@.+)? # maybe @modifier $""", re.VERBOSE, ) def format_list( env: odoo.api.Environment, lst: Sequence[str], style: Literal["standard", "standard-short", "or", "or-short", "unit", "unit-short", "unit-narrow"] = "standard", lang_code: Optional[str] = None, ) -> str: """ Format the items in `lst` as a list in a locale-dependent manner with the chosen style. The available styles are defined by babel according to the Unicode TR35-49 spec: * standard: A typical 'and' list for arbitrary placeholders. e.g. "January, February, and March" * standard-short: A short version of an 'and' list, suitable for use with short or abbreviated placeholder values. e.g. "Jan., Feb., and Mar." * or: A typical 'or' list for arbitrary placeholders. e.g. "January, February, or March" * or-short: A short version of an 'or' list. e.g. "Jan., Feb., or Mar." * unit: A list suitable for wide units. e.g. "3 feet, 7 inches" * unit-short: A list suitable for short units e.g. "3 ft, 7 in" * unit-narrow: A list suitable for narrow units, where space on the screen is very limited. e.g. "3′ 7″" See https://www.unicode.org/reports/tr35/tr35-49/tr35-general.html#ListPatterns for more details. :param env: the current environment. :param lst: the sequence of items to format into a list. :param style: the style to format the list with. :param lang_code: the locale (i.e. en_US). :return: the formatted list. """ locale = babel_locale_parse(lang_code or get_lang(env).code) # Some styles could be unavailable for the chosen locale if style not in locale.list_patterns: style = "standard" return lists.format_list(lst, style, locale) def py_to_js_locale(locale: str) -> str: """ Converts a locale from Python to JavaScript format. Most of the time the conversion is simply to replace _ with -. Example: fr_BE → fr-BE Exception: Serbian can be written in both Latin and Cyrillic scripts interchangeably, therefore its locale includes a special modifier to indicate which script to use. Example: sr@latin → sr-Latn BCP 47 (JS): language[-extlang][-script][-region][-variant][-extension][-privateuse] https://www.ietf.org/rfc/rfc5646.txt XPG syntax (Python): language[_territory][.codeset][@modifier] https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html :param locale: The locale formatted for use on the Python-side. :return: The locale formatted for use on the JavaScript-side. """ match_ = XPG_LOCALE_RE.match(locale) if not match_: return locale language, territory, modifier = match_.groups() subtags = [language] if modifier == "@Cyrl": subtags.append("Cyrl") elif modifier == "@latin": subtags.append("Latn") if territory: subtags.append(territory.removeprefix("_")) return "-".join(subtags)