105 lines
3.4 KiB
Python
105 lines
3.4 KiB
Python
|
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)
|