From c48e2ad23fb211ea71030611fc9fa239efb50d0d 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 21:43:53 +0900 Subject: [PATCH] feat(ui): i18n for playground chat UI --- .../components/playground/chat_ui/ChatUI.tsx | 306 ++++++++++-------- ui/litellm-dashboard/src/locales/en.json | 115 +++++++ ui/litellm-dashboard/src/locales/zh-CN.json | 115 +++++++ 3 files changed, 396 insertions(+), 140 deletions(-) diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx index 5ba2df607cc..da04b11760e 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx @@ -24,6 +24,7 @@ import { import { Card, Text, TextInput, Title, Button as TremorButton } from "@tremor/react"; import { Button, Input, Modal, Popover, Select, Spin, Tooltip, Typography, Upload } from "antd"; import React, { useEffect, useRef, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; import ReactMarkdown from "react-markdown"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coy } from "react-syntax-highlighter/dist/esm/styles/prism"; @@ -109,6 +110,7 @@ const ChatUI: React.FC = ({ simplified = false, fixedModel, }) => { + const { t } = useTranslation(); const [mcpServers, setMCPServers] = useState([]); const [mcpToolsets, setMCPToolsets] = useState([]); const [isToolsetsInfoModalVisible, setIsToolsetsInfoModalVisible] = useState(false); @@ -491,7 +493,7 @@ const ChatUI: React.FC = ({ abortControllerRef.current.abort(); abortControllerRef.current = null; setIsLoading(false); - NotificationsManager.info("Request cancelled"); + NotificationsManager.info(t("playground.chatUi.requestCancelled")); } }; @@ -565,19 +567,19 @@ const ChatUI: React.FC = ({ // For image edits, require both image and prompt if (endpointType === EndpointType.IMAGE_EDITS && uploadedImages.length === 0) { - NotificationsManager.fromBackend("Please upload at least one image for editing"); + NotificationsManager.fromBackend(t("playground.chatUi.pleaseUploadImage")); return; } // For audio transcriptions, require audio file if (endpointType === EndpointType.TRANSCRIPTION && !uploadedAudio) { - NotificationsManager.fromBackend("Please upload an audio file for transcription"); + NotificationsManager.fromBackend(t("playground.chatUi.pleaseUploadAudio")); return; } // For A2A agents, require agent selection if (endpointType === EndpointType.A2A_AGENTS && !selectedAgent) { - NotificationsManager.fromBackend("Please select an agent to send a message"); + NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectAgent")); return; } @@ -587,13 +589,13 @@ const ChatUI: React.FC = ({ const rawSelected = selectedMCPServers.length === 1 && selectedMCPServers[0] !== "__all__" ? selectedMCPServers[0] : null; if (!rawSelected) { - NotificationsManager.fromBackend("Please select an MCP server to test"); + NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectMCPServer")); return; } // Resolve the real server ID (toolsets use toolset: prefix) const mcpServerId = rawSelected.startsWith("toolset:") ? rawSelected : rawSelected; if (!selectedMCPDirectTool) { - NotificationsManager.fromBackend("Please select an MCP tool to call"); + NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectMCPTool")); return; } // For toolsets, find the tool in the servers that back this toolset @@ -611,13 +613,15 @@ const ChatUI: React.FC = ({ } const mcpTool = searchPool.find((t: any) => t.name === selectedMCPDirectTool); if (!mcpTool) { - NotificationsManager.fromBackend("Please wait for tool schema to load"); + NotificationsManager.fromBackend(t("playground.chatUi.waitForToolSchema")); return; } try { mcpToolArguments = (await mcpToolArgsFormRef.current?.getSubmitValues()) ?? {}; } catch (err) { - NotificationsManager.fromBackend(err instanceof Error ? err.message : "Please fill in all required parameters"); + NotificationsManager.fromBackend( + err instanceof Error ? err.message : t("playground.chatUi.fillRequiredParameters"), + ); return; } } @@ -636,7 +640,7 @@ const ChatUI: React.FC = ({ ]; if (modelRequiredEndpoints.includes(endpointType as EndpointType) && !selectedModel) { - NotificationsManager.fromBackend("Please select a model before sending a request"); + NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectModel")); return; } @@ -647,7 +651,7 @@ const ChatUI: React.FC = ({ const effectiveApiKey = simplified ? accessToken : apiKeySource === "session" ? accessToken : apiKey; if (!effectiveApiKey) { - NotificationsManager.fromBackend("Please provide a Virtual Key or select Current UI Session"); + NotificationsManager.fromBackend(t("playground.chatUi.pleaseProvideVirtualKey")); return; } @@ -663,7 +667,7 @@ const ChatUI: React.FC = ({ try { newUserMessage = await createMultimodalMessage(inputMessage, responsesUploadedImage); } catch (error) { - NotificationsManager.fromBackend("Failed to process image. Please try again."); + NotificationsManager.fromBackend(t("playground.chatUi.failedToProcessImage")); return; } } @@ -672,7 +676,7 @@ const ChatUI: React.FC = ({ try { newUserMessage = await createChatMultimodalMessage(inputMessage, chatUploadedImage); } catch (error) { - NotificationsManager.fromBackend("Failed to process image. Please try again."); + NotificationsManager.fromBackend(t("playground.chatUi.failedToProcessImage")); return; } } else { @@ -939,7 +943,7 @@ const ChatUI: React.FC = ({ 2, ) : JSON.stringify(result, null, 2); - updateTextUI("assistant", resultText || "Tool executed successfully."); + updateTextUI("assistant", resultText || t("playground.chatUi.toolExecutedSuccessfully")); } } @@ -963,7 +967,7 @@ const ChatUI: React.FC = ({ console.log("Request was cancelled"); } else { console.error("Error fetching response", error); - updateTextUI("assistant", "Error fetching response:" + error); + updateTextUI("assistant", t("playground.chatUi.errorFetchingResponse", { error })); } } finally { setIsLoading(false); @@ -995,15 +999,15 @@ const ChatUI: React.FC = ({ handleRemoveResponsesImage(); handleRemoveChatImage(); handleRemoveAudio(); - NotificationsManager.success("Chat history cleared."); + NotificationsManager.success(t("playground.chatUi.chatHistoryCleared")); }; if (userRole && userRole === "Admin Viewer") { const { Title, Paragraph } = Typography; return (
- Access Denied - Ask your proxy admin for access to test models + {t("playground.chatUi.accessDenied")} + {t("playground.chatUi.askProxyAdminForAccess")}
); } @@ -1037,11 +1041,11 @@ const ChatUI: React.FC = ({ {/* Left Sidebar with Controls - hidden in simplified mode */} {!simplified && (
- Configurations + {t("playground.chatUi.configurations")}
- Virtual Key Source + {t("playground.chatUi.virtualKeySource")} = ({
- Select Model + {t("playground.chatUi.selectModelLabel")} {isChatModel() ? ( = ({ onMockTestFallbacksChange={setMockTestFallbacks} /> } - title="Model Settings" + title={t("playground.chatUi.modelSettings")} trigger="click" placement="right" > @@ -1200,12 +1206,12 @@ const ChatUI: React.FC = ({ size="small" icon={} className="text-gray-500 hover:text-gray-700" - aria-label="Model Settings" + aria-label={t("playground.chatUi.modelSettings")} data-testid="model-settings-button" /> ) : ( - +
)}
- Tags + {t("playground.chatUi.tagsLabel")} = ({
- {endpointType === EndpointType.MCP ? "MCP Server" : "MCP Servers"} + {endpointType === EndpointType.MCP + ? t("playground.chatUi.mcpServerLabel") + : t("playground.chatUi.mcpServersLabel")} = ({ setSelectedMCPDirectTool(value)} options={toolOptions} @@ -1542,13 +1556,15 @@ const ChatUI: React.FC = ({ return (
- Limit tools for {server?.alias || server?.server_name || serverId}: + {t("playground.chatUi.limitToolsFor", { + serverName: server?.alias || server?.server_name || serverId, + })} = ({

-

Click or drag audio file to upload

+

{t("playground.chatUi.dragAudioToUpload")}

- Support for MP3, MP4, MPEG, MPGA, M4A, WAV, WEBM formats. Max file size: 25 MB. + {t("playground.chatUi.audioUploadHint")}

) : ( @@ -1897,7 +1913,7 @@ const ChatUI: React.FC = ({ className="bg-white shadow-sm border border-gray-200 rounded px-2 py-1 text-red-500 hover:bg-red-50 text-xs" onClick={handleRemoveAudio} > - Remove + {t("playground.chatUi.removeButton")}
)} @@ -1929,12 +1945,16 @@ const ChatUI: React.FC = ({ {isLoading ? ( <> - Running Python code... + + {t("playground.chatUi.runningPythonCode")} + ) : ( <> - Code Interpreter Active + + {t("playground.chatUi.codeInterpreterActive")} + )}
@@ -1942,16 +1962,16 @@ const ChatUI: React.FC = ({ className="text-xs text-blue-500 hover:text-blue-700" onClick={() => codeInterpreter.setEnabled(false)} > - Disable + {t("playground.chatUi.disableButton")}
{/* Sample prompts - only show when not loading */} {!isLoading && (
{[ - "Generate sample sales data CSV and create a chart", - "Create a PNG bar chart comparing AI gateway providers including LiteLLM", - "Generate a CSV of LLM pricing data and visualize it as a line chart", + t("playground.chatUi.samplePrompt1"), + t("playground.chatUi.samplePrompt2"), + t("playground.chatUi.samplePrompt3"), ].map((prompt, idx) => (
@@ -2139,7 +2167,7 @@ const ChatUI: React.FC = ({
setIsGetCodeModalVisible(false)} footer={null} @@ -2147,24 +2175,24 @@ const ChatUI: React.FC = ({ >
- SDK Type + {t("playground.chatUi.sdkTypeLabel")}