Skip to content

Commit

Permalink
Add formality support to DeepL translator (#779)
Browse files Browse the repository at this point in the history
  • Loading branch information
Abdul-Dridi authored and zerolab committed Mar 8, 2024
1 parent 2773147 commit cf12512
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 10 deletions.
5 changes: 4 additions & 1 deletion docs/how-to/integrations/machine-translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,16 @@ WAGTAILLOCALIZE_MACHINE_TRANSLATOR = {

Website: [https://www.deepl.com/](https://www.deepl.com/)

Note that You will need an account to get an API key. Deepl offers a free or paid subscription.
Note that You will need an account to get an API key. DeepL offers a free or paid subscription.

```python
WAGTAILLOCALIZE_MACHINE_TRANSLATOR = {
"CLASS": "wagtail_localize.machine_translators.deepl.DeepLTranslator",
"OPTIONS": {
"AUTH_KEY": "<Your DeepL key here>",
# Optional DeepL API setting. Accepts "default", "prefer_more" or "prefer_less".\
# For more information see the API docs https://www.deepl.com/docs-api/translate-text/
"FORMALITY": "<Your DeepL formality preference here>",
},
}
```
Expand Down
31 changes: 22 additions & 9 deletions wagtail_localize/machine_translators/deepl.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import warnings

import requests

from wagtail_localize.strings import StringValue

from .base import BaseMachineTranslator


SUPPORTED_FORMALITY_OPTIONS = {"default", "prefer_less", "prefer_more"}


def language_code(code, is_target=False):
# DeepL supports targeting Brazilian Portuguese and requires to specifically request American or British English.
# @see https://www.deepl.com/en/docs-api/translate-text/translate-text
Expand All @@ -24,17 +29,25 @@ def get_api_endpoint(self):
return "https://api.deepl.com/v2/translate"

def translate(self, source_locale, target_locale, strings):
parameters = {
"auth_key": self.options["AUTH_KEY"],
"text": [string.data for string in strings],
"tag_handling": "xml",
"source_lang": language_code(source_locale.language_code),
"target_lang": language_code(target_locale.language_code, is_target=True),
}

if self.options.get("FORMALITY"):
if self.options["FORMALITY"] in SUPPORTED_FORMALITY_OPTIONS:
parameters["formality"] = self.options["FORMALITY"]
else:
warnings.warn(
f"Unsupported formality option '{self.options['FORMALITY']}'. Supported options are: {', '.join(SUPPORTED_FORMALITY_OPTIONS)}"
)

response = requests.post(
self.get_api_endpoint(),
{
"auth_key": self.options["AUTH_KEY"],
"text": [string.data for string in strings],
"tag_handling": "xml",
"source_lang": language_code(source_locale.language_code),
"target_lang": language_code(
target_locale.language_code, is_target=True
),
},
parameters,
timeout=30,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from unittest.mock import Mock, patch

from django.test import TestCase, override_settings

from wagtail_localize.machine_translators import get_machine_translator
from wagtail_localize.machine_translators.deepl import DeepLTranslator, language_code
from wagtail_localize.strings import StringValue


DEEPL_SETTINGS_FREE_ENDPOINT = {
Expand All @@ -14,6 +17,22 @@
"OPTIONS": {"AUTH_KEY": "asd-23-ssd-243-adsf-dummy-auth-key:bla"},
}

DEEPL_SETTINGS_WITH_FORMALITY = {
"CLASS": "wagtail_localize.machine_translators.deepl.DeepLTranslator",
"OPTIONS": {
"AUTH_KEY": "asd-23-ssd-243-adsf-dummy-auth-key:bla",
"FORMALITY": "prefer_less",
},
}

DEEPL_SETTINGS_WITH_UNSUPPORTED_FORMALITY = {
"CLASS": "wagtail_localize.machine_translators.deepl.DeepLTranslator",
"OPTIONS": {
"AUTH_KEY": "asd-23-ssd-243-adsf-dummy-auth-key:bla",
"FORMALITY": "less",
},
}


class TestDeeplTranslator(TestCase):
@override_settings(WAGTAILLOCALIZE_MACHINE_TRANSLATOR=DEEPL_SETTINGS_FREE_ENDPOINT)
Expand All @@ -30,6 +49,54 @@ def test_paid_api_endpoint(self):
paid_api_endpoint = translator.get_api_endpoint()
self.assertEqual(paid_api_endpoint, "https://api.deepl.com/v2/translate")

@override_settings(WAGTAILLOCALIZE_MACHINE_TRANSLATOR=DEEPL_SETTINGS_WITH_FORMALITY)
@patch("requests.post")
def test_translate_with_formality_option(self, mock_post):
translator = get_machine_translator()
source_locale = Mock(language_code="en")
target_locale = Mock(language_code="de")
strings = [StringValue("Test string")]

translator.translate(source_locale, target_locale, strings)

mock_post.assert_called_once()
called_args, called_kwargs = mock_post.call_args
self.assertIn("formality", called_args[1])
self.assertEqual(called_args[1]["formality"], "prefer_less")

@override_settings(WAGTAILLOCALIZE_MACHINE_TRANSLATOR=DEEPL_SETTINGS_PAID_ENDPOINT)
@patch("requests.post")
def test_translate_without_formality_option(self, mock_post):
translator = get_machine_translator()
source_locale = Mock(language_code="en")
target_locale = Mock(language_code="de")
strings = [StringValue("Test string")]

translator.translate(source_locale, target_locale, strings)

mock_post.assert_called_once()
called_args, called_kwargs = mock_post.call_args
self.assertNotIn("formality", called_args[1])

@override_settings(
WAGTAILLOCALIZE_MACHINE_TRANSLATOR=DEEPL_SETTINGS_WITH_UNSUPPORTED_FORMALITY
)
@patch("requests.post")
@patch("warnings.warn")
def test_translate_with_non_supported_formality_option(self, mock_warn, mock_post):
translator = get_machine_translator()
source_locale = Mock(language_code="en")
target_locale = Mock(language_code="de")
strings = [StringValue("Test string")]

translator.translate(source_locale, target_locale, strings)

mock_warn.assert_called_once()

mock_post.assert_called_once()
called_args, called_kwargs = mock_post.call_args
self.assertNotIn("formality", called_args[1])

def test_language_code_as_source(self):
mapping = {
"pt-pt": "PT",
Expand Down

0 comments on commit cf12512

Please sign in to comment.