mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-19 19:50:26 +08:00
i18n: complete usage panel and settings internationalization
- Add missing i18n keys for usage statistics panel (trends, cost, perMillion, etc.) - Add i18n keys for settings advanced section (configDir, proxy, modelTest, etc.) - Add streamCheck i18n keys for health check configuration - Remove hardcoded Chinese fallback values from t() calls - Add common keys (all, search, reset, actions, deleting)
This commit is contained in:
@@ -215,7 +215,7 @@ export function SettingsPage({
|
||||
{t("settings.tabAdvanced")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="usage">
|
||||
{t("usage.title", "使用统计")}
|
||||
{t("usage.title")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="about">{t("common.about")}</TabsTrigger>
|
||||
</TabsList>
|
||||
@@ -254,10 +254,10 @@ export function SettingsPage({
|
||||
<FolderSearch className="h-5 w-5 text-primary" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-base font-semibold">
|
||||
配置文件目录
|
||||
{t("settings.advanced.configDir.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground font-normal">
|
||||
管理 Claude、Codex 和 Gemini 的配置存储路径
|
||||
{t("settings.advanced.configDir.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -289,10 +289,10 @@ export function SettingsPage({
|
||||
<Server className="h-5 w-5 text-green-500" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-base font-semibold">
|
||||
本地代理
|
||||
{t("settings.advanced.proxy.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground font-normal">
|
||||
控制代理服务开关、查看状态与端口信息
|
||||
{t("settings.advanced.proxy.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -307,7 +307,7 @@ export function SettingsPage({
|
||||
<Activity
|
||||
className={`h-3 w-3 ${isRunning ? "animate-pulse" : ""}`}
|
||||
/>
|
||||
{isRunning ? "运行中" : "已停止"}
|
||||
{isRunning ? t("settings.advanced.proxy.running") : t("settings.advanced.proxy.stopped")}
|
||||
</Badge>
|
||||
<Switch
|
||||
checked={isRunning}
|
||||
@@ -330,10 +330,10 @@ export function SettingsPage({
|
||||
<Activity className="h-5 w-5 text-indigo-500" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-base font-semibold">
|
||||
模型测试配置
|
||||
{t("settings.advanced.modelTest.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground font-normal">
|
||||
配置模型测试使用的默认模型和提示词
|
||||
{t("settings.advanced.modelTest.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -353,10 +353,10 @@ export function SettingsPage({
|
||||
<Activity className="h-5 w-5 text-orange-500" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-base font-semibold">
|
||||
自动故障转移
|
||||
{t("settings.advanced.failover.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground font-normal">
|
||||
配置故障转移队列和熔断策略
|
||||
{t("settings.advanced.failover.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -376,13 +376,10 @@ export function SettingsPage({
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold">
|
||||
{t("proxy.failoverQueue.title", "故障转移队列")}
|
||||
{t("proxy.failoverQueue.title")}
|
||||
</h4>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"proxy.failoverQueue.description",
|
||||
"管理各应用的供应商故障转移顺序",
|
||||
)}
|
||||
{t("proxy.failoverQueue.description")}
|
||||
</p>
|
||||
</div>
|
||||
<Tabs defaultValue="claude" className="w-full">
|
||||
@@ -432,10 +429,10 @@ export function SettingsPage({
|
||||
<Coins className="h-5 w-5 text-yellow-500" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-base font-semibold">
|
||||
成本定价
|
||||
{t("settings.advanced.pricing.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground font-normal">
|
||||
管理各模型 Token 计费规则
|
||||
{t("settings.advanced.pricing.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -454,10 +451,10 @@ export function SettingsPage({
|
||||
<Database className="h-5 w-5 text-blue-500" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-base font-semibold">
|
||||
数据管理
|
||||
{t("settings.advanced.data.title")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground font-normal">
|
||||
导入导出配置与备份恢复
|
||||
{t("settings.advanced.data.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,12 +47,12 @@ export function ModelTestConfigPanel() {
|
||||
try {
|
||||
setIsSaving(true);
|
||||
await saveStreamCheckConfig(config);
|
||||
toast.success(t("streamCheck.configSaved", "健康检查配置已保存"), {
|
||||
toast.success(t("streamCheck.configSaved"), {
|
||||
closeButton: true,
|
||||
});
|
||||
} catch (e) {
|
||||
toast.error(
|
||||
t("streamCheck.configSaveFailed", "保存失败") + ": " + String(e),
|
||||
t("streamCheck.configSaveFailed") + ": " + String(e),
|
||||
);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
@@ -78,12 +78,12 @@ export function ModelTestConfigPanel() {
|
||||
{/* 测试模型配置 */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-sm font-medium text-muted-foreground">
|
||||
{t("streamCheck.testModels", "测试模型")}
|
||||
{t("streamCheck.testModels")}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="claudeModel">
|
||||
{t("streamCheck.claudeModel", "Claude 模型")}
|
||||
{t("streamCheck.claudeModel")}
|
||||
</Label>
|
||||
<Input
|
||||
id="claudeModel"
|
||||
@@ -97,7 +97,7 @@ export function ModelTestConfigPanel() {
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="codexModel">
|
||||
{t("streamCheck.codexModel", "Codex 模型")}
|
||||
{t("streamCheck.codexModel")}
|
||||
</Label>
|
||||
<Input
|
||||
id="codexModel"
|
||||
@@ -111,7 +111,7 @@ export function ModelTestConfigPanel() {
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="geminiModel">
|
||||
{t("streamCheck.geminiModel", "Gemini 模型")}
|
||||
{t("streamCheck.geminiModel")}
|
||||
</Label>
|
||||
<Input
|
||||
id="geminiModel"
|
||||
@@ -128,12 +128,12 @@ export function ModelTestConfigPanel() {
|
||||
{/* 检查参数配置 */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-sm font-medium text-muted-foreground">
|
||||
{t("streamCheck.checkParams", "检查参数")}
|
||||
{t("streamCheck.checkParams")}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="timeoutSecs">
|
||||
{t("streamCheck.timeout", "超时时间(秒)")}
|
||||
{t("streamCheck.timeout")}
|
||||
</Label>
|
||||
<Input
|
||||
id="timeoutSecs"
|
||||
@@ -152,7 +152,7 @@ export function ModelTestConfigPanel() {
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="maxRetries">
|
||||
{t("streamCheck.maxRetries", "最大重试次数")}
|
||||
{t("streamCheck.maxRetries")}
|
||||
</Label>
|
||||
<Input
|
||||
id="maxRetries"
|
||||
@@ -171,7 +171,7 @@ export function ModelTestConfigPanel() {
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="degradedThresholdMs">
|
||||
{t("streamCheck.degradedThreshold", "降级阈值(毫秒)")}
|
||||
{t("streamCheck.degradedThreshold")}
|
||||
</Label>
|
||||
<Input
|
||||
id="degradedThresholdMs"
|
||||
@@ -196,12 +196,12 @@ export function ModelTestConfigPanel() {
|
||||
{isSaving ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
{t("common.saving", "保存中...")}
|
||||
{t("common.saving")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
{t("common.save", "保存")}
|
||||
{t("common.save")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@@ -63,7 +63,7 @@ export function PricingConfigPanel() {
|
||||
<div className="flex items-center gap-2">
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
<CardTitle className="text-base">
|
||||
{t("usage.modelPricing", "模型定价")}
|
||||
{t("usage.modelPricing")}
|
||||
</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
@@ -85,7 +85,7 @@ export function PricingConfigPanel() {
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
)}
|
||||
<CardTitle className="text-base">
|
||||
{t("usage.modelPricing", "模型定价")}
|
||||
{t("usage.modelPricing")}
|
||||
</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
@@ -93,7 +93,7 @@ export function PricingConfigPanel() {
|
||||
<CardContent>
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>
|
||||
{t("usage.loadPricingError", "加载定价数据失败")}:{" "}
|
||||
{t("usage.loadPricingError")}:{" "}
|
||||
{String(error)}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
@@ -107,7 +107,7 @@ export function PricingConfigPanel() {
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h4 className="text-sm font-medium text-muted-foreground">
|
||||
{t("usage.modelPricingDesc", "配置各模型的 Token 成本")} (每百万)
|
||||
{t("usage.modelPricingDesc")} {t("usage.perMillion")}
|
||||
</h4>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
@@ -117,7 +117,7 @@ export function PricingConfigPanel() {
|
||||
size="sm"
|
||||
>
|
||||
<Plus className="mr-1 h-4 w-4" />
|
||||
{t("common.add", "新增")}
|
||||
{t("common.add")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -125,10 +125,7 @@ export function PricingConfigPanel() {
|
||||
{!pricing || pricing.length === 0 ? (
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
{t(
|
||||
"usage.noPricingData",
|
||||
'暂无定价数据。点击"新增"添加模型定价配置。',
|
||||
)}
|
||||
{t("usage.noPricingData")}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : (
|
||||
@@ -136,22 +133,22 @@ export function PricingConfigPanel() {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{t("usage.model", "模型")}</TableHead>
|
||||
<TableHead>{t("usage.displayName", "显示名称")}</TableHead>
|
||||
<TableHead>{t("usage.model")}</TableHead>
|
||||
<TableHead>{t("usage.displayName")}</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("usage.inputCost", "输入成本")}
|
||||
{t("usage.inputCost")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("usage.outputCost", "输出成本")}
|
||||
{t("usage.outputCost")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("usage.cacheReadCost", "缓存读取")}
|
||||
{t("usage.cacheReadCost")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("usage.cacheWriteCost", "缓存写入")}
|
||||
{t("usage.cacheWriteCost")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("common.actions", "操作")}
|
||||
{t("common.actions")}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -183,7 +180,7 @@ export function PricingConfigPanel() {
|
||||
setIsAddingNew(false);
|
||||
setEditingModel(model);
|
||||
}}
|
||||
title={t("common.edit", "编辑")}
|
||||
title={t("common.edit")}
|
||||
>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -191,7 +188,7 @@ export function PricingConfigPanel() {
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setDeleteConfirm(model.modelId)}
|
||||
title={t("common.delete", "删除")}
|
||||
title={t("common.delete")}
|
||||
className="text-destructive hover:text-destructive"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
@@ -224,18 +221,15 @@ export function PricingConfigPanel() {
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t("usage.deleteConfirmTitle", "确认删除")}
|
||||
{t("usage.deleteConfirmTitle")}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t(
|
||||
"usage.deleteConfirmDesc",
|
||||
"确定要删除此模型定价配置吗?此操作无法撤销。",
|
||||
)}
|
||||
{t("usage.deleteConfirmDesc")}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setDeleteConfirm(null)}>
|
||||
{t("common.cancel", "取消")}
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
@@ -243,8 +237,8 @@ export function PricingConfigPanel() {
|
||||
disabled={deleteMutation.isPending}
|
||||
>
|
||||
{deleteMutation.isPending
|
||||
? t("common.deleting", "删除中...")
|
||||
: t("common.delete", "删除")}
|
||||
? t("common.deleting")
|
||||
: t("common.delete")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@@ -84,11 +84,11 @@ export function RequestLogTable() {
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[130px] bg-background">
|
||||
<SelectValue placeholder={t("usage.appType", "应用类型")} />
|
||||
<SelectValue placeholder={t("usage.appType")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">
|
||||
{t("usage.allApps", { defaultValue: "全部应用" })}
|
||||
{t("usage.allApps")}
|
||||
</SelectItem>
|
||||
<SelectItem value="claude">Claude</SelectItem>
|
||||
<SelectItem value="codex">Codex</SelectItem>
|
||||
@@ -106,10 +106,10 @@ export function RequestLogTable() {
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[130px] bg-background">
|
||||
<SelectValue placeholder={t("usage.statusCode", "状态码")} />
|
||||
<SelectValue placeholder={t("usage.statusCode")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">{t("common.all", "全部状态")}</SelectItem>
|
||||
<SelectItem value="all">{t("common.all")}</SelectItem>
|
||||
<SelectItem value="200">200 OK</SelectItem>
|
||||
<SelectItem value="400">400 Bad Request</SelectItem>
|
||||
<SelectItem value="401">401 Unauthorized</SelectItem>
|
||||
@@ -122,10 +122,7 @@ export function RequestLogTable() {
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder={t(
|
||||
"usage.searchProviderPlaceholder",
|
||||
"搜索供应商...",
|
||||
)}
|
||||
placeholder={t("usage.searchProviderPlaceholder")}
|
||||
className="pl-9 bg-background"
|
||||
value={tempFilters.providerName || ""}
|
||||
onChange={(e) =>
|
||||
@@ -137,7 +134,7 @@ export function RequestLogTable() {
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
placeholder={t("usage.searchModelPlaceholder", "搜索模型...")}
|
||||
placeholder={t("usage.searchModelPlaceholder")}
|
||||
className="w-[180px] bg-background"
|
||||
value={tempFilters.model || ""}
|
||||
onChange={(e) =>
|
||||
@@ -153,7 +150,7 @@ export function RequestLogTable() {
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<span className="whitespace-nowrap">
|
||||
{t("usage.timeRange", { defaultValue: "时间范围" })}:
|
||||
{t("usage.timeRange")}:
|
||||
</span>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
@@ -204,7 +201,7 @@ export function RequestLogTable() {
|
||||
className="h-8"
|
||||
>
|
||||
<Search className="mr-2 h-3.5 w-3.5" />
|
||||
{t("common.search", "查询")}
|
||||
{t("common.search")}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -213,7 +210,7 @@ export function RequestLogTable() {
|
||||
className="h-8"
|
||||
>
|
||||
<X className="mr-2 h-3.5 w-3.5" />
|
||||
{t("common.reset", "重置")}
|
||||
{t("common.reset")}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -236,34 +233,34 @@ export function RequestLogTable() {
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="whitespace-nowrap">
|
||||
{t("usage.time", "时间")}
|
||||
{t("usage.time")}
|
||||
</TableHead>
|
||||
<TableHead className="whitespace-nowrap">
|
||||
{t("usage.provider", "供应商")}
|
||||
{t("usage.provider")}
|
||||
</TableHead>
|
||||
<TableHead className="min-w-[280px] whitespace-nowrap">
|
||||
{t("usage.billingModel", "计费模型")}
|
||||
{t("usage.billingModel")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right whitespace-nowrap">
|
||||
{t("usage.inputTokens", "输入")}
|
||||
{t("usage.inputTokens")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right whitespace-nowrap">
|
||||
{t("usage.outputTokens", "输出")}
|
||||
{t("usage.outputTokens")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right min-w-[90px] whitespace-nowrap">
|
||||
{t("usage.cacheReadTokens", "缓存读取")}
|
||||
{t("usage.cacheReadTokens")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right min-w-[90px] whitespace-nowrap">
|
||||
{t("usage.cacheCreationTokens", "缓存写入")}
|
||||
{t("usage.cacheCreationTokens")}
|
||||
</TableHead>
|
||||
<TableHead className="text-right whitespace-nowrap">
|
||||
{t("usage.totalCost", "成本")}
|
||||
{t("usage.totalCost")}
|
||||
</TableHead>
|
||||
<TableHead className="text-center min-w-[140px] whitespace-nowrap">
|
||||
{t("usage.timingInfo", "用时/首字")}
|
||||
{t("usage.timingInfo")}
|
||||
</TableHead>
|
||||
<TableHead className="whitespace-nowrap">
|
||||
{t("usage.status", "状态")}
|
||||
{t("usage.status")}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -274,7 +271,7 @@ export function RequestLogTable() {
|
||||
colSpan={10}
|
||||
className="text-center text-muted-foreground"
|
||||
>
|
||||
{t("usage.noData", "暂无数据")}
|
||||
{t("usage.noData")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
@@ -287,7 +284,7 @@ export function RequestLogTable() {
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{log.providerName ||
|
||||
t("usage.unknownProvider", "未知供应商")}
|
||||
t("usage.unknownProvider")}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className="font-mono text-sm max-w-[280px] truncate"
|
||||
@@ -355,8 +352,8 @@ export function RequestLogTable() {
|
||||
}`}
|
||||
>
|
||||
{log.isStreaming
|
||||
? t("usage.stream", "流")
|
||||
: t("usage.nonStream", "非流")}
|
||||
? t("usage.stream")
|
||||
: t("usage.nonStream")}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -382,7 +379,7 @@ export function RequestLogTable() {
|
||||
{total > 0 && (
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{t("usage.totalRecords", "共 {{total}} 条记录", { total })}
|
||||
{t("usage.totalRecords", { total })}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
|
||||
@@ -25,9 +25,9 @@ export function UsageDashboard() {
|
||||
>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h2 className="text-2xl font-bold">{t("usage.title", "使用统计")}</h2>
|
||||
<h2 className="text-2xl font-bold">{t("usage.title")}</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("usage.subtitle", "查看 AI 模型的使用情况和成本统计")}
|
||||
{t("usage.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -41,19 +41,19 @@ export function UsageDashboard() {
|
||||
value="1d"
|
||||
className="flex-1 sm:flex-none sm:px-6 data-[state=active]:bg-primary/10 data-[state=active]:text-primary hover:text-primary transition-colors"
|
||||
>
|
||||
{t("usage.today", "24小时")}
|
||||
{t("usage.today")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="7d"
|
||||
className="flex-1 sm:flex-none sm:px-6 data-[state=active]:bg-primary/10 data-[state=active]:text-primary hover:text-primary transition-colors"
|
||||
>
|
||||
{t("usage.last7days", "7天")}
|
||||
{t("usage.last7days")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="30d"
|
||||
className="flex-1 sm:flex-none sm:px-6 data-[state=active]:bg-primary/10 data-[state=active]:text-primary hover:text-primary transition-colors"
|
||||
>
|
||||
{t("usage.last30days", "30天")}
|
||||
{t("usage.last30days")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
@@ -69,15 +69,15 @@ export function UsageDashboard() {
|
||||
<TabsList className="bg-muted/50">
|
||||
<TabsTrigger value="logs" className="gap-2">
|
||||
<ListFilter className="h-4 w-4" />
|
||||
{t("usage.requestLogs", "请求日志")}
|
||||
{t("usage.requestLogs")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="providers" className="gap-2">
|
||||
<Activity className="h-4 w-4" />
|
||||
{t("usage.providerStats", "Provider 统计")}
|
||||
{t("usage.providerStats")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="models" className="gap-2">
|
||||
<BarChart3 className="h-4 w-4" />
|
||||
{t("usage.modelStats", "模型统计")}
|
||||
{t("usage.modelStats")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,7 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
|
||||
|
||||
return [
|
||||
{
|
||||
title: t("usage.totalRequests", "总请求数"),
|
||||
title: t("usage.totalRequests"),
|
||||
value: totalRequests.toLocaleString(),
|
||||
icon: Activity,
|
||||
color: "text-blue-500",
|
||||
@@ -42,7 +42,7 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
|
||||
subValue: null,
|
||||
},
|
||||
{
|
||||
title: t("usage.totalCost", "总成本"),
|
||||
title: t("usage.totalCost"),
|
||||
value: `$${totalCost.toFixed(4)}`,
|
||||
icon: DollarSign,
|
||||
color: "text-green-500",
|
||||
@@ -50,7 +50,7 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
|
||||
subValue: null,
|
||||
},
|
||||
{
|
||||
title: t("usage.totalTokens", "总 Token 数"),
|
||||
title: t("usage.totalTokens"),
|
||||
value: totalTokens.toLocaleString(),
|
||||
icon: Layers,
|
||||
color: "text-purple-500",
|
||||
@@ -58,13 +58,13 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
|
||||
subValue: (
|
||||
<div className="flex flex-col gap-1 text-xs text-muted-foreground mt-3 pt-3 border-t border-border/50">
|
||||
<div className="flex justify-between items-center">
|
||||
<span>{t("usage.input", { defaultValue: "Input" })}</span>
|
||||
<span>{t("usage.input")}</span>
|
||||
<span className="text-foreground/80">
|
||||
{(inputTokens / 1000).toFixed(1)}k
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span>{t("usage.output", { defaultValue: "Output" })}</span>
|
||||
<span>{t("usage.output")}</span>
|
||||
<span className="text-foreground/80">
|
||||
{(outputTokens / 1000).toFixed(1)}k
|
||||
</span>
|
||||
@@ -73,7 +73,7 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("usage.cacheTokens", "缓存 Token"),
|
||||
title: t("usage.cacheTokens"),
|
||||
value: totalCacheTokens.toLocaleString(),
|
||||
icon: Database,
|
||||
color: "text-orange-500",
|
||||
@@ -81,13 +81,13 @@ export function UsageSummaryCards({ days }: UsageSummaryCardsProps) {
|
||||
subValue: (
|
||||
<div className="flex flex-col gap-1 text-xs text-muted-foreground mt-3 pt-3 border-t border-border/50">
|
||||
<div className="flex justify-between items-center">
|
||||
<span>{t("usage.cacheWrite", { defaultValue: "Write" })}</span>
|
||||
<span>{t("usage.cacheWrite")}</span>
|
||||
<span className="text-foreground/80">
|
||||
{(cacheWriteTokens / 1000).toFixed(1)}k
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span>{t("usage.cacheRead", { defaultValue: "Read" })}</span>
|
||||
<span>{t("usage.cacheRead")}</span>
|
||||
<span className="text-foreground/80">
|
||||
{(cacheReadTokens / 1000).toFixed(1)}k
|
||||
</span>
|
||||
|
||||
@@ -32,7 +32,11 @@
|
||||
"back": "Back",
|
||||
"refresh": "Refresh",
|
||||
"refreshing": "Refreshing...",
|
||||
"notInstalled": "Not installed"
|
||||
"all": "All",
|
||||
"search": "Search",
|
||||
"reset": "Reset",
|
||||
"actions": "Actions",
|
||||
"deleting": "Deleting..."
|
||||
},
|
||||
"apiKeyInput": {
|
||||
"placeholder": "Enter API Key",
|
||||
@@ -142,6 +146,34 @@
|
||||
"general": "General",
|
||||
"tabGeneral": "General",
|
||||
"tabAdvanced": "Advanced",
|
||||
"advanced": {
|
||||
"configDir": {
|
||||
"title": "Configuration Directory",
|
||||
"description": "Manage storage paths for Claude, Codex and Gemini configurations"
|
||||
},
|
||||
"proxy": {
|
||||
"title": "Local Proxy",
|
||||
"description": "Control proxy service toggle, view status and port info",
|
||||
"running": "Running",
|
||||
"stopped": "Stopped"
|
||||
},
|
||||
"modelTest": {
|
||||
"title": "Model Test Config",
|
||||
"description": "Configure default models and prompts for model testing"
|
||||
},
|
||||
"failover": {
|
||||
"title": "Auto Failover",
|
||||
"description": "Configure failover queue and circuit breaker strategy"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "Cost Pricing",
|
||||
"description": "Manage token pricing rules for each model"
|
||||
},
|
||||
"data": {
|
||||
"title": "Data Management",
|
||||
"description": "Import/export configurations and backup/restore"
|
||||
}
|
||||
},
|
||||
"language": "Language",
|
||||
"languageHint": "Preview interface language immediately after switching, takes effect permanently after saving.",
|
||||
"theme": "Theme",
|
||||
@@ -365,6 +397,50 @@
|
||||
"hint": "You can continue to adjust the fields below after selecting a preset."
|
||||
},
|
||||
"usage": {
|
||||
"title": "Usage Statistics",
|
||||
"subtitle": "View AI model usage and cost statistics",
|
||||
"today": "24 Hours",
|
||||
"last7days": "7 Days",
|
||||
"last30days": "30 Days",
|
||||
"totalRequests": "Total Requests",
|
||||
"totalCost": "Total Cost",
|
||||
"cost": "Cost",
|
||||
"perMillion": "(per million)",
|
||||
"trends": "Usage Trends",
|
||||
"rangeToday": "Today (hourly)",
|
||||
"rangeLast7Days": "Last 7 days",
|
||||
"rangeLast30Days": "Last 30 days",
|
||||
"totalTokens": "Total Tokens",
|
||||
"cacheTokens": "Cache Tokens",
|
||||
"requestLogs": "Request Logs",
|
||||
"providerStats": "Provider Stats",
|
||||
"modelStats": "Model Stats",
|
||||
"time": "Time",
|
||||
"provider": "Provider",
|
||||
"billingModel": "Billing Model",
|
||||
"inputTokens": "Input",
|
||||
"outputTokens": "Output",
|
||||
"cacheReadTokens": "Cache Read",
|
||||
"cacheCreationTokens": "Cache Write",
|
||||
"timingInfo": "Duration/TTFT",
|
||||
"status": "Status",
|
||||
"noData": "No data",
|
||||
"unknownProvider": "Unknown Provider",
|
||||
"stream": "Stream",
|
||||
"nonStream": "Non-stream",
|
||||
"totalRecords": "{{total}} records total",
|
||||
"modelPricing": "Model Pricing",
|
||||
"loadPricingError": "Failed to load pricing data",
|
||||
"modelPricingDesc": "Configure token costs for each model",
|
||||
"noPricingData": "No pricing data. Click \"Add\" to add model pricing configuration.",
|
||||
"model": "Model",
|
||||
"displayName": "Display Name",
|
||||
"inputCost": "Input Cost",
|
||||
"outputCost": "Output Cost",
|
||||
"cacheReadCost": "Cache Read",
|
||||
"cacheWriteCost": "Cache Write",
|
||||
"deleteConfirmTitle": "Confirm Delete",
|
||||
"deleteConfirmDesc": "Are you sure you want to delete this model pricing? This action cannot be undone.",
|
||||
"queryFailed": "Query failed",
|
||||
"refreshUsage": "Refresh usage",
|
||||
"planUsage": "Plan usage",
|
||||
@@ -944,5 +1020,17 @@
|
||||
"errorRateLabel": "Error Rate Threshold",
|
||||
"errorRateExplain": "Open circuit breaker when error rate exceeds this value, even if failure threshold not reached"
|
||||
}
|
||||
},
|
||||
"streamCheck": {
|
||||
"configSaved": "Health check config saved",
|
||||
"configSaveFailed": "Save failed",
|
||||
"testModels": "Test Models",
|
||||
"claudeModel": "Claude Model",
|
||||
"codexModel": "Codex Model",
|
||||
"geminiModel": "Gemini Model",
|
||||
"checkParams": "Check Parameters",
|
||||
"timeout": "Timeout (seconds)",
|
||||
"maxRetries": "Max Retries",
|
||||
"degradedThreshold": "Degraded Threshold (ms)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,11 @@
|
||||
"back": "戻る",
|
||||
"refresh": "更新",
|
||||
"refreshing": "更新中...",
|
||||
"notInstalled": "未インストール"
|
||||
"all": "すべて",
|
||||
"search": "検索",
|
||||
"reset": "リセット",
|
||||
"actions": "操作",
|
||||
"deleting": "削除中..."
|
||||
},
|
||||
"apiKeyInput": {
|
||||
"placeholder": "API Key を入力",
|
||||
@@ -142,6 +146,34 @@
|
||||
"general": "一般",
|
||||
"tabGeneral": "一般",
|
||||
"tabAdvanced": "詳細",
|
||||
"advanced": {
|
||||
"configDir": {
|
||||
"title": "設定ディレクトリ",
|
||||
"description": "Claude、Codex、Gemini の設定保存パスを管理"
|
||||
},
|
||||
"proxy": {
|
||||
"title": "ローカルプロキシ",
|
||||
"description": "プロキシサービスの切り替え、ステータスとポート情報を表示",
|
||||
"running": "実行中",
|
||||
"stopped": "停止中"
|
||||
},
|
||||
"modelTest": {
|
||||
"title": "モデルテスト設定",
|
||||
"description": "モデルテストで使用するデフォルトモデルとプロンプトを設定"
|
||||
},
|
||||
"failover": {
|
||||
"title": "自動フェイルオーバー",
|
||||
"description": "フェイルオーバーキューとサーキットブレーカー戦略を設定"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "コスト計算",
|
||||
"description": "各モデルのトークン料金ルールを管理"
|
||||
},
|
||||
"data": {
|
||||
"title": "データ管理",
|
||||
"description": "設定のインポート/エクスポートとバックアップ/復元"
|
||||
}
|
||||
},
|
||||
"language": "言語",
|
||||
"languageHint": "切り替えるとすぐにプレビューされ、保存後に永続化されます。",
|
||||
"theme": "テーマ",
|
||||
@@ -365,6 +397,50 @@
|
||||
"hint": "プリセットを選んだ後でも、下のフィールドで調整できます。"
|
||||
},
|
||||
"usage": {
|
||||
"title": "利用統計",
|
||||
"subtitle": "AI モデルの利用状況とコスト統計を表示",
|
||||
"today": "24時間",
|
||||
"last7days": "7日間",
|
||||
"last30days": "30日間",
|
||||
"totalRequests": "総リクエスト数",
|
||||
"totalCost": "総コスト",
|
||||
"cost": "コスト",
|
||||
"perMillion": "(100万あたり)",
|
||||
"trends": "利用トレンド",
|
||||
"rangeToday": "今日 (時間別)",
|
||||
"rangeLast7Days": "過去7日間",
|
||||
"rangeLast30Days": "過去30日間",
|
||||
"totalTokens": "総トークン数",
|
||||
"cacheTokens": "キャッシュトークン",
|
||||
"requestLogs": "リクエストログ",
|
||||
"providerStats": "プロバイダー統計",
|
||||
"modelStats": "モデル統計",
|
||||
"time": "時間",
|
||||
"provider": "プロバイダー",
|
||||
"billingModel": "課金モデル",
|
||||
"inputTokens": "入力",
|
||||
"outputTokens": "出力",
|
||||
"cacheReadTokens": "キャッシュ読取",
|
||||
"cacheCreationTokens": "キャッシュ書込",
|
||||
"timingInfo": "応答時間/TTFT",
|
||||
"status": "ステータス",
|
||||
"noData": "データなし",
|
||||
"unknownProvider": "不明なプロバイダー",
|
||||
"stream": "ストリーム",
|
||||
"nonStream": "非ストリーム",
|
||||
"totalRecords": "全 {{total}} 件",
|
||||
"modelPricing": "モデル料金",
|
||||
"loadPricingError": "料金データの読み込みに失敗しました",
|
||||
"modelPricingDesc": "各モデルのトークンコストを設定",
|
||||
"noPricingData": "料金データがありません。「追加」をクリックしてモデル料金を設定してください。",
|
||||
"model": "モデル",
|
||||
"displayName": "表示名",
|
||||
"inputCost": "入力コスト",
|
||||
"outputCost": "出力コスト",
|
||||
"cacheReadCost": "キャッシュ読取",
|
||||
"cacheWriteCost": "キャッシュ書込",
|
||||
"deleteConfirmTitle": "削除の確認",
|
||||
"deleteConfirmDesc": "このモデル料金を削除しますか?この操作は元に戻せません。",
|
||||
"queryFailed": "照会に失敗しました",
|
||||
"refreshUsage": "利用状況を更新",
|
||||
"planUsage": "プラン利用状況",
|
||||
@@ -944,5 +1020,17 @@
|
||||
"errorRateLabel": "エラー率しきい値",
|
||||
"errorRateExplain": "失敗しきい値に達していなくても、エラー率がこの値を超えるとサーキットブレーカーが開きます"
|
||||
}
|
||||
},
|
||||
"streamCheck": {
|
||||
"configSaved": "ヘルスチェック設定を保存しました",
|
||||
"configSaveFailed": "保存に失敗しました",
|
||||
"testModels": "テストモデル",
|
||||
"claudeModel": "Claude モデル",
|
||||
"codexModel": "Codex モデル",
|
||||
"geminiModel": "Gemini モデル",
|
||||
"checkParams": "チェックパラメーター",
|
||||
"timeout": "タイムアウト(秒)",
|
||||
"maxRetries": "最大リトライ回数",
|
||||
"degradedThreshold": "劣化しきい値(ミリ秒)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,11 @@
|
||||
"back": "返回",
|
||||
"refresh": "刷新",
|
||||
"refreshing": "刷新中...",
|
||||
"notInstalled": "未安装"
|
||||
"all": "全部",
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"actions": "操作",
|
||||
"deleting": "删除中..."
|
||||
},
|
||||
"apiKeyInput": {
|
||||
"placeholder": "请输入API Key",
|
||||
@@ -142,6 +146,34 @@
|
||||
"general": "通用",
|
||||
"tabGeneral": "通用",
|
||||
"tabAdvanced": "高级",
|
||||
"advanced": {
|
||||
"configDir": {
|
||||
"title": "配置文件目录",
|
||||
"description": "管理 Claude、Codex 和 Gemini 的配置存储路径"
|
||||
},
|
||||
"proxy": {
|
||||
"title": "本地代理",
|
||||
"description": "控制代理服务开关、查看状态与端口信息",
|
||||
"running": "运行中",
|
||||
"stopped": "已停止"
|
||||
},
|
||||
"modelTest": {
|
||||
"title": "模型测试配置",
|
||||
"description": "配置模型测试使用的默认模型和提示词"
|
||||
},
|
||||
"failover": {
|
||||
"title": "自动故障转移",
|
||||
"description": "配置故障转移队列和熔断策略"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "成本定价",
|
||||
"description": "管理各模型 Token 计费规则"
|
||||
},
|
||||
"data": {
|
||||
"title": "数据管理",
|
||||
"description": "导入导出配置与备份恢复"
|
||||
}
|
||||
},
|
||||
"language": "界面语言",
|
||||
"languageHint": "切换后立即预览界面语言,保存后永久生效。",
|
||||
"theme": "外观主题",
|
||||
@@ -365,6 +397,50 @@
|
||||
"hint": "选择预设后可继续调整下方字段。"
|
||||
},
|
||||
"usage": {
|
||||
"title": "使用统计",
|
||||
"subtitle": "查看 AI 模型的使用情况和成本统计",
|
||||
"today": "24小时",
|
||||
"last7days": "7天",
|
||||
"last30days": "30天",
|
||||
"totalRequests": "总请求数",
|
||||
"totalCost": "总成本",
|
||||
"cost": "成本",
|
||||
"perMillion": "(每百万)",
|
||||
"trends": "使用趋势",
|
||||
"rangeToday": "今天 (按小时)",
|
||||
"rangeLast7Days": "过去 7 天",
|
||||
"rangeLast30Days": "过去 30 天",
|
||||
"totalTokens": "总 Token 数",
|
||||
"cacheTokens": "缓存 Token",
|
||||
"requestLogs": "请求日志",
|
||||
"providerStats": "Provider 统计",
|
||||
"modelStats": "模型统计",
|
||||
"time": "时间",
|
||||
"provider": "供应商",
|
||||
"billingModel": "计费模型",
|
||||
"inputTokens": "输入",
|
||||
"outputTokens": "输出",
|
||||
"cacheReadTokens": "缓存读取",
|
||||
"cacheCreationTokens": "缓存写入",
|
||||
"timingInfo": "用时/首字",
|
||||
"status": "状态",
|
||||
"noData": "暂无数据",
|
||||
"unknownProvider": "未知供应商",
|
||||
"stream": "流",
|
||||
"nonStream": "非流",
|
||||
"totalRecords": "共 {{total}} 条记录",
|
||||
"modelPricing": "模型定价",
|
||||
"loadPricingError": "加载定价数据失败",
|
||||
"modelPricingDesc": "配置各模型的 Token 成本",
|
||||
"noPricingData": "暂无定价数据。点击\"新增\"添加模型定价配置。",
|
||||
"model": "模型",
|
||||
"displayName": "显示名称",
|
||||
"inputCost": "输入成本",
|
||||
"outputCost": "输出成本",
|
||||
"cacheReadCost": "缓存读取",
|
||||
"cacheWriteCost": "缓存写入",
|
||||
"deleteConfirmTitle": "确认删除",
|
||||
"deleteConfirmDesc": "确定要删除此模型定价配置吗?此操作无法撤销。",
|
||||
"queryFailed": "查询失败",
|
||||
"refreshUsage": "刷新用量",
|
||||
"planUsage": "套餐用量",
|
||||
@@ -944,5 +1020,17 @@
|
||||
"errorRateLabel": "错误率阈值",
|
||||
"errorRateExplain": "错误率超过此值时,即使未达到失败阈值也会打开熔断器"
|
||||
}
|
||||
},
|
||||
"streamCheck": {
|
||||
"configSaved": "健康检查配置已保存",
|
||||
"configSaveFailed": "保存失败",
|
||||
"testModels": "测试模型",
|
||||
"claudeModel": "Claude 模型",
|
||||
"codexModel": "Codex 模型",
|
||||
"geminiModel": "Gemini 模型",
|
||||
"checkParams": "检查参数",
|
||||
"timeout": "超时时间(秒)",
|
||||
"maxRetries": "最大重试次数",
|
||||
"degradedThreshold": "降级阈值(毫秒)"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user