From fbb8477d23effb45ec6c5525cf04496ac37e346d 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 17:31:31 +0900 Subject: [PATCH] feat(ui): i18n for guardrail test playground --- .../guardrails/GuardrailTestPanel.tsx | 41 ++++++++++------- .../guardrails/GuardrailTestPlayground.tsx | 35 ++++++++++----- .../guardrails/GuardrailTestResults.tsx | 19 +++++--- ui/litellm-dashboard/src/locales/en.json | 44 +++++++++++++++++++ ui/litellm-dashboard/src/locales/zh-CN.json | 44 +++++++++++++++++++ 5 files changed, 149 insertions(+), 34 deletions(-) diff --git a/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPanel.tsx b/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPanel.tsx index 473871553b1..1a1fd425cb1 100644 --- a/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPanel.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPanel.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { Button } from "@tremor/react"; import { Input, Typography, Tooltip } from "antd"; import { CopyOutlined, InfoCircleOutlined } from "@ant-design/icons"; +import { useTranslation, Trans } from "react-i18next"; import NotificationsManager from "../molecules/notifications_manager"; import GuardrailTestResults from "./GuardrailTestResults"; @@ -25,11 +26,12 @@ export function GuardrailTestPanel({ errors, onClose, }: GuardrailTestPanelProps) { + const { t } = useTranslation(); const [inputText, setInputText] = useState(""); const handleSubmit = () => { if (!inputText.trim()) { - NotificationsManager.fromBackend("Please enter text to test"); + NotificationsManager.fromBackend(t("guardrails.guardrailTestPanel.pleaseEnterText")); return; } @@ -74,9 +76,9 @@ export function GuardrailTestPanel({ const handleCopyInput = async () => { const success = await copyToClipboard(inputText); if (success) { - NotificationsManager.success("Input copied to clipboard"); + NotificationsManager.success(t("guardrails.guardrailTestPanel.inputCopied")); } else { - NotificationsManager.fromBackend("Failed to copy input"); + NotificationsManager.fromBackend(t("guardrails.guardrailTestPanel.failedToCopyInput")); } }; @@ -87,7 +89,7 @@ export function GuardrailTestPanel({
-

Test Guardrails:

+

{t("guardrails.guardrailTestPanel.title")}

{guardrailNames.map((name) => (

- Test {guardrailNames.length > 1 ? "guardrails" : "guardrail"} and compare results + {t("guardrails.guardrailTestPanel.subtitle", { count: guardrailNames.length })}

@@ -112,14 +114,16 @@ export function GuardrailTestPanel({
- - + +
{inputText && ( )}
@@ -127,26 +131,31 @@ export function GuardrailTestPanel({ value={inputText} onChange={(e) => setInputText(e.target.value)} onKeyDown={handleKeyDown} - placeholder="Enter text to test with guardrails..." + placeholder={t("guardrails.guardrailTestPanel.inputPlaceholder")} rows={8} className="font-mono text-sm" />
- Press Enter to - submit •{" "} - Shift+Enter for - new line + , + shiftEnter: , + }} + /> + + + {t("guardrails.guardrailTestPanel.characters", { count: inputText.length })} - Characters: {inputText.length}
diff --git a/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPlayground.tsx b/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPlayground.tsx index 5774d078cbf..b0901d23b3a 100644 --- a/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPlayground.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/GuardrailTestPlayground.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { Card, List, Empty, Spin, Input, Typography } from "antd"; import { ExperimentOutlined, SearchOutlined } from "@ant-design/icons"; +import { useTranslation } from "react-i18next"; import GuardrailTestPanel from "./GuardrailTestPanel"; import { applyGuardrail } from "../networking"; import NotificationsManager from "../molecules/notifications_manager"; @@ -43,6 +44,7 @@ const GuardrailTestPlayground: React.FC = ({ accessToken, onClose, }) => { + const { t } = useTranslation(); const [selectedGuardrails, setSelectedGuardrails] = useState>(new Set()); const [searchQuery, setSearchQuery] = useState(""); const [testResults, setTestResults] = useState([]); @@ -103,10 +105,10 @@ const GuardrailTestPlayground: React.FC = ({ setIsTesting(false); if (results.length > 0) { - NotificationsManager.success(`${results.length} guardrail${results.length > 1 ? "s" : ""} applied successfully`); + NotificationsManager.success(t("guardrails.guardrailTestPlayground.appliedSuccess", { count: results.length })); } if (errors.length > 0) { - NotificationsManager.fromBackend(`${errors.length} guardrail${errors.length > 1 ? "s" : ""} failed`); + NotificationsManager.fromBackend(t("guardrails.guardrailTestPlayground.applyFailed", { count: errors.length })); } }; @@ -118,10 +120,12 @@ const GuardrailTestPlayground: React.FC = ({
-

Guardrails

+

+ {t("guardrails.guardrailTestPlayground.guardrailsHeading")} +

} - placeholder="Search guardrails..." + placeholder={t("guardrails.guardrailTestPlayground.searchPlaceholder")} value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} /> @@ -135,7 +139,13 @@ const GuardrailTestPlayground: React.FC = ({
) : filteredGuardrails.length === 0 ? (
- +
) : ( = ({ description={
- Type: + {t("guardrails.guardrailTestPlayground.typeLabel")} {guardrail.litellm_params.guardrail}
- Mode: + {t("guardrails.guardrailTestPlayground.modeLabel")} {guardrail.litellm_params.mode}
@@ -182,7 +192,10 @@ const GuardrailTestPlayground: React.FC = ({
- {selectedGuardrails.size} of {filteredGuardrails.length} selected + {t("guardrails.guardrailTestPlayground.selectedCount", { + selected: selectedGuardrails.size, + total: filteredGuardrails.length, + })}
@@ -191,7 +204,7 @@ const GuardrailTestPlayground: React.FC = ({
- Guardrail Testing Playground + {t("guardrails.guardrailTestPlayground.pageTitle")}
@@ -200,10 +213,10 @@ const GuardrailTestPlayground: React.FC = ({
- Select Guardrails to Test + {t("guardrails.guardrailTestPlayground.emptyStateTitle")} - Choose one or more guardrails from the left sidebar to start testing and comparing results. + {t("guardrails.guardrailTestPlayground.emptyStateDesc")}
) : ( diff --git a/ui/litellm-dashboard/src/components/guardrails/GuardrailTestResults.tsx b/ui/litellm-dashboard/src/components/guardrails/GuardrailTestResults.tsx index 52360085241..5d2c37369a2 100644 --- a/ui/litellm-dashboard/src/components/guardrails/GuardrailTestResults.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/GuardrailTestResults.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { Button, Card } from "@tremor/react"; import { Typography } from "antd"; import { CopyOutlined, CheckCircleOutlined, ClockCircleOutlined, DownOutlined, RightOutlined } from "@ant-design/icons"; +import { useTranslation } from "react-i18next"; import NotificationsManager from "../molecules/notifications_manager"; const { Text } = Typography; @@ -24,6 +25,7 @@ interface GuardrailTestResultsProps { } export function GuardrailTestResults({ results, errors }: GuardrailTestResultsProps) { + const { t } = useTranslation(); const [collapsedResults, setCollapsedResults] = useState>(new Set()); const toggleResultCollapse = (guardrailName: string) => { @@ -70,7 +72,7 @@ export function GuardrailTestResults({ results, errors }: GuardrailTestResultsPr return (
-

Results

+

{t("guardrails.guardrailTestResults.resultsHeading")}

{/* Success Results */} {results && @@ -105,13 +107,13 @@ export function GuardrailTestResults({ results, errors }: GuardrailTestResultsPr onClick={async () => { const success = await copyToClipboard(result.response_text); if (success) { - NotificationsManager.success("Result copied to clipboard"); + NotificationsManager.success(t("guardrails.guardrailTestResults.resultCopied")); } else { - NotificationsManager.fromBackend("Failed to copy result"); + NotificationsManager.fromBackend(t("guardrails.guardrailTestResults.failedToCopyResult")); } }} > - Copy + {t("common.copy")} )}
@@ -119,13 +121,16 @@ export function GuardrailTestResults({ results, errors }: GuardrailTestResultsPr {!isCollapsed && ( <>
- +
{result.response_text}
- Characters: {result.response_text.length} + {t("guardrails.guardrailTestResults.characters")}{" "} + {result.response_text.length}
)} @@ -163,7 +168,7 @@ export function GuardrailTestResults({ results, errors }: GuardrailTestResultsPr className="text-sm font-medium text-red-800 cursor-pointer" onClick={() => toggleResultCollapse(errorItem.guardrailName)} > - {errorItem.guardrailName} - Error + {t("guardrails.guardrailTestResults.errorTitle", { name: errorItem.guardrailName })}

diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index db9715984e7..0c0ba629b77 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -2421,5 +2421,49 @@ "showJson": "Show JSON", "copied": "Copied!" } + }, + "guardrails": { + "guardrailTestPanel": { + "title": "Test Guardrails:", + "subtitle_one": "Test guardrail and compare results", + "subtitle_other": "Test guardrails and compare results", + "inputText": "Input Text", + "inputTooltip": "Press Enter to submit. Use Shift+Enter for new line.", + "copyInput": "Copy Input", + "inputPlaceholder": "Enter text to test with guardrails...", + "keyboardHint": "Press Enter to submit • Shift+Enter for new line", + "characters": "Characters: {{count}}", + "testButton_one": "Test {{count}} guardrail", + "testButton_other": "Test {{count}} guardrails", + "testingButton_one": "Testing {{count}} guardrail...", + "testingButton_other": "Testing {{count}} guardrails...", + "pleaseEnterText": "Please enter text to test", + "inputCopied": "Input copied to clipboard", + "failedToCopyInput": "Failed to copy input" + }, + "guardrailTestPlayground": { + "pageTitle": "Guardrail Testing Playground", + "guardrailsHeading": "Guardrails", + "searchPlaceholder": "Search guardrails...", + "noGuardrailsMatch": "No guardrails match your search", + "noGuardrailsAvailable": "No guardrails available", + "typeLabel": "Type:", + "modeLabel": "Mode:", + "selectedCount": "{{selected}} of {{total}} selected", + "emptyStateTitle": "Select Guardrails to Test", + "emptyStateDesc": "Choose one or more guardrails from the left sidebar to start testing and comparing results.", + "appliedSuccess_one": "{{count}} guardrail applied successfully", + "appliedSuccess_other": "{{count}} guardrails applied successfully", + "applyFailed_one": "{{count}} guardrail failed", + "applyFailed_other": "{{count}} guardrails failed" + }, + "guardrailTestResults": { + "resultsHeading": "Results", + "outputText": "Output Text", + "characters": "Characters:", + "errorTitle": "{{name}} - Error", + "resultCopied": "Result copied to clipboard", + "failedToCopyResult": "Failed to copy result" + } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index a9af0e13127..5b33d7d06e8 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -2421,5 +2421,49 @@ "showJson": "显示 JSON", "copied": "已复制!" } + }, + "guardrails": { + "guardrailTestPanel": { + "title": "测试护栏:", + "subtitle_one": "测试护栏并比较结果", + "subtitle_other": "测试护栏并比较结果", + "inputText": "输入文本", + "inputTooltip": "按 Enter 提交,按 Shift+Enter 换行。", + "copyInput": "复制输入", + "inputPlaceholder": "输入要用护栏测试的文本...", + "keyboardHint": "按 Enter 提交 • Shift+Enter 换行", + "characters": "字符数:{{count}}", + "testButton_one": "测试 {{count}} 个护栏", + "testButton_other": "测试 {{count}} 个护栏", + "testingButton_one": "正在测试 {{count}} 个护栏...", + "testingButton_other": "正在测试 {{count}} 个护栏...", + "pleaseEnterText": "请输入要测试的文本", + "inputCopied": "输入已复制到剪贴板", + "failedToCopyInput": "复制输入失败" + }, + "guardrailTestPlayground": { + "pageTitle": "护栏测试 Playground", + "guardrailsHeading": "护栏", + "searchPlaceholder": "搜索护栏...", + "noGuardrailsMatch": "没有护栏匹配您的搜索", + "noGuardrailsAvailable": "暂无护栏可用", + "typeLabel": "类型:", + "modeLabel": "模式:", + "selectedCount": "已选 {{selected}} / {{total}}", + "emptyStateTitle": "选择要测试的护栏", + "emptyStateDesc": "从左侧边栏选择一个或多个护栏,开始测试并比较结果。", + "appliedSuccess_one": "{{count}} 个护栏已成功应用", + "appliedSuccess_other": "{{count}} 个护栏已成功应用", + "applyFailed_one": "{{count}} 个护栏执行失败", + "applyFailed_other": "{{count}} 个护栏执行失败" + }, + "guardrailTestResults": { + "resultsHeading": "结果", + "outputText": "输出文本", + "characters": "字符数:", + "errorTitle": "{{name}} - 错误", + "resultCopied": "结果已复制到剪贴板", + "failedToCopyResult": "复制结果失败" + } } }