This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 23140992902 Add possibility of removing extra translations missing in
en (#57060)
23140992902 is described below
commit 2314099290225765f53375891082bf45fd72524f
Author: Jarek Potiuk <[email protected]>
AuthorDate: Wed Oct 22 15:22:35 2025 +0200
Add possibility of removing extra translations missing in en (#57060)
Sometimes we rename/move translation - we did not have a tool to
remove such dangling translations. Now we have.
---
airflow-core/src/airflow/ui/public/i18n/README.md | 12 ++++
.../ui/public/i18n/locales/ar/components.json | 1 -
.../airflow/ui/public/i18n/locales/ar/dags.json | 3 +-
.../airflow/ui/public/i18n/locales/el/common.json | 1 -
.../ui/public/i18n/locales/es/components.json | 1 -
.../airflow/ui/public/i18n/locales/es/dags.json | 3 +-
.../airflow/ui/public/i18n/locales/fr/dags.json | 3 +-
.../ui/public/i18n/locales/he/components.json | 1 -
.../airflow/ui/public/i18n/locales/he/dags.json | 3 +-
.../airflow/ui/public/i18n/locales/hi/common.json | 3 -
.../ui/public/i18n/locales/hi/components.json | 1 -
.../src/airflow/ui/public/i18n/locales/hi/dag.json | 5 +-
.../airflow/ui/public/i18n/locales/hi/dags.json | 3 +-
.../airflow/ui/public/i18n/locales/hu/common.json | 2 -
.../ui/public/i18n/locales/hu/components.json | 1 -
.../airflow/ui/public/i18n/locales/hu/dags.json | 3 +-
.../ui/public/i18n/locales/it/components.json | 1 -
.../airflow/ui/public/i18n/locales/it/dags.json | 3 +-
.../ui/public/i18n/locales/nl/components.json | 1 -
.../airflow/ui/public/i18n/locales/nl/dags.json | 3 +-
.../ui/public/i18n/locales/pt/components.json | 1 -
.../airflow/ui/public/i18n/locales/pt/dags.json | 3 +-
.../ui/public/i18n/locales/tr/components.json | 1 -
.../airflow/ui/public/i18n/locales/tr/dags.json | 3 +-
.../ui/public/i18n/locales/zh-CN/components.json | 1 -
.../airflow/ui/public/i18n/locales/zh-CN/dags.json | 3 +-
dev/i18n/check_translations_completeness.py | 70 +++++++++++++++++++++-
27 files changed, 93 insertions(+), 43 deletions(-)
diff --git a/airflow-core/src/airflow/ui/public/i18n/README.md
b/airflow-core/src/airflow/ui/public/i18n/README.md
index 1d1c21f0a2a..e2fe51ff739 100644
--- a/airflow-core/src/airflow/ui/public/i18n/README.md
+++ b/airflow-core/src/airflow/ui/public/i18n/README.md
@@ -331,6 +331,18 @@ Adding missing translations (with `TODO: translate`
prefix):
uv run dev/i18n/check_translations_completeness.py --language <language_code>
--add-missing
```
+You can also remove extra translations from the language of your choice:
+
+```bash
+uv run dev/i18n/check_translations_completeness.py --language <language_code>
--remove-extra
+```
+
+Or from all languages:
+
+```bash
+uv run dev/i18n/check_translations_completeness.py --remove-extra
+```
+
The script is also added as a prek hook (manual) so that it can be run from
within `prek` and CI:
```bash
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/ar/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/ar/components.json
index 0b1c5d74aa9..df1b9d69d9b 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/ar/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/ar/components.json
@@ -120,7 +120,6 @@
"limitedList.clickToInteract": "انقر على علامة لتصفية Dags",
"limitedList.clickToOpenFull": "انقر \"+{{count}} المزيد\" لعرض القائمة
الكاملة",
"limitedList.copyPasteText": "يمكنك نسخ ولصق النص أعلاه",
- "limitedList.showingItems": "عرض {{count}} عنصرًا",
"logs": {
"file": "ملف",
"location": "سطر {{line}} في {{name}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/ar/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/ar/dags.json
index b7f8e60bbd8..12961f79af6 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/ar/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/ar/dags.json
@@ -20,8 +20,7 @@
"all": "الكل",
"paused": "متوقف"
},
- "runIdPatternFilter": "بحث تشغيلات الDag",
- "triggeringUserNameFilter": "البحث حسب المستخدم صاحب الاطلاق"
+ "runIdPatternFilter": "بحث تشغيلات الDag"
},
"ownerLink": "رابط المالك لـ{{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/el/common.json
b/airflow-core/src/airflow/ui/public/i18n/locales/el/common.json
index f3b725d02fd..eddc6a8f5a2 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/el/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/el/common.json
@@ -163,7 +163,6 @@
},
"tooltip": "Πατήστε {{hotkey}} για κύλιση προς τα {{direction}}"
},
- "seconds": "{{count}}δ",
"security": {
"actions": "Ενέργειες",
"permissions": "Δικαιώματα",
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/es/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/es/components.json
index 20f31033ee0..ad1edcd1b39 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/es/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/es/components.json
@@ -99,7 +99,6 @@
"limitedList.clickToInteract": "Haz clic en una etiqueta para filtrar Dags",
"limitedList.clickToOpenFull": "Haz clic en \"+{{count}} más\" para ver la
lista completa",
"limitedList.copyPasteText": "Puedes copiar y pegar el texto de arriba",
- "limitedList.showingItems": "Mostrando {{count}} elementos",
"logs": {
"file": "Archivo",
"location": "línea {{line}} en {{name}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/es/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/es/dags.json
index 7d93addccfc..353ae56534a 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/es/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/es/dags.json
@@ -20,8 +20,7 @@
"all": "Todos",
"paused": "Pausado"
},
- "runIdPatternFilter": "Buscar Ejecuciones de Dag",
- "triggeringUserNameFilter": "Buscar por usuario que activó"
+ "runIdPatternFilter": "Buscar Ejecuciones de Dag"
},
"ownerLink": "Enlace de Propietario para {{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/fr/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/fr/dags.json
index cd6b2c27356..b8a68745b81 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/fr/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/fr/dags.json
@@ -20,8 +20,7 @@
"all": "Tous",
"paused": "En pause"
},
- "runIdPatternFilter": "Rechercher des exécutions de Dag",
- "triggeringUserNameFilter": "Rechercher par utilisateur déclencheur"
+ "runIdPatternFilter": "Rechercher des exécutions de Dag"
},
"ownerLink": "Lien du propriétaire pour {{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/he/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/he/components.json
index 424d5f675b1..453f3819801 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/he/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/he/components.json
@@ -99,7 +99,6 @@
"limitedList.clickToInteract": "לחץ על תגית כדי לסנן Dags",
"limitedList.clickToOpenFull": "לחץ על \"+{{count}} נוספים\" כדי לפתוח את
התצוגה המלאה",
"limitedList.copyPasteText": "ניתן להעתיק ולהדביק את הטקסט שמעל",
- "limitedList.showingItems": "מציג {{count}} פריטים",
"logs": {
"file": "קובץ",
"location": "שורה {{line}} ב{{name}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/he/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/he/dags.json
index 15729c5f1e8..1b3b06d4bf6 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/he/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/he/dags.json
@@ -20,8 +20,7 @@
"all": "הכל",
"paused": "מושהה"
},
- "runIdPatternFilter": "חפש הרצת Dag",
- "triggeringUserNameFilter": "חפש לפי שם המשתמש המפעיל"
+ "runIdPatternFilter": "חפש הרצת Dag"
},
"ownerLink": "קישור בעלים ל-{{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/hi/common.json
b/airflow-core/src/airflow/ui/public/i18n/locales/hi/common.json
index a506b9198b8..1dcef4cb0cc 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/hi/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/hi/common.json
@@ -25,7 +25,6 @@
"dag_other": "डैग्स",
"dagDetails": {
"catchup": "पकड़ना",
- "concurrency": "समानांतरता",
"dagRunTimeout": "डैग रन टाइमआउट",
"defaultArgs": "डिफ़ॉल्ट तर्क",
"description": "विवरण",
@@ -188,8 +187,6 @@
"up_for_retry": "पुनः प्रयास के लिए",
"upstream_failed": "अपस्ट्रीम विफल"
},
- "switchToDarkMode": "डार्क मोड में स्विच करें",
- "switchToLightMode": "लाइट मोड में स्विच करें",
"table": {
"completedAt": "पर पूर्ण",
"createdAt": "पर बनाया",
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/hi/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/hi/components.json
index 4efe768e9be..d3caf8c34c5 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/hi/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/hi/components.json
@@ -92,7 +92,6 @@
"limitedList.clickToInteract": "टैग पर क्लिक करके Dags फ़िल्टर करें",
"limitedList.clickToOpenFull": "\"+{{count}} और\" पर क्लिक करके पूरी सूची
खोलें",
"limitedList.copyPasteText": "आप ऊपर दिए गए पाठ को कॉपी और पेस्ट कर सकते
हैं",
- "limitedList.showingItems": "{{count}} आइटम दिखाए जा रहे हैं",
"logs": {
"file": "फ़ाइल",
"location": "{{name}} में लाइन {{line}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/hi/dag.json
b/airflow-core/src/airflow/ui/public/i18n/locales/hi/dag.json
index 253ed958540..36e5d3adfca 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/hi/dag.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/hi/dag.json
@@ -40,7 +40,6 @@
"warning": "WARNING"
},
"navigation": {
- "jump": "जंप: Shift+{{arrow}}",
"navigation": "नेवीगेशन: {{arrow}}",
"toggleGroup": "ग्रुप टॉगल करें: Space"
},
@@ -64,9 +63,7 @@
},
"panel": {
"buttons": {
- "options": "विकल्प",
- "showGraph": "ग्राफ़ दिखाएं",
- "showGrid": "ग्रिड दिखाएं"
+ "options": "विकल्प"
},
"dagRuns": {
"label": "डैग रन्स की संख्या"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/hi/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/hi/dags.json
index 54cac115336..74c112d0dfb 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/hi/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/hi/dags.json
@@ -20,8 +20,7 @@
"all": "सभी",
"paused": "रोका गया"
},
- "runIdPatternFilter": "डैग रन्स खोजें",
- "triggeringUserNameFilter": "ट्रिगर करने वाले उपयोगकर्ता द्वारा खोजें"
+ "runIdPatternFilter": "डैग रन्स खोजें"
},
"ownerLink": "{{owner}} के लिए स्वामी लिंक",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/hu/common.json
b/airflow-core/src/airflow/ui/public/i18n/locales/hu/common.json
index f26a1540f08..084a73f3c44 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/hu/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/hu/common.json
@@ -189,8 +189,6 @@
"up_for_retry": "Újrapróbálkozásra vár",
"upstream_failed": "Előfeltétel sikertelen"
},
- "switchToDarkMode": "Váltás sötét módra",
- "switchToLightMode": "Váltás világos módra",
"table": {
"completedAt": "Befejezve ekkor",
"createdAt": "Létrehozva ekkor",
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/hu/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/hu/components.json
index ded34869e9d..014e8242b43 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/hu/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/hu/components.json
@@ -92,7 +92,6 @@
"limitedList.clickToInteract": "Kattintson egy címkére a Dag-ok szűréséhez",
"limitedList.clickToOpenFull": "Kattintson a \"+{{count}} további\" gombra a
teljes nézethez",
"limitedList.copyPasteText": "A fenti szöveg másolható és beilleszthető",
- "limitedList.showingItems": "{{count}} elem megjelenítése",
"logs": {
"file": "Fájl",
"location": "{{name}} fájl {{line}}. sora"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/hu/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/hu/dags.json
index 93a925c7c4d..6394092f747 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/hu/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/hu/dags.json
@@ -20,8 +20,7 @@
"all": "Összes",
"paused": "Szüneteltetett"
},
- "runIdPatternFilter": "Dag futások keresése",
- "triggeringUserNameFilter": "Keresés indító felhasználó alapján"
+ "runIdPatternFilter": "Dag futások keresése"
},
"ownerLink": "Tulajdonosi hivatkozás: {{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/it/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/it/components.json
index e8903d951a2..3978d69905b 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/it/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/it/components.json
@@ -106,7 +106,6 @@
"limitedList.clickToInteract": "Fai clic su un'etichetta per filtrare i Dag",
"limitedList.clickToOpenFull": "Fai clic su \"+{{count}} altro\" per
visualizzare tutto",
"limitedList.copyPasteText": "Puoi copiare e incollare il testo sopra",
- "limitedList.showingItems": "Visualizzazione di {{count}} elementi",
"logs": {
"file": "File",
"location": "linea {{line}} in {{name}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/it/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/it/dags.json
index 0cb4eac8ac7..6c6ab552ddc 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/it/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/it/dags.json
@@ -20,8 +20,7 @@
"all": "Tutti",
"paused": "In Pausa"
},
- "runIdPatternFilter": "Cercare Dag Runs",
- "triggeringUserNameFilter": "Cercare per Utente che ha Attivato"
+ "runIdPatternFilter": "Cercare Dag Runs"
},
"ownerLink": "Link dell'Owner per {{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/nl/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/nl/components.json
index 83785a7112a..9997e067172 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/nl/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/nl/components.json
@@ -92,7 +92,6 @@
"limitedList.clickToInteract": "Klik op een label om Dags te filteren",
"limitedList.clickToOpenFull": "Klik op \"+{{count}} meer\" om de volledige
lijst te openen",
"limitedList.copyPasteText": "Je kunt de bovenstaande tekst kopiëren en
plakken",
- "limitedList.showingItems": "{{count}} items weergegeven",
"logs": {
"file": "Bestand",
"location": "regel {{line}} in {{name}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/nl/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/nl/dags.json
index b57b2290a92..23fdb3bc2d4 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/nl/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/nl/dags.json
@@ -20,8 +20,7 @@
"all": "Alles",
"paused": "Gepauzeerd"
},
- "runIdPatternFilter": "Zoek Dag Runs",
- "triggeringUserNameFilter": "Zoek op Triggering User"
+ "runIdPatternFilter": "Zoek Dag Runs"
},
"ownerLink": "Eigenaarslink voor {{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/pt/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/pt/components.json
index ae83855677a..5b30826d8b7 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/pt/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/pt/components.json
@@ -106,7 +106,6 @@
"limitedList.clickToInteract": "Clique em uma etiqueta para filtrar os Dags",
"limitedList.clickToOpenFull": "Clique em \"+{{count}} mais\" para ver a
lista completa",
"limitedList.copyPasteText": "Você pode copiar e colar o texto acima",
- "limitedList.showingItems": "Exibindo {{count}} itens",
"logs": {
"file": "Arquivo",
"location": "linha {{line}} em {{name}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/pt/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/pt/dags.json
index 683cf090a8b..6ac6422c19e 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/pt/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/pt/dags.json
@@ -20,8 +20,7 @@
"all": "Todos",
"paused": "Pausado"
},
- "runIdPatternFilter": "Pesquisar Execuções de DAG",
- "triggeringUserNameFilter": "Pesquisar por Usuário que Disparou"
+ "runIdPatternFilter": "Pesquisar Execuções de DAG"
},
"ownerLink": "Link do Proprietário para {{owner}}",
"runAndTaskActions": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json
index 69d80a7672b..0a32afc28b3 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json
@@ -92,7 +92,6 @@
"limitedList.clickToInteract": "Bir etikete tıklayarak Dag'leri filtreleyin",
"limitedList.clickToOpenFull": "\"+{{count}} daha\" tıklayarak tam görünümü
açın",
"limitedList.copyPasteText": "Yukarıdaki metni kopyalayıp
yapıştırabilirsiniz",
- "limitedList.showingItems": "{{count}} öğe gösteriliyor",
"logs": {
"file": "Dosya",
"location": "{{name}} içinde satır {{line}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/tr/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/tr/dags.json
index 2974a4d4f48..8c6d0213052 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/tr/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/tr/dags.json
@@ -20,8 +20,7 @@
"all": "Tümü",
"paused": "Duraklatılmış"
},
- "runIdPatternFilter": "Dag Çalıştırmalarında Ara",
- "triggeringUserNameFilter": "Tetikleyen Kullanıcıya Göre Ara"
+ "runIdPatternFilter": "Dag Çalıştırmalarında Ara"
},
"ownerLink": "{{owner}} için sahip bağlantısı",
"runAndTaskActions": {
diff --git
a/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/components.json
b/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/components.json
index e7255110e8f..652011da2be 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/components.json
@@ -90,7 +90,6 @@
"limitedList.clickToInteract": "点击标签以筛选 DAGs",
"limitedList.clickToOpenFull": "点击 \"+{{count}} 更多\" 打开完整视图",
"limitedList.copyPasteText": "你可以复制并粘贴上方文本",
- "limitedList.showingItems": "显示 {{count}} 项",
"logs": {
"file": "文件",
"location": "第 {{line}} 行,位于 {{name}}"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/dags.json
b/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/dags.json
index b8e4f0929d4..90ba4ff9588 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/dags.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/zh-CN/dags.json
@@ -20,8 +20,7 @@
"all": "全部",
"paused": "暂停"
},
- "runIdPatternFilter": "搜索 Dag 执行",
- "triggeringUserNameFilter": "搜索触发用户名称"
+ "runIdPatternFilter": "搜索 Dag 执行"
},
"ownerLink": "拥有者 {{owner}} 的地址",
"runAndTaskActions": {
diff --git a/dev/i18n/check_translations_completeness.py
b/dev/i18n/check_translations_completeness.py
index e3c2e0d8418..5972087b9ff 100755
--- a/dev/i18n/check_translations_completeness.py
+++ b/dev/i18n/check_translations_completeness.py
@@ -458,7 +458,13 @@ def print_translation_progress(console, locale_files,
missing_counts, summary):
default=False,
help="Add missing translations for all languages except English, prefixed
with 'TODO: translate:'.",
)
-def cli(language: str | None = None, add_missing: bool = False):
[email protected](
+ "--remove-extra",
+ is_flag=True,
+ default=False,
+ help="Remove extra translations that are present in the language but
missing in English.",
+)
+def cli(language: str | None = None, add_missing: bool = False, remove_extra:
bool = False):
locale_files = get_locale_files()
console = Console(force_terminal=True, color_system="auto")
print_locale_file_table(locale_files, console, language)
@@ -481,6 +487,22 @@ def cli(language: str | None = None, add_missing: bool =
False):
add_missing_translations(lf.locale, filtered_summary, console)
# After adding, re-run the summary for all languages
summary, missing_counts = compare_keys(get_locale_files(), console)
+ if remove_extra and language != "en":
+ # Loop through all languages except 'en' and remove extra translations
+ if language:
+ language_files = [lf for lf in locale_files if lf.locale ==
language]
+ else:
+ language_files = [lf for lf in locale_files if lf.locale != "en"]
+ for lf in language_files:
+ filtered_summary = {}
+ for filename, diff in summary.items():
+ filtered_summary[filename] = LocaleSummary(
+ missing_keys={lf.locale: diff.missing_keys.get(lf.locale,
[])},
+ extra_keys={lf.locale: diff.extra_keys.get(lf.locale, [])},
+ )
+ remove_extra_translations(lf.locale, filtered_summary, console)
+ # After removing, re-run the summary for all languages
+ summary, missing_counts = compare_keys(get_locale_files(), console)
if language:
locales = [lf.locale for lf in locale_files]
if language not in locales:
@@ -594,5 +616,51 @@ def add_missing_translations(language: str, summary:
dict[str, LocaleSummary], c
console.print(f"[green]Added missing translations to
{lang_path}[/green]")
+def remove_extra_translations(language: str, summary: dict[str,
LocaleSummary], console: Console):
+ """
+ Remove extra translations for the selected language.
+
+ Removes keys that are present in the language file but missing in the
English file.
+ """
+ for filename, diff in summary.items():
+ extra_keys = set(diff.extra_keys.get(language, []))
+ if not extra_keys:
+ continue
+ lang_path = LOCALES_DIR / language / filename
+ try:
+ lang_data = load_json(lang_path)
+ except Exception as e:
+ console.print(f"[yellow]Failed to load {language} file
{lang_path}: {e}[/yellow]")
+ continue
+
+ # Helper to recursively remove extra keys
+ def remove_keys(dst, prefix=""):
+ keys_to_remove = []
+ for k, v in list(dst.items()):
+ full_key = f"{prefix}.{k}" if prefix else k
+ if full_key in extra_keys:
+ keys_to_remove.append(k)
+ elif isinstance(v, dict):
+ remove_keys(v, full_key)
+ # Remove empty dictionaries after recursion
+ if not v:
+ keys_to_remove.append(k)
+ for k in keys_to_remove:
+ del dst[k]
+
+ remove_keys(lang_data)
+
+ def natural_key_sort(obj):
+ if isinstance(obj, dict):
+ return {k: natural_key_sort(obj[k]) for k in sorted(obj)}
+ return obj
+
+ lang_data = natural_key_sort(lang_data)
+ with open(lang_path, "w", encoding="utf-8") as f:
+ json.dump(lang_data, f, ensure_ascii=False, indent=2)
+ f.write("\n") # Ensure newline at the end of the file
+ console.print(f"[green]Removed {len(extra_keys)} extra translations
from {lang_path}[/green]")
+
+
if __name__ == "__main__":
cli()