From 524569b9ae95f2bf93c640d0274a451b348fa99e 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 22:53:17 +0900 Subject: [PATCH] feat(ui): i18n for prompt editor components --- .../components/prompts/add_prompt_form.tsx | 49 +++++++----- .../DeveloperMessageCard.tsx | 13 ++- .../prompt_editor_view/DotpromptViewTab.tsx | 6 +- .../prompt_editor_view/ModelConfigCard.tsx | 10 ++- .../prompt_editor_view/PromptCodeSnippets.tsx | 24 ++++-- .../prompt_editor_view/PromptEditorHeader.tsx | 20 +++-- .../prompt_editor_view/PromptMessagesCard.tsx | 19 +++-- .../prompt_editor_view/PublishModal.tsx | 16 ++-- .../prompts/prompt_editor_view/ToolsCard.tsx | 10 ++- .../VersionHistorySidePanel.tsx | 16 ++-- ui/litellm-dashboard/src/locales/en.json | 80 +++++++++++++++++++ ui/litellm-dashboard/src/locales/zh-CN.json | 80 +++++++++++++++++++ 12 files changed, 275 insertions(+), 68 deletions(-) diff --git a/ui/litellm-dashboard/src/components/prompts/add_prompt_form.tsx b/ui/litellm-dashboard/src/components/prompts/add_prompt_form.tsx index cdb77bb66fc..848c6b578a7 100644 --- a/ui/litellm-dashboard/src/components/prompts/add_prompt_form.tsx +++ b/ui/litellm-dashboard/src/components/prompts/add_prompt_form.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; import { Modal, Form, Select, Upload, Button, Divider } from "antd"; import { TextInput } from "@tremor/react"; import { UploadOutlined } from "@ant-design/icons"; @@ -22,6 +23,7 @@ interface PromptFormData { } const AddPromptForm: React.FC = ({ visible, onClose, accessToken, onSuccess }) => { + const { t } = useTranslation(); const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [fileList, setFileList] = useState([]); @@ -40,12 +42,12 @@ const AddPromptForm: React.FC = ({ visible, onClose, accessT console.log("values: ", values); if (!accessToken) { - NotificationsManager.fromBackend("Access token is required"); + NotificationsManager.fromBackend(t("promptsPage.addPromptForm.accessTokenRequired")); return; } if (promptIntegration === "dotprompt" && fileList.length === 0) { - NotificationsManager.fromBackend("Please upload a .prompt file"); + NotificationsManager.fromBackend(t("promptsPage.addPromptForm.uploadPromptFileRequired")); return; } @@ -75,7 +77,7 @@ const AddPromptForm: React.FC = ({ visible, onClose, accessT }; } catch (conversionError) { console.error("Error converting prompt file:", conversionError); - NotificationsManager.fromBackend("Failed to convert prompt file to JSON"); + NotificationsManager.fromBackend(t("promptsPage.addPromptForm.conversionFailed")); setLoading(false); return; } @@ -84,12 +86,12 @@ const AddPromptForm: React.FC = ({ visible, onClose, accessT // Create the prompt try { await createPromptCall(accessToken, promptData); - NotificationsManager.success("Prompt created successfully!"); + NotificationsManager.success(t("promptsPage.addPromptForm.createSuccess")); handleCancel(); onSuccess(); } catch (createError) { console.error("Error creating prompt:", createError); - NotificationsManager.fromBackend("Failed to create prompt"); + NotificationsManager.fromBackend(t("promptsPage.addPromptForm.createFailed")); } } catch (error) { console.error("Form validation error:", error); @@ -101,10 +103,10 @@ const AddPromptForm: React.FC = ({ visible, onClose, accessT const uploadProps: UploadProps = { beforeUpload: (file) => { if (!file.name.endsWith(".prompt")) { - NotificationsManager.fromBackend("Please upload a .prompt file"); + NotificationsManager.fromBackend(t("promptsPage.addPromptForm.uploadPromptFileRequired")); return false; } - return false; // Prevent automatic upload + return false; }, fileList, onChange: ({ fileList: newFileList }) => { @@ -117,35 +119,39 @@ const AddPromptForm: React.FC = ({ visible, onClose, accessT return ( - Cancel + {t("common.cancel")} , , ]} width={600} >
- + - + @@ -154,11 +160,18 @@ const AddPromptForm: React.FC = ({ visible, onClose, accessT {promptIntegration === "dotprompt" && ( <> - + - + - {fileList.length > 0 &&
Selected: {fileList[0].name}
} + {fileList.length > 0 && ( +
+ {t("promptsPage.addPromptForm.selected", { name: fileList[0].name })} +
+ )}
)} diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DeveloperMessageCard.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DeveloperMessageCard.tsx index b2e524e9e78..6feb469441f 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DeveloperMessageCard.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DeveloperMessageCard.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useTranslation } from "react-i18next"; import { Card, Text } from "@tremor/react"; import VariableTextArea from "../variable_textarea"; @@ -8,11 +9,17 @@ interface DeveloperMessageCardProps { } const DeveloperMessageCard: React.FC = ({ value, onChange }) => { + const { t } = useTranslation(); return ( - Developer message - Optional system instructions for the model - + {t("promptsPage.developerMessageCard.title")} + {t("promptsPage.developerMessageCard.subtitle")} + ); }; diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DotpromptViewTab.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DotpromptViewTab.tsx index 957833badda..f81cb9e9427 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DotpromptViewTab.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/DotpromptViewTab.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useTranslation } from "react-i18next"; import { PromptType } from "./types"; import { convertToDotPrompt } from "./utils"; @@ -7,13 +8,14 @@ interface DotpromptViewTabProps { } const DotpromptViewTab: React.FC = ({ prompt }) => { + const { t } = useTranslation(); const dotpromptContent = convertToDotPrompt(prompt); return (
-

Generated .prompt file

-

This is the dotprompt format that will be saved to the database

+

{t("promptsPage.dotpromptViewTab.title")}

+

{t("promptsPage.dotpromptViewTab.subtitle")}

{dotpromptContent}
diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ModelConfigCard.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ModelConfigCard.tsx index aa564160ddf..442715cb79d 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ModelConfigCard.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ModelConfigCard.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; import { Text } from "@tremor/react"; import { Input } from "antd"; import { SettingsIcon } from "lucide-react"; @@ -23,6 +24,7 @@ const ModelConfigCard: React.FC = ({ onTemperatureChange, onMaxTokensChange, }) => { + const { t } = useTranslation(); const [showConfig, setShowConfig] = useState(false); return ( @@ -36,14 +38,14 @@ const ModelConfigCard: React.FC = ({ className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50" > - Parameters + {t("promptsPage.modelConfigCard.parameters")} {showConfig && (
-

Model Parameters

+

{t("promptsPage.modelConfigCard.modelParameters")}

@@ -51,7 +53,7 @@ const ModelConfigCard: React.FC = ({
- Temperature + {t("promptsPage.modelConfigCard.temperature")} = ({
- Max Tokens + {t("promptsPage.modelConfigCard.maxTokens")} = ({ version = "1", proxySettings, }) => { + const { t } = useTranslation(); const [isModalVisible, setIsModalVisible] = useState(false); const [selectedLanguage, setSelectedLanguage] = useState<"curl" | "python" | "javascript">("curl"); const [selectedTab, setSelectedTab] = useState("basic"); @@ -235,13 +237,19 @@ main();`; return ( <> - Get Code + {t("promptsPage.promptCodeSnippets.getCode")} - +
- Language + {t("promptsPage.promptCodeSnippets.language")} = ({ style={{ width: 140 }} size="small" options={[ - { label: "Development", value: "development" }, - { label: "Staging", value: "staging" }, - { label: "Production", value: "production" }, + { label: t("promptsPage.promptEditorHeader.envDevelopment"), value: "development" }, + { label: t("promptsPage.promptEditorHeader.envStaging"), value: "staging" }, + { label: t("promptsPage.promptEditorHeader.envProduction"), value: "production" }, ]} /> - Draft - Unsaved changes + + {t("promptsPage.promptEditorHeader.draft")} + + {t("promptsPage.promptEditorHeader.unsavedChanges")}
= ({ /> {editMode && onShowHistory && ( - History + {t("promptsPage.promptEditorHeader.history")} )} - {editMode ? "Update" : "Save"} + {editMode ? t("common.update") : t("common.save")}
diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PromptMessagesCard.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PromptMessagesCard.tsx index ea9a0df0e50..2e99bd5b7ea 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PromptMessagesCard.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PromptMessagesCard.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import { useTranslation, Trans } from "react-i18next"; import { Card, Text } from "@tremor/react"; import { Select } from "antd"; import { PlusIcon, TrashIcon, GripVerticalIcon } from "lucide-react"; @@ -22,6 +23,7 @@ const PromptMessagesCard: React.FC = ({ onRemoveMessage, onMoveMessage, }) => { + const { t } = useTranslation(); const [draggedIndex, setDraggedIndex] = useState(null); const [dragOverIndex, setDragOverIndex] = useState(null); @@ -51,9 +53,12 @@ const PromptMessagesCard: React.FC = ({ return (
- Prompt messages + {t("promptsPage.promptMessagesCard.title")} - Use {"{{variable}}"} syntax for template variables + }} + />
@@ -77,9 +82,9 @@ const PromptMessagesCard: React.FC = ({ size="small" bordered={false} > - - - + + +
{messages.length > 1 && ( @@ -97,7 +102,7 @@ const PromptMessagesCard: React.FC = ({ value={message.content} onChange={(value) => onUpdateMessage(index, "content", value)} rows={3} - placeholder="Enter prompt content..." + placeholder={t("promptsPage.promptMessagesCard.contentPlaceholder")} />
@@ -105,7 +110,7 @@ const PromptMessagesCard: React.FC = ({
); diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PublishModal.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PublishModal.tsx index 8548ec9cce0..b0eb4178c94 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PublishModal.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/PublishModal.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useTranslation } from "react-i18next"; import { Button as TremorButton, Text } from "@tremor/react"; import { Input, Modal } from "antd"; @@ -19,34 +20,33 @@ const PublishModal: React.FC = ({ onPublish, onCancel, }) => { + const { t } = useTranslation(); return ( - Cancel + {t("common.cancel")} - Publish + {t("promptsPage.publishModal.publish")}
, ]} >
- Name + {t("common.name")} onNameChange(e.target.value)} - placeholder="Enter prompt name" + placeholder={t("promptsPage.publishModal.namePlaceholder")} onPressEnter={onPublish} autoFocus /> - - Published prompts can be used in API calls and are versioned for easy tracking. - + {t("promptsPage.publishModal.versionedHint")}
); diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.tsx index 969c9c84821..6435f813b68 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useTranslation } from "react-i18next"; import { Card, Text } from "@tremor/react"; import { PlusIcon, TrashIcon } from "lucide-react"; import { Tool } from "./types"; @@ -11,17 +12,18 @@ interface ToolsCardProps { } const ToolsCard: React.FC = ({ tools, onAddTool, onEditTool, onRemoveTool }) => { + const { t } = useTranslation(); return (
- Tools + {t("promptsPage.toolsCard.title")}
{tools.length === 0 ? ( - No tools added + {t("promptsPage.toolsCard.noTools")} ) : (
{tools.map((tool, index) => ( @@ -35,7 +37,7 @@ const ToolsCard: React.FC = ({ tools, onAddTool, onEditTool, onR
{isSelected && ( - Active + {t("promptsPage.versionHistorySidePanel.active")} )}
@@ -127,7 +129,9 @@ const VersionHistorySidePanel: React.FC = ({
{formatDate(item.created_at)} - {item.prompt_info?.prompt_type === "db" ? "Saved to Database" : "Config Prompt"} + {item.prompt_info?.prompt_type === "db" + ? t("promptsPage.versionHistorySidePanel.savedToDatabase") + : t("promptsPage.versionHistorySidePanel.configPrompt")}
diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index b9a0ae9a6c6..bcdd37074a0 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -4486,5 +4486,85 @@ "deleteModalMessage": "Are you sure you want to delete this fallback? This action cannot be undone.", "deleteModalResourceTitle": "Fallback Information" } + }, + "promptsPage": { + "addPromptForm": { + "title": "Add New Prompt", + "createPrompt": "Create Prompt", + "accessTokenRequired": "Access token is required", + "uploadPromptFileRequired": "Please upload a .prompt file", + "conversionFailed": "Failed to convert prompt file to JSON", + "createSuccess": "Prompt created successfully!", + "createFailed": "Failed to create prompt", + "promptIdLabel": "Prompt ID", + "promptIdRequired": "Please enter a prompt ID", + "promptIdPattern": "Prompt ID can only contain letters, numbers, underscores, and hyphens", + "promptIdPlaceholder": "Enter unique prompt ID (e.g., my_prompt_id)", + "integrationLabel": "Prompt Integration", + "fileLabel": "Prompt File", + "fileExtra": "Upload a .prompt file that follows the Dotprompt specification", + "selectFile": "Select .prompt File", + "selected": "Selected: {{name}}" + }, + "developerMessageCard": { + "title": "Developer message", + "subtitle": "Optional system instructions for the model", + "placeholder": "e.g., You are a helpful assistant..." + }, + "dotpromptViewTab": { + "title": "Generated .prompt file", + "subtitle": "This is the dotprompt format that will be saved to the database" + }, + "modelConfigCard": { + "parameters": "Parameters", + "modelParameters": "Model Parameters", + "temperature": "Temperature", + "maxTokens": "Max Tokens" + }, + "promptCodeSnippets": { + "getCode": "Get Code", + "modalTitle": "Generated Code", + "language": "Language", + "copyToClipboard": "Copy to Clipboard", + "copiedToClipboard": "Copied to clipboard!", + "tabBasic": "Basic", + "tabWithMessages": "With Messages", + "tabWithVersion": "With Version" + }, + "promptEditorHeader": { + "envDevelopment": "Development", + "envStaging": "Staging", + "envProduction": "Production", + "draft": "Draft", + "unsavedChanges": "Unsaved changes", + "history": "History" + }, + "promptMessagesCard": { + "title": "Prompt messages", + "variableSyntaxHint": "Use {{variable}} syntax for template variables", + "roleUser": "User", + "roleAssistant": "Assistant", + "roleSystem": "System", + "contentPlaceholder": "Enter prompt content...", + "addMessage": "Add message" + }, + "publishModal": { + "title": "Publish Prompt", + "publish": "Publish", + "namePlaceholder": "Enter prompt name", + "versionedHint": "Published prompts can be used in API calls and are versioned for easy tracking." + }, + "toolsCard": { + "title": "Tools", + "noTools": "No tools added" + }, + "versionHistorySidePanel": { + "title": "Version History", + "noHistory": "No version history available.", + "latest": "Latest", + "active": "Active", + "savedToDatabase": "Saved to Database", + "configPrompt": "Config Prompt" + } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index ef366522f26..b993d528f9a 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -4486,5 +4486,85 @@ "deleteModalMessage": "确定要删除此回退吗?此操作无法撤销。", "deleteModalResourceTitle": "回退信息" } + }, + "promptsPage": { + "addPromptForm": { + "title": "添加新提示词", + "createPrompt": "创建提示词", + "accessTokenRequired": "访问令牌不能为空", + "uploadPromptFileRequired": "请上传 .prompt 文件", + "conversionFailed": "提示词文件转换为 JSON 失败", + "createSuccess": "提示词创建成功!", + "createFailed": "创建提示词失败", + "promptIdLabel": "提示词 ID", + "promptIdRequired": "请输入提示词 ID", + "promptIdPattern": "提示词 ID 只能包含字母、数字、下划线和连字符", + "promptIdPlaceholder": "输入唯一提示词 ID(如:my_prompt_id)", + "integrationLabel": "提示词集成", + "fileLabel": "提示词文件", + "fileExtra": "上传符合 Dotprompt 规范的 .prompt 文件", + "selectFile": "选择 .prompt 文件", + "selected": "已选择:{{name}}" + }, + "developerMessageCard": { + "title": "开发者消息", + "subtitle": "可选的模型系统指令", + "placeholder": "例如:你是一个有帮助的助手..." + }, + "dotpromptViewTab": { + "title": "生成的 .prompt 文件", + "subtitle": "这是将保存到数据库的 dotprompt 格式" + }, + "modelConfigCard": { + "parameters": "参数", + "modelParameters": "模型参数", + "temperature": "Temperature", + "maxTokens": "最大 Token 数" + }, + "promptCodeSnippets": { + "getCode": "获取代码", + "modalTitle": "生成的代码", + "language": "语言", + "copyToClipboard": "复制到剪贴板", + "copiedToClipboard": "已复制到剪贴板!", + "tabBasic": "基础", + "tabWithMessages": "含消息", + "tabWithVersion": "含版本" + }, + "promptEditorHeader": { + "envDevelopment": "开发", + "envStaging": "预发布", + "envProduction": "生产", + "draft": "草稿", + "unsavedChanges": "有未保存的更改", + "history": "历史" + }, + "promptMessagesCard": { + "title": "提示词消息", + "variableSyntaxHint": "使用 {{variable}} 语法定义模板变量", + "roleUser": "用户", + "roleAssistant": "助手", + "roleSystem": "系统", + "contentPlaceholder": "输入提示词内容...", + "addMessage": "添加消息" + }, + "publishModal": { + "title": "发布提示词", + "publish": "发布", + "namePlaceholder": "输入提示词名称", + "versionedHint": "已发布的提示词可在 API 调用中使用,并支持版本管理以便追踪。" + }, + "toolsCard": { + "title": "工具", + "noTools": "暂无工具" + }, + "versionHistorySidePanel": { + "title": "版本历史", + "noHistory": "暂无版本历史。", + "latest": "最新", + "active": "当前", + "savedToDatabase": "已保存到数据库", + "configPrompt": "配置提示词" + } } }