diff --git a/projects/test_views.py b/projects/test_views.py
index f222a7a..838ac38 100644
--- a/projects/test_views.py
+++ b/projects/test_views.py
@@ -1,8 +1,11 @@
+from unittest.mock import Mock, patch
+
from authlib.little_auth.models import User
from django.test import Client, TestCase
from django.test.utils import override_settings
-from projects.models import Project
+from projects.models import Catalog, Project
+from projects.translators import TranslationError
class ProjectsTest(TestCase):
@@ -75,6 +78,11 @@ def test_smoke(self):
)
self.assertEqual(r.content.decode("utf-8"), c.pofile)
+ r = su_client.post(
+ "/api/pofile/fr/djangojs/", headers={"x-project-token": p.token}
+ )
+ self.assertEqual(r.status_code, 405)
+
r = su_client.get(
"/api/pofile/de/djangojs/", headers={"x-project-token": p.token}
)
@@ -160,3 +168,36 @@ def test_smoke(self):
self.assertContains(r, '
- | ')
self.assertContains(r, 'use***@***.com | ')
+
+ def test_suggest(self):
+ c = Client()
+
+ r = c.get("/suggest/")
+ self.assertEqual(r.status_code, 405)
+
+ r = c.post("/suggest/")
+ self.assertEqual(r.status_code, 403)
+
+ user = User.objects.create_user("user@example.com", "user")
+ c.force_login(user)
+
+ r = c.post("/suggest/")
+ self.assertEqual(r.status_code, 400)
+
+ with patch(
+ "projects.views.translators.translate_by_deepl", lambda *a: "Bonjour"
+ ):
+ r = c.post("/suggest/", {"language_code": "fr", "msgid": "Anything"})
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.json(), {"msgstr": "Bonjour"})
+
+ mock = Mock()
+ mock.side_effect = TranslationError("Oops")
+ with patch("projects.views.translators.translate_by_deepl", mock):
+ r = c.post("/suggest/", {"language_code": "fr", "msgid": "Anything"})
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.json(), {"error": "Oops"})
+
+ def test_invalid_catalog(self):
+ c = Catalog(language_code="it", domain="django", pofile="blub")
+ self.assertEqual(str(c), "Italian, django (Invalid)")
diff --git a/projects/translators.py b/projects/translators.py
new file mode 100644
index 0000000..4f1c24e
--- /dev/null
+++ b/projects/translators.py
@@ -0,0 +1,33 @@
+import requests
+
+
+class TranslationError(Exception):
+ pass
+
+
+def translate_by_deepl(text, to_language, auth_key):
+ # Copied 1:1 from django-rosetta, thanks!
+ if auth_key.lower().endswith(":fx"):
+ endpoint = "https://api-free.deepl.com"
+ else:
+ endpoint = "https://api.deepl.com"
+
+ r = requests.post(
+ f"{endpoint}/v2/translate",
+ headers={"Authorization": f"DeepL-Auth-Key {auth_key}"},
+ data={
+ "target_lang": to_language.upper(),
+ "text": text,
+ },
+ timeout=5,
+ )
+ if r.status_code != 200:
+ raise TranslationError(
+ f"Deepl response is {r.status_code}. Please check your API key or try again later."
+ )
+ try:
+ return r.json().get("translations")[0].get("text")
+ except Exception as exc:
+ raise TranslationError(
+ "Deepl returned a non-JSON or unexpected response."
+ ) from exc
diff --git a/projects/views.py b/projects/views.py
index 406d935..d69bb20 100644
--- a/projects/views.py
+++ b/projects/views.py
@@ -1,5 +1,4 @@
import polib
-import requests
from django import forms, http
from django.conf import settings
from django.contrib.auth.decorators import login_required
@@ -9,8 +8,10 @@
from django.utils.timezone import localtime
from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.http import require_POST
from form_rendering import adapt_rendering
+from projects import translators
from projects.models import Catalog, Project
@@ -235,42 +236,12 @@ def catalog(request, project, language_code, domain):
)
-class TranslationError(Exception):
- pass
-
-
-def translate_by_deepl(text, to_language, auth_key):
- if auth_key.lower().endswith(":fx"):
- endpoint = "https://api-free.deepl.com"
- else:
- endpoint = "https://api.deepl.com"
-
- r = requests.post(
- f"{endpoint}/v2/translate",
- headers={"Authorization": f"DeepL-Auth-Key {auth_key}"},
- data={
- "target_lang": to_language.upper(),
- "text": text,
- },
- timeout=5,
- )
- if r.status_code != 200:
- raise TranslationError(
- f"Deepl response is {r.status_code}. Please check your API key or try again later."
- )
- try:
- return r.json().get("translations")[0].get("text")
- except Exception as exc:
- raise TranslationError(
- "Deepl returned a non-JSON or unexpected response."
- ) from exc
-
-
class SuggestForm(forms.Form):
language_code = forms.CharField()
msgid = forms.CharField()
+@require_POST
def suggest(request):
if not request.user.is_authenticated:
return http.HttpResponseForbidden()
@@ -279,10 +250,10 @@ def suggest(request):
if form.is_valid():
data = form.cleaned_data
try:
- translation = translate_by_deepl(
+ translation = translators.translate_by_deepl(
data["msgid"], data["language_code"], settings.DEEPL_AUTH_KEY
)
- except TranslationError as exc:
+ except translators.TranslationError as exc:
return http.JsonResponse({"error": str(exc)})
return http.JsonResponse({"msgstr": translation})