feat(ui): i18n for cache settings

This commit is contained in:
吹雪 2026-06-11 05:44:34 +09:00
parent c81c0265ec
commit c8228990a8
5 changed files with 101 additions and 25 deletions

View file

@ -4,6 +4,7 @@ import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { NumberInput, TextInput } from "@tremor/react";
import { Select } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { fetchAvailableModels, ModelGroup } from "../playground/llm_calls/fetch_models";
import NumericalInput from "../shared/numerical_input";
@ -16,6 +17,7 @@ const CacheFieldRenderer: React.FC<CacheFieldRendererProps> = ({ field, currentV
const [modelInfo, setModelInfo] = useState<ModelGroup[]>([]);
const [selectedModel, setSelectedModel] = useState<string>(currentValue || "");
const { accessToken } = useAuthorized();
const { t } = useTranslation();
useEffect(() => {
if (!accessToken) return;
@ -99,7 +101,7 @@ const CacheFieldRenderer: React.FC<CacheFieldRendererProps> = ({ field, currentV
value={selectedModel}
onChange={setSelectedModel}
showSearch={true}
placeholder="Search and select a model..."
placeholder={t("cacheSettings.cacheFieldRenderer.searchAndSelectModel")}
options={embeddingModels}
style={{ width: "100%" }}
className="rounded-md"

View file

@ -1,5 +1,6 @@
import React from "react";
import { Select, SelectItem } from "@tremor/react";
import { useTranslation } from "react-i18next";
interface RedisTypeSelectorProps {
redisType: string;
@ -8,17 +9,18 @@ interface RedisTypeSelectorProps {
}
const RedisTypeSelector: React.FC<RedisTypeSelectorProps> = ({ redisType, redisTypeDescriptions, onTypeChange }) => {
const { t } = useTranslation();
return (
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700">Redis Type</label>
<label className="text-sm font-medium text-gray-700">{t("cacheSettings.redisTypeSelector.redisType")}</label>
<Select value={redisType} onValueChange={onTypeChange}>
<SelectItem value="node">Node (Single Instance)</SelectItem>
<SelectItem value="cluster">Cluster</SelectItem>
<SelectItem value="sentinel">Sentinel</SelectItem>
<SelectItem value="semantic">Semantic</SelectItem>
<SelectItem value="node">{t("cacheSettings.redisTypeSelector.nodeType")}</SelectItem>
<SelectItem value="cluster">{t("cacheSettings.redisTypeSelector.clusterType")}</SelectItem>
<SelectItem value="sentinel">{t("cacheSettings.redisTypeSelector.sentinelType")}</SelectItem>
<SelectItem value="semantic">{t("cacheSettings.redisTypeSelector.semanticType")}</SelectItem>
</Select>
<p className="text-xs text-gray-500">
{redisTypeDescriptions[redisType] || "Select the type of Redis deployment you're using"}
{redisTypeDescriptions[redisType] || t("cacheSettings.redisTypeSelector.selectTypeDescription")}
</p>
</div>
);

View file

@ -1,4 +1,5 @@
import React, { useState, useEffect, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { Button, Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
import { getCacheSettingsCall, testCacheConnectionCall, updateCacheSettingsCall } from "../networking";
import NotificationsManager from "../molecules/notifications_manager";
@ -13,6 +14,7 @@ interface CacheSettingsProps {
}
const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, userID }) => {
const { t } = useTranslation();
const [cacheSettings, setCacheSettings] = useState<{ [key: string]: any }>({});
const [fields, setFields] = useState<any[]>([]);
const [redisTypeDescriptions, setRedisTypeDescriptions] = useState<{ [key: string]: string }>({});
@ -43,7 +45,7 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
}
} catch (error) {
console.error("Failed to load cache settings:", error);
NotificationsManager.fromBackend("Failed to load cache settings");
NotificationsManager.fromBackend(t("cacheSettings.index.failedToLoad"));
}
}, [accessToken]);
@ -65,13 +67,17 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
const result = await testCacheConnectionCall(accessToken, testSettings);
if (result.status === "success") {
NotificationsManager.success("Cache connection test successful!");
NotificationsManager.success(t("cacheSettings.index.testSuccessful"));
} else {
NotificationsManager.fromBackend(`Connection test failed: ${result.message || result.error}`);
NotificationsManager.fromBackend(
t("cacheSettings.index.testFailed", { message: result.message || result.error }),
);
}
} catch (error: any) {
console.error("Test connection error:", error);
NotificationsManager.fromBackend(`Connection test failed: ${error.message || "Unknown error"}`);
NotificationsManager.fromBackend(
t("cacheSettings.index.testFailed", { message: error.message || t("common.unknown") }),
);
} finally {
setIsTesting(false);
}
@ -89,12 +95,12 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
settingsToSave.type = "redis-semantic";
}
await updateCacheSettingsCall(accessToken, settingsToSave);
NotificationsManager.success("Cache settings updated successfully");
NotificationsManager.success(t("cacheSettings.index.updateSuccess"));
// Reload settings to reflect saved values
await loadCacheSettings();
} catch (error) {
console.error("Failed to save cache settings:", error);
NotificationsManager.fromBackend("Failed to update cache settings");
NotificationsManager.fromBackend(t("cacheSettings.index.failedToUpdate"));
} finally {
setIsSaving(false);
}
@ -111,8 +117,8 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
<div className="w-full space-y-8 py-2">
<div className="space-y-6">
<div className="max-w-3xl">
<h3 className="text-sm font-medium text-gray-900">Cache Settings</h3>
<p className="text-xs text-gray-500 mt-1">Configure Redis cache for LiteLLM</p>
<h3 className="text-sm font-medium text-gray-900">{t("cacheSettings.index.title")}</h3>
<p className="text-xs text-gray-500 mt-1">{t("cacheSettings.index.subtitle")}</p>
</div>
{/* Redis Type Selector */}
@ -124,7 +130,7 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{/* Basic Fields */}
<div className="space-y-6 pt-4 border-t border-gray-200">
<h4 className="text-sm font-medium text-gray-900">Connection Settings</h4>
<h4 className="text-sm font-medium text-gray-900">{t("cacheSettings.index.connectionSettings")}</h4>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
{basicFields.map((field: any) => {
if (!field) return null;
@ -137,7 +143,7 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{/* Redis Type-Specific Fields */}
{redisType === "cluster" && clusterFields.length > 0 && (
<div className="space-y-6 pt-4 border-t border-gray-200">
<h4 className="text-sm font-medium text-gray-900">Cluster Configuration</h4>
<h4 className="text-sm font-medium text-gray-900">{t("cacheSettings.index.clusterConfiguration")}</h4>
<div className="grid grid-cols-1 gap-6">
{clusterFields.map((field: any) => {
const currentValue = cacheSettings[field.field_name] ?? field.field_default ?? "";
@ -149,7 +155,7 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{redisType === "sentinel" && sentinelFields.length > 0 && (
<div className="space-y-6 pt-4 border-t border-gray-200">
<h4 className="text-sm font-medium text-gray-900">Sentinel Configuration</h4>
<h4 className="text-sm font-medium text-gray-900">{t("cacheSettings.index.sentinelConfiguration")}</h4>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
{sentinelFields.map((field: any) => {
const currentValue = cacheSettings[field.field_name] ?? field.field_default ?? "";
@ -161,7 +167,7 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{redisType === "semantic" && semanticFields.length > 0 && (
<div className="space-y-6 pt-4 border-t border-gray-200">
<h4 className="text-sm font-medium text-gray-900">Semantic Configuration</h4>
<h4 className="text-sm font-medium text-gray-900">{t("cacheSettings.index.semanticConfiguration")}</h4>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
{semanticFields.map((field: any) => {
const currentValue = cacheSettings[field.field_name] ?? field.field_default ?? "";
@ -174,14 +180,14 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{/* Advanced Settings Accordion */}
<Accordion className="mt-4">
<AccordionHeader>
<span className="text-sm font-medium text-gray-900">Advanced Settings</span>
<span className="text-sm font-medium text-gray-900">{t("cacheSettings.index.advancedSettings")}</span>
</AccordionHeader>
<AccordionBody>
<div className="space-y-6">
{/* SSL Settings */}
{sslFields.length > 0 && (
<div className="space-y-4">
<h5 className="text-sm font-medium text-gray-700">SSL Settings</h5>
<h5 className="text-sm font-medium text-gray-700">{t("cacheSettings.index.sslSettings")}</h5>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
{sslFields.map((field: any) => {
if (!field) return null;
@ -195,7 +201,7 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{/* Cache Management */}
{cacheManagementFields.length > 0 && (
<div className="space-y-4 pt-4 border-t border-gray-200">
<h5 className="text-sm font-medium text-gray-700">Cache Management</h5>
<h5 className="text-sm font-medium text-gray-700">{t("cacheSettings.index.cacheManagement")}</h5>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
{cacheManagementFields.map((field: any) => {
if (!field) return null;
@ -209,7 +215,7 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{/* GCP Authentication */}
{gcpFields.length > 0 && (
<div className="space-y-4 pt-4 border-t border-gray-200">
<h5 className="text-sm font-medium text-gray-700">GCP Authentication</h5>
<h5 className="text-sm font-medium text-gray-700">{t("cacheSettings.index.gcpAuthentication")}</h5>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
{gcpFields.map((field: any) => {
if (!field) return null;
@ -227,10 +233,10 @@ const CacheSettings: React.FC<CacheSettingsProps> = ({ accessToken, userRole, us
{/* Actions */}
<div className="border-t border-gray-200 pt-6 flex justify-end gap-3">
<Button variant="secondary" size="sm" onClick={handleTestConnection} disabled={isTesting} className="text-sm">
{isTesting ? "Testing..." : "Test Connection"}
{isTesting ? t("cacheSettings.index.testing") : t("cacheSettings.index.testConnection")}
</Button>
<Button size="sm" onClick={handleSaveChanges} disabled={isSaving} className="text-sm font-medium">
{isSaving ? "Saving..." : "Save Changes"}
{isSaving ? t("common.saving") : t("cacheSettings.index.saveChanges")}
</Button>
</div>
</div>

View file

@ -7675,5 +7675,38 @@
"weekly": "weekly",
"monthly": "monthly"
}
},
"cacheSettings": {
"cacheFieldRenderer": {
"searchAndSelectModel": "Search and select a model..."
},
"redisTypeSelector": {
"redisType": "Redis Type",
"nodeType": "Node (Single Instance)",
"clusterType": "Cluster",
"sentinelType": "Sentinel",
"semanticType": "Semantic",
"selectTypeDescription": "Select the type of Redis deployment you're using"
},
"index": {
"failedToLoad": "Failed to load cache settings",
"testSuccessful": "Cache connection test successful!",
"testFailed": "Connection test failed: {{message}}",
"updateSuccess": "Cache settings updated successfully",
"failedToUpdate": "Failed to update cache settings",
"title": "Cache Settings",
"subtitle": "Configure Redis cache for LiteLLM",
"connectionSettings": "Connection Settings",
"clusterConfiguration": "Cluster Configuration",
"sentinelConfiguration": "Sentinel Configuration",
"semanticConfiguration": "Semantic Configuration",
"advancedSettings": "Advanced Settings",
"sslSettings": "SSL Settings",
"cacheManagement": "Cache Management",
"gcpAuthentication": "GCP Authentication",
"testing": "Testing...",
"testConnection": "Test Connection",
"saveChanges": "Save Changes"
}
}
}

View file

@ -7675,5 +7675,38 @@
"weekly": "每周",
"monthly": "每月"
}
},
"cacheSettings": {
"cacheFieldRenderer": {
"searchAndSelectModel": "搜索并选择模型..."
},
"redisTypeSelector": {
"redisType": "Redis 类型",
"nodeType": "节点(单实例)",
"clusterType": "集群",
"sentinelType": "哨兵",
"semanticType": "语义",
"selectTypeDescription": "选择您使用的 Redis 部署类型"
},
"index": {
"failedToLoad": "加载缓存设置失败",
"testSuccessful": "缓存连接测试成功!",
"testFailed": "连接测试失败:{{message}}",
"updateSuccess": "缓存设置已成功更新",
"failedToUpdate": "更新缓存设置失败",
"title": "缓存设置",
"subtitle": "为 LiteLLM 配置 Redis 缓存",
"connectionSettings": "连接设置",
"clusterConfiguration": "集群配置",
"sentinelConfiguration": "哨兵配置",
"semanticConfiguration": "语义配置",
"advancedSettings": "高级设置",
"sslSettings": "SSL 设置",
"cacheManagement": "缓存管理",
"gcpAuthentication": "GCP 身份验证",
"testing": "测试中...",
"testConnection": "测试连接",
"saveChanges": "保存更改"
}
}
}