Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a machine translation option when syncing pages (Rebase of #807) #842

Merged
merged 7 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading