From 04cb66f5102370c37cadb58cc1c98fd432e19850 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sun, 11 Dec 2022 17:57:49 +0100
Subject: [PATCH 1/6] i18n: add |translate template filter (#71590)

---
 tests/test_i18n.py                | 41 +++++++++++++++++++++++++++++++
 wcs/qommon/publisher.py           | 34 ++++++++++++++++++++++---
 wcs/qommon/templatetags/qommon.py |  5 ++++
 3 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/tests/test_i18n.py b/tests/test_i18n.py
index 443915a90..af63cd5f5 100644
--- a/tests/test_i18n.py
+++ b/tests/test_i18n.py
@@ -4,6 +4,7 @@ import pytest
 
 from wcs import sql
 from wcs.i18n import TranslatableMessage
+from wcs.qommon.template import Template
 
 from .test_sql import column_exists_in_table
 from .utilities import clean_temporary_pub, create_temporary_pub, get_app
@@ -74,3 +75,43 @@ def test_load_as_catalog(pub):
 
     catalog = TranslatableMessage.load_as_catalog('fr')
     assert catalog == {(None, 'string 1'): 'chaine 1', (None, 'string 2'): 'chaine 2'}
+
+
+def test_translate_template_tag(pub):
+    pub.cfg['language'] = {'language': 'en', 'multilinguism': True, 'languages': ['fr', 'en']}
+    pub.write_cfg()
+    TranslatableMessage.do_table()
+    TranslatableMessage.wipe()
+
+    msg = TranslatableMessage()
+    msg.string = 'string 1'
+    msg.string_fr = 'chaine 1'
+    msg.store()
+
+    tmpl1 = Template('{{ "string 1"|translate }}')
+    tmpl2 = Template('{{ "string 2"|translate }}')
+    assert tmpl1.render() == 'string 1'
+    assert tmpl2.render() == 'string 2'
+    assert TranslatableMessage.count() == 1
+    with pub.with_language('fr'):
+        assert tmpl1.render() == 'chaine 1'
+        assert tmpl2.render() == 'string 2'
+        assert TranslatableMessage.count() == 2  # string 2 has been added to catalog
+
+        # check it's not added a second time
+        del pub._i18n_catalog['fr']
+        assert tmpl2.render() == 'string 2'
+        assert TranslatableMessage.count() == 2
+
+    # test context
+    tmpl3 = Template('{{ "string 1"|translate:"plop" }}')
+    assert tmpl3.render() == 'string 1'
+    with pub.with_language('fr'):
+        assert tmpl3.render() == 'string 1'
+        assert TranslatableMessage.count() == 3  # added to catalog
+
+        msg = TranslatableMessage.select([sql.Equal('string', 'string 1'), sql.Equal('context', 'plop')])[0]
+        msg.string_fr = 'alt chaine 1'
+        msg.store()
+        del pub._i18n_catalog['fr']
+        assert tmpl3.render() == 'alt chaine 1'
diff --git a/wcs/qommon/publisher.py b/wcs/qommon/publisher.py
index f63edd84b..9d09da6e2 100644
--- a/wcs/qommon/publisher.py
+++ b/wcs/qommon/publisher.py
@@ -383,14 +383,42 @@ class QommonPublisher(Publisher):
             yield
             self.activate_language(current_language, request=self.get_request())
 
-    def translate(self, string, context=None):
+    def is_using_default_language(self):
+        if not (self.has_i18n_enabled() and self.current_language):
+            return True
+        return bool(self.cfg.get('language', {}).get('language') == self.current_language)
+
+    def translate(self, string, context=None, register=False):
         if type(string).__name__ == '__proxy__':  # lazy gettext
             return str(string)
         if not self.has_i18n_enabled():
             return string
         string = str(string)  # unlazy
-        if self.current_language and self.cfg.get('language', {}).get('language') != self.current_language:
-            return self._i18n_catalog[self.current_language].get((context, string.strip())) or string
+        if not self.is_using_default_language():
+            from wcs.i18n import TranslatableMessage
+
+            string = string.strip()
+            if self.current_language not in self._i18n_catalog:
+                self._i18n_catalog[self.current_language] = TranslatableMessage.load_as_catalog(
+                    self.current_language
+                )
+            catalog_string = self._i18n_catalog[self.current_language].get((context, string))
+            if register and not catalog_string:
+                from wcs.sql import Equal, Null
+
+                criteria = [Equal('string', string)]
+                if context:
+                    criteria.append(Equal('context', context))
+                else:
+                    criteria.append(Null('context'))
+                msgs = TranslatableMessage.exists(criteria)
+                if not msgs:
+                    msg = TranslatableMessage()
+                    msg.context = context
+                    msg.string = string
+                    msg.locations = []
+                    msg.store()
+            return catalog_string or string
         return string
 
     def load_site_options(self):
diff --git a/wcs/qommon/templatetags/qommon.py b/wcs/qommon/templatetags/qommon.py
index e633e7fdc..7ddd7e0a3 100644
--- a/wcs/qommon/templatetags/qommon.py
+++ b/wcs/qommon/templatetags/qommon.py
@@ -1048,3 +1048,8 @@ def stripsometags(value, arg=None):
     allowed_tags = [t.strip() for t in allowed_tags]
     allowed_tags = [t for t in allowed_tags if t]
     return strip_some_tags(unlazy(value), allowed_tags)
+
+
+@register.filter(is_safe=True)
+def translate(string, context=None):
+    return get_publisher().translate(string, context=context, register=True)
-- 
2.39.0

