diff --git a/ui/litellm-dashboard/src/components/guardrails/content_filter/CategoryTable.tsx b/ui/litellm-dashboard/src/components/guardrails/content_filter/CategoryTable.tsx index cffb634e3d2..7f5e46c08a8 100644 --- a/ui/litellm-dashboard/src/components/guardrails/content_filter/CategoryTable.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/content_filter/CategoryTable.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Typography, Select, Table, Tag, Button } from "antd"; import { DeleteOutlined } from "@ant-design/icons"; +import { useTranslation } from "react-i18next"; const { Text } = Typography; const { Option } = Select; @@ -28,9 +29,10 @@ const CategoryTable: React.FC = ({ onRemove, readOnly = false, }) => { + const { t } = useTranslation(); const columns = [ { - title: "Category", + title: t("guardrails.categoryTable.colCategory"), dataIndex: "display_name", key: "display_name", render: (displayName: string, record: ContentCategory) => ( @@ -47,7 +49,7 @@ const CategoryTable: React.FC = ({ ), }, { - title: "Severity Threshold", + title: t("guardrails.categoryTable.colSeverityThreshold"), dataIndex: "severity_threshold", key: "severity_threshold", width: 180, @@ -67,15 +69,15 @@ const CategoryTable: React.FC = ({ style={{ width: 150 }} size="small" > - - - + + + ); }, }, { - title: "Action", + title: t("guardrails.categoryTable.colAction"), dataIndex: "action", key: "action", width: 150, @@ -90,8 +92,8 @@ const CategoryTable: React.FC = ({ style={{ width: 120 }} size="small" > - - + + ); }, @@ -105,14 +107,18 @@ const CategoryTable: React.FC = ({ width: 100, render: (_: any, record: ContentCategory) => ( ), } as any); } if (categories.length === 0) { - return
No categories configured.
; + return ( +
+ {t("guardrails.categoryTable.noCategories")} +
+ ); } return ; diff --git a/ui/litellm-dashboard/src/components/guardrails/content_filter/CompetitorIntentConfiguration.tsx b/ui/litellm-dashboard/src/components/guardrails/content_filter/CompetitorIntentConfiguration.tsx index 4e740b03b21..becc9787925 100644 --- a/ui/litellm-dashboard/src/components/guardrails/content_filter/CompetitorIntentConfiguration.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/content_filter/CompetitorIntentConfiguration.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { Card, Typography, Select, Switch, Form, Space, InputNumber } from "antd"; import { getMajorAirlines } from "../../networking"; +import { useTranslation } from "react-i18next"; const { Title, Text } = Typography; const { Option } = Select; @@ -51,6 +52,7 @@ const CompetitorIntentConfiguration: React.FC { + const { t } = useTranslation(); const effectiveConfig = config ?? DEFAULT_CONFIG; const [airlineOptions, setAirlineOptions] = useState([]); const [loadingAirlines, setLoadingAirlines] = useState(false); @@ -117,17 +119,14 @@ const CompetitorIntentConfiguration: React.FC - Competitor Intent Filter + {t("guardrails.competitorIntentConfiguration.title")} } size="small" > - - Block or reframe competitor comparison questions. When enabled, airline type auto-loads competitors from IATA; - generic type requires manual competitor list. - + {t("guardrails.competitorIntentConfiguration.disabledDescription")} ); } @@ -137,7 +136,7 @@ const CompetitorIntentConfiguration: React.FC - Competitor Intent Filter + {t("guardrails.competitorIntentConfiguration.title")} @@ -145,28 +144,27 @@ const CompetitorIntentConfiguration: React.FC - Block or reframe competitor comparison questions. Airline type uses major airlines (excluding your brand); - generic requires manual competitor list. + {t("guardrails.competitorIntentConfiguration.enabledDescription")}
- + handleNestedArrayChange("locations", v ?? [])} tokenSeparators={[","]} @@ -227,11 +225,15 @@ const CompetitorIntentConfiguration: React.FC + handlePolicyChange("competitor_comparison", v)} style={{ width: "100%" }} > - - + + - + - Classify competitor intent by confidence (0–1). Higher confidence → stronger intent. + {t("guardrails.competitorIntentConfiguration.confidenceThresholdsHelp")}
  • - High (≥): Treat as full competitor comparison → uses "Competitor - comparison" policy + {t("guardrails.competitorIntentConfiguration.thresholdHighLabel")}:{" "} + {t("guardrails.competitorIntentConfiguration.thresholdHighDesc")}
  • - Medium (≥): Treat as possible comparison → uses "Possible competitor - comparison" policy + {t("guardrails.competitorIntentConfiguration.thresholdMediumLabel")}:{" "} + {t("guardrails.competitorIntentConfiguration.thresholdMediumDesc")}
  • - Low (≥): Log only; allow request. Below Low → allow with no action + {t("guardrails.competitorIntentConfiguration.thresholdLowLabel")}:{" "} + {t("guardrails.competitorIntentConfiguration.thresholdLowDesc")}
