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