# 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")