From 625feb0ab7799cba32556438518131f247b83802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B9=E9=9B=AA?= <2b@yorha.xyz> Date: Wed, 10 Jun 2026 13:19:22 +0900 Subject: [PATCH] feat(ui): i18n for top-level components batch 8 --- .../src/components/claude_code_plugins.tsx | 29 ++-- .../src/components/cloudzero_export_modal.tsx | 71 ++++++---- .../src/components/edit_user.tsx | 37 +++-- .../src/components/email_settings.tsx | 47 +++---- .../src/components/general_settings.tsx | 28 ++-- .../src/components/guardrails.tsx | 38 ++--- .../src/components/key_value_input.tsx | 12 +- .../src/components/logging_settings_view.tsx | 32 +++-- .../src/components/mcp_hub_table_columns.tsx | 38 ++--- ui/litellm-dashboard/src/locales/en.json | 131 ++++++++++++++++++ ui/litellm-dashboard/src/locales/zh-CN.json | 131 ++++++++++++++++++ 11 files changed, 454 insertions(+), 140 deletions(-) diff --git a/ui/litellm-dashboard/src/components/claude_code_plugins.tsx b/ui/litellm-dashboard/src/components/claude_code_plugins.tsx index 0ec775e0c0b..da8455e4179 100644 --- a/ui/litellm-dashboard/src/components/claude_code_plugins.tsx +++ b/ui/litellm-dashboard/src/components/claude_code_plugins.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import { Button } from "@tremor/react"; import { Modal } from "antd"; +import { useTranslation, Trans } from "react-i18next"; import { getClaudeCodePluginsList, deleteClaudeCodePlugin } from "./networking"; import AddPluginForm from "./claude_code_plugins/add_plugin_form"; import PluginTable from "./claude_code_plugins/plugin_table"; @@ -15,6 +16,7 @@ interface ClaudeCodePluginsPanelProps { } const ClaudeCodePluginsPanel: React.FC = ({ accessToken, userRole }) => { + const { t } = useTranslation(); const [pluginsList, setPluginsList] = useState([]); const [isAddModalVisible, setIsAddModalVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -55,11 +57,11 @@ const ClaudeCodePluginsPanel: React.FC = ({ accessT setIsDeleting(true); try { await deleteClaudeCodePlugin(accessToken, pluginToDelete.name); - NotificationsManager.success(`Skill "${pluginToDelete.displayName}" deleted successfully`); + NotificationsManager.success(t("claudeCodePlugins.deleteSuccess", { name: pluginToDelete.displayName })); fetchPlugins(); } catch (error) { console.error("Error deleting skill:", error); - NotificationsManager.error("Failed to delete skill"); + NotificationsManager.error(t("claudeCodePlugins.deleteFailed")); } finally { setIsDeleting(false); setPluginToDelete(null); @@ -79,14 +81,17 @@ const ClaudeCodePluginsPanel: React.FC = ({ accessT ) : ( <>
-

Skills

+

{t("claudeCodePlugins.skillsTitle")}

- Register Claude Code skills. Published skills appear in the Skill Hub for all users and are served via{" "} - /claude-code/marketplace.json. + }} + />

@@ -114,18 +119,22 @@ const ClaudeCodePluginsPanel: React.FC = ({ accessT {pluginToDelete && ( setPluginToDelete(null)} confirmLoading={isDeleting} - okText="Delete" + okText={t("common.delete")} okButtonProps={{ danger: true }} >

- Are you sure you want to delete skill: {pluginToDelete.displayName}? + }} + />

-

This action cannot be undone.

+

{t("claudeCodePlugins.cannotUndone")}

)} diff --git a/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx b/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx index 6a3d5ded452..d970801da77 100644 --- a/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx +++ b/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import { Text, Button, Callout, TextInput } from "@tremor/react"; import { Modal, Form, Spin, Select } from "antd"; +import { useTranslation } from "react-i18next"; import { getGlobalLitellmHeaderName } from "@/components/networking"; import NotificationsManager from "./molecules/notifications_manager"; @@ -24,6 +25,7 @@ interface CloudZeroSettingsView { type ExportType = "cloudzero" | "csv"; const CloudZeroExportModal: React.FC = ({ isOpen, onClose, accessToken }) => { + const { t } = useTranslation(); const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [existingSettings, setExistingSettings] = useState(null); @@ -59,11 +61,13 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC } else if (response.status !== 404) { // 404 means no settings configured yet, which is fine const errorData = await response.json(); - NotificationsManager.fromBackend(`Failed to load existing settings: ${errorData.error || "Unknown error"}`); + NotificationsManager.fromBackend( + t("cloudzeroExportModal.failedToLoadSettings", { error: errorData.error || t("common.unknown") }), + ); } } catch (error) { console.error("Error loading CloudZero settings:", error); - NotificationsManager.fromBackend("Failed to load existing settings"); + NotificationsManager.fromBackend(t("cloudzeroExportModal.failedToLoadSettingsGeneric")); } finally { setSettingsLoading(false); } @@ -71,7 +75,7 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC const handleSaveCloudZeroSettings = async (values: CloudZeroSettings) => { if (!accessToken) { - NotificationsManager.fromBackend("No access token available"); + NotificationsManager.fromBackend(t("cloudzeroExportModal.noAccessToken")); return; } @@ -98,7 +102,7 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC const data = await response.json(); if (response.ok) { - NotificationsManager.success(data.message || "CloudZero settings saved successfully"); + NotificationsManager.success(data.message || t("cloudzeroExportModal.settingsSavedSuccess")); setExistingSettings({ api_key_masked: values.api_key.substring(0, 4) + "****" + values.api_key.slice(-4), connection_id: values.connection_id, @@ -106,12 +110,12 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC }); return true; } else { - NotificationsManager.fromBackend(data.error || "Failed to save CloudZero settings"); + NotificationsManager.fromBackend(data.error || t("cloudzeroExportModal.failedToSaveSettings")); return false; } } catch (error) { console.error("Error saving CloudZero settings:", error); - NotificationsManager.fromBackend("Failed to save CloudZero settings"); + NotificationsManager.fromBackend(t("cloudzeroExportModal.failedToSaveSettings")); return false; } finally { setLoading(false); @@ -120,7 +124,7 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC const handleExportCloudZero = async () => { if (!accessToken) { - NotificationsManager.fromBackend("No access token available"); + NotificationsManager.fromBackend(t("cloudzeroExportModal.noAccessToken")); return; } @@ -141,14 +145,14 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC const data = await response.json(); if (response.ok) { - NotificationsManager.success(data.message || "Export to CloudZero completed successfully"); + NotificationsManager.success(data.message || t("cloudzeroExportModal.exportSuccess")); onClose(); } else { - NotificationsManager.fromBackend(data.error || "Failed to export to CloudZero"); + NotificationsManager.fromBackend(data.error || t("cloudzeroExportModal.failedToExport")); } } catch (error) { console.error("Error exporting to CloudZero:", error); - NotificationsManager.fromBackend("Failed to export to CloudZero"); + NotificationsManager.fromBackend(t("cloudzeroExportModal.failedToExport")); } finally { setExportLoading(false); } @@ -158,11 +162,11 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC setExportLoading(true); try { // TODO: Implement CSV export functionality - NotificationsManager.info("CSV export functionality coming soon!"); + NotificationsManager.info(t("cloudzeroExportModal.csvComingSoon")); onClose(); } catch (error) { console.error("Error exporting CSV:", error); - NotificationsManager.fromBackend("Failed to export CSV"); + NotificationsManager.fromBackend(t("cloudzeroExportModal.failedToExportCsv")); } finally { setExportLoading(false); } @@ -203,7 +207,7 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC (e.target as HTMLImageElement).style.display = "none"; }} /> - Export to CloudZero + {t("cloudzeroExportModal.exportToCloudZero")} ), }, @@ -219,18 +223,25 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> - Export to CSV + {t("cloudzeroExportModal.exportToCsv")} ), }, ]; return ( - +
{/* Export Type Selection */}
- Export Destination + {t("cloudzeroExportModal.exportDestination")}