From bfe84f4dee5fa7b1c89f532b09fa03a83534ecce Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Sat, 31 Oct 2026 16:50:11 -0700 Subject: [PATCH] refactor(ui): move admin and settings text onto i18n Route the page-authored English on the admin panel, caching, access groups, tag management, logging and alerts, and router settings pages through react-i18next, with 1,183 messages added to en.json. The rendered English is unchanged --- .../_components/AccessGroupsDetailsPage.tsx | 79 +- .../AccessGroupsModal/AccessGroupBaseForm.tsx | 44 +- .../AccessGroupEditModal.tsx | 11 +- .../_components/AccessGroupsPage.tsx | 28 +- .../_components/AccessGroupsTable.tsx | 15 +- .../_components/AccessGroupsTableColumns.tsx | 44 +- .../AccessGroupCreateDialog.tsx | 48 +- .../admin-panel/_components/AdminPanel.tsx | 105 +- .../caching/_components/ErrorDrilldown.tsx | 61 +- .../caching/_components/cache_dashboard.tsx | 136 +- .../caching/_components/cache_health.tsx | 87 +- .../cache_settings/CacheFormField.tsx | 22 +- .../cache_settings/RedisTypeSelector.tsx | 23 +- .../cache_settings/cacheSettingsFields.ts | 121 +- .../_components/cache_settings/index.tsx | 46 +- .../CoordinationRedisFormField.tsx | 8 +- .../CoordinationRedisTypeSelector.tsx | 44 +- .../coordinationRedisFields.ts | 77 +- .../coordinationRedisUtils.ts | 39 +- .../coordination_redis_settings/index.tsx | 53 +- .../_components/response_time_indicator.tsx | 4 +- .../_components/RequestTagMappings.tsx | 72 +- .../_components/general_settings.tsx | 52 +- .../tag-management/_components/TagTable.tsx | 28 +- .../_components/components/CreateTagModal.tsx | 56 +- .../tag-management/_components/index.tsx | 64 +- .../_components/tagTableColumns.tsx | 44 +- .../tag-management/_components/tag_info.tsx | 111 +- .../CloudZeroCostTracking.tsx | 7 +- .../CloudZeroCreateModal.tsx | 46 +- .../CloudZeroEmptyPlaceholder.tsx | 8 +- .../CloudZeroFormControls.tsx | 6 +- .../CloudZeroIntegrationSettings.tsx | 74 +- .../CloudZeroUpdateModal.tsx | 47 +- .../src/components/MSTeamsSettings.tsx | 46 +- ui/litellm-dashboard/src/components/SCIM.tsx | 60 +- .../src/components/SSOModals.tsx | 48 +- .../AdminSettings/CyberArk/CyberArk.tsx | 72 +- .../CyberArk/CyberArkEmptyPlaceholder.tsx | 8 +- .../CyberArk/EditCyberArkModal.tsx | 47 +- .../AdminSettings/CyberArk/constants.ts | 20 +- .../EditHashicorpVaultModal.tsx | 49 +- .../HashicorpVault/HashicorpVault.tsx | 77 +- .../HashicorpVaultEmptyPlaceholder.tsx | 10 +- .../AdminSettings/HashicorpVault/constants.ts | 30 +- .../LoggingSettings/LoggingSettings.tsx | 80 +- .../PluginSettings/PluginSettings.tsx | 82 +- .../AdminSettings/PluginSettings/schema.ts | 12 +- .../Modals/AddSSOSettingsModal.tsx | 14 +- .../Modals/BaseSSOSettingsForm.tsx | 195 +- .../Modals/DeleteSSOSettingsModal.tsx | 21 +- .../Modals/EditSSOSettingsModal.tsx | 16 +- .../SSOSettings/RedactableField.tsx | 12 +- .../SSOSettings/RoleMappings.tsx | 25 +- .../AdminSettings/SSOSettings/SSOSettings.tsx | 132 +- .../SSOSettingsEmptyPlaceholder.tsx | 9 +- .../SSOSettingsLoadingSkeleton.tsx | 8 +- .../AdminSettings/SSOSettings/constants.ts | 23 +- .../UISettings/BackgroundChatSettings.tsx | 36 +- .../UISettings/PageVisibilitySettings.tsx | 23 +- .../TeamAdminEditableFieldsSettings.tsx | 37 +- .../AdminSettings/UISettings/UISettings.tsx | 150 +- .../UserBannerSettings/UserBannerSettings.tsx | 46 +- .../WebSearchInterceptionSettings.tsx | 51 +- .../LoggingCallbacksTable.tsx | 21 +- .../LoggingCallbacksTableColumns.tsx | 53 +- .../RouterSettings/Fallbacks/AddFallbacks.tsx | 16 +- .../Fallbacks/AddFallbacksModal.tsx | 8 +- .../Fallbacks/EditFallbacks.tsx | 8 +- .../Fallbacks/FallbackGroupConfig.tsx | 37 +- .../Fallbacks/FallbackSelectionForm.tsx | 23 +- .../RouterSettings/Fallbacks/Fallbacks.tsx | 64 +- .../src/components/UIAccessControlForm.tsx | 40 +- .../components/alerting/alerting_settings.tsx | 4 +- .../src/components/alerting/dynamic_form.tsx | 14 +- .../MetadataKeyValueFields.tsx | 18 +- .../email_events/email_event_settings.tsx | 20 +- .../src/components/email_settings.tsx | 67 +- .../routing_groups/RoutingGroupModal.tsx | 86 +- .../routing_groups/RoutingGroupUsagePanel.tsx | 24 +- .../routing_groups/RoutingGroupsTable.tsx | 15 +- .../RoutingGroupsTableColumns.tsx | 34 +- .../src/components/routing_groups/index.tsx | 48 +- .../routing_groups/modelOwnership.ts | 9 +- .../routing_groups/routingGroupPayload.ts | 13 +- .../src/components/routing_groups/strategy.ts | 17 +- .../src/components/settings.tsx | 151 +- .../src/components/skills/SkillSelector.tsx | 24 +- ui/litellm-dashboard/src/i18n/locales/en.json | 1839 +++++++++++++++++ 89 files changed, 4214 insertions(+), 1588 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx index 11d75c0f750..f0529121b21 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx @@ -1,4 +1,5 @@ import { useAccessGroupDetails } from "@/app/(dashboard)/hooks/accessGroups/useAccessGroupDetails"; +import type { TFunction } from "i18next"; import { ArrowLeftIcon, BotIcon, @@ -10,6 +11,7 @@ import { UsersIcon, } from "lucide-react"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import DefaultProxyAdminTag from "@/components/common_components/DefaultProxyAdminTag"; import { BadgeLink } from "@/components/shared/BadgeLink"; import CopyButton from "@/components/shared/CopyButton"; @@ -34,11 +36,11 @@ const MAX_PREVIEW = 5; const shortId = (id: string) => (id.length > 20 ? `${id.slice(0, 10)}...${id.slice(-6)}` : id); -const describeAllowedTools = (tools: readonly string[] | undefined): string => { +const describeAllowedTools = (tools: readonly string[] | undefined, t: TFunction): string => { if (tools === undefined) { - return "All tools"; + return t("accessGroups.detail.toolPermissions.allTools"); } - return tools.length === 0 ? "No tools" : tools.join(", "); + return tools.length === 0 ? t("accessGroups.detail.toolPermissions.noTools") : tools.join(", "); }; function MCPToolPermissionList({ @@ -48,16 +50,17 @@ function MCPToolPermissionList({ servers: readonly AccessGroupResource[]; toolPermissions: Readonly> | null; }) { + const { t } = useTranslation(); if (toolPermissions === null || servers.length === 0) { return null; } return ( -
+
{servers.map(({ id, name }) => { return (
{name ?? id} - {describeAllowedTools(toolPermissions[id])} + {describeAllowedTools(toolPermissions[id], t)}
); })} @@ -106,6 +109,7 @@ function ResourceBadge({ } export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailProps) { + const { t } = useTranslation(); const { data: accessGroup, isLoading } = useAccessGroupDetails(accessGroupId); const [isEditModalVisible, setIsEditModalVisible] = useState(false); const [showAllKeys, setShowAllKeys] = useState(false); @@ -124,10 +128,16 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr if (!accessGroup) { return (
- -

Access group not found

+

{t("accessGroups.detail.notFound")}

); } @@ -146,47 +156,50 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
-

{accessGroup.access_group_name}

- ID: {accessGroup.access_group_id} - + + {t("accessGroups.detail.idLabel")} + {accessGroup.access_group_id} + +
- Group Details + {t("accessGroups.detail.groupDetails")}
-
Description
+
{t("accessGroups.detail.description")}
{accessGroup.description || "—"}
-
Created
+
{t("accessGroups.detail.created")}
{new Date(accessGroup.created_at).toLocaleString()} {accessGroup.created_by && ( <> - by + {t("accessGroups.detail.by")} )}
-
Last Updated
+
{t("accessGroups.detail.lastUpdated")}
{new Date(accessGroup.updated_at).toLocaleString()} {accessGroup.updated_by && ( <> - by + {t("accessGroups.detail.by")} )} @@ -200,13 +213,15 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr - Attached Keys + {t("accessGroups.detail.attachedKeys")} {keys.length} {keys.length > MAX_PREVIEW && ( )} @@ -219,7 +234,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr ))}
) : ( -

No keys attached

+

{t("accessGroups.detail.noKeys")}

)} @@ -228,13 +243,15 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr - Attached Teams + {t("accessGroups.detail.attachedTeams")} {teams.length} {teams.length > MAX_PREVIEW && ( )} @@ -247,7 +264,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr ))} ) : ( -

No teams attached

+

{t("accessGroups.detail.noTeams")}

)} @@ -259,37 +276,37 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr - Models + {t("accessGroups.detail.tabs.models")} {models.length} - MCP Servers + {t("accessGroups.detail.tabs.mcpServers")} {mcpServers.length} - Agents + {t("accessGroups.detail.tabs.agents")} {agents.length} - Skills + {t("accessGroups.detail.tabs.skills")} {skills.length} - + - + - + - + diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx index 43eaf11b60d..35089234d06 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx @@ -2,6 +2,7 @@ import { BotIcon, InfoIcon, LayersIcon, ServerIcon, SparklesIcon } from "lucide-react"; import type { UseFormReturn } from "react-hook-form"; +import { useTranslation } from "react-i18next"; import { z } from "zod"; import { useAgents } from "@/app/(dashboard)/hooks/agents/useAgents"; @@ -16,9 +17,10 @@ import SkillSelector from "@/components/skills/SkillSelector"; import { Input } from "@/components/ui/input"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; +import i18n from "@/i18n/i18n"; export const accessGroupFormSchema = z.object({ - name: z.string().min(1, "Please enter the access group name"), + name: z.string().min(1, { error: () => i18n.t("accessGroups.form.name.required") }), description: z.string(), modelIds: z.array(z.string()), mcpServerIds: z.array(z.string()), @@ -80,6 +82,7 @@ export function AccessGroupBaseForm({ onTabChange, initialServerIds, }: AccessGroupBaseFormProps) { + const { t } = useTranslation(); const { accessToken } = useAuthorized(); const { data: agentsData } = useAgents(); const { data: mcpServersData } = useMCPServers(); @@ -98,56 +101,61 @@ export function AccessGroupBaseForm({ - General Info + {t("accessGroups.form.tabs.general")} - Models + {t("accessGroups.form.tabs.models")} - MCP Servers + {t("accessGroups.form.tabs.mcpServers")} - Agents + {t("accessGroups.form.tabs.agents")} - Skills + {t("accessGroups.form.tabs.skills")} - + {({ ref, ...field }) => ( - + )} - + {({ ref, ...field }) => ( -