This is an automated email from the ASF dual-hosted git repository.

bbovenzi 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 2c02285ae9b Add translation completeness lint (#51166)
2c02285ae9b is described below

commit 2c02285ae9b9586ecc32e93653be186519db1490
Author: Guan Ming(Wesley) Chiu <[email protected]>
AuthorDate: Thu May 29 23:45:12 2025 +0800

    Add translation completeness lint (#51166)
---
 airflow-core/src/airflow/ui/eslint.config.js       |   2 +
 airflow-core/src/airflow/ui/rules/i18n.js          | 167 +++++++++++++++++++++
 .../src/airflow/ui/src/i18n/locales/de/common.json | 128 ++++++++--------
 .../airflow/ui/src/i18n/locales/de/dashboard.json  |  34 ++---
 .../src/airflow/ui/src/i18n/locales/en/common.json | 130 ++++++++--------
 .../airflow/ui/src/i18n/locales/en/dashboard.json  |  34 ++---
 .../src/airflow/ui/src/i18n/locales/ko/common.json | 102 ++++++-------
 .../airflow/ui/src/i18n/locales/ko/dashboard.json  |  34 ++---
 .../src/airflow/ui/src/i18n/locales/nl/common.json | 102 ++++++-------
 .../airflow/ui/src/i18n/locales/nl/dashboard.json  |  34 ++---
 .../airflow/ui/src/i18n/locales/zh-TW/common.json  |  86 +++++------
 .../ui/src/i18n/locales/zh-TW/dashboard.json       |  28 ++--
 12 files changed, 525 insertions(+), 356 deletions(-)

diff --git a/airflow-core/src/airflow/ui/eslint.config.js 
b/airflow-core/src/airflow/ui/eslint.config.js
index e6fc770f1f7..e9edfa5ace7 100644
--- a/airflow-core/src/airflow/ui/eslint.config.js
+++ b/airflow-core/src/airflow/ui/eslint.config.js
@@ -21,6 +21,7 @@
  * @import { FlatConfig } from "@typescript-eslint/utils/ts-eslint";
  */
 import { coreRules } from "./rules/core.js";
+import { i18nRules } from "./rules/i18n.js";
 import { i18nextRules } from "./rules/i18next.js";
 import { perfectionistRules } from "./rules/perfectionist.js";
 import { prettierRules } from "./rules/prettier.js";
@@ -46,4 +47,5 @@ export default /** @type {const} @satisfies 
{ReadonlyArray<FlatConfig.Config>} *
   stylisticRules,
   unicornRules,
   i18nextRules,
+  i18nRules,
 ]);
diff --git a/airflow-core/src/airflow/ui/rules/i18n.js 
b/airflow-core/src/airflow/ui/rules/i18n.js
new file mode 100644
index 00000000000..f96e71b37b7
--- /dev/null
+++ b/airflow-core/src/airflow/ui/rules/i18n.js
@@ -0,0 +1,167 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+export const i18nNamespace = "i18n";
+
+/**
+ * Extract all nested keys from translation object
+ * @param {Record<string, any>} obj
+ * @param {string} [prefix]
+ * @returns {string[]}
+ */
+const getKeys = (obj, prefix = "") => {
+  if (Array.isArray(obj)) {
+    return [];
+  }
+
+  return Object.keys(obj).flatMap((key) => {
+    const newPrefix = prefix ? `${prefix}.${key}` : key;
+    const value = obj[key];
+
+    if (typeof value === "object" && value !== null && !Array.isArray(value)) {
+      return [newPrefix, ...getKeys(value, newPrefix)];
+    }
+
+    return [newPrefix];
+  });
+};
+
+// Path to locales directory
+const localesDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 
"../src/i18n/locales");
+
+// Default language (English) as reference
+const defaultLanguage = "en";
+const defaultLanguageKeys = /** @type {Record<string, string[]>} */ ({});
+const defaultLanguageDir = path.join(localesDir, defaultLanguage);
+
+// Load translation keys from default language files
+fs.readdirSync(defaultLanguageDir)
+  .filter((file) => file.endsWith(".json"))
+  .forEach((jsonFile) => {
+    const ns = path.basename(jsonFile, ".json");
+    const filePath = path.join(defaultLanguageDir, jsonFile);
+    const fileContent = fs.readFileSync(filePath, "utf8");
+    const parsedJson = JSON.parse(fileContent);
+
+    if (typeof parsedJson === "object" && parsedJson !== null && 
!Array.isArray(parsedJson)) {
+      defaultLanguageKeys[ns] = getKeys(parsedJson);
+    }
+  });
+
+export const i18nPlugin = {
+  files: ["**/i18n/locales/**/*.json"],
+  rules: {
+    "check-translation-completeness": {
+      /** @param 
{import('@typescript-eslint/utils').TSESLint.RuleContext<'missingKeys' | 
'fileError', []>} context */
+      create(context) {
+        return {
+          /** @param {import('@typescript-eslint/utils').TSESTree.Program} 
node */
+          Program(node) {
+            // Get language code and namespace from file path
+            const currentFilePath = context.filename;
+            const langCode = path.dirname(path.relative(localesDir, 
currentFilePath));
+            const namespace = path.basename(currentFilePath, ".json");
+
+            if (langCode === defaultLanguage) {
+              return;
+            }
+
+            // Get keys from current file
+            const referenceKeys = defaultLanguageKeys[namespace];
+            let langKeys;
+
+            try {
+              const parsedLangJson = JSON.parse(context.sourceCode.text);
+
+              if (
+                typeof parsedLangJson === "object" &&
+                parsedLangJson !== null &&
+                !Array.isArray(parsedLangJson)
+              ) {
+                langKeys = getKeys(parsedLangJson);
+              } else {
+                context.report({
+                  data: { error: "Invalid JSON object.", filePath: 
currentFilePath },
+                  messageId: "fileError",
+                  node,
+                });
+
+                return;
+              }
+            } catch (error) {
+              const message = error instanceof Error ? error.message : 
String(error);
+
+              context.report({
+                data: { error: message, filePath: currentFilePath },
+                messageId: "fileError",
+                node,
+              });
+
+              return;
+            }
+
+            // Check for missing translations
+            const langKeysSet = new Set(langKeys);
+            const missingKeys = referenceKeys.filter((key) => 
!langKeysSet.has(key));
+
+            if (missingKeys.length > 0) {
+              context.report({
+                data: { keys: missingKeys.join(", "), lang: langCode, 
namespace },
+                messageId: "missingKeys",
+                node,
+              });
+            }
+          },
+        };
+      },
+      meta: {
+        docs: {
+          category: "Best Practices",
+          description: "Ensures non-default lang files have all keys from 
default.",
+          recommended: "warn",
+        },
+        messages: {
+          fileError: "Failed to read/parse {{filePath}}. Error: {{error}}",
+          missingKeys: "Lang '{{lang}}' (namespace: {{namespace}}) missing 
keys: {{keys}}",
+        },
+        type: "problem",
+      },
+    },
+  },
+};
+
+/** @type {import("@typescript-eslint/utils/ts-eslint").FlatConfig.Config} */
+export const i18nRules = {
+  files: ["**/i18n/locales/**/*.json"],
+  plugins: {
+    [i18nNamespace]: i18nPlugin,
+  },
+  rules: {
+    "@typescript-eslint/no-unused-expressions": "off",
+    [`${i18nNamespace}/check-translation-completeness`]: "warn",
+    "no-unused-expressions": "off",
+  },
+};
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/de/common.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/de/common.json
index 7fc04de18eb..04b2403506a 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/de/common.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/de/common.json
@@ -1,92 +1,92 @@
 {
+  "admin": {
+    "Config": "Konfiguration",
+    "Connections": "Verbindungen",
+    "Plugins": "Plug-ins",
+    "Pools": "Pools",
+    "Providers": "Providers",
+    "Variables": "Variablen"
+  },
+  "assetEvent_one": "Ereignis zu Datensets (Asset)",
+  "assetEvent_other": "Ereignisse zu Datensets (Asset)",
+  "browse": {
+    "auditLog": "Prüf-Log",
+    "xcoms": "Task Kommunikation (XComs)"
+  },
+  "dagRun_one": "Dag Lauf",
+  "dagRun_other": "Dag Läufe",
   "defaultToGraphView": "Graph-Ansicht als Standard",
   "defaultToGridView": "Gitter-Ansicht als Standard",
+  "docs": {
+    "documentation": "Dokumentation",
+    "githubRepo": "GitHub Ablage",
+    "restApiReference": "REST API Referenz"
+  },
   "logout": "Abmelden",
   "logoutConfirmation": "Sie sind dabei sich von dem System abzumelden.",
-  "switchToDarkMode": "Zum Dunkelmodus wechseln",
-  "switchToLightMode": "Zum Hellmodus wechseln",
-  "timezone": "Zeitzone",
-  "user": "Benutzer",
-  "selectLanguage": "Sprache wählen",
+  "modal": {
+    "cancel": "Abbrechen",
+    "confirm": "Bestätigen"
+  },
   "nav": {
-    "home": "Start",
+    "admin": "Verwaltung",
     "assets": "Datensets (Assets)",
     "browse": "Browsen",
-    "admin": "Verwaltung",
-    "security": "Sicherheit",
     "docs": "Doku",
-    "plugins": "Plug-ins"
-  },
-  "browse": {
-    "auditLog": "Prüf-Log",
-    "xcoms": "Task Kommunikation (XComs)"
+    "home": "Start",
+    "plugins": "Plug-ins",
+    "security": "Sicherheit"
   },
-  "admin": {
-    "Variables": "Variablen",
-    "Pools": "Pools",
-    "Providers": "Providers",
-    "Plugins": "Plug-ins",
-    "Connections": "Verbindungen",
-    "Config": "Konfiguration"
+  "pools": {
+    "deferred": "Delegiert",
+    "open": "Frei",
+    "pools_one": "Pool",
+    "pools_other": "Pools",
+    "queued": "Wartend",
+    "running": "Laufend",
+    "scheduled": "Geplant"
   },
   "security": {
-    "users": "Benutzer",
-    "roles": "Rollen",
     "actions": "Aktionen",
+    "permissions": "Berechtigungen",
     "resources": "Ressourcen",
-    "permissions": "Berechtigungen"
-  },
-  "timeRange": {
-    "duration": "Laufzeit",
-    "lastHour": "Letzte Stunde",
-    "last12Hours": "Letzte 12 Stunden",
-    "last24Hours": "Letzte 24 Stunden",
-    "pastWeek": "Letzte Woche"
-  },
-  "docs": {
-    "documentation": "Dokumentation",
-    "githubRepo": "GitHub Ablage",
-    "restApiReference": "REST API Referenz"
+    "roles": "Rollen",
+    "users": "Benutzer"
   },
+  "selectLanguage": "Sprache wählen",
   "states": {
-    "queued": "Wartend",
-    "running": "Laufend",
-    "success": "Erfolgreich",
+    "deferred": "Delegiert",
     "failed": "Fehlgeschlagen",
-    "skipped": "Übersprungen",
+    "no_status": "Kein Status",
+    "queued": "Wartend",
     "removed": "Entfernt",
-    "scheduled": "Geplant",
     "restarting": "Im Neustart",
-    "up_for_retry": "Wartet auf neuen Versuch",
+    "running": "Laufend",
+    "scheduled": "Geplant",
+    "skipped": "Übersprungen",
+    "success": "Erfolgreich",
     "up_for_reschedule": "Wartet auf Neuplanung",
-    "upstream_failed": "Vorgelagerte fehlgeschlagen",
-    "deferred": "Delegiert",
-    "no_status": "Kein Status"
+    "up_for_retry": "Wartet auf neuen Versuch",
+    "upstream_failed": "Vorgelagerte fehlgeschlagen"
   },
-  "dagRun_one": "Dag Lauf",
-  "dagRun_other": "Dag Läufe",
+  "switchToDarkMode": "Zum Dunkelmodus wechseln",
+  "switchToLightMode": "Zum Hellmodus wechseln",
   "taskInstance_one": "Task Instanz",
   "taskInstance_other": "Task Instanzen",
-  "assetEvent_one": "Ereignis zu Datensets (Asset)",
-  "assetEvent_other": "Ereignisse zu Datensets (Asset)",
-  "triggered": "Angestoßen",
-  "pools": {
-    "open": "Frei",
-    "running": "Laufend",
-    "queued": "Wartend",
-    "scheduled": "Geplant",
-    "deferred": "Delegiert",
-    "pools_one": "Pool",
-    "pools_other": "Pools"
-  },
-  "modal": {
-    "cancel": "Abbrechen",
-    "confirm": "Bestätigen"
+  "timeRange": {
+    "duration": "Laufzeit",
+    "last12Hours": "Letzte 12 Stunden",
+    "last24Hours": "Letzte 24 Stunden",
+    "lastHour": "Letzte Stunde",
+    "pastWeek": "Letzte Woche"
   },
+  "timezone": "Zeitzone",
   "timezoneModal": {
-    "title": "Auswahl der Zeitzone",
-    "placeholder": "Wählen Sie eine Zeitzone",
     "current-timezone": "Aktuelle Zeit in",
+    "placeholder": "Wählen Sie eine Zeitzone",
+    "title": "Auswahl der Zeitzone",
     "utc": "UTC (Koordinierte Weltzeit)"
-  }
+  },
+  "triggered": "Angestoßen",
+  "user": "Benutzer"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/de/dashboard.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/de/dashboard.json
index 53e27af44aa..dc53117a11b 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/de/dashboard.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/de/dashboard.json
@@ -1,38 +1,38 @@
 {
-  "welcome": "Willkommen",
-  "stats": {
-    "stats": "Statistiken",
-    "failedDags": "Fehlgeschlagene Dags",
-    "runningDags": "Laufende Dags",
-    "activeDags": "Aktive Dags",
-    "queuedDags": "Dags in Warteschlange"
-  },
+  "group": "Gruppe",
   "health": {
+    "dagProcessor": "Dag Prozessor",
     "health": "System-Gesundheit",
+    "healthy": "Gut",
+    "lastHeartbeat": "Letztes Lebenszeichen",
     "metaDatabase": "Meta-Datenbank",
     "scheduler": "Planer",
-    "triggerer": "Triggerer",
-    "dagProcessor": "Dag Prozessor",
     "status": "Status",
-    "lastHeartbeat": "Letztes Lebenszeichen",
-    "healthy": "Gut",
+    "triggerer": "Triggerer",
     "unhealthy": "Nicht in Ordnung"
   },
+  "history": "Historie",
   "importErrors": {
-    "searchByFile": "Nach Datei suchen",
     "dagImportError_one": "Fehler beim Laden des Dags",
     "dagImportError_other": "Fehler beim Laden der Dags",
+    "searchByFile": "Nach Datei suchen",
     "timestamp": "Zeitstempel"
   },
-  "poolSlots": "Pool Belegung",
   "managePools": "Pools verwalten",
-  "history": "Historie",
+  "noAssetEvents": "Keine Ereignisse zu Datensets vorliegend.",
+  "poolSlots": "Pool Belegung",
   "sortBy": {
     "newestFirst": "Neueste zuerst",
     "oldestFirst": "Älteste zuerst"
   },
   "source": "Quellcode",
-  "group": "Gruppe",
+  "stats": {
+    "activeDags": "Aktive Dags",
+    "failedDags": "Fehlgeschlagene Dags",
+    "queuedDags": "Dags in Warteschlange",
+    "runningDags": "Laufende Dags",
+    "stats": "Statistiken"
+  },
   "uri": "Uri",
-  "noAssetEvents": "Keine Ereignisse zu Datensets vorliegend."
+  "welcome": "Willkommen"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json
index 7cc03e6b1bb..b1a3a0d0c42 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json
@@ -1,93 +1,93 @@
 {
+  "admin": {
+    "Config": "Config",
+    "Connections": "Connections",
+    "Plugins": "Plugins",
+    "Pools": "Pools",
+    "Providers": "Providers",
+    "Variables": "Variables"
+  },
+  "assetEvent_one": "Asset Event",
+  "assetEvent_other": "Asset Events",
+  "browse": {
+    "auditLog": "Audit Log",
+    "xcoms": "XComs"
+  },
+  "dagRun_one": "Dag Run",
+  "dagRun_other": "Dag Runs",
   "defaultToGraphView": "Default to graph view",
   "defaultToGridView": "Default to grid view",
+  "docs": {
+    "documentation": "Documentation",
+    "githubRepo": "GitHub Repo",
+    "restApiReference": "REST API Reference"
+  },
   "logout": "Logout",
   "logoutConfirmation": "You are about to logout from the application.",
-  "switchToDarkMode": "Switch to Dark Mode",
-  "switchToLightMode": "Switch to Light Mode",
-  "timezone": "timezone",
-  "user": "User",
-  "selectLanguage": "Select Language",
+  "modal": {
+    "cancel": "Cancel",
+    "confirm": "Confirm"
+  },
   "nav": {
-    "home": "Home",
-    "dags": "Dags",
+    "admin": "Admin",
     "assets": "Assets",
     "browse": "Browse",
-    "admin": "Admin",
-    "security": "Security",
+    "dags": "Dags",
     "docs": "Docs",
-    "plugins": "Plugins"
-  },
-  "browse": {
-    "auditLog": "Audit Log",
-    "xcoms": "XComs"
+    "home": "Home",
+    "plugins": "Plugins",
+    "security": "Security"
   },
-  "admin": {
-    "Variables": "Variables",
-    "Pools": "Pools",
-    "Providers": "Providers",
-    "Plugins": "Plugins",
-    "Connections": "Connections",
-    "Config": "Config"
+  "pools": {
+    "deferred": "Deferred",
+    "open": "Open",
+    "pools_one": "pool",
+    "pools_other": "pools",
+    "queued": "Queued",
+    "running": "Running",
+    "scheduled": "Scheduled"
   },
   "security": {
-    "users": "Users",
-    "roles": "Roles",
     "actions": "Actions",
+    "permissions": "Permissions",
     "resources": "Resources",
-    "permissions": "Permissions"
-  },
-  "timeRange": {
-    "duration": "Duration",
-    "lastHour": "Last Hour",
-    "last12Hours": "Last 12 Hours",
-    "last24Hours": "Last 24 Hours",
-    "pastWeek": "Past Week"
-  },
-  "docs": {
-    "documentation": "Documentation",
-    "githubRepo": "GitHub Repo",
-    "restApiReference": "REST API Reference"
+    "roles": "Roles",
+    "users": "Users"
   },
+  "selectLanguage": "Select Language",
   "states": {
-    "queued": "Queued",
-    "running": "Running",
-    "success": "Success",
+    "deferred": "Deferred",
     "failed": "Failed",
-    "skipped": "Skipped",
+    "no_status": "No Status",
+    "queued": "Queued",
     "removed": "Removed",
-    "scheduled": "Scheduled",
     "restarting": "Restarting",
-    "up_for_retry": "Up For Retry",
+    "running": "Running",
+    "scheduled": "Scheduled",
+    "skipped": "Skipped",
+    "success": "Success",
     "up_for_reschedule": "Up For Reschedule",
-    "upstream_failed": "Upstream Failed",
-    "deferred": "Deferred",
-    "no_status": "No Status"
+    "up_for_retry": "Up For Retry",
+    "upstream_failed": "Upstream Failed"
   },
-  "dagRun_one": "Dag Run",
-  "dagRun_other": "Dag Runs",
+  "switchToDarkMode": "Switch to Dark Mode",
+  "switchToLightMode": "Switch to Light Mode",
   "taskInstance_one": "Task Instance",
   "taskInstance_other": "Task Instances",
-  "assetEvent_one": "Asset Event",
-  "assetEvent_other": "Asset Events",
-  "triggered": "Triggered",
-  "pools": {
-    "open": "Open",
-    "running": "Running",
-    "queued": "Queued",
-    "scheduled": "Scheduled",
-    "deferred": "Deferred",
-    "pools_one": "pool",
-    "pools_other": "pools"
-  },
-  "modal": {
-    "cancel": "Cancel",
-    "confirm": "Confirm"
+  "timeRange": {
+    "duration": "Duration",
+    "last12Hours": "Last 12 Hours",
+    "last24Hours": "Last 24 Hours",
+    "lastHour": "Last Hour",
+    "pastWeek": "Past Week"
   },
+  "timezone": "timezone",
   "timezoneModal": {
-    "title": "Select Timezone",
-    "placeholder": "Select a timezone",
     "current-timezone": "Current time in",
+    "placeholder": "Select a timezone",
+    "title": "Select Timezone",
     "utc": "UTC (Coordinated Universal Time)"
-  }
+  },
+  "triggered": "Triggered",
+  "user": "User"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/dashboard.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/en/dashboard.json
index 2c24501203b..3b945abbb45 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/en/dashboard.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/dashboard.json
@@ -1,38 +1,38 @@
 {
-  "welcome": "Welcome",
-  "stats": {
-    "stats": "Stats",
-    "failedDags": "Failed Dags",
-    "runningDags": "Running Dags",
-    "activeDags": "Active Dags",
-    "queuedDags": "Queued Dags"
-  },
+  "group": "Group",
   "health": {
+    "dagProcessor": "Dag Processor",
     "health": "Health",
+    "healthy": "Healthy",
+    "lastHeartbeat": "Last Heartbeat",
     "metaDatabase": "MetaDatabase",
     "scheduler": "Scheduler",
-    "triggerer": "Triggerer",
-    "dagProcessor": "Dag Processor",
     "status": "Status",
-    "lastHeartbeat": "Last Heartbeat",
-    "healthy": "Healthy",
+    "triggerer": "Triggerer",
     "unhealthy": "Unhealthy"
   },
+  "history": "History",
   "importErrors": {
-    "searchByFile": "Search by file",
     "dagImportError_one": "Dag Import Error",
     "dagImportError_other": "Dag Import Errors",
+    "searchByFile": "Search by file",
     "timestamp": "Timestamp"
   },
-  "poolSlots": "Pool Slots",
   "managePools": "Manage Pools",
-  "history": "History",
+  "noAssetEvents": "No Asset Events found.",
+  "poolSlots": "Pool Slots",
   "sortBy": {
     "newestFirst": "Newest First",
     "oldestFirst": "Oldest First"
   },
   "source": "Source",
-  "group": "Group",
+  "stats": {
+    "activeDags": "Active Dags",
+    "failedDags": "Failed Dags",
+    "queuedDags": "Queued Dags",
+    "runningDags": "Running Dags",
+    "stats": "Stats"
+  },
   "uri": "Uri",
-  "noAssetEvents": "No Asset Events found."
+  "welcome": "Welcome"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/ko/common.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/ko/common.json
index 0412aea4444..b35a100d506 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/ko/common.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/ko/common.json
@@ -1,73 +1,73 @@
 {
-  "defaultToGraphView": "그래프 뷰 기본 보기",
-  "defaultToGridView": "그리드 뷰 기본 보기",
-  "logout": "로그아웃",
-  "switchToDarkMode": "다크 모드로 전환",
-  "switchToLightMode": "라이트 모드로 전환",
-  "timezone": "시간대",
-  "user": "사용자",
-  "selectLanguage": "언어 선택",
-  "nav": {
-    "home": "홈",
-    "assets": "에셋",
-    "browse": "탐색",
-    "admin": "관리자",
-    "docs": "문서",
-    "plugins": "플러그인"
-  },
-  "browse": {
-    "auditLog": "감사 로그",
-    "xcoms": "XComs"
-  },
   "admin": {
-    "Variables": "변수들",
+    "Config": "설정",
+    "Connections": "연결들",
+    "Plugins": "플러그인들",
     "Pools": "Pools",
     "Providers": "제공자들",
-    "Plugins": "플러그인들",
-    "Connections": "연결들",
-    "Config": "설정"
+    "Variables": "변수들"
   },
-  "timeRange": {
-    "duration": "지속 시간",
-    "lastHour": "지난 1시간",
-    "last12Hours": "지난 12 시간",
-    "last24Hours": "지난 24 시간",
-    "pastWeek": "지난 주"
+  "assetEvent_one": "에셋 이벤트",
+  "assetEvent_other": "에셋 이벤트들",
+  "browse": {
+    "auditLog": "감사 로그",
+    "xcoms": "XComs"
   },
+  "dagRun_one": "Dag 실행",
+  "dagRun_other": "Dag 실행들",
+  "defaultToGraphView": "그래프 뷰 기본 보기",
+  "defaultToGridView": "그리드 뷰 기본 보기",
   "docs": {
     "documentation": "문서",
     "githubRepo": "GitHub 저장소",
     "restApiReference": "REST API 참조"
   },
-  "states": {
+  "logout": "로그아웃",
+  "nav": {
+    "admin": "관리자",
+    "assets": "에셋",
+    "browse": "탐색",
+    "docs": "문서",
+    "home": "홈",
+    "plugins": "플러그인"
+  },
+  "pools": {
+    "deferred": "연기됨",
+    "open": "열림",
+    "pools_one": "pool",
+    "pools_other": "pools",
     "queued": "대기 중",
     "running": "실행 중",
-    "success": "성공",
+    "scheduled": "예약됨"
+  },
+  "selectLanguage": "언어 선택",
+  "states": {
+    "deferred": "연기됨",
     "failed": "실패",
-    "skipped": "건너뜀",
+    "no_status": "상태 없음",
+    "queued": "대기 중",
     "removed": "제거됨",
-    "scheduled": "예약됨",
     "restarting": "다시 시작 중",
-    "up_for_retry": "재시도 대기 중",
+    "running": "실행 중",
+    "scheduled": "예약됨",
+    "skipped": "건너뜀",
+    "success": "성공",
     "up_for_reschedule": "재예약 대기 중",
-    "upstream_failed": "업스트림 실패",
-    "deferred": "연기됨",
-    "no_status": "상태 없음"
+    "up_for_retry": "재시도 대기 중",
+    "upstream_failed": "업스트림 실패"
   },
-  "dagRun_one": "Dag 실행",
-  "dagRun_other": "Dag 실행들",
+  "switchToDarkMode": "다크 모드로 전환",
+  "switchToLightMode": "라이트 모드로 전환",
   "taskInstance_one": "작업 인스턴스",
   "taskInstance_other": "작업 인스턴스들",
-  "assetEvent_one": "에셋 이벤트",
-  "assetEvent_other": "에셋 이벤트들",
+  "timeRange": {
+    "duration": "지속 시간",
+    "last12Hours": "지난 12 시간",
+    "last24Hours": "지난 24 시간",
+    "lastHour": "지난 1시간",
+    "pastWeek": "지난 주"
+  },
+  "timezone": "시간대",
   "triggered": "트리거됨",
-  "pools": {
-    "open": "열림",
-    "running": "실행 중",
-    "queued": "대기 중",
-    "scheduled": "예약됨",
-    "deferred": "연기됨",
-    "pools_one": "pool",
-    "pools_other": "pools"
-  }
+  "user": "사용자"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/ko/dashboard.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/ko/dashboard.json
index a9121295b34..c8f200c8eaf 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/ko/dashboard.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/ko/dashboard.json
@@ -1,38 +1,38 @@
 {
-  "welcome": "환영합니다",
-  "stats": {
-    "stats": "통계",
-    "failedDags": "실패한 Dags",
-    "runningDags": "실행 중인 Dags",
-    "activeDags": "활성 Dags",
-    "queuedDags": "대기 중인 Dags"
-  },
+  "group": "그룹",
   "health": {
+    "dagProcessor": "Dag 프로세서",
     "health": "상태",
+    "healthy": "정상",
+    "lastHeartbeat": "마지막 Heartbeat",
     "metaDatabase": "메타데이터베이스",
     "scheduler": "스케줄러",
-    "triggerer": "트리거러",
-    "dagProcessor": "Dag 프로세서",
     "status": "상태",
-    "lastHeartbeat": "마지막 Heartbeat",
-    "healthy": "정상",
+    "triggerer": "트리거러",
     "unhealthy": "비정상"
   },
+  "history": "기록",
   "importErrors": {
-    "searchByFile": "파일로 검색",
     "dagImportError_one": "Dag 가져오기 오류",
     "dagImportError_other": "Dag 가져오기 오류들",
+    "searchByFile": "파일로 검색",
     "timestamp": "타임스탬프"
   },
-  "poolSlots": "Pool 슬롯",
   "managePools": "Pools 관리",
-  "history": "기록",
+  "noAssetEvents": "자산 이벤트를 찾을 수 없습니다.",
+  "poolSlots": "Pool 슬롯",
   "sortBy": {
     "newestFirst": "최신순",
     "oldestFirst": "오래된 순"
   },
   "source": "소스",
-  "group": "그룹",
+  "stats": {
+    "activeDags": "활성 Dags",
+    "failedDags": "실패한 Dags",
+    "queuedDags": "대기 중인 Dags",
+    "runningDags": "실행 중인 Dags",
+    "stats": "통계"
+  },
   "uri": "Uri",
-  "noAssetEvents": "자산 이벤트를 찾을 수 없습니다."
+  "welcome": "환영합니다"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/nl/common.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/nl/common.json
index 656886d0ebb..a68a472f207 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/nl/common.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/nl/common.json
@@ -1,73 +1,73 @@
 {
-  "defaultToGraphView": "Standaard grafiekweergave",
-  "defaultToGridView": "Standaard rasterweergave",
-  "logout": "Uitloggen",
-  "switchToDarkMode": "Schakel over naar donkere modus",
-  "switchToLightMode": "Schakel over naar lichte modus",
-  "timezone": "Tijdzone",
-  "user": "Gebruiker",
-  "selectLanguage": "Selecteer taal",
-  "nav": {
-    "home": "Home",
-    "assets": "Assets",
-    "browse": "Verken",
-    "admin": "Beheer",
-    "docs": "Documentatie",
-    "plugins": "Plugins"
-  },
-  "browse": {
-    "auditLog": "Audit Log",
-    "xcoms": "XComs"
-  },
   "admin": {
-    "Variables": "Variabelen",
+    "Config": "Configuratie",
+    "Connections": "Connecties",
+    "Plugins": "Plugins",
     "Pools": "Pools",
     "Providers": "Providers",
-    "Plugins": "Plugins",
-    "Connections": "Connecties",
-    "Config": "Configuratie"
+    "Variables": "Variabelen"
   },
-  "timeRange": {
-    "duration": "Duur",
-    "lastHour": "Laatste uur",
-    "last12Hours": "Laatste 12 uur",
-    "last24Hours": "Laatste 24 uur",
-    "pastWeek": "Laatste week"
+  "assetEvent_one": "Asset Event",
+  "assetEvent_other": "Asset Events",
+  "browse": {
+    "auditLog": "Audit Log",
+    "xcoms": "XComs"
   },
+  "dagRun_one": "Dag Run",
+  "dagRun_other": "Dag Runs",
+  "defaultToGraphView": "Standaard grafiekweergave",
+  "defaultToGridView": "Standaard rasterweergave",
   "docs": {
     "documentation": "Documentatie",
     "githubRepo": "GitHub Repo",
     "restApiReference": "REST API referentie"
   },
-  "states": {
+  "logout": "Uitloggen",
+  "nav": {
+    "admin": "Beheer",
+    "assets": "Assets",
+    "browse": "Verken",
+    "docs": "Documentatie",
+    "home": "Home",
+    "plugins": "Plugins"
+  },
+  "pools": {
+    "deferred": "Uitgesteld",
+    "open": "Open",
+    "pools_one": "pool",
+    "pools_other": "pools",
     "queued": "Wachtend",
     "running": "Lopend",
-    "success": "Succesvol",
+    "scheduled": "Gepland"
+  },
+  "selectLanguage": "Selecteer taal",
+  "states": {
+    "deferred": "Uitgesteld",
     "failed": "Mislukt",
-    "skipped": "Overgeslagen",
+    "no_status": "Geen Status",
+    "queued": "Wachtend",
     "removed": "Verwijderd",
-    "scheduled": "Gepland",
     "restarting": "Herstartend",
-    "up_for_retry": "Wachtend op een nieuwe poging",
+    "running": "Lopend",
+    "scheduled": "Gepland",
+    "skipped": "Overgeslagen",
+    "success": "Succesvol",
     "up_for_reschedule": "Wachtend op herplanning",
-    "upstream_failed": "Upstream mislukt",
-    "deferred": "Uitgesteld",
-    "no_status": "Geen Status"
+    "up_for_retry": "Wachtend op een nieuwe poging",
+    "upstream_failed": "Upstream mislukt"
   },
-  "dagRun_one": "Dag Run",
-  "dagRun_other": "Dag Runs",
+  "switchToDarkMode": "Schakel over naar donkere modus",
+  "switchToLightMode": "Schakel over naar lichte modus",
   "taskInstance_one": "Task Instance",
   "taskInstance_other": "Task Instances",
-  "assetEvent_one": "Asset Event",
-  "assetEvent_other": "Asset Events",
+  "timeRange": {
+    "duration": "Duur",
+    "last12Hours": "Laatste 12 uur",
+    "last24Hours": "Laatste 24 uur",
+    "lastHour": "Laatste uur",
+    "pastWeek": "Laatste week"
+  },
+  "timezone": "Tijdzone",
   "triggered": "Triggered",
-  "pools": {
-    "open": "Open",
-    "running": "Lopend",
-    "queued": "Wachtend",
-    "scheduled": "Gepland",
-    "deferred": "Uitgesteld",
-    "pools_one": "pool",
-    "pools_other": "pools"
-  }
+  "user": "Gebruiker"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/nl/dashboard.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/nl/dashboard.json
index 0577d6cfbcf..3243ef47f40 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/nl/dashboard.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/nl/dashboard.json
@@ -1,38 +1,38 @@
 {
-  "welcome": "Welkom",
-  "stats": {
-    "stats": "Statistieken",
-    "failedDags": "Mislukte Dags",
-    "runningDags": "Lopende Dags",
-    "activeDags": "Actieve Dags",
-    "queuedDags": "Dags in de wachtrij"
-  },
+  "group": "Groep",
   "health": {
+    "dagProcessor": "Dag Processor",
     "health": "Status",
+    "healthy": "OK",
+    "lastHeartbeat": "Laatste hartslag",
     "metaDatabase": "MetaDatabase",
     "scheduler": "Scheduler",
-    "triggerer": "Triggerer",
-    "dagProcessor": "Dag Processor",
     "status": "Status",
-    "lastHeartbeat": "Laatste hartslag",
-    "healthy": "OK",
+    "triggerer": "Triggerer",
     "unhealthy": "Fout"
   },
+  "history": "Geschiedenis",
   "importErrors": {
-    "searchByFile": "Zoek op bestand",
     "dagImportError_one": "Dag leesfout",
     "dagImportError_other": "Dag leesfouten",
+    "searchByFile": "Zoek op bestand",
     "timestamp": "Tijd"
   },
-  "poolSlots": "Pool Slots",
   "managePools": "Beheer Pools",
-  "history": "Geschiedenis",
+  "noAssetEvents": "Geen Asset Events gevonden.",
+  "poolSlots": "Pool Slots",
   "sortBy": {
     "newestFirst": "Nieuwste eerst",
     "oldestFirst": "Oudste eerst"
   },
   "source": "Bron",
-  "group": "Groep",
+  "stats": {
+    "activeDags": "Actieve Dags",
+    "failedDags": "Mislukte Dags",
+    "queuedDags": "Dags in de wachtrij",
+    "runningDags": "Lopende Dags",
+    "stats": "Statistieken"
+  },
   "uri": "Uri",
-  "noAssetEvents": "Geen Asset Events gevonden."
+  "welcome": "Welkom"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/common.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/common.json
index 9c46b155bdd..16a7f4588a2 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/common.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/common.json
@@ -1,63 +1,63 @@
 {
-  "defaultToGraphView": "預設使用圖形視圖",
-  "defaultToGridView": "預設使用網格視圖",
-  "logout": "登出",
-  "switchToDarkMode": "切換到深色模式",
-  "switchToLightMode": "切換到淺色模式",
-  "timezone": "時區",
-  "user": "使用者",
-  "selectLanguage": "選擇語言",
-  "nav": {
-    "home": "首頁",
-    "assets": "資源",
-    "browse": "瀏覽",
-    "admin": "管理者",
-    "docs": "文件",
-    "plugins": "插件"
-  },
-  "browse": {
-    "auditLog": "審計日誌",
-    "xcoms": "XComs"
-  },
   "admin": {
-    "Variables": "變數",
+    "Config": "設定",
+    "Connections": "連結",
+    "Plugins": "插件",
     "Pools": "資源池",
     "Providers": "Providers",
-    "Plugins": "插件",
-    "Connections": "連結",
-    "Config": "設定"
+    "Variables": "變數"
   },
-  "timeRange": {
-    "duration": "時間範圍",
-    "lastHour": "最近 1 小時",
-    "last12Hours": "最近 12 小時",
-    "last24Hours": "最近 24 小時",
-    "pastWeek": "過去一週"
+  "assetEvent_one": "資源事件",
+  "assetEvent_other": "資源事件",
+  "browse": {
+    "auditLog": "審計日誌",
+    "xcoms": "XComs"
   },
+  "dagRun_one": "Dag 執行",
+  "dagRun_other": "Dag 執行",
+  "defaultToGraphView": "預設使用圖形視圖",
+  "defaultToGridView": "預設使用網格視圖",
   "docs": {
     "documentation": "文件",
     "githubRepo": "GitHub 倉庫",
     "restApiReference": "REST API 參考"
   },
+  "logout": "登出",
+  "nav": {
+    "admin": "管理者",
+    "assets": "資源",
+    "browse": "瀏覽",
+    "docs": "文件",
+    "home": "首頁",
+    "plugins": "插件"
+  },
+  "selectLanguage": "選擇語言",
   "states": {
-    "queued": "排隊中",
-    "running": "執行中",
-    "success": "成功",
+    "deferred": "已延後",
     "failed": "失敗",
-    "skipped": "已跳過",
+    "no_status": "無狀態",
+    "queued": "排隊中",
     "removed": "已移除",
-    "scheduled": "已排程",
     "restarting": "重啟中",
-    "up_for_retry": "等待重試",
+    "running": "執行中",
+    "scheduled": "已排程",
+    "skipped": "已跳過",
+    "success": "成功",
     "up_for_reschedule": "等待重新排程",
-    "upstream_failed": "上游任務失敗",
-    "deferred": "已延後",
-    "no_status": "無狀態"
+    "up_for_retry": "等待重試",
+    "upstream_failed": "上游任務失敗"
   },
-  "dagRun_one": "Dag 執行",
-  "dagRun_other": "Dag 執行",
+  "switchToDarkMode": "切換到深色模式",
+  "switchToLightMode": "切換到淺色模式",
   "taskInstance_one": "任務實例",
   "taskInstance_other": "任務實例",
-  "assetEvent_one": "資源事件",
-  "assetEvent_other": "資源事件"
+  "timeRange": {
+    "duration": "時間範圍",
+    "last12Hours": "最近 12 小時",
+    "last24Hours": "最近 24 小時",
+    "lastHour": "最近 1 小時",
+    "pastWeek": "過去一週"
+  },
+  "timezone": "時區",
+  "user": "使用者"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/dashboard.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/dashboard.json
index fd5aa0c97b6..05e83e40b64 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/dashboard.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/dashboard.json
@@ -1,28 +1,28 @@
 {
-  "welcome": "歡迎",
-  "stats": {
-    "stats": "統計",
-    "failedDags": "失敗的 Dags",
-    "runningDags": "執行中的 Dags",
-    "activeDags": "啟用中的 Dags",
-    "queuedDags": "排隊中的 Dags"
-  },
+  "group": "群組",
   "health": {
+    "dagProcessor": "Dag 處理器",
     "health": "健康狀態",
     "metaDatabase": "系統資料庫",
     "scheduler": "排程器",
-    "triggerer": "觸發器",
-    "dagProcessor": "Dag 處理器"
+    "triggerer": "觸發器"
   },
-  "poolSlots": "資源池配額",
-  "managePools": "管理資源池",
   "history": "歷史記錄",
+  "managePools": "管理資源池",
+  "noAssetEvents": "未找到資源事件",
+  "poolSlots": "資源池配額",
   "sortBy": {
     "newestFirst": "由新到舊",
     "oldestFirst": "由舊到新"
   },
   "source": "來源",
-  "group": "群組",
+  "stats": {
+    "activeDags": "啟用中的 Dags",
+    "failedDags": "失敗的 Dags",
+    "queuedDags": "排隊中的 Dags",
+    "runningDags": "執行中的 Dags",
+    "stats": "統計"
+  },
   "uri": "Uri",
-  "noAssetEvents": "未找到資源事件"
+  "welcome": "歡迎"
 }


Reply via email to