# HG changeset patch # User Vincent Hatakeyama <vincent.hatakeyama@xcg-consulting.fr> # Date 1615462412 -3600 # Thu Mar 11 12:33:32 2021 +0100 # Branch 13.0 # Node ID 02eab9709d6dcb11473869c744c2fbeb55c7fc28 # Parent d766745524f8f354bbbac3209fb5c7b73196b89f ✨ add a list formatter and a locale cache diff --git a/__init__.py b/__init__.py --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,3 @@ from .hooks import post_load # noqa: F401 -from .icu_format import icu_format # noqa: F401 +from .icu_format import icu_format, icu_list_format # noqa: F401 from .logger import get_logger # noqa: F401 diff --git a/hooks.py b/hooks.py --- a/hooks.py +++ b/hooks.py @@ -24,7 +24,7 @@ from odoo import _ from odoo.models import BaseModel -from .icu_format import icu_format +from .icu_format import icu_format, icu_list_format from babel.messages import extract from icu import ICUError @@ -90,8 +90,14 @@ ) return icu_format("C", untranslated_message, args, raise_exception) + def model_icu_list_format(self, list_data: list) -> str: + return icu_list_format( + self.env.context.get("lang") or self.env.user.lang, list_data + ) + setattr(BaseModel, "icu_format", model_icu_format) setattr(BaseModel, ICU_FORMAT_TRANSLATE, model_icu_format_translate) + setattr(BaseModel, "icu_list_format", model_icu_list_format) def __patch_babel_extract(): diff --git a/icu_format.py b/icu_format.py --- a/icu_format.py +++ b/icu_format.py @@ -20,7 +20,21 @@ import datetime from typing import Dict, Optional, Union -from icu import Formattable, ICUError, Locale, MessageFormat +from icu import Formattable, ICUError, ListFormatter, Locale, MessageFormat + + +class Cache: + def __init__(self, factory): + self.__data = dict() + self.__factory = factory + + def __getitem__(self, item): + if item not in self.__data: + self.__data[item] = self.__factory(item) + return self.__data[item] + + +__locale_cache = Cache(Locale) def icu_format( @@ -40,7 +54,7 @@ return the original message without formatting :return: formatted message """ - # TODO cache the MessageFormat somehow + # TODO cache the MessageFormat somehow, maybe using @lru_cache key_list = list() value_list = list() for key, value in args.items(): @@ -55,10 +69,25 @@ if not lang: # Use the neutral locale if nothing is set lang = "C" - locale = Locale(lang) + locale = __locale_cache[lang] try: return MessageFormat(msg, locale).format(key_list, value_list) except ICUError: if raise_exception: raise return msg + + +__list_formatter_cache = Cache( + lambda lang: ListFormatter.createInstance(__locale_cache[lang]) +) + + +def icu_list_format(lang: str, list_data: list) -> str: + """Format a list using ICU ListFormatter + + :param lang: the lang to use + :param list_data: The list of elements to format + :return: formatted message + """ + return __list_formatter_cache[lang].format(list_data) diff --git a/tests/test_icuformat.py b/tests/test_icuformat.py --- a/tests/test_icuformat.py +++ b/tests/test_icuformat.py @@ -21,7 +21,7 @@ from odoo import tests -from odoo.addons.icuformat import icu_format +from odoo.addons.icuformat import icu_format, icu_list_format class Test(tests.TransactionCase): @@ -112,3 +112,13 @@ self.assertEqual( "A message", user.icu_format_translate("A message", dict()) ) + + def test_list_format(self): + """Test the list formatter provided on BaseModel. + """ + self.assertEqual(icu_list_format("fr", ["a", "b", "c"]), "a, b et c") + self.assertEqual( + icu_list_format("en_GB", ["1", "2", "5"]), "1, 2 and 5" + ) + user = self.env["res.users"].search([], limit=1) + self.assertEqual(user.icu_list_format(["1", "2", "5"]), "1, 2, and 5")