Skip to content

Commit 5a0c11f

Browse files
authored
Remove SYSTRAN machine translation integration (mozilla#4180) (mozilla#4181)
SYSTRAN integration is no longer used. Remove the view, URL, frontend fetch/component, Locale model fields (and add matching migration), API field, SYSTRAN_TRANSLATE_API_KEY family of settings, the data-is-systran-translate-supported flag passed to the translate view, and the l10n strings. Translation.MachinerySource.SYSTRAN_TRANSLATE is intentionally retained so existing translations attributed to SYSTRAN remain valid; only the live integration is removed. Also: Fix Node version mismatch in the backend workflow
1 parent aba3464 commit 5a0c11f

20 files changed

Lines changed: 22 additions & 217 deletions

File tree

.github/workflows/backend.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ jobs:
7676
-e '/^SITE_URL=/d'
7777
docker/config/server.env.template > .env
7878
# Run collectstatic with minimal dependencies, skipping the actual front-end build
79+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
80+
with: { node-version: '24' }
7981
- run: npm install
8082
working-directory: pontoon
8183
- run: mkdir -p translate/dist translate/public

documentation/docs/dev/deployment.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,6 @@ cache is cleared and the subsequent task can run. The value should
297297
exceed the longest sync task of the instance. The default value is 3600
298298
seconds (1 hour).
299299

300-
`SYSTRAN_TRANSLATE_API_KEY`
301-
Optional. Set your [Systran Translate](https://auth.systran.net/oidc/interaction/OcxBMUAbEIkN6tIg1yIcp) API key to use machine translation by Systran.
302-
303300
`TBX_DESCRIPTION`
304301
Optional. Description to be used in the header of the Terminology (`.TBX`)
305302
files.

documentation/docs/localizer/translation-workspace.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,6 @@ Machinery shows possible translations from a variety of sources. These sources i
273273

274274
* [Pontoon’s internal translation memory](translate.md#downloading-and-uploading-translations).
275275
* [Google Translate](https://translate.google.com).
276-
* [SYSTRAN](https://www.systran.net/).
277276
* [Caighdean](https://github.com/kscanne/caighdean).
278277
* [Bing Translator](https://www.bing.com/translator) (not currently enabled on pontoon.mozilla.org).
279278

pontoon/api/serializers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ class Meta:
112112
"google_translate_code",
113113
"ms_terminology_code",
114114
"ms_translator_code",
115-
"systran_translate_code",
116115
"team_description",
117116
] + TRANSLATION_STATS_FIELDS
118117

pontoon/api/tests/test_views.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ def test_locale(django_assert_num_queries):
138138
"google_translate_code": "",
139139
"ms_terminology_code": "",
140140
"ms_translator_code": "",
141-
"systran_translate_code": "",
142141
"team_description": "",
143142
"total_strings": 25,
144143
"approved_strings": 10,
@@ -221,7 +220,6 @@ def test_locales(django_assert_num_queries):
221220
"google_translate_code": loc.google_translate_code,
222221
"ms_terminology_code": loc.ms_terminology_code,
223222
"ms_translator_code": loc.ms_translator_code,
224-
"systran_translate_code": loc.systran_translate_code,
225223
"team_description": loc.team_description,
226224
"total_strings": loc.total_strings,
227225
"approved_strings": loc.approved_strings,
@@ -967,7 +965,6 @@ def test_project_locale(django_assert_num_queries):
967965
"google_translate_code": "af",
968966
"ms_terminology_code": "af-za",
969967
"ms_translator_code": "af",
970-
"systran_translate_code": "",
971968
"team_description": "",
972969
"total_strings": 25,
973970
"approved_strings": 10,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.2.13 on 2026-05-29 19:12
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("base", "0115_alter_userprofile_username_and_more"),
9+
]
10+
11+
operations = [
12+
migrations.RemoveField(
13+
model_name="locale",
14+
name="systran_translate_code",
15+
),
16+
migrations.RemoveField(
17+
model_name="locale",
18+
name="systran_translate_profile",
19+
),
20+
]

pontoon/base/models/locale.py

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
import json
21
import logging
32

4-
import requests
5-
6-
from django.conf import settings
73
from django.contrib.auth.models import Group
84
from django.core.exceptions import ValidationError
95
from django.db import models
@@ -186,26 +182,6 @@ def aggregated_stats_query(self):
186182
""",
187183
)
188184

189-
# Fields used by optional SYSTRAN services
190-
systran_translate_code = models.CharField(
191-
max_length=20,
192-
blank=True,
193-
help_text="""
194-
SYSTRAN maintains its own list of
195-
<a href="https://platform.systran.net/index">supported locales</a>.
196-
Choose a matching locale from the list or leave blank to disable
197-
support for SYSTRAN machine translation service.
198-
""",
199-
)
200-
systran_translate_profile = models.CharField(
201-
max_length=128,
202-
blank=True,
203-
help_text="""
204-
SYSTRAN Profile UUID to specify the engine trained on the en-locale language pair.
205-
The field is updated automatically after the systran_translate_code field changes.
206-
""",
207-
)
208-
209185
db_collation = models.CharField(
210186
max_length=20,
211187
blank=True,
@@ -336,7 +312,6 @@ def serialize(self):
336312
"script": self.script,
337313
"google_translate_code": self.google_translate_code,
338314
"ms_translator_code": self.ms_translator_code,
339-
"systran_translate_code": self.systran_translate_code,
340315
"ms_terminology_code": self.ms_terminology_code,
341316
}
342317

@@ -369,50 +344,3 @@ def get_latest_activity(self):
369344
return (
370345
self.latest_translation.latest_activity if self.latest_translation else None
371346
)
372-
373-
def save(self, *args, **kwargs):
374-
old = Locale.objects.get(pk=self.pk) if self.pk else None
375-
super().save(*args, **kwargs)
376-
377-
# If SYSTRAN Translate code changes, update SYSTRAN Profile UUID.
378-
if old is None or old.systran_translate_code == self.systran_translate_code:
379-
return
380-
381-
if not self.systran_translate_code:
382-
return
383-
384-
api_key = settings.SYSTRAN_TRANSLATE_API_KEY
385-
server = settings.SYSTRAN_TRANSLATE_SERVER
386-
profile_owner = settings.SYSTRAN_TRANSLATE_PROFILE_OWNER
387-
if not (api_key or server or profile_owner):
388-
return
389-
390-
url = f"{server}/translation/supportedLanguages"
391-
392-
payload = {
393-
"key": api_key,
394-
"source": "en",
395-
"target": self.systran_translate_code,
396-
}
397-
398-
try:
399-
r = requests.post(url, params=payload)
400-
root = json.loads(r.content)
401-
402-
if "error" in root:
403-
log.error(
404-
"Unable to retrieve SYSTRAN Profile UUID: {error}".format(
405-
error=root
406-
)
407-
)
408-
return
409-
410-
for languagePair in root["languagePairs"]:
411-
for profile in languagePair["profiles"]:
412-
if profile["selectors"]["owner"] == profile_owner:
413-
self.systran_translate_profile = profile["id"]
414-
self.save(update_fields=["systran_translate_profile"])
415-
return
416-
417-
except requests.exceptions.RequestException as e:
418-
log.error(f"Unable to retrieve SYSTRAN Profile UUID: {e}")

pontoon/machinery/urls.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@
2222
views.microsoft_translator,
2323
name="pontoon.microsoft_translator",
2424
),
25-
path(
26-
"systran-translate/",
27-
views.systran_translate,
28-
name="pontoon.systran_translate",
29-
),
3025
path("caighdean/", views.caighdean, name="pontoon.caighdean"),
3126
path(
3227
"microsoft-terminology/",

pontoon/machinery/views.py

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
from sacremoses import MosesDetokenizer
1010

11-
from django.conf import settings
1211
from django.contrib.auth.decorators import login_required
1312
from django.core.paginator import EmptyPage, Paginator
1413
from django.http import JsonResponse
@@ -228,62 +227,6 @@ def gpt_transform(request):
228227
return _machinery_error_response("GPT Transform", e)
229228

230229

231-
@login_required(redirect_field_name="", login_url="/403")
232-
def systran_translate(request):
233-
"""Get translations from SYSTRAN machine translation service."""
234-
try:
235-
text = request.GET["text"]
236-
locale_code = request.GET["locale"]
237-
238-
if not locale_code:
239-
raise ValueError("Locale code is empty")
240-
241-
locale = Locale.objects.filter(systran_translate_code=locale_code).first()
242-
243-
api_key = settings.SYSTRAN_TRANSLATE_API_KEY
244-
if not api_key:
245-
raise ValueError("Missing api key")
246-
247-
except (Locale.DoesNotExist, MultiValueDictKeyError, ValueError) as e:
248-
return JsonResponse(
249-
{"status": False, "message": f"Bad Request: {e}"},
250-
status=400,
251-
)
252-
253-
url = "https://api-translate.systran.net/translation/text/translate"
254-
255-
payload = {
256-
"key": api_key,
257-
"input": text,
258-
"source": "en",
259-
"target": locale_code,
260-
"profile": locale.systran_translate_profile,
261-
"format": "text",
262-
}
263-
264-
r = None
265-
try:
266-
r = requests.post(url, params=payload)
267-
r.raise_for_status()
268-
269-
root = json.loads(r.content)
270-
271-
if "error" in root:
272-
log.error(f"SYSTRAN error: {root}")
273-
return JsonResponse(
274-
{"status": False, "message": f"Bad Request: {root}"},
275-
status=400,
276-
)
277-
278-
return JsonResponse({"translation": root["outputs"][0]["output"]})
279-
280-
except requests.exceptions.RequestException as e:
281-
return JsonResponse(
282-
{"status": False, "message": f"{e}"},
283-
status=r.status_code if r is not None else 500,
284-
)
285-
286-
287230
def caighdean(request):
288231
"""Get translation from Caighdean machine translation service."""
289232
try:

pontoon/settings/base.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,6 @@ def path(*args):
173173
# Microsoft Translator API Key
174174
MICROSOFT_TRANSLATOR_API_KEY = os.environ.get("MICROSOFT_TRANSLATOR_API_KEY", "")
175175

176-
# SYSTRAN Translate Settings
177-
SYSTRAN_TRANSLATE_API_KEY = os.environ.get("SYSTRAN_TRANSLATE_API_KEY", "")
178-
SYSTRAN_TRANSLATE_SERVER = os.environ.get("SYSTRAN_TRANSLATE_SERVER", "")
179-
SYSTRAN_TRANSLATE_PROFILE_OWNER = os.environ.get("SYSTRAN_TRANSLATE_PROFILE_OWNER", "")
180-
181176
# Microsoft Translator API Key
182177
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
183178
OPENAI_MODEL = os.environ.get("OPENAI_MODEL", "gpt-4.1-2025-04-14")

0 commit comments

Comments
 (0)