- Raise thresholds to be more permissive; lower them to be stricter. + {t("guardrails.competitorIntentConfiguration.thresholdsNote")} } > - + - + - + pendingSelection, onPendingSelectionChange, }) => { + const { t } = useTranslation(); // Use controlled state if parent provides it, otherwise use local state const [localSelectedCategoryName, setLocalSelectedCategoryName] = React.useState(""); const selectedCategoryName = pendingSelection !== undefined ? pendingSelection : localSelectedCategoryName; @@ -166,7 +168,7 @@ const ContentCategoryConfiguration: React.FC const columns = [ { - title: "Category", + title: t("guardrails.contentCategoryConfiguration.colCategory"), dataIndex: "display_name", key: "display_name", render: (text: string, record: SelectedCategory) => { @@ -182,7 +184,7 @@ const ContentCategoryConfiguration: React.FC }, }, { - title: "Action", + title: t("guardrails.contentCategoryConfiguration.colAction"), dataIndex: "action", key: "action", width: 150, @@ -202,7 +204,7 @@ const ContentCategoryConfiguration: React.FC ), }, { - title: "Severity Threshold", + title: t("guardrails.contentCategoryConfiguration.colSeverityThreshold"), dataIndex: "severity_threshold", key: "severity_threshold", width: 180, @@ -212,9 +214,9 @@ const ContentCategoryConfiguration: React.FC onChange={(value) => onCategoryUpdate(record.id, "severity_threshold", value)} style={{ width: "100%" }} > - - - + + + ), }, @@ -224,7 +226,7 @@ const ContentCategoryConfiguration: React.FC width: 80, render: (_: any, record: SelectedCategory) => ( ), }, @@ -241,10 +243,10 @@ const ContentCategoryConfiguration: React.FC style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }} > - Blocked topics + {t("guardrails.contentCategoryConfiguration.title")} - Select topics to block using keyword and semantic analysis + {t("guardrails.contentCategoryConfiguration.subtitle")} } @@ -252,7 +254,7 @@ const ContentCategoryConfiguration: React.FC >
@@ -296,7 +298,9 @@ const ContentCategoryConfiguration: React.FC )} {loadingPreviewYaml ? ( -
Loading content...
+
+ {t("guardrails.contentCategoryConfiguration.loadingContent")} +
) : previewYaml ? (
             
) : (
- Unable to load category content + {t("guardrails.contentCategoryConfiguration.unableToLoadContent")}
)} @@ -355,12 +359,17 @@ const ContentCategoryConfiguration: React.FC
- View {fileTypeLabel} for {category.display_name} + {t("guardrails.contentCategoryConfiguration.viewFileFor", { + fileType: fileTypeLabel, + name: category.display_name, + })}
), children: loadingYaml[category.category] ? ( -
Loading content...
+
+ {t("guardrails.contentCategoryConfiguration.loadingContent")} +
) : categoryYaml[category.category] ? (
                     
) : (
- Content will load when expanded + {t("guardrails.contentCategoryConfiguration.contentWillLoad")}
), }; @@ -396,7 +405,7 @@ const ContentCategoryConfiguration: React.FC borderRadius: "4px", }} > - No blocked topics selected. Add topics to detect and block harmful content. + {t("guardrails.contentCategoryConfiguration.noTopicsSelected")} )} diff --git a/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterConfiguration.tsx b/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterConfiguration.tsx index 34f92b8618c..41839943967 100644 --- a/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterConfiguration.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterConfiguration.tsx @@ -3,6 +3,7 @@ import { Typography, Space, Upload, Card, Button } from "antd"; import { PlusOutlined, UploadOutlined } from "@ant-design/icons"; import { validateBlockedWordsFile } from "../../networking"; import NotificationsManager from "../../molecules/notifications_manager"; +import { useTranslation } from "react-i18next"; import PatternModal from "./PatternModal"; import CustomPatternModal from "./CustomPatternModal"; import KeywordModal from "./KeywordModal"; @@ -102,6 +103,7 @@ const ContentFilterConfiguration: React.FC = ({ competitorIntentConfig = null, onCompetitorIntentChange, }) => { + const { t } = useTranslation(); const [patternModalVisible, setPatternModalVisible] = useState(false); const [keywordModalVisible, setKeywordModalVisible] = useState(false); const [customPatternModalVisible, setCustomPatternModalVisible] = useState(false); @@ -118,7 +120,7 @@ const ContentFilterConfiguration: React.FC = ({ const handleAddPrebuiltPattern = () => { if (!selectedPatternName) { - NotificationsManager.error("Please select a pattern"); + NotificationsManager.error(t("guardrails.contentFilterConfiguration.pleaseSelectPattern")); return; } @@ -139,7 +141,7 @@ const ContentFilterConfiguration: React.FC = ({ const handleAddCustomPattern = () => { if (!customPatternName || !customPatternRegex) { - NotificationsManager.error("Please provide pattern name and regex"); + NotificationsManager.error(t("guardrails.contentFilterConfiguration.pleaseProvidePatternNameAndRegex")); return; } @@ -159,7 +161,7 @@ const ContentFilterConfiguration: React.FC = ({ const handleAddKeyword = () => { if (!newKeyword) { - NotificationsManager.error("Please enter a keyword"); + NotificationsManager.error(t("guardrails.contentFilterConfiguration.pleaseEnterKeyword")); return; } @@ -187,14 +189,21 @@ const ContentFilterConfiguration: React.FC = ({ if (onFileUpload) { onFileUpload(content); } - NotificationsManager.success(result.message || "File uploaded successfully"); + NotificationsManager.success( + result.message || t("guardrails.contentFilterConfiguration.fileUploadedSuccessfully"), + ); } else { - const errorMessage = result.error || (result.errors && result.errors.join(", ")) || "Invalid file"; - NotificationsManager.error(`Validation failed: ${errorMessage}`); + const errorMessage = + result.error || + (result.errors && result.errors.join(", ")) || + t("guardrails.contentFilterConfiguration.invalidFile"); + NotificationsManager.error( + t("guardrails.contentFilterConfiguration.validationFailed", { error: errorMessage }), + ); } } } catch (error) { - NotificationsManager.error(`Failed to upload file: ${error}`); + NotificationsManager.error(t("guardrails.contentFilterConfiguration.failedToUploadFile", { error })); } finally { setUploadValidating(false); } @@ -210,10 +219,7 @@ const ContentFilterConfiguration: React.FC = ({
{!showStep && (
- - Configure patterns, keywords, and content categories to detect and filter sensitive information in requests - and responses. - + {t("guardrails.contentFilterConfiguration.description")}
)} @@ -222,10 +228,10 @@ const ContentFilterConfiguration: React.FC = ({ title={
- Pattern Detection + {t("guardrails.contentFilterConfiguration.patternDetectionTitle")} - Detect sensitive information using regex patterns (SSN, credit cards, API keys, etc.) + {t("guardrails.contentFilterConfiguration.patternDetectionSubtitle")}
} @@ -234,10 +240,10 @@ const ContentFilterConfiguration: React.FC = ({
@@ -250,10 +256,10 @@ const ContentFilterConfiguration: React.FC = ({ title={
- Blocked Keywords + {t("guardrails.contentFilterConfiguration.blockedKeywordsTitle")} - Block or mask specific sensitive terms and phrases + {t("guardrails.contentFilterConfiguration.blockedKeywordsSubtitle")}
} @@ -262,11 +268,11 @@ const ContentFilterConfiguration: React.FC = ({
diff --git a/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterDisplay.tsx b/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterDisplay.tsx index 3e7ee600943..e2e1fdfb3f9 100644 --- a/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterDisplay.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/content_filter/ContentFilterDisplay.tsx @@ -3,6 +3,7 @@ import { Card, Text, Badge } from "@tremor/react"; import PatternTable from "./PatternTable"; import KeywordTable from "./KeywordTable"; import CategoryTable from "./CategoryTable"; +import { useTranslation } from "react-i18next"; interface Pattern { id: string; @@ -55,6 +56,8 @@ const ContentFilterDisplay: React.FC = ({ onCategorySeverityChange, onCategoryRemove, }) => { + const { t } = useTranslation(); + if (patterns.length === 0 && blockedWords.length === 0 && categories.length === 0) { return null; } @@ -67,8 +70,10 @@ const ContentFilterDisplay: React.FC = ({ {categories.length > 0 && (
- Content Categories - {categories.length} categories configured + {t("guardrails.contentFilterDisplay.contentCategories")} + + {t("guardrails.contentFilterDisplay.categoriesConfigured", { count: categories.length })} +
= ({ {patterns.length > 0 && (
- Pattern Detection - {patterns.length} patterns configured + {t("guardrails.contentFilterDisplay.patternDetection")} + + {t("guardrails.contentFilterDisplay.patternsConfigured", { count: patterns.length })} +
= ({ {blockedWords.length > 0 && (
- Blocked Keywords - {blockedWords.length} keywords configured + {t("guardrails.contentFilterDisplay.blockedKeywords")} + + {t("guardrails.contentFilterDisplay.keywordsConfigured", { count: blockedWords.length })} +
= ({ onDataChange, onUnsavedChanges, }) => { + const { t } = useTranslation(); const [selectedPatterns, setSelectedPatterns] = useState([]); const [blockedWords, setBlockedWords] = useState([]); const [selectedContentCategories, setSelectedContentCategories] = useState([]); @@ -233,18 +235,13 @@ const ContentFilterManager: React.FC = ({ // Edit mode return ( <> - Content Filter Configuration + {t("guardrails.contentFilterManager.dividerTitle")} {hasUnsavedChanges && ( - You have unsaved changes to patterns or keywords. Remember to click "Save Changes" at the - bottom. - - } + message={{t("guardrails.contentFilterManager.unsavedChangesWarning")}} /> )}
diff --git a/ui/litellm-dashboard/src/components/guardrails/content_filter/CustomPatternModal.tsx b/ui/litellm-dashboard/src/components/guardrails/content_filter/CustomPatternModal.tsx index 6e2f8b07847..3ddb3af4fb5 100644 --- a/ui/litellm-dashboard/src/components/guardrails/content_filter/CustomPatternModal.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/content_filter/CustomPatternModal.tsx @@ -1,5 +1,6 @@ import React from "react"; import { Typography, Select, Modal, Space, Button, Input } from "antd"; +import { useTranslation } from "react-i18next"; const { Text } = Typography; const { Option } = Select; @@ -27,13 +28,20 @@ const CustomPatternModal: React.FC = ({ onAdd, onCancel, }) => { + const { t } = useTranslation(); return ( - +
- Pattern name + {t("guardrails.customPatternModal.patternNameLabel")} onNameChange(e.target.value)} style={{ marginTop: 8 }} @@ -41,34 +49,34 @@ const CustomPatternModal: React.FC = ({
- Regex pattern + {t("guardrails.customPatternModal.regexPatternLabel")} onRegexChange(e.target.value)} style={{ marginTop: 8 }} /> - Enter a valid regular expression to match sensitive data + {t("guardrails.customPatternModal.regexPatternHelp")}
- Action + {t("guardrails.customPatternModal.actionLabel")} - Choose what action the guardrail should take when this pattern is detected + {t("guardrails.customPatternModal.actionHelp")}
- +
diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index a3fafe3dd4b..0b7747a7673 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -2607,6 +2607,113 @@ "errorTitle": "{{name}} - Error", "resultCopied": "Result copied to clipboard", "failedToCopyResult": "Failed to copy result" + }, + "categoryTable": { + "colCategory": "Category", + "colSeverityThreshold": "Severity Threshold", + "colAction": "Action", + "severityHigh": "High", + "severityMedium": "Medium", + "severityLow": "Low", + "actionBlock": "Block", + "actionMask": "Mask", + "noCategories": "No categories configured." + }, + "competitorIntentConfiguration": { + "title": "Competitor Intent Filter", + "disabledDescription": "Block or reframe competitor comparison questions. When enabled, airline type auto-loads competitors from IATA; generic type requires manual competitor list.", + "enabledDescription": "Block or reframe competitor comparison questions. Airline type uses major airlines (excluding your brand); generic requires manual competitor list.", + "typeAirline": "Airline (auto-load competitors from IATA)", + "typeGeneric": "Generic (specify competitors manually)", + "brandSelfLabel": "Your Brand (brand_self)", + "brandSelfHelpAirline": "Select your airline from the list (excluded from competitors) or type to add a custom term", + "brandSelfHelpGeneric": "Names/codes users use for your brand", + "loadingAirlines": "Loading airlines...", + "brandSelfPlaceholderAirline": "Search or select airline, or type to add custom", + "brandSelfPlaceholderGeneric": "Type and press Enter to add", + "locationsLabel": "Locations (optional)", + "locationsHelp": "Countries, cities, airports for disambiguation (e.g. qatar, doha)", + "competitorsLabel": "Competitors", + "competitorsHelp": "Competitor names to detect (required for generic type)", + "tagsPlaceholder": "Type and press Enter to add", + "policyCompetitorLabel": "Policy: Competitor comparison", + "policyPossibleCompetitorLabel": "Policy: Possible competitor comparison", + "policyRefuse": "Refuse (block request)", + "policyReframe": "Reframe (suggest alternative)", + "policyReframeBackend": "Reframe (suggest alternative to backend LLM)", + "confidenceThresholdsLabel": "Confidence thresholds", + "confidenceThresholdsHelp": "Classify competitor intent by confidence (0–1). Higher confidence → stronger intent.", + "thresholdHighLabel": "High (≥)", + "thresholdHighDesc": "Treat as full competitor comparison → uses \"Competitor comparison\" policy", + "thresholdMediumLabel": "Medium (≥)", + "thresholdMediumDesc": "Treat as possible comparison → uses \"Possible competitor comparison\" policy", + "thresholdLowLabel": "Low (≥)", + "thresholdLowDesc": "Log only; allow request. Below Low → allow with no action", + "thresholdsNote": "Raise thresholds to be more permissive; lower them to be stricter.", + "thresholdHighShort": "High", + "thresholdHighExample": "e.g. 0.7", + "thresholdMediumShort": "Medium", + "thresholdMediumExample": "e.g. 0.45", + "thresholdLowShort": "Low", + "thresholdLowExample": "e.g. 0.3" + }, + "contentCategoryConfiguration": { + "title": "Blocked topics", + "subtitle": "Select topics to block using keyword and semantic analysis", + "colCategory": "Category", + "colAction": "Action", + "colSeverityThreshold": "Severity Threshold", + "severityLow": "Low", + "severityMedium": "Medium", + "severityHigh": "High", + "selectPlaceholder": "Select a content category", + "loadingContent": "Loading content...", + "unableToLoadContent": "Unable to load category content", + "viewFileFor": "View {{fileType}} for {{name}}", + "contentWillLoad": "Content will load when expanded", + "noTopicsSelected": "No blocked topics selected. Add topics to detect and block harmful content." + }, + "contentFilterConfiguration": { + "description": "Configure patterns, keywords, and content categories to detect and filter sensitive information in requests and responses.", + "patternDetectionTitle": "Pattern Detection", + "patternDetectionSubtitle": "Detect sensitive information using regex patterns (SSN, credit cards, API keys, etc.)", + "addPrebuiltPattern": "Add prebuilt pattern", + "addCustomRegex": "Add custom regex", + "blockedKeywordsTitle": "Blocked Keywords", + "blockedKeywordsSubtitle": "Block or mask specific sensitive terms and phrases", + "addKeyword": "Add keyword", + "uploadYamlFile": "Upload YAML file", + "pleaseSelectPattern": "Please select a pattern", + "pleaseProvidePatternNameAndRegex": "Please provide pattern name and regex", + "pleaseEnterKeyword": "Please enter a keyword", + "fileUploadedSuccessfully": "File uploaded successfully", + "invalidFile": "Invalid file", + "validationFailed": "Validation failed: {{error}}", + "failedToUploadFile": "Failed to upload file: {{error}}" + }, + "contentFilterDisplay": { + "contentCategories": "Content Categories", + "categoriesConfigured": "{{count}} categories configured", + "patternDetection": "Pattern Detection", + "patternsConfigured": "{{count}} patterns configured", + "blockedKeywords": "Blocked Keywords", + "keywordsConfigured": "{{count}} keywords configured" + }, + "contentFilterManager": { + "dividerTitle": "Content Filter Configuration", + "unsavedChangesWarning": "You have unsaved changes to patterns or keywords. Remember to click \"Save Changes\" at the bottom." + }, + "customPatternModal": { + "title": "Add custom regex pattern", + "patternNameLabel": "Pattern name", + "patternNamePlaceholder": "e.g., internal_id, employee_code", + "regexPatternLabel": "Regex pattern", + "regexPatternPlaceholder": "e.g., ID-[0-9]{6}", + "regexPatternHelp": "Enter a valid regular expression to match sensitive data", + "actionLabel": "Action", + "actionHelp": "Choose what action the guardrail should take when this pattern is detected", + "actionBlock": "Block", + "actionMask": "Mask" } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index 0a8130459d6..df6776f9398 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -2607,6 +2607,113 @@ "errorTitle": "{{name}} - 错误", "resultCopied": "结果已复制到剪贴板", "failedToCopyResult": "复制结果失败" + }, + "categoryTable": { + "colCategory": "分类", + "colSeverityThreshold": "严重程度阈值", + "colAction": "操作", + "severityHigh": "高", + "severityMedium": "中", + "severityLow": "低", + "actionBlock": "拦截", + "actionMask": "遮蔽", + "noCategories": "未配置任何分类。" + }, + "competitorIntentConfiguration": { + "title": "竞争对手意图过滤器", + "disabledDescription": "拦截或重构竞争对手比较问题。启用后,航空公司类型会自动从 IATA 加载竞争对手;通用类型需手动指定竞争对手列表。", + "enabledDescription": "拦截或重构竞争对手比较问题。航空公司类型使用主要航空公司(排除您的品牌);通用类型需手动指定竞争对手列表。", + "typeAirline": "航空公司(自动从 IATA 加载竞争对手)", + "typeGeneric": "通用(手动指定竞争对手)", + "brandSelfLabel": "您的品牌(brand_self)", + "brandSelfHelpAirline": "从列表中选择您的航空公司(已排除在竞争对手之外),或输入自定义词语", + "brandSelfHelpGeneric": "用户用来指代您品牌的名称或代码", + "loadingAirlines": "正在加载航空公司...", + "brandSelfPlaceholderAirline": "搜索或选择航空公司,或输入自定义内容", + "brandSelfPlaceholderGeneric": "输入后按 Enter 添加", + "locationsLabel": "地点(可选)", + "locationsHelp": "用于消歧的国家、城市、机场(如:qatar、doha)", + "competitorsLabel": "竞争对手", + "competitorsHelp": "要检测的竞争对手名称(通用类型必填)", + "tagsPlaceholder": "输入后按 Enter 添加", + "policyCompetitorLabel": "策略:竞争对手比较", + "policyPossibleCompetitorLabel": "策略:可能的竞争对手比较", + "policyRefuse": "拒绝(拦截请求)", + "policyReframe": "重构(建议替代方案)", + "policyReframeBackend": "重构(向后端 LLM 建议替代方案)", + "confidenceThresholdsLabel": "置信度阈值", + "confidenceThresholdsHelp": "按置信度(0–1)对竞争对手意图分类。置信度越高,意图越强。", + "thresholdHighLabel": "高(≥)", + "thresholdHighDesc": "视为完整的竞争对手比较 → 使用「竞争对手比较」策略", + "thresholdMediumLabel": "中(≥)", + "thresholdMediumDesc": "视为可能的比较 → 使用「可能的竞争对手比较」策略", + "thresholdLowLabel": "低(≥)", + "thresholdLowDesc": "仅记录日志,允许请求。低于低阈值 → 无操作直接允许", + "thresholdsNote": "提高阈值以放宽限制;降低阈值以收紧限制。", + "thresholdHighShort": "高", + "thresholdHighExample": "例如:0.7", + "thresholdMediumShort": "中", + "thresholdMediumExample": "例如:0.45", + "thresholdLowShort": "低", + "thresholdLowExample": "例如:0.3" + }, + "contentCategoryConfiguration": { + "title": "封锁主题", + "subtitle": "使用关键词和语义分析选择要封锁的主题", + "colCategory": "分类", + "colAction": "操作", + "colSeverityThreshold": "严重程度阈值", + "severityLow": "低", + "severityMedium": "中", + "severityHigh": "高", + "selectPlaceholder": "选择内容分类", + "loadingContent": "正在加载内容...", + "unableToLoadContent": "无法加载分类内容", + "viewFileFor": "查看 {{fileType}} 文件:{{name}}", + "contentWillLoad": "展开后将加载内容", + "noTopicsSelected": "未选择封锁主题。添加主题以检测和拦截有害内容。" + }, + "contentFilterConfiguration": { + "description": "配置模式、关键词和内容分类,以检测和过滤请求及响应中的敏感信息。", + "patternDetectionTitle": "模式检测", + "patternDetectionSubtitle": "使用正则表达式模式检测敏感信息(身份证号、信用卡、API 密钥等)", + "addPrebuiltPattern": "添加预置模式", + "addCustomRegex": "添加自定义正则", + "blockedKeywordsTitle": "封锁关键词", + "blockedKeywordsSubtitle": "拦截或遮蔽特定敏感词语和短语", + "addKeyword": "添加关键词", + "uploadYamlFile": "上传 YAML 文件", + "pleaseSelectPattern": "请选择一个模式", + "pleaseProvidePatternNameAndRegex": "请提供模式名称和正则表达式", + "pleaseEnterKeyword": "请输入关键词", + "fileUploadedSuccessfully": "文件上传成功", + "invalidFile": "无效文件", + "validationFailed": "验证失败:{{error}}", + "failedToUploadFile": "上传文件失败:{{error}}" + }, + "contentFilterDisplay": { + "contentCategories": "内容分类", + "categoriesConfigured": "已配置 {{count}} 个分类", + "patternDetection": "模式检测", + "patternsConfigured": "已配置 {{count}} 个模式", + "blockedKeywords": "封锁关键词", + "keywordsConfigured": "已配置 {{count}} 个关键词" + }, + "contentFilterManager": { + "dividerTitle": "内容过滤器配置", + "unsavedChangesWarning": "模式或关键词有未保存的更改,请记得点击底部的「保存更改」。" + }, + "customPatternModal": { + "title": "添加自定义正则模式", + "patternNameLabel": "模式名称", + "patternNamePlaceholder": "例如:internal_id、employee_code", + "regexPatternLabel": "正则表达式", + "regexPatternPlaceholder": "例如:ID-[0-9]{6}", + "regexPatternHelp": "输入有效的正则表达式以匹配敏感数据", + "actionLabel": "操作", + "actionHelp": "选择当检测到此模式时护栏应执行的操作", + "actionBlock": "拦截", + "actionMask": "遮蔽" } } }