Skip to content

Commit

Permalink
Add a machine translation option when syncing pages (#807)
Browse files Browse the repository at this point in the history
  • Loading branch information
zerolab authored Dec 20, 2024
1 parent 7445433 commit f791117
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@
</div>
<p class="help">{{ field.help_text }}</p>
</li>
{% elif field.name == 'use_machine_translation' %}
<li class="use-machine-translation-field">
<div class="field boolean_field checkbox_input">
<div class="field-content">
<div class="input">
<input type="checkbox" name="{{ field.name }}" id="{{ field.auto_id }}">
<label class="use-machine-translation-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
</div>
</div>
</div>
<p class="help">{{ field.help_text }}</p>
</li>
{% else %}
<li>{% include "wagtailadmin/shared/field.html" %}</li>
{% endif %}
Expand Down
116 changes: 115 additions & 1 deletion wagtail_localize/tests/test_update_translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 "
"&quot;Use machine translation&quot;.",
)

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")
31 changes: 19 additions & 12 deletions wagtail_localize/views/edit_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
)

Expand All @@ -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:
Expand Down
35 changes: 32 additions & 3 deletions wagtail_localize/views/update_translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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"):
Expand Down

0 comments on commit f791117

Please sign in to comment.