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 ab3f0eca2a8 Migrate all plaintext to i18n (#51635)
ab3f0eca2a8 is described below

commit ab3f0eca2a809b7d6f8a7d499538d30ce6d69d09
Author: Brent Bovenzi <br...@astronomer.io>
AuthorDate: Thu Jun 12 12:41:47 2025 -0400

    Migrate all plaintext to i18n (#51635)
    
    Fix tryNumber key
    
    Fix run details merge issues
    
    Replace all plaintext and change eslint to ERROR
    
    Fix zh-tw assets file
---
 airflow-core/src/airflow/ui/rules/i18next.js       |   8 +-
 .../src/airflow/ui/src/components/ConfigForm.tsx   |   4 +-
 .../src/airflow/ui/src/components/DagVersion.tsx   |   5 +-
 .../airflow/ui/src/components/DagVersionSelect.tsx |   4 +-
 .../airflow/ui/src/components/DurationChart.tsx    |   4 +-
 .../airflow/ui/src/i18n/locales/de/components.json |   4 +-
 .../src/airflow/ui/src/i18n/locales/en/assets.json |  31 ++-
 .../src/airflow/ui/src/i18n/locales/en/browse.json |   5 +-
 .../src/airflow/ui/src/i18n/locales/en/common.json |  17 ++
 .../airflow/ui/src/i18n/locales/en/components.json |  12 +-
 .../src/airflow/ui/src/i18n/locales/en/dag.json    |  26 ++-
 .../airflow/ui/src/i18n/locales/pl/components.json |   4 +-
 .../airflow/ui/src/i18n/locales/zh-TW/assets.json  |  12 +-
 .../airflow/ui/src/layouts/Details/Gantt/Gantt.tsx |  21 --
 .../airflow/ui/src/layouts/Details/Gantt/index.ts  |  20 --
 .../src/airflow/ui/src/pages/Asset/AssetLayout.tsx |   4 +-
 .../ui/src/pages/Asset/CreateAssetEvent.tsx        |   6 +-
 .../ui/src/pages/Asset/CreateAssetEventModal.tsx   |  36 ++--
 .../src/airflow/ui/src/pages/Asset/Header.tsx      |   9 +-
 .../airflow/ui/src/pages/AssetsList/AssetsList.tsx |  10 +-
 .../ui/src/pages/Connections/ConnectionForm.tsx    |   4 +-
 airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx  |  26 +--
 airflow-core/src/airflow/ui/src/pages/Error.tsx    |  12 +-
 .../src/airflow/ui/src/pages/Events/Events.tsx     |   4 +-
 .../src/airflow/ui/src/pages/Run/Details.tsx       | 213 ++++++++++-----------
 .../src/airflow/ui/src/pages/Run/Header.tsx        |  20 +-
 airflow-core/src/airflow/ui/src/pages/Run/Run.tsx  |  18 +-
 .../src/airflow/ui/src/pages/Task/Header.tsx       |  45 +++--
 .../ui/src/pages/Task/Overview/Overview.tsx        |   6 +-
 .../src/airflow/ui/src/pages/Task/Task.tsx         |  16 +-
 .../ui/src/pages/TaskInstance/BlockingDeps.tsx     |   8 +-
 .../airflow/ui/src/pages/TaskInstance/Details.tsx  |  61 +++---
 .../ui/src/pages/TaskInstance/ExtraLinks.tsx       |   4 +-
 .../airflow/ui/src/pages/TaskInstance/Header.tsx   |  26 ++-
 .../pages/TaskInstance/Logs/ExternalLogLink.tsx    |   4 +-
 .../ui/src/pages/TaskInstance/Logs/Logs.tsx        |   4 +-
 .../ui/src/pages/TaskInstance/TaskInstance.tsx     |  30 +--
 .../ui/src/pages/TaskInstance/TriggererInfo.tsx    |  75 ++++----
 .../src/airflow/ui/src/pages/XCom/XCom.tsx         |   7 +-
 airflow-core/src/airflow/ui/src/utils/TrimText.tsx |   8 +-
 airflow-core/src/airflow/ui/src/utils/index.ts     |   1 -
 .../src/airflow/ui/src/utils/pluralize.test.ts     |  77 --------
 airflow-core/src/airflow/ui/src/utils/pluralize.ts |  28 ---
 43 files changed, 457 insertions(+), 482 deletions(-)

diff --git a/airflow-core/src/airflow/ui/rules/i18next.js 
b/airflow-core/src/airflow/ui/rules/i18next.js
index f255bc1cc03..1ba9fc3ded7 100644
--- a/airflow-core/src/airflow/ui/rules/i18next.js
+++ b/airflow-core/src/airflow/ui/rules/i18next.js
@@ -22,7 +22,7 @@
  */
 import i18nextPlugin from "eslint-plugin-i18next";
 
-import { WARN } from "./levels.js";
+import { ERROR } from "./levels.js";
 
 const allExtensions = "*.{j,t}s{x,}";
 
@@ -37,6 +37,10 @@ export const i18nextRules = /** @type {const} @satisfies 
{FlatConfig.Config} */
     // Check files in the ui/src directory
     `src/**/${allExtensions}`,
   ],
+  ignores: [
+    // Ignore test files
+    "src/**/*.test.tsx",
+  ],
   plugins: {
     i18next: i18nextPlugin,
   },
@@ -56,7 +60,7 @@ export const i18nextRules = /** @type {const} @satisfies 
{FlatConfig.Config} */
      * @see 
[i18next/no-literal-string](https://github.com/edvardchen/eslint-plugin-i18next#no-literal-string)
      */
     "i18next/no-literal-string": [
-      WARN,
+      ERROR,
       {
         markupOnly: true,
       },
diff --git a/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx 
b/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
index 2f9f408e244..1977d25236d 100644
--- a/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
+++ b/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
@@ -51,7 +51,7 @@ const ConfigForm = <T extends FieldValues = FieldValues>({
   setErrors,
   setFormError,
 }: ConfigFormProps<T>) => {
-  const { t: translate } = useTranslation("components");
+  const { t: translate } = useTranslation(["components", "common"]);
   const { conf, setConf } = useParamStore();
 
   const validateAndPrettifyJson = (value: string) => {
@@ -68,7 +68,7 @@ const ConfigForm = <T extends FieldValues = FieldValues>({
 
       return formattedJson;
     } catch (error) {
-      const errorMessage = error instanceof Error ? error.message : 
translate("configForm.unkownError");
+      const errorMessage = error instanceof Error ? error.message : 
translate("common:error.unknown");
 
       setErrors((prev) => ({
         ...prev,
diff --git a/airflow-core/src/airflow/ui/src/components/DagVersion.tsx 
b/airflow-core/src/airflow/ui/src/components/DagVersion.tsx
index 68174bfceae..2c65c3c09eb 100644
--- a/airflow-core/src/airflow/ui/src/components/DagVersion.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DagVersion.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { Text } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 
 import type { DagVersionResponse } from "openapi/requests/types.gen";
 
@@ -24,13 +25,15 @@ import Time from "./Time";
 import { Tooltip } from "./ui";
 
 export const DagVersion = ({ version }: { readonly version: DagVersionResponse 
| null | undefined }) => {
+  const { t: translate } = useTranslation("components");
+
   if (version === null || version === undefined) {
     return undefined;
   }
 
   return (
     <Tooltip content={<Time datetime={version.created_at} />}>
-      <Text as="span">v{version.version_number}</Text>
+      <Text as="span">{translate("versionSelect.versionCode", { versionCode: 
version.version_number })}</Text>
     </Tooltip>
   );
 };
diff --git a/airflow-core/src/airflow/ui/src/components/DagVersionSelect.tsx 
b/airflow-core/src/airflow/ui/src/components/DagVersionSelect.tsx
index b0f88ed9a46..9ea751d2ed2 100644
--- a/airflow-core/src/airflow/ui/src/components/DagVersionSelect.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DagVersionSelect.tsx
@@ -91,7 +91,9 @@ export const DagVersionSelect = ({ showLabel = true }: { 
readonly showLabel?: bo
         <Select.Content>
           {versionOptions.items.map((option) => (
             <Select.Item item={option} key={option.version.version_number}>
-              <Text>v{option.version.version_number}</Text>
+              <Text>
+                {translate("versionSelect.versionCode", { versionCode: 
option.version.version_number })}
+              </Text>
               <Time datetime={option.version.created_at} />
             </Select.Item>
           ))}
diff --git a/airflow-core/src/airflow/ui/src/components/DurationChart.tsx 
b/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
index bf8fb803db7..5d09441284f 100644
--- a/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
@@ -102,10 +102,10 @@ export const DurationChart = ({
         {entries.length > 1
           ? kind === "Dag Run"
             ? translate("durationChart.lastDagRun_other", { count: 
entries.length })
-            : translate("durationChart.lasttaskInstance_other", { count: 
entries.length })
+            : translate("durationChart.lastTaskInstance_other", { count: 
entries.length })
           : kind === "Dag Run"
             ? translate("durationChart.lastDagRun_one")
-            : translate("durationChart.lasttaskInstance_one")}
+            : translate("durationChart.lastTaskInstance_one")}
       </Heading>
       <Bar
         data={{
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/de/components.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/de/components.json
index 57f2717f4b6..845a7a62d27 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/de/components.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/de/components.json
@@ -41,8 +41,8 @@
     "duration": "Laufzeit (Sekunden)",
     "lastDagRun_one": "Letzter Dag Lauf",
     "lastDagRun_other": "Letzte {{count}} Dag Läufe",
-    "lasttaskInstance_one": "Letzte Task Instanz",
-    "lasttaskInstance_other": "Letzte {{count}} Task Instanzen",
+    "lastTaskInstance_one": "Letzte Task Instanz",
+    "lastTaskInstance_other": "Letzte {{count}} Task Instanzen",
     "queuedDuration": "Zeit in der Warteschlange",
     "runAfter": "Lauf ab",
     "runDuration": "Laufzeit"
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/assets.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/en/assets.json
index b927d6891ea..e82019096c8 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/en/assets.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/assets.json
@@ -1,10 +1,29 @@
 {
-  "columns": {
-    "consumingDags": "Consuming Dags",
-    "group": "Group",
-    "lastAssetEvent": "Last Asset Event",
-    "name": "Name",
-    "producingTasks": "Producing Tasks"
+  "consumingDags": "Consuming Dags",
+  "createEvent": {
+    "button": "Create Event",
+    "manual": {
+      "description": "Directly create an Asset Event",
+      "extra": "Asset Event Extra",
+      "label": "Manual"
+    },
+    "materialize": {
+      "description": "Trigger the Dag upstream of this asset",
+      "descriptionWithDag": "Trigger the Dag upstream of this asset: 
{{dagName}}",
+      "label": "Materialize",
+      "unpauseDag": "Unpause {{dagName}} on trigger"
+    },
+    "success": {
+      "manualDescription": "Manual asset event creation was successful.",
+      "manualTitle": "Asset Event Created",
+      "materializeDescription": "Upstream Dag {{dagId}} was triggered 
successfully.",
+      "materializeTitle": "Materializing Asset"
+    },
+    "title": "Create Asset Event for {{name}}"
   },
+  "group": "Group",
+  "lastAssetEvent": "Last Asset Event",
+  "name": "Name",
+  "producingTasks": "Producing Tasks",
   "searchPlaceholder": "Search Assets"
 }
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/browse.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/en/browse.json
index 56d89a8dc5c..8bdbec5aa9c 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/en/browse.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/browse.json
@@ -10,13 +10,14 @@
       "user": "User",
       "when": "When"
     },
-    "title": "Audit Log Events"
+    "title": "Audit Log"
   },
   "xcom":{
     "columns":{
       "dag": "Dag",
       "key": "Key",
       "value": "Value"
-    }
+    },
+    "title": "XCom"
   }
 }
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 8908acf24e5..92ed06fc380 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
@@ -53,6 +53,7 @@
     "queuedAt": "Queued At",
     "runAfter": "Run After",
     "runType": "Run Type",
+    "sourceAssetEvent": "Source Asset Event",
     "triggeredBy": "Triggered By"
   },
   "dagRun_one": "Dag Run",
@@ -68,6 +69,13 @@
   },
   "duration": "Duration",
   "endDate": "End Date",
+  "error": {
+    "back": "Back",
+    "defaultMessage": "An unexpected error occurred",
+    "home": "Home",
+    "notFound": "Page Not Found",
+    "title": "Error"
+  },
   "expression": {
     "all": "All",
     "and": "AND",
@@ -169,6 +177,7 @@
     "to": "To"
   },
   "task": {
+    "documentation": "Task Documentation",
     "lastInstance": "Last Instance",
     "operator": "Operator",
     "triggerRule": "Trigger Rule"
@@ -189,6 +198,14 @@
     "queue": "Queue",
     "queuedWhen": "Queued At",
     "scheduledWhen": "Scheduled At",
+    "triggerer": {
+      "assigned": "Assigned triggerer",
+      "class": "Trigger class",
+      "createdAt": "Trigger creation time",
+      "id": "Trigger ID",
+      "latestHeartbeat": "Latest triggerer heartbeat",
+      "title": "Triggerer Info"
+    },
     "unixname": "Unix Name"
   },
   "taskInstance_one": "Task Instance",
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/components.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/en/components.json
index 5e1b1abd9d7..d4526010cd1 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/en/components.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/components.json
@@ -28,8 +28,7 @@
   "configForm": {
     "advancedOptions": "Advanced Options",
     "configJson": "Configuration JSON",
-    "invalidJson": "Invalid JSON format: {{errorMessage}}",
-    "unkownError": "Unknown error occurred."
+    "invalidJson": "Invalid JSON format: {{errorMessage}}"
   },
   "dagWarnings": {
     "error_one": "1 Error",
@@ -41,8 +40,8 @@
     "duration": "Duration (seconds)",
     "lastDagRun_one": "Last Dag Run",
     "lastDagRun_other": "Last {{count}} Dag Runs",
-    "lasttaskInstance_one": "Last Task Instance",
-    "lasttaskInstance_other": "Last {{count}} Task Instances",
+    "lastTaskInstance_one": "Last Task Instance",
+    "lastTaskInstance_other": "Last {{count}} Task Instances",
     "queuedDuration": "Queued Duration",
     "runAfter": "Run After",
     "runDuration": "Run Duration"
@@ -96,6 +95,11 @@
     "title": "Trigger Dag",
     "unpause": "Unpause {{dagDisplayName}} on trigger"
   },
+  "trimText": {
+    "details": "Details",
+    "empty": "Empty",
+    "noContent": "No content available."
+  },
   "versionDetails": {
     "bundleLink": "Bundle Link",
     "bundleName": "Bundle Name",
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/dag.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/en/dag.json
index 4e94582a22d..4a1a3c6f9f6 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/en/dag.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/dag.json
@@ -1,10 +1,16 @@
 {
   "allRuns": "All Runs",
+  "blockingDeps": {
+    "dependency": "Dependency",
+    "reason": "Reason",
+    "title": "Dependencies Blocking Task From Getting Scheduled"
+  },
   "code": {
     "bundleUrl": "Bundle Url",
     "noCode": "No Code Found",
     "parsedAt": "Parsed at:"
   },
+  "extraLinks": "Extra Links",
   "grid": {
     "buttons": {
       "resetToLatest": "Reset to latest",
@@ -16,12 +22,18 @@
       "dagDocs": "Dag Docs"
     }
   },
+  "logs": {
+    "noTryNumber": "No try number",
+    "viewInExternal": "View logs in {{name}} (attempt {{attempt}})"
+  },
   "overview": {
     "buttons": {
       "failedRun_one": "Failed Run",
       "failedRun_other": "Failed Runs",
       "failedTask_one": "Failed Task",
-      "failedTask_other": "Failed Tasks"
+      "failedTask_other": "Failed Tasks",
+      "failedTaskInstance_one": "Failed Task Instance",
+      "failedTaskInstance_other": "Failed Task Instances"
     },
     "charts": {
       "assetEvent_one": "Created Asset Event",
@@ -55,14 +67,20 @@
     }
   },
   "tabs": {
-    "asset_events": "Asset Events",
+    "assetEvents": "Asset Events",
+    "auditLog": "Audit Log",
     "backfills": "Backfills",
     "code": "Code",
     "details": "Details",
-    "events": "Audit Logs",
+    "logs": "Logs",
+    "mappedTaskInstances_one": "Task Instance [{{count}}]",
+    "mappedTaskInstances_other": "Task Instances [{{count}}]",
     "overview": "Overview",
+    "renderedTemplates": "Rendered Templates",
     "runs": "Runs",
-    "tasks": "Tasks"
+    "taskInstances": "Task Instances",
+    "tasks": "Tasks",
+    "xcom": "XCom"
   },
   "taskGroups": {
     "collapseAll": "Collapse all task groups",
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/pl/components.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/pl/components.json
index 25526c053ae..363fd119d84 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/pl/components.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/pl/components.json
@@ -41,8 +41,8 @@
     "duration": "Czas trwania (sekundy)",
     "lastDagRun_one": "Ostatnie wykonanie Daga",
     "lastDagRun_other": "Ostatnie {{count}} wykonania Daga",
-    "lasttaskInstance_one": "Ostatnia instancja zadania",
-    "lasttaskInstance_other": "Ostatnie {{count}} instancje zadania",
+    "lastTaskInstance_one": "Ostatnia instancja zadania",
+    "lastTaskInstance_other": "Ostatnie {{count}} instancje zadania",
     "queuedDuration": "Czas oczekiwania",
     "runAfter": "Uruchom po",
     "runDuration": "Czas trwania wykonania"
diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/assets.json 
b/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/assets.json
index 04e46d86abf..133c2177884 100644
--- a/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/assets.json
+++ b/airflow-core/src/airflow/ui/src/i18n/locales/zh-TW/assets.json
@@ -1,10 +1,8 @@
 {
-  "columns": {
-    "consumingDags": "消費者 Dags",
-    "group": "群組",
-    "lastAssetEvent": "最後資源事件",
-    "name": "名稱",
-    "producingTasks": "生產任務"
-  },
+  "consumingDags": "消費者 Dags",
+  "group": "群組",
+  "lastAssetEvent": "最後資源事件",
+  "name": "名稱",
+  "producingTasks": "生產任務",
   "searchPlaceholder": "搜尋資源"
 }
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
deleted file mode 100644
index 90530c4b7b0..00000000000
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-/*!
- * 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.
- */
-import { Box } from "@chakra-ui/react";
-
-export const Gantt = () => <Box>gantt</Box>;
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/index.ts 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/index.ts
deleted file mode 100644
index 24f6dabe4cf..00000000000
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*!
- * 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.
- */
-
-export * from "./Gantt";
diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx 
b/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx
index c41be956098..e136bf18ce5 100644
--- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx
@@ -19,6 +19,7 @@
 import { HStack, Box } from "@chakra-ui/react";
 import { useReactFlow } from "@xyflow/react";
 import { useCallback } from "react";
+import { useTranslation } from "react-i18next";
 import { PanelGroup, Panel, PanelResizeHandle } from "react-resizable-panels";
 import { useParams } from "react-router-dom";
 
@@ -33,6 +34,7 @@ import { CreateAssetEvent } from "./CreateAssetEvent";
 import { Header } from "./Header";
 
 export const AssetLayout = () => {
+  const { t: translate } = useTranslation(["assets", "common"]);
   const { assetId } = useParams();
 
   const { setTableURLState, tableURLState } = useTableURLState();
@@ -51,7 +53,7 @@ export const AssetLayout = () => {
   const links = [
     {
       label: asset?.name,
-      title: "Asset",
+      title: translate("common:asset_one"),
       value: `/assets/${assetId}`,
     },
   ];
diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEvent.tsx 
b/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEvent.tsx
index 6310e6de50d..10b00c9310a 100644
--- a/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEvent.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEvent.tsx
@@ -18,6 +18,7 @@
  */
 import { Box } from "@chakra-ui/react";
 import { useDisclosure } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 import { FiPlay } from "react-icons/fi";
 
 import type { AssetResponse } from "openapi/requests/types.gen";
@@ -32,16 +33,17 @@ type Props = {
 
 export const CreateAssetEvent = ({ asset, withText = true }: Props) => {
   const { onClose, onOpen, open } = useDisclosure();
+  const { t: translate } = useTranslation("assets");
 
   return (
     <Box>
       <ActionButton
-        actionName="Create Asset Event"
+        actionName={translate("createEvent.button")}
         colorPalette="blue"
         disabled={asset === undefined}
         icon={<FiPlay />}
         onClick={onOpen}
-        text="Create Asset Event"
+        text={translate("createEvent.button")}
         variant="solid"
         withText={withText}
       />
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEventModal.tsx 
b/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEventModal.tsx
index f54e87182d6..212e9d6a187 100644
--- a/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEventModal.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Asset/CreateAssetEventModal.tsx
@@ -19,6 +19,7 @@
 import { Button, Field, Heading, HStack, VStack, Text } from 
"@chakra-ui/react";
 import { useQueryClient } from "@tanstack/react-query";
 import { useState } from "react";
+import { useTranslation } from "react-i18next";
 import { FiPlay } from "react-icons/fi";
 
 import {
@@ -52,6 +53,7 @@ type Props = {
 };
 
 export const CreateAssetEventModal = ({ asset, onClose, open }: Props) => {
+  const { t: translate } = useTranslation(["assets", "components"]);
   const [eventType, setEventType] = useState("manual");
   const [extraError, setExtraError] = useState<string | undefined>();
   const [unpause, setUnpause] = useState(true);
@@ -82,7 +84,7 @@ export const CreateAssetEventModal = ({ asset, onClose, open 
}: Props) => {
         setExtra(formattedJson); // Update only if the value is different
       }
     } catch (error) {
-      const errorMessage = error instanceof Error ? error.message : "Unknown 
error occurred.";
+      const errorMessage = error instanceof Error ? error.message : 
translate("common:error.unknown");
 
       setExtraError(errorMessage);
     }
@@ -107,14 +109,14 @@ export const CreateAssetEventModal = ({ asset, onClose, 
open }: Props) => {
       ];
 
       toaster.create({
-        description: `Upstream Dag ${response.dag_id} was triggered 
successfully.`,
-        title: "Materializing Asset",
+        description: translate("createEvent.success.materializeDescription", { 
dagId: response.dag_id }),
+        title: translate("createEvent.success.materializeTitle"),
         type: "success",
       });
     } else {
       toaster.create({
-        description: "Manual asset event creation was successful.",
-        title: "Asset Event Created",
+        description: translate("createEvent.success.manualDescription"),
+        title: translate("createEvent.success.manualTitle"),
         type: "success",
       });
     }
@@ -164,7 +166,7 @@ export const CreateAssetEventModal = ({ asset, onClose, 
open }: Props) => {
       <Dialog.Content backdrop>
         <Dialog.Header paddingBottom={0}>
           <VStack align="start" gap={4}>
-            <Heading size="xl">Create Asset Event for {asset.name}</Heading>
+            <Heading size="xl">{translate("createEvent.title", { name: 
asset.name })}</Heading>
           </VStack>
         </Dialog.Header>
 
@@ -180,24 +182,34 @@ export const CreateAssetEventModal = ({ asset, onClose, 
open }: Props) => {
           >
             <HStack align="stretch">
               <RadioCardItem
-                description={`Trigger the Dag upstream of this 
asset${upstreamDagId === undefined ? "" : `: ${dag?.dag_display_name ?? 
upstreamDagId}`}`}
+                description={
+                  upstreamDagId === undefined
+                    ? translate("createEvent.materialize.description")
+                    : translate("createEvent.materialize.descriptionWithDag", {
+                        dagName: dag?.dag_display_name ?? upstreamDagId,
+                      })
+                }
                 disabled={!hasUpstreamDag}
-                label="Materialize"
+                label={translate("createEvent.materialize.label")}
                 value="materialize"
               />
-              <RadioCardItem description="Directly create an Asset Event" 
label="Manual" value="manual" />
+              <RadioCardItem
+                description={translate("createEvent.manual.description")}
+                label={translate("createEvent.manual.label")}
+                value="manual"
+              />
             </HStack>
           </RadioCardRoot>
           {eventType === "manual" ? (
             <Field.Root mt={6}>
-              <Field.Label fontSize="md">Asset Event Extra</Field.Label>
+              <Field.Label 
fontSize="md">{translate("createEvent.manual.extra")}</Field.Label>
               <JsonEditor onChange={validateAndPrettifyJson} value={extra} />
               <Text color="fg.error">{extraError}</Text>
             </Field.Root>
           ) : undefined}
           {eventType === "materialize" && dag?.is_paused ? (
             <Checkbox checked={unpause} colorPalette="blue" onChange={() => 
setUnpause(!unpause)}>
-              Unpause {dag.dag_display_name} on trigger
+              {translate("createEvent.materialize.unpauseDag", { dagName: 
dag.dag_display_name })}
             </Checkbox>
           ) : undefined}
           <ErrorAlert error={eventType === "manual" ? manualError : 
materializeError} />
@@ -209,7 +221,7 @@ export const CreateAssetEventModal = ({ asset, onClose, 
open }: Props) => {
             loading={isPending || isMaterializePending}
             onClick={handleSubmit}
           >
-            <FiPlay /> Create Event
+            <FiPlay /> {translate("createEvent.button")}
           </Button>
         </Dialog.Footer>
       </Dialog.Content>
diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/Header.tsx 
b/airflow-core/src/airflow/ui/src/pages/Asset/Header.tsx
index b7fdfa46043..0ab20fa3e73 100644
--- a/airflow-core/src/airflow/ui/src/pages/Asset/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Asset/Header.tsx
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { useTranslation } from "react-i18next";
 import { FiDatabase } from "react-icons/fi";
 
 import type { AssetResponse } from "openapi/requests/types.gen";
@@ -30,14 +31,16 @@ export const Header = ({
   readonly asset?: AssetResponse;
   readonly isRefreshing?: boolean;
 }) => {
+  const { t: translate } = useTranslation("assets");
+
   const stats = [
-    { label: "Group", value: asset?.group },
+    { label: translate("group"), value: asset?.group },
     {
-      label: "Producing Tasks",
+      label: translate("producingTasks"),
       value: <DependencyPopover dependencies={asset?.producing_tasks ?? []} 
type="Task" />,
     },
     {
-      label: "Consuming Dags",
+      label: translate("consumingDags"),
       value: <DependencyPopover dependencies={asset?.consuming_dags ?? []} 
type="Dag" />,
     },
   ];
diff --git a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx 
b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx
index d05404f7ff5..51767172c67 100644
--- a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx
@@ -44,7 +44,7 @@ const createColumns = (translate: (key: string) => string): 
Array<ColumnDef<Asse
         <RouterLink to={`/assets/${original.id}`}>{original.name}</RouterLink>
       </Link>
     ),
-    header: () => translate("columns.name"),
+    header: () => translate("name"),
   },
   {
     accessorKey: "last_asset_event",
@@ -59,12 +59,12 @@ const createColumns = (translate: (key: string) => string): 
Array<ColumnDef<Asse
       return <Time datetime={timestamp} />;
     },
     enableSorting: false,
-    header: () => translate("columns.lastAssetEvent"),
+    header: () => translate("lastAssetEvent"),
   },
   {
     accessorKey: "group",
     enableSorting: false,
-    header: () => translate("columns.group"),
+    header: () => translate("group"),
   },
   {
     accessorKey: "consuming_dags",
@@ -73,7 +73,7 @@ const createColumns = (translate: (key: string) => string): 
Array<ColumnDef<Asse
         <DependencyPopover dependencies={original.consuming_dags} type="Dag" />
       ) : undefined,
     enableSorting: false,
-    header: () => translate("columns.consumingDags"),
+    header: () => translate("consumingDags"),
   },
   {
     accessorKey: "producing_tasks",
@@ -82,7 +82,7 @@ const createColumns = (translate: (key: string) => string): 
Array<ColumnDef<Asse
         <DependencyPopover dependencies={original.producing_tasks} type="Task" 
/>
       ) : undefined,
     enableSorting: false,
-    header: () => translate("columns.producingTasks"),
+    header: () => translate("producingTasks"),
   },
   {
     accessorKey: "trigger",
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Connections/ConnectionForm.tsx 
b/airflow-core/src/airflow/ui/src/pages/Connections/ConnectionForm.tsx
index 2badcd37966..bf921a1de3c 100644
--- a/airflow-core/src/airflow/ui/src/pages/Connections/ConnectionForm.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Connections/ConnectionForm.tsx
@@ -66,7 +66,7 @@ const ConnectionForm = ({
     mode: "onBlur",
   });
 
-  const { t: translate } = useTranslation("admin");
+  const { t: translate } = useTranslation(["admin", "common"]);
   const selectedConnType = watch("conn_type"); // Get the selected connection 
type
   const standardFields = connectionTypeMeta[selectedConnType]?.standard_fields 
?? {};
   const paramsDic = { paramsDict: 
connectionTypeMeta[selectedConnType]?.extra_fields ?? ({} as ParamsSpec) };
@@ -107,7 +107,7 @@ const ConnectionForm = ({
 
       return formattedJson;
     } catch (error_) {
-      const errorMessage = error_ instanceof Error ? error_.message : "Unknown 
error occurred.";
+      const errorMessage = error_ instanceof Error ? error_.message : 
translate("common:error.unknown");
 
       setErrors((prev) => ({
         ...prev,
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
index 5190a357853..cca182140b8 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
@@ -34,20 +34,20 @@ import { isStatePending, useAutoRefresh } from "src/utils";
 
 import { Header } from "./Header";
 
-const getTabs = (translate: (key: string) => string) => [
-  { icon: <LuChartColumn />, label: translate("tabs.overview"), value: "" },
-  { icon: <FiBarChart />, label: translate("tabs.runs"), value: "runs" },
-  { icon: <TaskIcon />, label: translate("tabs.tasks"), value: "tasks" },
-  { icon: <RiArrowGoBackFill />, label: translate("tabs.backfills"), value: 
"backfills" },
-  { icon: <MdOutlineEventNote />, label: translate("tabs.events"), value: 
"events" },
-  { icon: <FiCode />, label: translate("tabs.code"), value: "code" },
-  { icon: <MdDetails />, label: translate("tabs.details"), value: "details" },
-];
-
 export const Dag = () => {
   const { t: translate } = useTranslation("dag");
   const { dagId = "" } = useParams();
 
+  const tabs = [
+    { icon: <LuChartColumn />, label: translate("tabs.overview"), value: "" },
+    { icon: <FiBarChart />, label: translate("tabs.runs"), value: "runs" },
+    { icon: <TaskIcon />, label: translate("tabs.tasks"), value: "tasks" },
+    { icon: <RiArrowGoBackFill />, label: translate("tabs.backfills"), value: 
"backfills" },
+    { icon: <MdOutlineEventNote />, label: translate("tabs.auditLog"), value: 
"events" },
+    { icon: <FiCode />, label: translate("tabs.code"), value: "code" },
+    { icon: <MdDetails />, label: translate("tabs.details"), value: "details" 
},
+  ];
+
   const {
     data: dag,
     error,
@@ -89,15 +89,15 @@ export const Dag = () => {
     } satisfies DAGWithLatestDagRunsResponse;
   }
 
+  const displayTabs = tabs.filter((tab) => !(dag?.timetable_summary === null 
&& tab.value === "backfills"));
+
   return (
     <ReactFlowProvider>
       <DetailsLayout
         dag={dag}
         error={error ?? runsError}
         isLoading={isLoading || isLoadingRuns}
-        tabs={getTabs(translate).filter(
-          (tab) => !(dag?.timetable_summary === null && tab.value === 
"backfills"),
-        )}
+        tabs={displayTabs}
       >
         <Header
           dag={dag}
diff --git a/airflow-core/src/airflow/ui/src/pages/Error.tsx 
b/airflow-core/src/airflow/ui/src/pages/Error.tsx
index 525cbb9e764..f486c9e7306 100644
--- a/airflow-core/src/airflow/ui/src/pages/Error.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Error.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { Box, VStack, Heading, Text, Button, Container, HStack, Code } from 
"@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 import { useNavigate, useRouteError, isRouteErrorResponse } from 
"react-router-dom";
 
 import { AirflowPin } from "src/assets/AirflowPin";
@@ -24,15 +25,16 @@ import { AirflowPin } from "src/assets/AirflowPin";
 export const ErrorPage = () => {
   const navigate = useNavigate();
   const error = useRouteError();
+  const { t: translate } = useTranslation();
 
-  let errorMessage = "An unexpected error occurred";
+  let errorMessage = translate("error.defaultMessage");
   let statusCode = "";
 
   if (isRouteErrorResponse(error)) {
     statusCode = String(error.status);
     errorMessage =
       ((error as unknown as Error).message || (error as { statusText?: string 
}).statusText) ??
-      "Page Not Found";
+      translate("error.notFound");
   } else if (error instanceof Error) {
     errorMessage = error.message;
   } else if (typeof error === "string") {
@@ -49,7 +51,7 @@ export const ErrorPage = () => {
           <AirflowPin height="50px" width="50px" />
 
           <VStack gap={4}>
-            <Heading>{statusCode || "Error"}</Heading>
+            <Heading>{statusCode || translate("error.title")}</Heading>
             <Text fontSize="lg">{errorMessage}</Text>
             {error instanceof Error && isDev ? (
               <Code borderRadius="md" fontSize="sm" p={3} width="full">
@@ -60,10 +62,10 @@ export const ErrorPage = () => {
 
           <HStack gap={4}>
             <Button colorPalette="blue" onClick={() => navigate(-1)} size="lg">
-              Back
+              {translate("error.back")}
             </Button>
             <Button colorPalette="blue" onClick={() => navigate("/")} 
size="lg" variant="outline">
-              Home
+              {translate("error.home")}
             </Button>
           </HStack>
         </VStack>
diff --git a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx 
b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx
index a3b306a4af3..f71e57b6419 100644
--- a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx
@@ -167,7 +167,9 @@ export const Events = () => {
   return (
     <Box>
       <Flex alignItems="center" justifyContent="space-between">
-        <Heading>{translate("auditLog.title")}</Heading>
+        {dagId === undefined && runId === undefined && taskId === undefined ? (
+          <Heading size="md">{translate("auditLog.title")}</Heading>
+        ) : undefined}
         <ButtonGroup attached mt="1" size="sm" variant="surface">
           <IconButton
             aria-label={translate("auditLog.actions.expandAllExtra")}
diff --git a/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx 
b/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx
index 75860092487..23d525b0edf 100644
--- a/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx
@@ -16,7 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Flex, HStack, StackSeparator, Table, Text, VStack } from 
"@chakra-ui/react";
+import { Flex, HStack, StackSeparator, Table, Text, VStack } from 
"@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 import { useParams } from "react-router-dom";
 
 import { useDagRunServiceGetDagRun } from "openapi/queries";
@@ -29,6 +30,7 @@ import { ClipboardRoot, ClipboardIconButton } from 
"src/components/ui";
 import { getDuration, isStatePending, useAutoRefresh } from "src/utils";
 
 export const Details = () => {
+  const { t: translate } = useTranslation(["common", "components"]);
   const { dagId = "", runId = "" } = useParams();
 
   const refetchInterval = useAutoRefresh({ dagId });
@@ -42,112 +44,109 @@ export const Details = () => {
     { refetchInterval: (query) => (isStatePending(query.state.data?.state) ? 
refetchInterval : false) },
   );
 
-  // TODO : Render DagRun configuration object
+  if (!dagRun) {
+    return undefined;
+  }
+
   return (
-    <Box p={2}>
-      {dagRun === undefined ? (
-        <div />
-      ) : (
-        <Table.Root striped>
-          <Table.Body>
-            <Table.Row>
-              <Table.Cell>State</Table.Cell>
-              <Table.Cell>
-                <Flex gap={1}>
-                  <StateBadge state={dagRun.state} />
-                  {dagRun.state}
-                </Flex>
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Run ID</Table.Cell>
-              <Table.Cell>
-                <HStack>
-                  {dagRun.dag_run_id}
-                  <ClipboardRoot value={dagRun.dag_run_id}>
-                    <ClipboardIconButton />
-                  </ClipboardRoot>
-                </HStack>
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Run Type</Table.Cell>
-              <Table.Cell>
-                <HStack>
-                  <RunTypeIcon runType={dagRun.run_type} />
-                  <Text>{dagRun.run_type}</Text>
-                </HStack>
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Run Duration</Table.Cell>
-              <Table.Cell>{getDuration(dagRun.start_date, 
dagRun.end_date)}</Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Last Scheduling Decision</Table.Cell>
-              <Table.Cell>
-                <Time datetime={dagRun.last_scheduling_decision} />
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Queued at</Table.Cell>
-              <Table.Cell>
-                <Time datetime={dagRun.queued_at} />
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Start Date</Table.Cell>
-              <Table.Cell>
-                <Time datetime={dagRun.start_date} />
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>End Date</Table.Cell>
-              <Table.Cell>
-                <Time datetime={dagRun.end_date} />
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Data Interval Start</Table.Cell>
-              <Table.Cell>
-                <Time datetime={dagRun.data_interval_start} />
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Data Interval End</Table.Cell>
-              <Table.Cell>
-                <Time datetime={dagRun.data_interval_end} />
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Trigger Source</Table.Cell>
-              <Table.Cell>{dagRun.triggered_by}</Table.Cell>
-            </Table.Row>
-            {dagRun.bundle_version !== null && (
-              <Table.Row>
-                <Table.Cell>Bundle Version</Table.Cell>
-                <Table.Cell>{dagRun.bundle_version}</Table.Cell>
-              </Table.Row>
-            )}
-            <Table.Row>
-              <Table.Cell>Dag Version(s)</Table.Cell>
-              <Table.Cell>
-                <VStack separator={<StackSeparator />}>
-                  {dagRun.dag_versions.map((dagVersion) => (
-                    <DagVersionDetails dagVersion={dagVersion} 
key={dagVersion.id} />
-                  ))}
-                </VStack>
-              </Table.Cell>
-            </Table.Row>
-            <Table.Row>
-              <Table.Cell>Run Config</Table.Cell>
-              <Table.Cell>
-                <RenderedJsonField content={dagRun.conf ?? {}} />
-              </Table.Cell>
-            </Table.Row>
-          </Table.Body>
-        </Table.Root>
-      )}
-    </Box>
+    <Table.Root striped>
+      <Table.Body>
+        <Table.Row>
+          <Table.Cell>{translate("state")}</Table.Cell>
+          <Table.Cell>
+            <Flex gap={1}>
+              <StateBadge state={dagRun.state} />
+              {dagRun.state}
+            </Flex>
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("runId")}</Table.Cell>
+          <Table.Cell>
+            <HStack>
+              {dagRun.dag_run_id}
+              <ClipboardRoot value={dagRun.dag_run_id}>
+                <ClipboardIconButton />
+              </ClipboardRoot>
+            </HStack>
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.runType")}</Table.Cell>
+          <Table.Cell>
+            <HStack>
+              <RunTypeIcon runType={dagRun.run_type} />
+              <Text>{dagRun.run_type}</Text>
+            </HStack>
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("duration")}</Table.Cell>
+          <Table.Cell>{getDuration(dagRun.start_date, 
dagRun.end_date)}</Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.lastSchedulingDecision")}</Table.Cell>
+          <Table.Cell>
+            <Time datetime={dagRun.last_scheduling_decision} />
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.queuedAt")}</Table.Cell>
+          <Table.Cell>
+            <Time datetime={dagRun.queued_at} />
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("startDate")}</Table.Cell>
+          <Table.Cell>
+            <Time datetime={dagRun.start_date} />
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("endDate")}</Table.Cell>
+          <Table.Cell>
+            <Time datetime={dagRun.end_date} />
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.dataIntervalStart")}</Table.Cell>
+          <Table.Cell>
+            <Time datetime={dagRun.data_interval_start} />
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.dataIntervalEnd")}</Table.Cell>
+          <Table.Cell>
+            <Time datetime={dagRun.data_interval_end} />
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.triggeredBy")}</Table.Cell>
+          <Table.Cell>{dagRun.triggered_by}</Table.Cell>
+        </Table.Row>
+        {dagRun.bundle_version !== null && (
+          <Table.Row>
+            
<Table.Cell>{translate("components:versionDetails.bundleVersion")}</Table.Cell>
+            <Table.Cell>{dagRun.bundle_version}</Table.Cell>
+          </Table.Row>
+        )}
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.dagVersions")}</Table.Cell>
+          <Table.Cell>
+            <VStack separator={<StackSeparator />}>
+              {dagRun.dag_versions.map((dagVersion) => (
+                <DagVersionDetails dagVersion={dagVersion} key={dagVersion.id} 
/>
+              ))}
+            </VStack>
+          </Table.Cell>
+        </Table.Row>
+        <Table.Row>
+          <Table.Cell>{translate("dagRun.conf")}</Table.Cell>
+          <Table.Cell>
+            <RenderedJsonField content={dagRun.conf ?? {}} />
+          </Table.Cell>
+        </Table.Row>
+      </Table.Body>
+    </Table.Root>
   );
 };
diff --git a/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx 
b/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx
index c412da36b9d..2a338baf3a1 100644
--- a/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx
@@ -18,6 +18,7 @@
  */
 import { HStack, Text, Box } from "@chakra-ui/react";
 import { useCallback, useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
 import { FiBarChart, FiMessageSquare } from "react-icons/fi";
 
 import type { DAGRunResponse } from "openapi/requests/types.gen";
@@ -39,6 +40,7 @@ export const Header = ({
   readonly dagRun: DAGRunResponse;
   readonly isRefreshing?: boolean;
 }) => {
+  const { t: translate } = useTranslation();
   const [note, setNote] = useState<string | null>(dagRun.note);
 
   const dagId = dagRun.dag_id;
@@ -67,14 +69,14 @@ export const Header = ({
         actions={
           <>
             <EditableMarkdownButton
-              header="Dag Run Note"
+              header={translate("note.dagRun")}
               icon={<FiMessageSquare />}
               isPending={isPending}
               mdContent={note}
               onConfirm={onConfirm}
-              placeholder="Add a note..."
+              placeholder={translate("note.placeholder")}
               setMdContent={setNote}
-              text={Boolean(dagRun.note) ? "Note" : "Add a note"}
+              text={Boolean(dagRun.note) ? translate("note.label") : 
translate("note.add")}
               withText={containerWidth > 700}
             />
             <ClearRunButton dagRun={dagRun} isHotkeyEnabled 
withText={containerWidth > 700} />
@@ -89,12 +91,12 @@ export const Header = ({
             ? []
             : [
                 {
-                  label: "Logical Date",
+                  label: translate("logicalDate"),
                   value: <Time datetime={dagRun.logical_date} />,
                 },
               ]),
           {
-            label: "Run Type",
+            label: translate("dagRun.runType"),
             value: (
               <HStack>
                 <RunTypeIcon runType={dagRun.run_type} />
@@ -102,11 +104,11 @@ export const Header = ({
               </HStack>
             ),
           },
-          { label: "Start", value: <Time datetime={dagRun.start_date} /> },
-          { label: "End", value: <Time datetime={dagRun.end_date} /> },
-          { label: "Duration", value: getDuration(dagRun.start_date, 
dagRun.end_date) },
+          { label: translate("startDate"), value: <Time 
datetime={dagRun.start_date} /> },
+          { label: translate("endDate"), value: <Time 
datetime={dagRun.end_date} /> },
+          { label: translate("duration"), value: 
getDuration(dagRun.start_date, dagRun.end_date) },
           {
-            label: "Dag Version(s)",
+            label: translate("dagRun.dagVersions"),
             value: (
               <LimitedItemsList
                 items={dagRun.dag_versions.map((version) => (
diff --git a/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx 
b/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx
index 5b4dfffd3e5..0b9d8c9db94 100644
--- a/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { ReactFlowProvider } from "@xyflow/react";
+import { useTranslation } from "react-i18next";
 import { FiCode, FiDatabase } from "react-icons/fi";
 import { MdDetails, MdOutlineEventNote, MdOutlineTask } from "react-icons/md";
 import { useParams } from "react-router-dom";
@@ -27,17 +28,18 @@ import { isStatePending, useAutoRefresh } from "src/utils";
 
 import { Header } from "./Header";
 
-const tabs = [
-  { icon: <MdOutlineTask />, label: "Task Instances", value: "" },
-  { icon: <MdOutlineEventNote />, label: "Audit Logs", value: "events" },
-  { icon: <FiCode />, label: "Code", value: "code" },
-  { icon: <MdDetails />, label: "Details", value: "details" },
-  { icon: <FiDatabase />, label: "Asset Events", value: "asset_events" },
-];
-
 export const Run = () => {
+  const { t: translate } = useTranslation("dag");
   const { dagId = "", runId = "" } = useParams();
 
+  const tabs = [
+    { icon: <MdOutlineTask />, label: translate("tabs.taskInstances"), value: 
"" },
+    { icon: <FiDatabase />, label: translate("tabs.assetEvents"), value: 
"asset_events" },
+    { icon: <MdOutlineEventNote />, label: translate("tabs.auditLog"), value: 
"events" },
+    { icon: <FiCode />, label: translate("tabs.code"), value: "code" },
+    { icon: <MdDetails />, label: translate("tabs.details"), value: "details" 
},
+  ];
+
   const refetchInterval = useAutoRefresh({ dagId });
 
   const {
diff --git a/airflow-core/src/airflow/ui/src/pages/Task/Header.tsx 
b/airflow-core/src/airflow/ui/src/pages/Task/Header.tsx
index e90793c2f12..2681dd52e39 100644
--- a/airflow-core/src/airflow/ui/src/pages/Task/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Task/Header.tsx
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { useTranslation } from "react-i18next";
 import { FiBookOpen } from "react-icons/fi";
 
 import type { TaskResponse } from "openapi/requests/types.gen";
@@ -23,23 +24,27 @@ import { TaskIcon } from "src/assets/TaskIcon";
 import DisplayMarkdownButton from "src/components/DisplayMarkdownButton";
 import { HeaderCard } from "src/components/HeaderCard";
 
-export const Header = ({ task }: { readonly task: TaskResponse }) => (
-  <HeaderCard
-    actions={
-      task.doc_md === null ? undefined : (
-        <DisplayMarkdownButton
-          header="Task Documentation"
-          icon={<FiBookOpen />}
-          mdContent={task.doc_md}
-          text="Task Docs"
-        />
-      )
-    }
-    icon={<TaskIcon />}
-    stats={[
-      { label: "Operator", value: task.operator_name },
-      { label: "Trigger Rule", value: task.trigger_rule },
-    ]}
-    title={`${task.task_display_name}${task.is_mapped ? " [ ]" : ""}`}
-  />
-);
+export const Header = ({ task }: { readonly task: TaskResponse }) => {
+  const { t: translate } = useTranslation();
+
+  return (
+    <HeaderCard
+      actions={
+        task.doc_md === null ? undefined : (
+          <DisplayMarkdownButton
+            header={translate("task.documentation")}
+            icon={<FiBookOpen />}
+            mdContent={task.doc_md}
+            text={translate("docs.documentation")}
+          />
+        )
+      }
+      icon={<TaskIcon />}
+      stats={[
+        { label: translate("task.operator"), value: task.operator_name },
+        { label: translate("task.triggerRule"), value: task.trigger_rule },
+      ]}
+      title={`${task.task_display_name}${task.is_mapped ? " [ ]" : ""}`}
+    />
+  );
+};
diff --git a/airflow-core/src/airflow/ui/src/pages/Task/Overview/Overview.tsx 
b/airflow-core/src/airflow/ui/src/pages/Task/Overview/Overview.tsx
index e36a613eb61..79b39d21b9f 100644
--- a/airflow-core/src/airflow/ui/src/pages/Task/Overview/Overview.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Task/Overview/Overview.tsx
@@ -19,6 +19,7 @@
 import { Box, HStack, Skeleton, SimpleGrid } from "@chakra-ui/react";
 import dayjs from "dayjs";
 import { useState } from "react";
+import { useTranslation } from "react-i18next";
 import { useParams } from "react-router-dom";
 
 import { useTaskInstanceServiceGetTaskInstances } from "openapi/queries";
@@ -31,6 +32,7 @@ const defaultHour = "24";
 
 export const Overview = () => {
   const { dagId = "", groupId, taskId } = useParams();
+  const { t: translate } = useTranslation("dag");
 
   const now = dayjs();
   const [startDate, setStartDate] = useState(now.subtract(Number(defaultHour), 
"hour").toISOString());
@@ -86,7 +88,9 @@ export const Overview = () => {
             timestamp: ti.start_date ?? ti.logical_date,
           }))}
           isLoading={isFailedTaskInstancesLoading}
-          label="Failed Task Instance"
+          label={translate("overview.buttons.failedTaskInstance", {
+            count: failedTaskInstances?.total_entries ?? 0,
+          })}
           route={{
             pathname: "task_instances",
             search: "state=failed",
diff --git a/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx 
b/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx
index 0dc3dce538c..1095642b3d2 100644
--- a/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { ReactFlowProvider } from "@xyflow/react";
+import { useTranslation } from "react-i18next";
 import { LuChartColumn } from "react-icons/lu";
 import { MdOutlineEventNote, MdOutlineTask } from "react-icons/md";
 import { useParams } from "react-router-dom";
@@ -28,16 +29,17 @@ import { getGroupTask } from "src/utils/groupTask";
 import { GroupTaskHeader } from "./GroupTaskHeader";
 import { Header } from "./Header";
 
-const tabs = [
-  { icon: <LuChartColumn />, label: "Overview", value: "" },
-  { icon: <MdOutlineTask />, label: "Task Instances", value: "task_instances" 
},
-  { icon: <MdOutlineEventNote />, label: "Audit Logs", value: "events" },
-];
-
 export const Task = () => {
+  const { t: translate } = useTranslation("dag");
   const { dagId = "", groupId, taskId } = useParams();
 
-  const displayTabs = groupId === undefined ? tabs : tabs.filter((tab) => 
tab.label !== "Events");
+  const tabs = [
+    { icon: <LuChartColumn />, label: translate("tabs.overview"), value: "" },
+    { icon: <MdOutlineTask />, label: translate("tabs.taskInstances"), value: 
"task_instances" },
+    { icon: <MdOutlineEventNote />, label: translate("tabs.auditLog"), value: 
"events" },
+  ];
+
+  const displayTabs = groupId === undefined ? tabs : tabs.filter((tab) => 
tab.value !== "events");
 
   const {
     data: task,
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/BlockingDeps.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/BlockingDeps.tsx
index e14502b7475..2ed76613df7 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/BlockingDeps.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/BlockingDeps.tsx
@@ -17,11 +17,13 @@
  * under the License.
  */
 import { Box, Table, Heading } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 
 import { useTaskInstanceServiceGetTaskInstanceDependencies } from 
"openapi/queries";
 import type { TaskInstanceResponse } from "openapi/requests/types.gen";
 
 export const BlockingDeps = ({ taskInstance }: { readonly taskInstance: 
TaskInstanceResponse }) => {
+  const { t: translate } = useTranslation();
   const { data } = useTaskInstanceServiceGetTaskInstanceDependencies({
     dagId: taskInstance.dag_id,
     dagRunId: taskInstance.dag_run_id,
@@ -36,13 +38,13 @@ export const BlockingDeps = ({ taskInstance }: { readonly 
taskInstance: TaskInst
   return (
     <Box flexGrow={1} mt={3}>
       <Heading py={2} size="sm">
-        Dependencies Blocking Task From Getting Scheduled
+        {translate("dag.blockingDeps.title")}
       </Heading>
       <Table.Root striped>
         <Table.Body>
           <Table.Row>
-            <Table.ColumnHeader>Dependency</Table.ColumnHeader>
-            <Table.ColumnHeader>Reason</Table.ColumnHeader>
+            
<Table.ColumnHeader>{translate("dag.blockingDeps.dependency")}</Table.ColumnHeader>
+            
<Table.ColumnHeader>{translate("dag.blockingDeps.reason")}</Table.ColumnHeader>
           </Table.Row>
           {data.dependencies.map((dep) => (
             <Table.Row key={dep.name}>
diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx
index 9fed186008f..62ef8472e0d 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx
@@ -16,7 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Flex, HStack, Table, Heading } from "@chakra-ui/react";
+import { Box, Flex, HStack, Table } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 import { useParams, useSearchParams } from "react-router-dom";
 
 import {
@@ -35,6 +36,7 @@ import { ExtraLinks } from "./ExtraLinks";
 import { TriggererInfo } from "./TriggererInfo";
 
 export const Details = () => {
+  const { t: translate } = useTranslation();
   const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams();
   const [searchParams, setSearchParams] = useSearchParams();
 
@@ -94,22 +96,19 @@ export const Details = () => {
       {taskInstance !== undefined && (taskInstance.trigger ?? 
taskInstance.triggerer_job) ? (
         <TriggererInfo taskInstance={taskInstance} />
       ) : undefined}
-      <Heading py={2} size="sm">
-        Task Instance Info
-      </Heading>
       <Table.Root striped>
         <Table.Body>
           <Table.Row>
-            <Table.Cell>State</Table.Cell>
+            <Table.Cell>{translate("state")}</Table.Cell>
             <Table.Cell>
               <Flex gap={1}>
                 <StateBadge state={tryInstance?.state} />
-                {tryInstance?.state ?? "no status"}
+                {tryInstance?.state ?? translate("states.no_status")}
               </Flex>
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Task ID</Table.Cell>
+            <Table.Cell>{translate("taskId")}</Table.Cell>
             <Table.Cell>
               <HStack>
                 {tryInstance?.task_id}
@@ -120,7 +119,7 @@ export const Details = () => {
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Run ID</Table.Cell>
+            <Table.Cell>{translate("runId")}</Table.Cell>
             <Table.Cell>
               <HStack>
                 {tryInstance?.dag_run_id}
@@ -131,15 +130,15 @@ export const Details = () => {
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Map Index</Table.Cell>
+            <Table.Cell>{translate("mapIndex")}</Table.Cell>
             <Table.Cell>{tryInstance?.map_index}</Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Operator</Table.Cell>
+            <Table.Cell>{translate("task.operator")}</Table.Cell>
             <Table.Cell>{tryInstance?.operator}</Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Duration</Table.Cell>
+            <Table.Cell>{translate("duration")}</Table.Cell>
             <Table.Cell>
               {Boolean(tryInstance?.start_date) // eslint-disable-next-line 
unicorn/no-null
                 ? getDuration(tryInstance?.start_date ?? null, 
tryInstance?.end_date ?? null)
@@ -147,25 +146,25 @@ export const Details = () => {
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Started</Table.Cell>
+            <Table.Cell>{translate("startDate")}</Table.Cell>
             <Table.Cell>
               <Time datetime={tryInstance?.start_date} />
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Ended</Table.Cell>
+            <Table.Cell>{translate("endDate")}</Table.Cell>
             <Table.Cell>
               <Time datetime={tryInstance?.end_date} />
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Dag Version</Table.Cell>
+            <Table.Cell>{translate("taskInstance.dagVersion")}</Table.Cell>
             <Table.Cell>
               <DagVersionDetails dagVersion={taskInstance?.dag_version} />
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Process ID (PID)</Table.Cell>
+            <Table.Cell>{translate("taskInstance.pid")}</Table.Cell>
             <Table.Cell>
               <HStack>
                 {tryInstance?.pid}
@@ -176,7 +175,7 @@ export const Details = () => {
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Hostname</Table.Cell>
+            <Table.Cell>{translate("taskInstance.hostname")}</Table.Cell>
             <Table.Cell>
               <HStack>
                 {tryInstance?.hostname}
@@ -187,37 +186,25 @@ export const Details = () => {
             </Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Pool</Table.Cell>
-            <Table.Cell>{tryInstance?.pool}</Table.Cell>
-          </Table.Row>
-          <Table.Row>
-            <Table.Cell>Pool Slots</Table.Cell>
-            <Table.Cell>{tryInstance?.pool_slots}</Table.Cell>
-          </Table.Row>
-          <Table.Row>
-            <Table.Cell>Executor</Table.Cell>
-            <Table.Cell>{tryInstance?.executor}</Table.Cell>
-          </Table.Row>
-          <Table.Row>
-            <Table.Cell>Executor Config</Table.Cell>
-            <Table.Cell>{tryInstance?.executor_config}</Table.Cell>
-          </Table.Row>
-          <Table.Row>
-            <Table.Cell>Unix Name</Table.Cell>
+            <Table.Cell>{translate("taskInstance.unixname")}</Table.Cell>
             <Table.Cell>{tryInstance?.unixname}</Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Max Tries</Table.Cell>
-            <Table.Cell>{tryInstance?.max_tries}</Table.Cell>
+            <Table.Cell>{translate("taskInstance.pool")}</Table.Cell>
+            <Table.Cell>{tryInstance?.pool}</Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Queue</Table.Cell>
+            <Table.Cell>{translate("taskInstance.queue")}</Table.Cell>
             <Table.Cell>{tryInstance?.queue}</Table.Cell>
           </Table.Row>
           <Table.Row>
-            <Table.Cell>Priority Weight</Table.Cell>
+            <Table.Cell>{translate("taskInstance.priorityWeight")}</Table.Cell>
             <Table.Cell>{tryInstance?.priority_weight}</Table.Cell>
           </Table.Row>
+          <Table.Row>
+            <Table.Cell>{translate("taskInstance.executor")}</Table.Cell>
+            <Table.Cell>{tryInstance?.executor_config}</Table.Cell>
+          </Table.Row>
         </Table.Body>
       </Table.Root>
     </Box>
diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/ExtraLinks.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/ExtraLinks.tsx
index efad2007b4e..167346175eb 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/ExtraLinks.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/ExtraLinks.tsx
@@ -17,11 +17,13 @@
  * under the License.
  */
 import { Box, Button, Heading, HStack } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 import { useParams } from "react-router-dom";
 
 import { useTaskInstanceServiceGetExtraLinks } from "openapi/queries";
 
 export const ExtraLinks = () => {
+  const { t: translate } = useTranslation();
   const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams();
 
   const { data } = useTaskInstanceServiceGetExtraLinks({
@@ -33,7 +35,7 @@ export const ExtraLinks = () => {
 
   return data && Object.keys(data.extra_links).length > 0 ? (
     <Box py={1}>
-      <Heading size="sm">Extra Links</Heading>
+      <Heading size="sm">{translate("dag.extraLinks")}</Heading>
       <HStack gap={2} py={2}>
         {Object.entries(data.extra_links).map(([key, value], _) =>
           value === null ? undefined : (
diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
index 702060ae285..34e80d3716a 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
@@ -18,6 +18,7 @@
  */
 import { Box } from "@chakra-ui/react";
 import { useCallback, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
 import { FiMessageSquare } from "react-icons/fi";
 import { MdOutlineTask } from "react-icons/md";
 
@@ -38,20 +39,25 @@ export const Header = ({
   readonly isRefreshing?: boolean;
   readonly taskInstance: TaskInstanceResponse;
 }) => {
+  const { t: translate } = useTranslation();
   const containerRef = useRef<HTMLDivElement>();
   const containerWidth = useContainerWidth(containerRef);
 
   const stats = [
-    { label: "Operator", value: taskInstance.operator },
-    ...(taskInstance.map_index > -1 ? [{ label: "Map Index", value: 
taskInstance.rendered_map_index }] : []),
-    ...(taskInstance.try_number > 1 ? [{ label: "Try Number", value: 
taskInstance.try_number }] : []),
-    { label: "Start", value: <Time datetime={taskInstance.start_date} /> },
-    { label: "End", value: <Time datetime={taskInstance.end_date} /> },
+    { label: translate("task.operator"), value: taskInstance.operator },
+    ...(taskInstance.map_index > -1
+      ? [{ label: translate("mapIndex"), value: 
taskInstance.rendered_map_index }]
+      : []),
+    ...(taskInstance.try_number > 1
+      ? [{ label: translate("tryNumber"), value: taskInstance.try_number }]
+      : []),
+    { label: translate("startDate"), value: <Time 
datetime={taskInstance.start_date} /> },
+    { label: translate("endDate"), value: <Time 
datetime={taskInstance.end_date} /> },
     ...(Boolean(taskInstance.start_date)
-      ? [{ label: "Duration", value: getDuration(taskInstance.start_date, 
taskInstance.end_date) }]
+      ? [{ label: translate("duration"), value: 
getDuration(taskInstance.start_date, taskInstance.end_date) }]
       : []),
     {
-      label: "DAG Version",
+      label: translate("taskInstance.dagVersion"),
       value: <DagVersion version={taskInstance.dag_version} />,
     },
   ];
@@ -88,14 +94,14 @@ export const Header = ({
         actions={
           <>
             <EditableMarkdownButton
-              header="Task Instance Note"
+              header={translate("note.taskInstance")}
               icon={<FiMessageSquare />}
               isPending={isPending}
               mdContent={note}
               onConfirm={onConfirm}
-              placeholder="Add a note..."
+              placeholder={translate("note.placeholder")}
               setMdContent={setNote}
-              text={Boolean(taskInstance.note) ? "Note" : "Add a note"}
+              text={Boolean(taskInstance.note) ? translate("note.label") : 
translate("note.add")}
               withText={containerWidth > 700}
             />
             <ClearTaskInstanceButton
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/ExternalLogLink.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/ExternalLogLink.tsx
index e54d2bbd873..50aafc5d773 100644
--- 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/ExternalLogLink.tsx
+++ 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/ExternalLogLink.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { Button } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 import { useParams } from "react-router-dom";
 
 import { useTaskInstanceServiceGetExternalLogUrl } from "openapi/queries";
@@ -29,6 +30,7 @@ type Props = {
 };
 
 export const ExternalLogLink = ({ externalLogName, taskInstance, tryNumber }: 
Props) => {
+  const { t: translate } = useTranslation("dag");
   const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams();
 
   const {
@@ -57,7 +59,7 @@ export const ExternalLogLink = ({ externalLogName, 
taskInstance, tryNumber }: Pr
   return (
     <Button asChild colorScheme="blue" variant="outline">
       <a href={externalLogData.url} rel="noopener noreferrer" target="_blank">
-        View logs in {externalLogName} (attempt {tryNumber})
+        {translate("logs.viewInExternal", { attempt: tryNumber, name: 
externalLogName })}
       </a>
     </Button>
   );
diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx
index 25fce170dd0..96e9d06fccc 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx
@@ -19,6 +19,7 @@
 import { Box, Heading, VStack } from "@chakra-ui/react";
 import { useState } from "react";
 import { useHotkeys } from "react-hotkeys-hook";
+import { useTranslation } from "react-i18next";
 import { useParams, useSearchParams } from "react-router-dom";
 
 import { useTaskInstanceServiceGetMappedTaskInstance } from "openapi/queries";
@@ -34,6 +35,7 @@ import { TaskLogHeader } from "./TaskLogHeader";
 export const Logs = () => {
   const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams();
   const [searchParams, setSearchParams] = useSearchParams();
+  const { t: translate } = useTranslation("dag");
 
   const tryNumberParam = searchParams.get(SearchParamsKeys.TRY_NUMBER);
   const logLevelFilters = searchParams.getAll(SearchParamsKeys.LOG_LEVEL);
@@ -104,7 +106,7 @@ export const Logs = () => {
       />
       {showExternalLogRedirect && externalLogName && taskInstance ? (
         tryNumber === undefined ? (
-          <p>No try number</p>
+          <p>{translate("logs.noTryNumber")}</p>
         ) : (
           <ExternalLogLink
             externalLogName={externalLogName}
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx
index c11fce76741..c7fa69d6811 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { ReactFlowProvider } from "@xyflow/react";
+import { useTranslation } from "react-i18next";
 import { FiCode, FiDatabase } from "react-icons/fi";
 import { MdDetails, MdOutlineEventNote, MdOutlineTask, MdReorder, MdSyncAlt } 
from "react-icons/md";
 import { PiBracketsCurlyBold } from "react-icons/pi";
@@ -32,19 +33,24 @@ import { isStatePending, useAutoRefresh } from "src/utils";
 
 import { Header } from "./Header";
 
-const tabs = [
-  { icon: <MdReorder />, label: "Logs", value: "" },
-  { icon: <PiBracketsCurlyBold />, label: "Rendered Templates", value: 
"rendered_templates" },
-  { icon: <MdSyncAlt />, label: "XCom", value: "xcom" },
-  { icon: <MdOutlineEventNote />, label: "Audit Logs", value: "events" },
-  { icon: <FiCode />, label: "Code", value: "code" },
-  { icon: <MdDetails />, label: "Details", value: "details" },
-  { icon: <FiDatabase />, label: "Asset Events", value: "asset_events" },
-];
-
 export const TaskInstance = () => {
+  const { t: translate } = useTranslation("dag");
   const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams();
 
+  const tabs = [
+    { icon: <MdReorder />, label: translate("tabs.logs"), value: "" },
+    {
+      icon: <PiBracketsCurlyBold />,
+      label: translate("tabs.renderedTemplates"),
+      value: "rendered_templates",
+    },
+    { icon: <MdSyncAlt />, label: translate("tabs.xcom"), value: "xcom" },
+    { icon: <FiDatabase />, label: translate("tabs.assetEvents"), value: 
"asset_events" },
+    { icon: <MdOutlineEventNote />, label: translate("tabs.auditLog"), value: 
"events" },
+    { icon: <FiCode />, label: translate("tabs.code"), value: "code" },
+    { icon: <MdDetails />, label: translate("tabs.details"), value: "details" 
},
+  ];
+
   const refetchInterval = useAutoRefresh({ dagId });
 
   const {
@@ -98,7 +104,9 @@ export const TaskInstance = () => {
       ...tabs.slice(0, 1),
       {
         icon: <MdOutlineTask />,
-        label: `Task Instances [${mappedTaskInstance?.task_count ?? ""}]`,
+        label: translate("tabs.mappedTaskInstances_other", {
+          count: Number(mappedTaskInstance?.task_count ?? 0),
+        }),
         value: "task_instances",
       },
       ...tabs.slice(1),
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/TriggererInfo.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/TriggererInfo.tsx
index cc718810d58..9787635b38a 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/TriggererInfo.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/TriggererInfo.tsx
@@ -17,42 +17,47 @@
  * under the License.
  */
 import { Box, Table, Heading } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 
 import type { TaskInstanceResponse } from "openapi/requests/types.gen";
 import Time from "src/components/Time";
 
-export const TriggererInfo = ({ taskInstance }: { readonly taskInstance: 
TaskInstanceResponse }) => (
-  <Box py={1}>
-    <Heading py={1} size="sm">
-      Triggerer Info
-    </Heading>
-    <Table.Root striped>
-      <Table.Body>
-        <Table.Row>
-          <Table.Cell>Trigger class</Table.Cell>
-          <Table.Cell>{taskInstance.trigger?.classpath}</Table.Cell>
-        </Table.Row>
-        <Table.Row>
-          <Table.Cell>Trigger ID</Table.Cell>
-          <Table.Cell>{taskInstance.trigger?.id}</Table.Cell>
-        </Table.Row>
-        <Table.Row>
-          <Table.Cell>Trigger creation time</Table.Cell>
-          <Table.Cell>
-            <Time datetime={taskInstance.trigger?.created_date} />
-          </Table.Cell>
-        </Table.Row>
-        <Table.Row>
-          <Table.Cell>Assigned triggerer</Table.Cell>
-          <Table.Cell>{taskInstance.triggerer_job?.hostname}</Table.Cell>
-        </Table.Row>
-        <Table.Row>
-          <Table.Cell>Latest triggerer heartbeat</Table.Cell>
-          <Table.Cell>
-            <Time datetime={taskInstance.triggerer_job?.latest_heartbeat} />
-          </Table.Cell>
-        </Table.Row>
-      </Table.Body>
-    </Table.Root>
-  </Box>
-);
+export const TriggererInfo = ({ taskInstance }: { readonly taskInstance: 
TaskInstanceResponse }) => {
+  const { t: translate } = useTranslation();
+
+  return (
+    <Box py={1}>
+      <Heading py={1} size="sm">
+        {translate("taskInstance.triggerer.title")}
+      </Heading>
+      <Table.Root striped>
+        <Table.Body>
+          <Table.Row>
+            
<Table.Cell>{translate("taskInstance.triggerer.class")}</Table.Cell>
+            <Table.Cell>{taskInstance.trigger?.classpath}</Table.Cell>
+          </Table.Row>
+          <Table.Row>
+            <Table.Cell>{translate("taskInstance.triggerer.id")}</Table.Cell>
+            <Table.Cell>{taskInstance.trigger?.id}</Table.Cell>
+          </Table.Row>
+          <Table.Row>
+            
<Table.Cell>{translate("taskInstance.triggerer.createdAt")}</Table.Cell>
+            <Table.Cell>
+              <Time datetime={taskInstance.trigger?.created_date} />
+            </Table.Cell>
+          </Table.Row>
+          <Table.Row>
+            
<Table.Cell>{translate("taskInstance.triggerer.assigned")}</Table.Cell>
+            <Table.Cell>{taskInstance.triggerer_job?.hostname}</Table.Cell>
+          </Table.Row>
+          <Table.Row>
+            
<Table.Cell>{translate("taskInstance.triggerer.latestHeartbeat")}</Table.Cell>
+            <Table.Cell>
+              <Time datetime={taskInstance.triggerer_job?.latest_heartbeat} />
+            </Table.Cell>
+          </Table.Row>
+        </Table.Body>
+      </Table.Root>
+    </Box>
+  );
+};
diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx 
b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
index 1fe894d3edf..842bd7ac7d4 100644
--- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Link } from "@chakra-ui/react";
+import { Box, Heading, Link } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import { useTranslation } from "react-i18next";
 import { Link as RouterLink, useParams } from "react-router-dom";
@@ -119,6 +119,9 @@ export const XCom = () => {
 
   return (
     <Box>
+      {dagId === "~" && runId === "~" && taskId === "~" ? (
+        <Heading size="md">{translate("xcom.title")}</Heading>
+      ) : undefined}
       <ErrorAlert error={error} />
       <DataTable
         columns={columns(translate)}
@@ -127,7 +130,7 @@ export const XCom = () => {
         initialState={tableURLState}
         isFetching={isFetching}
         isLoading={isLoading}
-        modelName="XCom"
+        modelName={translate("xcom.title")}
         onStateChange={setTableURLState}
         skeletonCount={undefined}
         total={data ? data.total_entries : 0}
diff --git a/airflow-core/src/airflow/ui/src/utils/TrimText.tsx 
b/airflow-core/src/airflow/ui/src/utils/TrimText.tsx
index 39d8993bad8..3ae671d529a 100644
--- a/airflow-core/src/airflow/ui/src/utils/TrimText.tsx
+++ b/airflow-core/src/airflow/ui/src/utils/TrimText.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { Text, Box, useDisclosure, Heading, Stack } from "@chakra-ui/react";
+import { useTranslation } from "react-i18next";
 
 import { Dialog, Tooltip } from "src/components/ui";
 
@@ -44,6 +45,7 @@ export const TrimText = ({
   showTooltip = false,
   text,
 }: TrimTextProps) => {
+  const { t: translate } = useTranslation(["components"]);
   const safeText = text ?? "";
   const { isTrimmed, trimmedText } = trimText(safeText, charLimit);
 
@@ -70,7 +72,7 @@ export const TrimText = ({
       <Dialog.Root onOpenChange={onClose} open={isClickable ? open : 
undefined} size="xl">
         <Dialog.Content backdrop>
           <Dialog.Header>
-            <Heading size="xl">Details</Heading>
+            <Heading size="xl">{translate("trimText.details")}</Heading>
           </Dialog.Header>
 
           <Dialog.CloseTrigger />
@@ -100,14 +102,14 @@ export const TrimText = ({
                           color={isEmpty ? "gray.emphasized" : undefined}
                           fontWeight={isEmpty ? "bold" : "normal"}
                         >
-                          {isEmpty ? "Empty" : String(value)}
+                          {isEmpty ? translate("trimText.empty") : 
String(value)}
                         </Text>
                       </Box>
                     </Box>
                   );
                 })
               ) : (
-                <Text>No content available.</Text>
+                <Text>{translate("trimText.noContent")}</Text>
               )}
             </Stack>
           </Dialog.Body>
diff --git a/airflow-core/src/airflow/ui/src/utils/index.ts 
b/airflow-core/src/airflow/ui/src/utils/index.ts
index 49924fabcad..d1badfb8f00 100644
--- a/airflow-core/src/airflow/ui/src/utils/index.ts
+++ b/airflow-core/src/airflow/ui/src/utils/index.ts
@@ -18,7 +18,6 @@
  */
 
 export { capitalize } from "./capitalize";
-export { pluralize } from "./pluralize";
 export { getDuration, renderDuration } from "./datetimeUtils";
 export { getMetaKey } from "./getMetaKey";
 export { useContainerWidth } from "./useContainerWidth";
diff --git a/airflow-core/src/airflow/ui/src/utils/pluralize.test.ts 
b/airflow-core/src/airflow/ui/src/utils/pluralize.test.ts
deleted file mode 100644
index 541caf62318..00000000000
--- a/airflow-core/src/airflow/ui/src/utils/pluralize.test.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*!
- * 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.
- */
-import { describe, expect, it } from "vitest";
-
-import { pluralize } from "./pluralize";
-
-type PluralizeTestCase = {
-  in: [string, number, string?, boolean?];
-  out: string;
-};
-
-const pluralizeTestCases = [
-  { in: ["DAG", 0, undefined, undefined], out: "0 DAGs" },
-  { in: ["DAG", 1, undefined, undefined], out: "1 DAG" },
-  { in: ["DAG", 12_000, undefined, undefined], out: "12,000 DAGs" },
-  { in: ["DAG", 12_000_000, undefined, undefined], out: "12,000,000 DAGs" },
-  { in: ["DAG", 0, undefined, undefined], out: "0 DAGs" },
-  { in: ["DAG", 1, undefined, undefined], out: "1 DAG" },
-  { in: ["DAG", 12_000, undefined, undefined], out: "12,000 DAGs" },
-  { in: ["DAG", 12_000_000, undefined, undefined], out: "12,000,000 DAGs" },
-  // Omit the count.
-  { in: ["DAG", 0, undefined, true], out: "DAGs" },
-  { in: ["DAG", 1, undefined, true], out: "DAG" },
-  { in: ["DAG", 12_000, undefined, true], out: "DAGs" },
-  { in: ["DAG", 12_000_000, undefined, true], out: "DAGs" },
-  { in: ["DAG", 0, undefined, true], out: "DAGs" },
-  { in: ["DAG", 1, undefined, true], out: "DAG" },
-  { in: ["DAG", 12_000, undefined, true], out: "DAGs" },
-  { in: ["DAG", 12_000_000, undefined, true], out: "DAGs" },
-  // The casing of the string is preserved.
-  { in: ["goose", 0, "geese", undefined], out: "0 geese" },
-  { in: ["goose", 1, "geese", undefined], out: "1 goose" },
-  // The plural form is different from the singular form.
-  { in: ["Goose", 0, "Geese", undefined], out: "0 Geese" },
-  { in: ["Goose", 1, "Geese", undefined], out: "1 Goose" },
-  { in: ["Goose", 12_000, "Geese", undefined], out: "12,000 Geese" },
-  { in: ["Goose", 12_000_000, "Geese", undefined], out: "12,000,000 Geese" },
-  { in: ["Goose", 0, "Geese", undefined], out: "0 Geese" },
-  { in: ["Goose", 1, "Geese", undefined], out: "1 Goose" },
-  { in: ["Goose", 12_000, "Geese", undefined], out: "12,000 Geese" },
-  { in: ["Goose", 12_000_000, "Geese", undefined], out: "12,000,000 Geese" },
-  // In the case of "Moose", the plural is the same as the singular and you
-  // probably wouldn't elect to use this function at all, but there could be
-  // cases where dynamic data makes it unavoidable.
-  { in: ["Moose", 0, "Moose", undefined], out: "0 Moose" },
-  { in: ["Moose", 1, "Moose", undefined], out: "1 Moose" },
-  { in: ["Moose", 12_000, "Moose", undefined], out: "12,000 Moose" },
-  { in: ["Moose", 12_000_000, "Moose", undefined], out: "12,000,000 Moose" },
-  { in: ["Moose", 0, "Moose", undefined], out: "0 Moose" },
-  { in: ["Moose", 1, "Moose", undefined], out: "1 Moose" },
-  { in: ["Moose", 12_000, "Moose", undefined], out: "12,000 Moose" },
-  { in: ["Moose", 12_000_000, "Moose", undefined], out: "12,000,000 Moose" },
-] as const satisfies Array<PluralizeTestCase>;
-
-describe("pluralize", () => {
-  it("case", () => {
-    pluralizeTestCases.forEach((testCase) =>
-      expect(pluralize(testCase.in[0], testCase.in[1], testCase.in[2], 
testCase.in[3])).toEqual(testCase.out),
-    );
-  });
-});
diff --git a/airflow-core/src/airflow/ui/src/utils/pluralize.ts 
b/airflow-core/src/airflow/ui/src/utils/pluralize.ts
deleted file mode 100644
index b8e13dc84a8..00000000000
--- a/airflow-core/src/airflow/ui/src/utils/pluralize.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*!
- * 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.
- */
-
-export const pluralize = (
-  singularLabel: string,
-  count = 0,
-  pluralLabel = `${singularLabel}s`,
-  omitCount = false,
-  // eslint-disable-next-line @typescript-eslint/max-params
-) =>
-  // toLocaleString() will add commas for thousands, millions, etc.
-  `${omitCount ? "" : `${count.toLocaleString()} `}${count === 1 ? 
singularLabel : pluralLabel}`;

Reply via email to