Repository: plone/plone.restapi (language: python)

Issue:
Translate validation error messages
Translate validation error messages in the deserializer.


Discussion / hints:

Fix patch:
diff --git a/news/1742.feature b/news/1742.feature
new file mode 100644
index 0000000000..72bf2781b4
--- /dev/null
+++ b/news/1742.feature
@@ -0,0 +1,1 @@
+Translate validation error messages in the deserializer. @wesleybl
diff --git a/requirements-docs.txt b/requirements-docs.txt
index dbff148650..a522f65309 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -1,5 +1,10 @@
 docutils<0.17,>=0.15  # sphinx-book-theme 0.2.0 has requirement docutils<0.17,>=0.15
 Sphinx<5,>=3  # sphinx-book-theme 0.3.3 has requirement sphinx<5,>=3
+sphinxcontrib-applehelp==1.0.4  # newer versions require sphinx 5
+sphinxcontrib-devhelp==1.0.2   # newer versions require sphinx 5
+sphinxcontrib-htmlhelp==2.0.1  # newer versions require sphinx 5
+sphinxcontrib-qthelp==1.0.3  # newer versions require sphinx 5
+sphinxcontrib-serializinghtml==1.1.5  # newer versions require sphinx 5
 jsx-lexer
 lesscpy
 linkify-it-py
diff --git a/src/plone/restapi/deserializer/dxcontent.py b/src/plone/restapi/deserializer/dxcontent.py
index 88258ab044..dd6bac1851 100644
--- a/src/plone/restapi/deserializer/dxcontent.py
+++ b/src/plone/restapi/deserializer/dxcontent.py
@@ -3,6 +3,7 @@
 from plone.autoform.interfaces import WRITE_PERMISSIONS_KEY
 from plone.dexterity.interfaces import IDexterityContent
 from plone.dexterity.utils import iterSchemata
+from plone.restapi import _
 from plone.restapi.deserializer import json_body
 from plone.restapi.interfaces import IDeserializeFromJson
 from plone.restapi.interfaces import IFieldDeserializer
@@ -14,6 +15,7 @@
 from zope.component import queryMultiAdapter
 from zope.component import queryUtility
 from zope.event import notify
+from zope.i18n import translate
 from zope.interface import implementer
 from zope.interface import Interface
 from zope.lifecycleevent import Attributes
@@ -49,7 +51,7 @@ def __call__(
                 (self.context, self.request, None, schema, None), IManagerValidator
             )
             for error in validator.validate(field_data):
-                errors.append({"error": error, "message": str(error)})
+                errors.append({"error": error, "message": error.args[0]})
 
         if errors:
             if mask_validation_errors:
