From f791117cbbad92c0103f82a6aff742bbb0026ded Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Braghi=C8=99?=
<31622+zerolab@users.noreply.github.com>
Date: Fri, 20 Dec 2024 14:50:19 +0100
Subject: [PATCH] Add a machine translation option when syncing pages (#807)
---
.../admin/update_translations.html | 12 ++
.../tests/test_update_translations.py | 116 +++++++++++++++++-
wagtail_localize/views/edit_translation.py | 31 +++--
wagtail_localize/views/update_translations.py | 35 +++++-
4 files changed, 178 insertions(+), 16 deletions(-)
diff --git a/wagtail_localize/templates/wagtail_localize/admin/update_translations.html b/wagtail_localize/templates/wagtail_localize/admin/update_translations.html
index 19149490..df97de90 100644
--- a/wagtail_localize/templates/wagtail_localize/admin/update_translations.html
+++ b/wagtail_localize/templates/wagtail_localize/admin/update_translations.html
@@ -49,6 +49,18 @@
{{ field.help_text }}
+ {% elif field.name == 'use_machine_translation' %}
+
+
+ {{ field.help_text }}
+
{% else %}
{% include "wagtailadmin/shared/field.html" %}
{% endif %}
diff --git a/wagtail_localize/tests/test_update_translations.py b/wagtail_localize/tests/test_update_translations.py
index c828aae0..be275a88 100644
--- a/wagtail_localize/tests/test_update_translations.py
+++ b/wagtail_localize/tests/test_update_translations.py
@@ -5,12 +5,18 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
+from django.forms.widgets import CheckboxInput
from django.test import TestCase, override_settings
from django.urls import reverse
from wagtail.models import Locale, Page, PageViewRestriction
from wagtail.test.utils import WagtailTestUtils
-from wagtail_localize.models import StringSegment, Translation, TranslationSource
+from wagtail_localize.models import (
+ StringSegment,
+ StringTranslation,
+ Translation,
+ TranslationSource,
+)
from wagtail_localize.test.models import NonTranslatableSnippet, TestSnippet
from .utils import assert_permission_denied, make_test_page
@@ -612,3 +618,111 @@ def test_post_update_page_translation_will_run_update_target_view_restrictions(
)
# one call from the first post, and one from above
self.assertEqual(update_target_view_restrictions.call_count, 2)
+
+ @mock.patch(
+ "wagtail_localize.views.update_translations.get_machine_translator",
+ return_value=None,
+ )
+ def test_update_translations_form_without_machine_translator(
+ self, mocked_get_machine_translator
+ ):
+ response = self.client.get(
+ reverse(
+ "wagtail_localize:update_translations",
+ args=[self.snippet_source.id],
+ )
+ )
+
+ self.assertEqual(response.status_code, 200)
+
+ self.assertContains(
+ response,
+ "Apply the updates and publish immediately. The changes will use the original language until translated.",
+ )
+ self.assertNotContains(response, "use_machine_translation")
+ self.assertNotIn("use_machine_translation", response.context["form"].fields)
+
+ def test_update_translations_form_with_machine_translator(self):
+ response = self.client.get(
+ reverse(
+ "wagtail_localize:update_translations",
+ args=[self.snippet_source.id],
+ )
+ )
+
+ self.assertEqual(response.status_code, 200)
+
+ self.assertIsInstance(
+ response.context["form"].fields["use_machine_translation"].widget,
+ CheckboxInput,
+ )
+
+ self.assertContains(
+ response,
+ "Apply the updates and publish immediately. The changes will use "
+ "the original language until translated unless you also select "
+ ""Use machine translation".",
+ )
+
+ def test_post_update_page_translation_with_publish_translations_and_use_machine_translation(
+ self,
+ ):
+ self.en_blog_post.test_charfield = "Edited blog post"
+ self.en_blog_post.save_revision().publish()
+
+ response = self.client.post(
+ reverse(
+ "wagtail_localize:update_translations",
+ args=[self.page_source.id],
+ ),
+ {
+ "publish_translations": "on",
+ "use_machine_translation": "on",
+ },
+ )
+
+ self.assertRedirects(
+ response, reverse("wagtailadmin_explore", args=[self.en_blog_index.id])
+ )
+
+ # The FR version should be translated (Dummy translator will reverse) and updated
+ self.fr_blog_post.refresh_from_db()
+ self.assertEqual(self.fr_blog_post.test_charfield, "post blog Edited")
+
+ def test_post_update_page_translation_with_use_machine_translation(self):
+ self.en_blog_post.test_charfield = "Edited blog post"
+ self.en_blog_post.save_revision().publish()
+
+ response = self.client.post(
+ reverse(
+ "wagtail_localize:update_translations",
+ args=[self.page_source.id],
+ ),
+ {
+ "use_machine_translation": "on",
+ },
+ )
+
+ self.assertRedirects(
+ response, reverse("wagtailadmin_explore", args=[self.en_blog_index.id])
+ )
+
+ self.fr_blog_post.refresh_from_db()
+ self.assertEqual(self.fr_blog_post.test_charfield, "Test content")
+
+ # Check that the translation is done, awaiting publication
+ fr = Locale.objects.get(language_code="fr")
+ translation_source = TranslationSource.objects.get_for_instance_or_none(
+ self.en_blog_post
+ )
+ translation = translation_source.translations.get(target_locale=fr)
+
+ string_segment = translation.source.stringsegment_set.get(
+ string__data="Edited blog post"
+ )
+ string_translation = StringTranslation.objects.get(
+ translation_of_id=string_segment.string_id,
+ locale=fr,
+ context_id=string_segment.context_id,
+ )
+ self.assertEqual(string_translation.data, "post blog Edited")
diff --git a/wagtail_localize/views/edit_translation.py b/wagtail_localize/views/edit_translation.py
index 4f2ff023..b2a13076 100644
--- a/wagtail_localize/views/edit_translation.py
+++ b/wagtail_localize/views/edit_translation.py
@@ -1370,19 +1370,14 @@ def upload_pofile(request, translation_id):
return redirect(next_url)
-@require_POST
-def machine_translate(request, translation_id):
+def apply_machine_translation(translation_id, user, machine_translator):
translation = get_object_or_404(Translation, id=translation_id)
instance = translation.get_target_instance()
- if not user_can_edit_instance(request.user, instance):
+ if not user_can_edit_instance(user, instance):
raise PermissionDenied
- translator = get_machine_translator()
- if translator is None:
- raise Http404
-
- if not translator.can_translate(
+ if not machine_translator.can_translate(
translation.source.locale, translation.target_locale
):
raise Http404
@@ -1411,7 +1406,7 @@ def machine_translate(request, translation_id):
)
if segments:
- translations = translator.translate(
+ translations = machine_translator.translate(
translation.source.locale, translation.target_locale, segments.keys()
)
@@ -1425,16 +1420,28 @@ def machine_translate(request, translation_id):
defaults={
"data": translations[string].data,
"translation_type": StringTranslation.TRANSLATION_TYPE_MACHINE,
- "tool_name": translator.display_name,
- "last_translated_by": request.user,
+ "tool_name": machine_translator.display_name,
+ "last_translated_by": user,
"has_error": False,
"field_error": "",
},
)
+ return True
+ return False
+
+@require_POST
+def machine_translate(request, translation_id):
+ machine_translator = get_machine_translator()
+ if machine_translator is None:
+ raise Http404
+
+ if apply_machine_translation(translation_id, request.user, machine_translator):
messages.success(
request,
- _("Successfully translated with {}.").format(translator.display_name),
+ _("Successfully translated with {}.").format(
+ machine_translator.display_name
+ ),
)
else:
diff --git a/wagtail_localize/views/update_translations.py b/wagtail_localize/views/update_translations.py
index 09093da6..f8977aae 100644
--- a/wagtail_localize/views/update_translations.py
+++ b/wagtail_localize/views/update_translations.py
@@ -17,18 +17,41 @@
from wagtail.snippets.models import get_snippet_models
from wagtail.utils.version import get_main_version
+from wagtail_localize.machine_translators import get_machine_translator
from wagtail_localize.models import TranslationSource
+from wagtail_localize.views.edit_translation import apply_machine_translation
from wagtail_localize.views.submit_translations import TranslationComponentManager
class UpdateTranslationsForm(forms.Form):
publish_translations = forms.BooleanField(
label=gettext_lazy("Publish immediately"),
- help_text=gettext_lazy(
- "This will apply the updates and publish immediately, before any new translations happen."
- ),
required=False,
)
+ _has_machine_translator = False
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._has_machine_translator = get_machine_translator() is not None
+
+ if self._has_machine_translator:
+ self.fields["publish_translations"].help_text = gettext_lazy(
+ "Apply the updates and publish immediately. The changes will use "
+ "the original language until translated unless you also select "
+ '"Use machine translation".'
+ )
+ self.fields["use_machine_translation"] = forms.BooleanField(
+ label=gettext_lazy("Use machine translation"),
+ help_text=gettext_lazy(
+ "Apply machine translations to the incoming changes."
+ ),
+ required=False,
+ )
+ else:
+ self.fields["publish_translations"].help_text = gettext_lazy(
+ "Apply the updates and publish immediately. The changes will use "
+ "the original language until translated."
+ )
class UpdateTranslationsView(SingleObjectMixin, TemplateView):
@@ -133,6 +156,12 @@ def form_valid(self, form):
self.object.update_from_db()
enabled_translations = self.object.translations.filter(enabled=True)
+ if form.cleaned_data["use_machine_translation"]:
+ machine_translator = get_machine_translator()
+ for translation in enabled_translations.select_related("target_locale"):
+ apply_machine_translation(
+ translation.id, self.request.user, machine_translator
+ )
if form.cleaned_data["publish_translations"]:
for translation in enabled_translations.select_related("target_locale"):