# HG changeset patch
# User Vincent Hatakeyama <vincent.hatakeyama@xcg-consulting.fr>
# Date 1668010812 -3600
#      Wed Nov 09 17:20:12 2022 +0100
# Branch 15.0
# Node ID ddadf40955885b19425e211825cae7d95dd2ac66
# Parent  d940cb24f4eeb8df313e5a3366208995f00ff348
✨ port to Odoo 15

diff --git a/.badges/code_style-black-000000.svg b/.badges/code_style-black-000000.svg
new file mode 100644
--- /dev/null
+++ b/.badges/code_style-black-000000.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20">
+  <linearGradient id="b" x2="0" y2="100%">
+    <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
+    <stop offset="1" stop-opacity=".1" />
+  </linearGradient>
+  <mask id="anybadge_1">
+    <rect width="114" height="20" rx="3" fill="#fff" />
+  </mask>
+  <g mask="url(#anybadge_1)">
+    <path fill="#555" d="M0 0h72v20H0z" />
+    <path fill="#000000" d="M72 0h42v20H72z" />
+    <path fill="url(#b)" d="M0 0h114v20H0z" />
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="37.0" y="15" fill="#010101" fill-opacity=".3">code style</text>
+    <text x="36.0" y="14">code style</text>
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="94.0" y="15" fill="#010101" fill-opacity=".3">black</text>
+    <text x="93.0" y="14">black</text>
+  </g>
+</svg>
diff --git a/.badges/code_style-prettier-ff69b4.svg b/.badges/code_style-prettier-ff69b4.svg
new file mode 100644
--- /dev/null
+++ b/.badges/code_style-prettier-ff69b4.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<svg xmlns="http://www.w3.org/2000/svg" width="129" height="20">
+  <linearGradient id="b" x2="0" y2="100%">
+    <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
+    <stop offset="1" stop-opacity=".1" />
+  </linearGradient>
+  <mask id="anybadge_1">
+    <rect width="129" height="20" rx="3" fill="#fff" />
+  </mask>
+  <g mask="url(#anybadge_1)">
+    <path fill="#555" d="M0 0h72v20H0z" />
+    <path fill="#ff69b4" d="M72 0h57v20H72z" />
+    <path fill="url(#b)" d="M0 0h129v20H0z" />
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="37.0" y="15" fill="#010101" fill-opacity=".3">code style</text>
+    <text x="36.0" y="14">code style</text>
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="101.5" y="15" fill="#010101" fill-opacity=".3">prettier</text>
+    <text x="100.5" y="14">prettier</text>
+  </g>
+</svg>
diff --git a/.badges/licence-AGPL--3-blue.svg b/.badges/licence-AGPL--3-blue.svg
new file mode 100644
--- /dev/null
+++ b/.badges/licence-AGPL--3-blue.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<svg xmlns="http://www.w3.org/2000/svg" width="107" height="20">
+  <linearGradient id="b" x2="0" y2="100%">
+    <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
+    <stop offset="1" stop-opacity=".1" />
+  </linearGradient>
+  <mask id="anybadge_1">
+    <rect width="107" height="20" rx="3" fill="#fff" />
+  </mask>
+  <g mask="url(#anybadge_1)">
+    <path fill="#555" d="M0 0h53v20H0z" />
+    <path fill="#0000FF" d="M53 0h54v20H53z" />
+    <path fill="url(#b)" d="M0 0h107v20H0z" />
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="27.5" y="15" fill="#010101" fill-opacity=".3">licence</text>
+    <text x="26.5" y="14">licence</text>
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="81.0" y="15" fill="#010101" fill-opacity=".3">AGPL-3</text>
+    <text x="80.0" y="14">AGPL-3</text>
+  </g>
+</svg>
diff --git a/.badges/maturity.svg b/.badges/maturity.svg
new file mode 100644
--- /dev/null
+++ b/.badges/maturity.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<svg xmlns="http://www.w3.org/2000/svg" width="177" height="20">
+  <linearGradient id="b" x2="0" y2="100%">
+    <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
+    <stop offset="1" stop-opacity=".1" />
+  </linearGradient>
+  <mask id="anybadge_1">
+    <rect width="177" height="20" rx="3" fill="#fff" />
+  </mask>
+  <g mask="url(#anybadge_1)">
+    <path fill="#555" d="M0 0h61v20H0z" />
+    <path fill="#4c1" d="M61 0h116v20H61z" />
+    <path fill="url(#b)" d="M0 0h177v20H0z" />
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="31.5" y="15" fill="#010101" fill-opacity=".3">maturity</text>
+    <text x="30.5" y="14">maturity</text>
+  </g>
+  <g
+    fill="#fff"
+    text-anchor="middle"
+    font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+    font-size="11"
+  >
+    <text x="120.0" y="15" fill="#010101" fill-opacity=".3">Production/Stable</text>
+    <text x="119.0" y="14">Production/Stable</text>
+  </g>
+</svg>
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+# Configuration for known file extensions
+[*.{css,htm,html,js,json,jsx,less,markdown,md,py,rst,sass,scss,toml,xml,yaml,yml}]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{json,yml,yaml,rst,markdown,md,toml}]
+indent_size = 2
+
+# Do not configure editor for libs
+[*/static/{lib,src/lib}/**]
+charset = unset
+end_of_line = unset
+indent_size = unset
+indent_style = unset
+insert_final_newline = false
+trim_trailing_whitespace = false
diff --git a/.flake8 b/.flake8
new file mode 100644
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 88
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,3 +1,3 @@
 include:
   - project: xcg/ci-templates
-    file: /odoo/13.0/gitlab-ci.yaml
+    file: /odoo/15.0/gitlab-ci.yaml
diff --git a/.prettierrc.yml b/.prettierrc.yml
new file mode 100644
--- /dev/null
+++ b/.prettierrc.yml
@@ -0,0 +1,8 @@
+# Defaults for all prettier-supported languages.
+# Prettier will complete this with settings from .editorconfig file.
+bracketSpacing: false
+printWidth: 88
+proseWrap: always
+semi: true
+trailingComma: "es5"
+xmlWhitespaceSensitivity: "ignore"
diff --git a/.yamllint.yaml b/.yamllint.yaml
new file mode 100644
--- /dev/null
+++ b/.yamllint.yaml
@@ -0,0 +1,4 @@
+rules:
+  document-start: disable
+  indentation:
+    indent-sequences: true
diff --git a/NEWS.rst b/NEWS.rst
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,24 +1,7 @@
-=======
-Changes
-=======
+Changelog
+=========
 
-13.0.1.1.1
-----------
-
-Fix reST in readme.
-
-13.0.1.1.0
+15.0.1.0.0
 ----------
 
-Add code to handle currency formatting.
-
-Stop using hook to add methods to base when inheriting works.
-
-Add formatter class to ease creating compute methods.
-
-Add more caches.
-
-13.0.1.0.0
-----------
-
-Port to Odoo 13.0
+Port to Odoo 15.0.
diff --git a/README.rst b/README.rst
--- a/README.rst
+++ b/README.rst
@@ -2,6 +2,22 @@
 ICU Format
 ==========
 
+.. Update the badge below depending on status
+.. |maturity| image:: .badges/maturity.svg
+    :target: https://odoo-community.org/page/development-status
+    :alt: Alpha
+.. |license| image:: .badges/licence-AGPL--3-blue.svg
+    :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+    :alt: License: AGPL-3
+.. |black| image:: .badges/code_style-black-000000.svg
+    :target: https://github.com/psf/black
+    :alt: Black
+.. |prettier| image:: .badges/code_style-prettier-ff69b4.svg
+    :target: https://github.com/prettier/prettier
+    :alt: Prettier
+
+|maturity| |license| |black| |prettier|
+
 Provides methods that encapsulates PyICU, including at the model level.
 
 ICU provides handling of plural and many other things that are not present in the gettext implementation of Odoo.
diff --git a/__manifest__.py b/__manifest__.py
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -21,7 +21,7 @@
 {
     "name": "ICU Format",
     "summary": "Format message using ICU",
-    "version": "13.0.1.1.1",
+    "version": "15.0.1.0.0",
     "category": "Technical",
     "author": "XCG Consulting",
     "website": "https://odoo.consulting/",
diff --git a/hooks.py b/hooks.py
--- a/hooks.py
+++ b/hooks.py
@@ -37,13 +37,13 @@
     change Odoo’s function odoo.tools.translate.trans_generate.
     """
     __original_extract = extract.extract
-    __ICU_FORMAT_TRANSLATE = "icu_format_translate"
+    __icu_format_translate = "icu_format_translate"
 
     # Same signature as babel.message.extract.extract
     def _extract(
         method,
         fileobj,
-        keywords={
+        keywords={  # noqa: B006
             "N_": None,
             "_": None,
             "dgettext": (2,),
@@ -61,8 +61,8 @@
         strip_comment_tags=True,
     ):
         if method == "python":
-            if __ICU_FORMAT_TRANSLATE not in keywords:
-                keywords[__ICU_FORMAT_TRANSLATE] = (1,)
+            if __icu_format_translate not in keywords:
+                keywords[__icu_format_translate] = (1,)
         # changed to include this tag by default
         if TRANSLATORS_TAG not in comment_tags:
             comment_tags += (TRANSLATORS_TAG,)
diff --git a/i18n/fr.po b/i18n/fr.po
new file mode 100644
--- /dev/null
+++ b/i18n/fr.po
@@ -0,0 +1,35 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# 	* icuformat
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 15.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2022-11-09 16:25+0000\n"
+"PO-Revision-Date: 2022-11-09 17:27+0100\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 2.4.2\n"
+"Last-Translator: Vincent Hatakeyama <vincent.hatakeyama@xcg-consulting.fr>\n"
+"Language: fr\n"
+
+#. module: icuformat
+#. This is a test message, no need for translation
+#: code:addons/icuformat/tests/test_icuformat.py:0
+#, python-format
+msgid "A message"
+msgstr ""
+
+#. module: icuformat
+#: model:ir.model,name:icuformat.model_base
+msgid "Base"
+msgstr "Base"
+
+#. module: icuformat
+#: model:ir.model,name:icuformat.model_res_currency
+msgid "Currency"
+msgstr "Devise"
diff --git a/icu_format.py b/icu_format.py
--- a/icu_format.py
+++ b/icu_format.py
@@ -21,6 +21,8 @@
 import logging
 from typing import Dict, Mapping, Optional, Union
 
+# Disable the warning as the library name is pyicu
+# pylint: disable=missing-manifest-dependency
 from icu import Formattable, ICUError, ListFormatter, Locale, MessageFormat
 
 _logger = logging.getLogger(__name__)
@@ -40,8 +42,7 @@
         """Return an item, eventually created with the factory"""
         # create a tuple out of args and kwargs
         key = tuple(
-            tuple(arg.items()) if isinstance(arg, dict) else arg
-            for arg in args
+            tuple(arg.items()) if isinstance(arg, dict) else arg for arg in args
         ) + tuple(
             (key, tuple(value.items()) if isinstance(value, dict) else value)
             for key, value in kwargs.items()
@@ -90,9 +91,7 @@
 
     def format(
         self,
-        args: Dict[
-            str, Union[int, str, datetime.date, datetime.datetime, bool, float]
-        ],
+        args: Dict[str, Union[int, str, datetime.date, datetime.datetime, bool, float]],
         raise_exception: bool = True,
     ):
         """Format a message with ICU.
@@ -127,9 +126,7 @@
 def icu_format(
     lang: Optional[str],
     msg: str,
-    args: Dict[
-        str, Union[int, str, datetime.date, datetime.datetime, bool, float]
-    ],
+    args: Dict[str, Union[int, str, datetime.date, datetime.datetime, bool, float]],
     raise_exception: bool = True,
     locale_kwargs: Optional[Mapping[str, str]] = None,
 ) -> str:
diff --git a/logger.py b/logger.py
--- a/logger.py
+++ b/logger.py
@@ -19,8 +19,6 @@
 ##############################################################################
 import logging
 
-from odoo.tools import GettextAlias
-
 from .icu_format import icu_format
 
 
@@ -39,9 +37,6 @@
        (C) to format the messages
     """
 
-    # This will be used to call _get_lang (an instance is needed)
-    __gettext_alias = GettextAlias()
-
     def log(self, level, msg, *args, **kwargs):
         """Changed to reformat the message according to the lang information,
         and from the provided dictionary.
@@ -51,7 +46,7 @@
                 args_dict = args[0]
                 args = args[1:]
             else:
-                args_dict = dict()
+                args_dict = {}
 
             msg = icu_format("C", msg, args_dict)
             msg, kwargs = self.process(msg, kwargs)
@@ -79,7 +74,7 @@
 
 def get_logger(name: str = None) -> logging.LoggerAdapter:
     """Provide a way to get a Logger that will call :func:`icu_format` only
-    when needed (to avoid spending time formating a message that will not be
+    when needed (to avoid spending time formatting a message that will not be
     displayed). This is similar to what is done in logging for formatting.
 
     .. code-block:: python
@@ -91,4 +86,4 @@
       _logger.info("Value: {value, number}", {"value": 1000})
     """
     logger = logging.getLogger(name)
-    return ICUFormatAdapter(logger, dict())
+    return ICUFormatAdapter(logger, {})
diff --git a/logging_test.py b/logging_test.py
new file mode 100644
--- /dev/null
+++ b/logging_test.py
@@ -0,0 +1,49 @@
+import inspect
+import logging
+
+MAGIC_KEY = "icuvalues"
+
+
+class CustomAdapter(logging.LoggerAdapter):
+    def log(self, level, msg, *args, **kwargs):
+        """
+        Delegate a log call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        if self.isEnabledFor(level):
+            # reuse _ way of finding the lang
+            lang = None
+            frame = inspect.currentframe()
+            if frame is not None:
+                frame = frame.f_back
+                if frame:
+                    lang = "en"
+                    # lang = GettextAlias._get_lang(frame)
+            #            pdb.set_trace()
+            if args:
+                args_dict = args[0]
+                args = args[1:]
+            else:
+                args_dict = {}
+
+            msg = "icuformat(" + lang + ", " + msg + ", " + str(args_dict) + ")"
+            msg, kwargs = self.process(msg, kwargs)
+            self.logger.log(level, msg, *args, **kwargs)
+
+
+logging.basicConfig(
+    level=logging.DEBUG,
+    format="%(relativeCreated)6d %(threadName)s %(message)s",
+)
+logger = logging.getLogger(__name__)
+adapter = CustomAdapter(logger, {})
+adapter.info("ABC", {"a": 123})
+adapter.critical("Fatal")
+
+brace_formatter = logging.Formatter(fmt="{message}", style="{")
+logger_brace = logger.getChild("test_brace")
+map(
+    lambda handler: handler.setFormatter(brace_formatter),
+    logger_brace.handlers,
+)
+logger_brace.debug("Test %s", "a")
diff --git a/models/base.py b/models/base.py
--- a/models/base.py
+++ b/models/base.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
 #    ICU Format, a module for Odoo
-#    Copyright (C) 2021 XCG Consulting <https://odoo.consulting>
+#    Copyright (C) 2021, 2022 XCG Consulting (https://xcg-consulting.fr/)
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -21,23 +21,18 @@
 import logging
 from typing import Dict, Mapping, Union
 
+# Disable the warning as the library name is pyicu
+from icu import ICUError  # pylint: disable=missing-manifest-dependency
+
 from odoo import _, models
 
-from ..icu_format import (
-    ICUFormatter,
-    _icu_formatter_cache,
-    icu_format,
-    icu_list_format,
-)
-
-from icu import ICUError
+from ..icu_format import ICUFormatter, _icu_formatter_cache, icu_format, icu_list_format
 
 _logger = logging.getLogger(__name__)
 
 
 class BaseModel(models.AbstractModel):
-    """Add methods to BaseModel to use icu formatting.
-    """
+    """Add methods to BaseModel to use icu formatting."""
 
     _inherit = "base"
 
@@ -49,9 +44,7 @@
     def icu_format(
         self,
         msg: str,
-        args: Dict[
-            str, Union[int, str, datetime.date, datetime.datetime, bool, float]
-        ],
+        args: Dict[str, Union[int, str, datetime.date, datetime.datetime, bool, float]],
         raise_exception: bool = False,
     ) -> str:
         """Provide a faster way to format the message, using the context lang,
@@ -68,9 +61,7 @@
     def icu_format_translate(
         self,
         untranslated_message: str,
-        args: Dict[
-            str, Union[int, str, datetime.date, datetime.datetime, bool, float]
-        ],
+        args: Dict[str, Union[int, str, datetime.date, datetime.datetime, bool, float]],
         raise_exception: bool = False,
     ) -> str:
         """Try to format and translate a message.
diff --git a/models/currency.py b/models/currency.py
--- a/models/currency.py
+++ b/models/currency.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
 #    ICU Format, a module for Odoo
-#    Copyright (C) 2021 XCG Consulting <https://odoo.consulting>
+#    Copyright (C) 2021, 2022 XCG Consulting <https://xcg-consulting.fr/>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,17 @@
+[tool.isort]
+py_version = 310
+profile = "black"
+known_odoo = ['odoo']
+known_odoo_addons = ['odoo.addons']
+sections = [
+  'FUTURE',
+  'STDLIB',
+  'THIRDPARTY',
+  'ODOO',
+  'ODOO_ADDONS',
+  'FIRSTPARTY',
+  'LOCALFOLDER'
+]
+
+[tool.black]
+target = 3.10
diff --git a/tests/test_icuformat.py b/tests/test_icuformat.py
--- a/tests/test_icuformat.py
+++ b/tests/test_icuformat.py
@@ -1,7 +1,7 @@
 ###############################################################################
 #
 #    ICU Format, a module for Odoo
-#    Copyright (C) 2019, 2021 XCG Consulting (https://odoo.consulting/)
+#    Copyright (C) 2019, 2021, 2022 XCG Consulting (https://xcg-consulting.fr/)
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -21,20 +21,20 @@
 
 from odoo import tests
 
-from odoo.addons.icuformat import ICUFormatter, icu_format, icu_list_format
+from ..icu_format import ICUFormatter, icu_format, icu_list_format
 
 
 class Test(tests.TransactionCase):
     def test_simple(self):
         msg = "{test}"
-        d = {"test": "foobar"}
+        data = {"test": "foobar"}
         expected = "foobar"
-        self.assertEqual(expected, icu_format("en", msg, d))
+        self.assertEqual(expected, icu_format("en", msg, data))
 
     def test_nothing_to_format(self):
         msg = "test"
-        d = dict()
-        self.assertEqual(msg, icu_format("en", msg, d))
+        data = {}
+        self.assertEqual(msg, icu_format("en", msg, data))
 
     def test_nested_plural_select(self):
         msg = (
@@ -59,16 +59,14 @@
             "other {{host} invites {guest} and # other people to their party.}"
             "}}}"
         )
-        d = dict(
+        data = dict(
             gender_of_host="female",
             num_guests=1005,
             host="Ms. Marple",
             guest="Robert",
         )
-        expected = (
-            "Ms. Marple invites Robert and 1,004 other people to her party."
-        )
-        self.assertEqual(expected, icu_format("en", msg, d))
+        expected = "Ms. Marple invites Robert and 1,004 other people to her party."
+        self.assertEqual(expected, icu_format("en", msg, data))
 
     def test_format_bool(self):
         # message with select should always have other, not just True/False
@@ -87,39 +85,34 @@
 
     def test_format_float(self):
         msg = "{i,number}"
-        d = {"i": 1000.12}
-        self.assertEqual("1,000.12", icu_format("en", msg, d))
-        self.assertEqual("1 000,12", icu_format("fr", msg, d))
-        self.assertEqual("1000.12", icu_format(None, msg, d))
+        data = {"i": 1000.12}
+        self.assertEqual("1,000.12", icu_format("en", msg, data))
+        self.assertEqual("1 000,12", icu_format("fr", msg, data))
+        self.assertEqual("1000.12", icu_format(None, msg, data))
 
     def test_format_year(self):
         msg = "{y,date,::yMMMM}"
-        d = {"y": date(2021, 3, 8)}
-        self.assertEqual("March 2021", icu_format(None, msg, d))
-        self.assertEqual("March 2021", icu_format("en", msg, d))
-        self.assertEqual("mars 2021", icu_format("fr", msg, d))
-        self.assertEqual("2021年3月", icu_format("ja", msg, d))
-        self.assertEqual("令和3年3月", icu_format("ja@calendar=japanese", msg, d))
+        data = {"y": date(2021, 3, 8)}
+        self.assertEqual("March 2021", icu_format(None, msg, data))
+        self.assertEqual("March 2021", icu_format("en", msg, data))
+        self.assertEqual("mars 2021", icu_format("fr", msg, data))
+        self.assertEqual("2021年3月", icu_format("ja", msg, data))
+        self.assertEqual("令和3年3月", icu_format("ja@calendar=japanese", msg, data))
 
     def test_record_icu_format(self):
         user = self.env["res.users"].search([], limit=1)
-        self.assertEqual("A message", user.icu_format("A message", dict()))
-        self.assertEqual("A message", user.icu_format("A message", dict()))
+        self.assertEqual("A message", user.icu_format("A message", {}))
+        self.assertEqual("A message", user.icu_format("A message", {}))
 
     def test_record_icu_format_and_translate(self):
         user = self.env["res.users"].search([], limit=1)
         # TRANSLATORS: This is a test message, no need for translation
-        self.assertEqual(
-            "A message", user.icu_format_translate("A message", dict())
-        )
+        self.assertEqual("A message", user.icu_format_translate("A message", {}))
 
     def test_list_format(self):
-        """Test the list formatter provided on BaseModel.
-        """
+        """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"
-        )
+        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")
 
@@ -173,15 +166,11 @@
         self.assertEqual(formatter.format(dict(total=2.3)), "Test 2.3")
         self.assertEqual(formatter.format(dict(total=11)), "Test 11")
         formatter = ICUFormatter("Test {total, number, currency}", "fr_FR")
-        self.assertEqual(
-            formatter.format(dict(total=147802.3)), "Test 147 802,30 €"
-        )
+        self.assertEqual(formatter.format(dict(total=147802.3)), "Test 147 802,30 €")
         formatter = ICUFormatter(
             "Test {total, number, currency}", "fr_FR", {"currency": "GBP"}
         )
-        self.assertEqual(
-            formatter.format(dict(total=147802.3)), "Test 147 802,30 £GB"
-        )
+        self.assertEqual(formatter.format(dict(total=147802.3)), "Test 147 802,30 £GB")
 
     def test_record_icu_formatter(self):
         user = self.env["res.users"].search([], limit=1)
diff --git a/tests/test_logger.py b/tests/test_logger.py
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -1,7 +1,7 @@
 ###############################################################################
 #
 #    ICU Format, a module for Odoo
-#    Copyright (C) 2019, 2021 XCG Consulting (https://odoo.consulting/)
+#    Copyright (C) 2019, 2021, 2022 XCG Consulting (https://xcg-consulting.fr/)
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -26,8 +26,7 @@
 
 class Test(tests.TransactionCase):
     def test_logging(self):
-        """Test adapter provided by this module.
-        """
+        """Test adapter provided by this module."""
         logger = get_logger(__name__)
         logger.info("Info level test message without formatting")
         logger.info(