@@ -57,6 +59,8 @@ def __call__(
                 # errors on front-end
                 for error in errors:
                     error["error"] = "ValidationError"
+            for error in errors:
+                error["message"] = translate(error["message"], context=self.request)
             raise BadRequest(errors)
 
         # We'll set the layout after the validation and even if there
@@ -109,11 +113,10 @@ def get_schema_data(self, data, validate_all, create=False):
                             errors.append(
                                 {
                                     "field": field.__name__,
-                                    "message": (
-                                        "{} is a required field.".format(
-                                            field.__name__
-                                        ),
-                                        "Setting it to null is not allowed.",
+                                    "message": _(
+                                        "${field_name} is a required field."
+                                        " Setting it to null is not allowed.",
+                                        mapping={"field_name": field.__name__},
                                     ),
                                 }
                             )
diff --git a/src/plone/restapi/locales/de/LC_MESSAGES/plone.restapi.po b/src/plone/restapi/locales/de/LC_MESSAGES/plone.restapi.po
index 0a4c1ec545..2d78df1696 100644
--- a/src/plone/restapi/locales/de/LC_MESSAGES/plone.restapi.po
+++ b/src/plone/restapi/locales/de/LC_MESSAGES/plone.restapi.po
@@ -1,7 +1,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2023-09-25 20:32+0000\n"
+"POT-Creation-Date: 2023-12-22 19:57+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
 "Last-Translator: Timo Stollenwerk <tisto@plone.org>\n"
 "Language-Team: German <DE@li.org>\n"
@@ -14,6 +14,10 @@ msgstr ""
 "Preferred-Encodings: utf-8 latin1\n"
 "Domain: plone.restapi\n"
 
+#: plone/restapi/deserializer/dxcontent.py:116
+msgid "${field_name} is a required field. Setting it to null is not allowed."
+msgstr ""
+
 #: plone/restapi/services/email_send/post.py:88
 msgid "${sender_fullname} via ${portal_title}"
 msgstr "${sender_fullname} via ${portal_title}"
diff --git a/src/plone/restapi/locales/es/LC_MESSAGES/plone.restapi.po b/src/plone/restapi/locales/es/LC_MESSAGES/plone.restapi.po
index 6510309a30..c9acdf7e22 100644
--- a/src/plone/restapi/locales/es/LC_MESSAGES/plone.restapi.po
+++ b/src/plone/restapi/locales/es/LC_MESSAGES/plone.restapi.po
@@ -4,7 +4,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: Plone\n"
-"POT-Creation-Date: 2023-09-25 20:32+0000\n"
+"POT-Creation-Date: 2023-12-22 19:57+0000\n"
 "PO-Revision-Date: 2023-08-23 21:19-0400\n"
 "Last-Translator: Leonardo J. Caballero G. <leonardocaballero@gmail.com>\n"
 "Language-Team: ES <LL@li.org>\n"
@@ -20,6 +20,10 @@ msgstr ""
 "X-Generator: Poedit 3.3.2\n"
 "X-Is-Fallback-For: es-ar es-bo es-cl es-co es-cr es-do es-ec es-es es-sv es-gt es-hn es-mx es-ni es-pa es-py es-pe es-pr es-us es-uy es-ve\n"
 
+#: plone/restapi/deserializer/dxcontent.py:116
+msgid "${field_name} is a required field. Setting it to null is not allowed."
+msgstr ""
+
 #: plone/restapi/services/email_send/post.py:88
 msgid "${sender_fullname} via ${portal_title}"
 msgstr "${sender_fullname} vía ${portal_title}"
diff --git a/src/plone/restapi/locales/fr/LC_MESSAGES/plone.restapi.po b/src/plone/restapi/locales/fr/LC_MESSAGES/plone.restapi.po
index 9d25934588..3570cdfa24 100644
--- a/src/plone/restapi/locales/fr/LC_MESSAGES/plone.restapi.po
+++ b/src/plone/restapi/locales/fr/LC_MESSAGES/plone.restapi.po
@@ -1,7 +1,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2023-09-25 20:32+0000\n"
+"POT-Creation-Date: 2023-12-22 19:57+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
 "Last-Translator: Julien Chandelle <jchandelle@affinitic.be>\n"
 "Language-Team: French <FR@li.org>\n"
@@ -14,6 +14,10 @@ msgstr ""
 "Preferred-Encodings: utf-8 latin1\n"
 "Domain: plone.restapi\n"
 
+#: plone/restapi/deserializer/dxcontent.py:116
+msgid "${field_name} is a required field. Setting it to null is not allowed."
+msgstr ""
+
 #: plone/restapi/services/email_send/post.py:88
 msgid "${sender_fullname} via ${portal_title}"
 msgstr "${sender_fullname} via ${portal_title}"
diff --git a/src/plone/restapi/locales/plone.restapi.pot b/src/plone/restapi/locales/plone.restapi.pot
index d1d2abf57a..8b6c3c59c3 100644
--- a/src/plone/restapi/locales/plone.restapi.pot
+++ b/src/plone/restapi/locales/plone.restapi.pot
@@ -4,7 +4,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2023-09-25 20:32+0000\n"
+"POT-Creation-Date: 2023-12-22 19:57+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,6 +17,10 @@ msgstr ""
 "Preferred-Encodings: utf-8 latin1\n"
 "Domain: plone.restapi\n"
 
+#: plone/restapi/deserializer/dxcontent.py:116
+msgid "${field_name} is a required field. Setting it to null is not allowed."
+msgstr ""
+
 #: plone/restapi/services/email_send/post.py:88
 msgid "${sender_fullname} via ${portal_title}"
 msgstr ""
diff --git a/src/plone/restapi/locales/pt_BR/LC_MESSAGES/plone.restapi.po b/src/plone/restapi/locales/pt_BR/LC_MESSAGES/plone.restapi.po
index dd921dca1f..8027d92919 100644
--- a/src/plone/restapi/locales/pt_BR/LC_MESSAGES/plone.restapi.po
+++ b/src/plone/restapi/locales/pt_BR/LC_MESSAGES/plone.restapi.po
@@ -1,7 +1,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: plone.restapi\n"
-"POT-Creation-Date: 2023-09-25 20:32+0000\n"
+"POT-Creation-Date: 2023-12-22 19:57+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
 "Last-Translator: Wesley Barroso Lopes\n"
 "Language-Team: \n"
@@ -14,6 +14,10 @@ msgstr ""
 "Preferred-Encodings: utf-8 latin1\n"
 "Domain: plone.restapi\n"
 
+#: plone/restapi/deserializer/dxcontent.py:116
+msgid "${field_name} is a required field. Setting it to null is not allowed."
+msgstr "${field_name} é um campo obrigatório. Não é permitido defini-lo como nulo."
+
 #: plone/restapi/services/email_send/post.py:88
 msgid "${sender_fullname} via ${portal_title}"
 msgstr ""


Test patch:
diff --git a/src/plone/restapi/tests/test_dxcontent_deserializer.py b/src/plone/restapi/tests/test_dxcontent_deserializer.py
index e9cc6fdf14..ddeb4ef767 100644
--- a/src/plone/restapi/tests/test_dxcontent_deserializer.py
+++ b/src/plone/restapi/tests/test_dxcontent_deserializer.py
@@ -195,10 +195,7 @@ def test_deserializer_sets_missing_value_on_required_field(self):
             "valid value", self.portal.doc1.test_missing_value_required_field
         )
         self.assertEqual(
-            (
-                "test_missing_value_required_field is a required field.",
-                "Setting it to null is not allowed.",
-            ),
+            "test_missing_value_required_field is a required field. Setting it to null is not allowed.",
             cm.exception.args[0][0]["message"],
         )
         self.assertEqual(