Skip to content
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
12 changes: 12 additions & 0 deletions airflow-core/src/airflow/ui/public/i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@
"limitedList.clickToInteract": "انقر على علامة لتصفية Dags",
"limitedList.clickToOpenFull": "انقر \"+{{count}} المزيد\" لعرض القائمة الكاملة",
"limitedList.copyPasteText": "يمكنك نسخ ولصق النص أعلاه",
"limitedList.showingItems": "عرض {{count}} عنصرًا",
"logs": {
"file": "ملف",
"location": "سطر {{line}} في {{name}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
"all": "الكل",
"paused": "متوقف"
},
"runIdPatternFilter": "بحث تشغيلات الDag",
"triggeringUserNameFilter": "البحث حسب المستخدم صاحب الاطلاق"
"runIdPatternFilter": "بحث تشغيلات الDag"
},
"ownerLink": "رابط المالك لـ{{owner}}",
"runAndTaskActions": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@
},
"tooltip": "Πατήστε {{hotkey}} για κύλιση προς τα {{direction}}"
},
"seconds": "{{count}}δ",
"security": {
"actions": "Ενέργειες",
"permissions": "Δικαιώματα",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@
"limitedList.clickToInteract": "לחץ על תגית כדי לסנן Dags",
"limitedList.clickToOpenFull": "לחץ על \"+{{count}} נוספים\" כדי לפתוח את התצוגה המלאה",
"limitedList.copyPasteText": "ניתן להעתיק ולהדביק את הטקסט שמעל",
"limitedList.showingItems": "מציג {{count}} פריטים",
"logs": {
"file": "קובץ",
"location": "שורה {{line}} ב{{name}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
"all": "הכל",
"paused": "מושהה"
},
"runIdPatternFilter": "חפש הרצת Dag",
"triggeringUserNameFilter": "חפש לפי שם המשתמש המפעיל"
"runIdPatternFilter": "חפש הרצת Dag"
},
"ownerLink": "קישור בעלים ל-{{owner}}",
"runAndTaskActions": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"dag_other": "डैग्स",
"dagDetails": {
"catchup": "पकड़ना",
"concurrency": "समानांतरता",
"dagRunTimeout": "डैग रन टाइमआउट",
"defaultArgs": "डिफ़ॉल्ट तर्क",
"description": "विवरण",
Expand Down Expand Up @@ -188,8 +187,6 @@
"up_for_retry": "पुनः प्रयास के लिए",
"upstream_failed": "अपस्ट्रीम विफल"
},
"switchToDarkMode": "डार्क मोड में स्विच करें",
"switchToLightMode": "लाइट मोड में स्विच करें",
"table": {
"completedAt": "पर पूर्ण",
"createdAt": "पर बनाया",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"limitedList.clickToInteract": "टैग पर क्लिक करके Dags फ़िल्टर करें",
"limitedList.clickToOpenFull": "\"+{{count}} और\" पर क्लिक करके पूरी सूची खोलें",
"limitedList.copyPasteText": "आप ऊपर दिए गए पाठ को कॉपी और पेस्ट कर सकते हैं",
"limitedList.showingItems": "{{count}} आइटम दिखाए जा रहे हैं",
"logs": {
"file": "फ़ाइल",
"location": "{{name}} में लाइन {{line}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"warning": "WARNING"
},
"navigation": {
"jump": "जंप: Shift+{{arrow}}",
"navigation": "नेवीगेशन: {{arrow}}",
"toggleGroup": "ग्रुप टॉगल करें: Space"
},
Expand All @@ -64,9 +63,7 @@
},
"panel": {
"buttons": {
"options": "विकल्प",
"showGraph": "ग्राफ़ दिखाएं",
"showGrid": "ग्रिड दिखाएं"
"options": "विकल्प"
},
"dagRuns": {
"label": "डैग रन्स की संख्या"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
"all": "सभी",
"paused": "रोका गया"
},
"runIdPatternFilter": "डैग रन्स खोजें",
"triggeringUserNameFilter": "ट्रिगर करने वाले उपयोगकर्ता द्वारा खोजें"
"runIdPatternFilter": "डैग रन्स खोजें"
},
"ownerLink": "{{owner}} के लिए स्वामी लिंक",
"runAndTaskActions": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
"limitedList.clickToInteract": "点击标签以筛选 DAGs",
"limitedList.clickToOpenFull": "点击 \"+{{count}} 更多\" 打开完整视图",
"limitedList.copyPasteText": "你可以复制并粘贴上方文本",
"limitedList.showingItems": "显示 {{count}} 项",
"logs": {
"file": "文件",
"location": "第 {{line}} 行,位于 {{name}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
"all": "全部",
"paused": "暂停"
},
"runIdPatternFilter": "搜索 Dag 执行",
"triggeringUserNameFilter": "搜索触发用户名称"
"runIdPatternFilter": "搜索 Dag 执行"
},
"ownerLink": "拥有者 {{owner}} 的地址",
"runAndTaskActions": {
Expand Down
70 changes: 69 additions & 1 deletion dev/i18n/check_translations_completeness.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
@click.option(
"--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)
Expand All @@ -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:
Expand Down Expand Up @@ -594,5 +616,51 @@ def natural_key_sort(obj):
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()
Loading