i18n: complete internationalization for provider and usage query panels
- Add 45+ new translation keys for usage query and usage script features - Fix duplicate provider object in translation files that caused missing translations - Remove all hardcoded Chinese text and defaultValue fallbacks from components - Add proper translations for: * Usage footer (query status, plan usage display) * Usage script modal (script editor, validation, test controls) * Provider forms (basic fields, endpoints, model selectors) * Provider dialogs (add/edit hints and titles) Modified 16 files: - 2 translation files (zh.json, en.json) - 14 component files (removed defaultValue, added t() calls) All UI text now properly supports Chinese/English switching.
This commit is contained in:
@@ -142,7 +142,7 @@ function App() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => setIsAddOpen(true)}>
|
<Button onClick={() => setIsAddOpen(true)}>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
{t("header.addProvider", { defaultValue: "添加供应商" })}
|
{t("header.addProvider")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -198,12 +198,11 @@ function App() {
|
|||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={Boolean(confirmDelete)}
|
isOpen={Boolean(confirmDelete)}
|
||||||
title={t("confirm.deleteProvider", { defaultValue: "删除供应商" })}
|
title={t("confirm.deleteProvider")}
|
||||||
message={
|
message={
|
||||||
confirmDelete
|
confirmDelete
|
||||||
? t("confirm.deleteProviderMessage", {
|
? t("confirm.deleteProviderMessage", {
|
||||||
name: confirmDelete.name,
|
name: confirmDelete.name,
|
||||||
defaultValue: `确定删除 ${confirmDelete.name} 吗?`,
|
|
||||||
})
|
})
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { RefreshCw, AlertCircle } from "lucide-react";
|
import { RefreshCw, AlertCircle } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { type AppType } from "@/lib/api";
|
import { type AppType } from "@/lib/api";
|
||||||
import { useUsageQuery } from "@/lib/query/queries";
|
import { useUsageQuery } from "@/lib/query/queries";
|
||||||
import { UsageData } from "../types";
|
import { UsageData } from "../types";
|
||||||
@@ -15,6 +16,7 @@ const UsageFooter: React.FC<UsageFooterProps> = ({
|
|||||||
appType,
|
appType,
|
||||||
usageEnabled,
|
usageEnabled,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
data: usage,
|
data: usage,
|
||||||
isLoading: loading,
|
isLoading: loading,
|
||||||
@@ -31,7 +33,7 @@ const UsageFooter: React.FC<UsageFooterProps> = ({
|
|||||||
<div className="flex items-center justify-between gap-2 text-xs">
|
<div className="flex items-center justify-between gap-2 text-xs">
|
||||||
<div className="flex items-center gap-2 text-red-500 dark:text-red-400">
|
<div className="flex items-center gap-2 text-red-500 dark:text-red-400">
|
||||||
<AlertCircle size={14} />
|
<AlertCircle size={14} />
|
||||||
<span>{usage.error || "查询失败"}</span>
|
<span>{usage.error || t("usage.queryFailed")}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 刷新按钮 */}
|
{/* 刷新按钮 */}
|
||||||
@@ -39,7 +41,7 @@ const UsageFooter: React.FC<UsageFooterProps> = ({
|
|||||||
onClick={() => refetch()}
|
onClick={() => refetch()}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors disabled:opacity-50 flex-shrink-0"
|
className="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors disabled:opacity-50 flex-shrink-0"
|
||||||
title="刷新用量"
|
title={t("usage.refreshUsage")}
|
||||||
>
|
>
|
||||||
<RefreshCw size={12} className={loading ? "animate-spin" : ""} />
|
<RefreshCw size={12} className={loading ? "animate-spin" : ""} />
|
||||||
</button>
|
</button>
|
||||||
@@ -58,13 +60,13 @@ const UsageFooter: React.FC<UsageFooterProps> = ({
|
|||||||
{/* 标题行:包含刷新按钮 */}
|
{/* 标题行:包含刷新按钮 */}
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<span className="text-xs text-gray-500 dark:text-gray-400 font-medium">
|
<span className="text-xs text-gray-500 dark:text-gray-400 font-medium">
|
||||||
套餐用量
|
{t("usage.planUsage")}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => refetch()}
|
onClick={() => refetch()}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors disabled:opacity-50"
|
className="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors disabled:opacity-50"
|
||||||
title="刷新用量"
|
title={t("usage.refreshUsage")}
|
||||||
>
|
>
|
||||||
<RefreshCw size={12} className={loading ? "animate-spin" : ""} />
|
<RefreshCw size={12} className={loading ? "animate-spin" : ""} />
|
||||||
</button>
|
</button>
|
||||||
@@ -82,6 +84,7 @@ const UsageFooter: React.FC<UsageFooterProps> = ({
|
|||||||
|
|
||||||
// 单个套餐数据展示组件
|
// 单个套餐数据展示组件
|
||||||
const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => {
|
const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
planName,
|
planName,
|
||||||
extra,
|
extra,
|
||||||
@@ -130,7 +133,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => {
|
|||||||
)}
|
)}
|
||||||
{isExpired && (
|
{isExpired && (
|
||||||
<span className="text-red-500 dark:text-red-400 font-medium text-[10px] px-1.5 py-0.5 bg-red-50 dark:bg-red-900/20 rounded flex-shrink-0">
|
<span className="text-red-500 dark:text-red-400 font-medium text-[10px] px-1.5 py-0.5 bg-red-50 dark:bg-red-900/20 rounded flex-shrink-0">
|
||||||
{invalidMessage || "已失效"}
|
{invalidMessage || t("usage.invalid")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -143,7 +146,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => {
|
|||||||
{/* 总额度 */}
|
{/* 总额度 */}
|
||||||
{total !== undefined && (
|
{total !== undefined && (
|
||||||
<>
|
<>
|
||||||
<span className="text-gray-500 dark:text-gray-400">总:</span>
|
<span className="text-gray-500 dark:text-gray-400">{t("usage.total")}</span>
|
||||||
<span className="tabular-nums text-gray-600 dark:text-gray-400">
|
<span className="tabular-nums text-gray-600 dark:text-gray-400">
|
||||||
{total === -1 ? "∞" : total.toFixed(2)}
|
{total === -1 ? "∞" : total.toFixed(2)}
|
||||||
</span>
|
</span>
|
||||||
@@ -154,7 +157,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => {
|
|||||||
{/* 已用额度 */}
|
{/* 已用额度 */}
|
||||||
{used !== undefined && (
|
{used !== undefined && (
|
||||||
<>
|
<>
|
||||||
<span className="text-gray-500 dark:text-gray-400">使用:</span>
|
<span className="text-gray-500 dark:text-gray-400">{t("usage.used")}</span>
|
||||||
<span className="tabular-nums text-gray-600 dark:text-gray-400">
|
<span className="tabular-nums text-gray-600 dark:text-gray-400">
|
||||||
{used.toFixed(2)}
|
{used.toFixed(2)}
|
||||||
</span>
|
</span>
|
||||||
@@ -165,7 +168,7 @@ const UsagePlanItem: React.FC<{ data: UsageData }> = ({ data }) => {
|
|||||||
{/* 剩余额度 - 突出显示 */}
|
{/* 剩余额度 - 突出显示 */}
|
||||||
{remaining !== undefined && (
|
{remaining !== undefined && (
|
||||||
<>
|
<>
|
||||||
<span className="text-gray-500 dark:text-gray-400">剩余:</span>
|
<span className="text-gray-500 dark:text-gray-400">{t("usage.remaining")}</span>
|
||||||
<span
|
<span
|
||||||
className={`font-semibold tabular-nums ${
|
className={`font-semibold tabular-nums ${
|
||||||
isExpired
|
isExpired
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Play, Wand2 } from "lucide-react";
|
import { Play, Wand2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { Provider, UsageScript } from "../types";
|
import { Provider, UsageScript } from "../types";
|
||||||
import { usageApi, type AppType } from "@/lib/api";
|
import { usageApi, type AppType } from "@/lib/api";
|
||||||
import JsonEditor from "./JsonEditor";
|
import JsonEditor from "./JsonEditor";
|
||||||
@@ -88,12 +89,13 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [script, setScript] = useState<UsageScript>(() => {
|
const [script, setScript] = useState<UsageScript>(() => {
|
||||||
return (
|
return (
|
||||||
provider.meta?.usage_script || {
|
provider.meta?.usage_script || {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
language: "javascript",
|
language: "javascript",
|
||||||
code: PRESET_TEMPLATES["通用模板"],
|
code: PRESET_TEMPLATES[t("usageScript.presetTemplate") === "预设模板" ? "通用模板" : "General"],
|
||||||
timeout: 10,
|
timeout: 10,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -104,13 +106,13 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
// 验证脚本格式
|
// 验证脚本格式
|
||||||
if (script.enabled && !script.code.trim()) {
|
if (script.enabled && !script.code.trim()) {
|
||||||
toast.error("脚本配置不能为空");
|
toast.error(t("usageScript.scriptEmpty"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基本的 JS 语法检查(检查是否包含 return 语句)
|
// 基本的 JS 语法检查(检查是否包含 return 语句)
|
||||||
if (script.enabled && !script.code.includes("return")) {
|
if (script.enabled && !script.code.includes("return")) {
|
||||||
toast.error("脚本必须包含 return 语句", { duration: 5000 });
|
toast.error(t("usageScript.mustHaveReturn"), { duration: 5000 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,17 +129,17 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
const summary = result.data
|
const summary = result.data
|
||||||
.map((plan) => {
|
.map((plan) => {
|
||||||
const planInfo = plan.planName ? `[${plan.planName}]` : "";
|
const planInfo = plan.planName ? `[${plan.planName}]` : "";
|
||||||
return `${planInfo} 剩余: ${plan.remaining} ${plan.unit}`;
|
return `${planInfo} ${t("usage.remaining")} ${plan.remaining} ${plan.unit}`;
|
||||||
})
|
})
|
||||||
.join(", ");
|
.join(", ");
|
||||||
toast.success(`测试成功!${summary}`, { duration: 3000 });
|
toast.success(`${t("usageScript.testSuccess")}${summary}`, { duration: 3000 });
|
||||||
} else {
|
} else {
|
||||||
toast.error(`测试失败: ${result.error || "无数据返回"}`, {
|
toast.error(`${t("usageScript.testFailed")}: ${result.error || t("endpointTest.noResult")}`, {
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(`测试失败: ${error?.message || "未知错误"}`, {
|
toast.error(`${t("usageScript.testFailed")}: ${error?.message || t("common.unknown")}`, {
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@@ -156,9 +158,9 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
printWidth: 80,
|
printWidth: 80,
|
||||||
});
|
});
|
||||||
setScript({ ...script, code: formatted.trim() });
|
setScript({ ...script, code: formatted.trim() });
|
||||||
toast.success("格式化成功", { duration: 1000 });
|
toast.success(t("usageScript.formatSuccess"), { duration: 1000 });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(`格式化失败: ${error?.message || "语法错误"}`, {
|
toast.error(`${t("usageScript.formatFailed")}: ${error?.message || t("jsonEditor.invalidJson")}`, {
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -175,7 +177,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||||
<DialogContent className="max-w-3xl max-h-[90vh] flex flex-col">
|
<DialogContent className="max-w-3xl max-h-[90vh] flex flex-col">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>配置用量查询 - {provider.name}</DialogTitle>
|
<DialogTitle>{t("usageScript.title")} - {provider.name}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
{/* Content - Scrollable */}
|
{/* Content - Scrollable */}
|
||||||
@@ -191,7 +193,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
className="w-4 h-4"
|
className="w-4 h-4"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||||
启用用量查询
|
{t("usageScript.enableUsageQuery")}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -200,7 +202,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
{/* 预设模板选择 */}
|
{/* 预设模板选择 */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2 text-gray-900 dark:text-gray-100">
|
<label className="block text-sm font-medium mb-2 text-gray-900 dark:text-gray-100">
|
||||||
预设模板
|
{t("usageScript.presetTemplate")}
|
||||||
</label>
|
</label>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{Object.keys(PRESET_TEMPLATES).map((name) => (
|
{Object.keys(PRESET_TEMPLATES).map((name) => (
|
||||||
@@ -218,7 +220,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
{/* 脚本编辑器 */}
|
{/* 脚本编辑器 */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2 text-gray-900 dark:text-gray-100">
|
<label className="block text-sm font-medium mb-2 text-gray-900 dark:text-gray-100">
|
||||||
查询脚本(JavaScript)
|
{t("usageScript.queryScript")}
|
||||||
</label>
|
</label>
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
value={script.code}
|
value={script.code}
|
||||||
@@ -227,9 +229,10 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
language="javascript"
|
language="javascript"
|
||||||
/>
|
/>
|
||||||
<p className="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
<p className="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||||
支持变量: <code>{"{{apiKey}}"}</code>,{" "}
|
{t("usageScript.variablesHint", {
|
||||||
<code>{"{{baseUrl}}"}</code> | extractor 函数接收 API 响应的
|
apiKey: "{{apiKey}}",
|
||||||
JSON 对象
|
baseUrl: "{{baseUrl}}"
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -237,7 +240,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<label className="block">
|
<label className="block">
|
||||||
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||||
超时时间(秒)
|
{t("usageScript.timeoutSeconds")}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
@@ -257,10 +260,10 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
|
|
||||||
{/* 脚本说明 */}
|
{/* 脚本说明 */}
|
||||||
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg text-sm text-gray-700 dark:text-gray-300">
|
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg text-sm text-gray-700 dark:text-gray-300">
|
||||||
<h4 className="font-medium mb-2">脚本编写说明:</h4>
|
<h4 className="font-medium mb-2">{t("usageScript.scriptHelp")}</h4>
|
||||||
<div className="space-y-3 text-xs">
|
<div className="space-y-3 text-xs">
|
||||||
<div>
|
<div>
|
||||||
<strong>配置格式:</strong>
|
<strong>{t("usageScript.configFormat")}</strong>
|
||||||
<pre className="mt-1 p-2 bg-white/50 dark:bg-black/20 rounded text-[10px] overflow-x-auto">
|
<pre className="mt-1 p-2 bg-white/50 dark:bg-black/20 rounded text-[10px] overflow-x-auto">
|
||||||
{`({
|
{`({
|
||||||
request: {
|
request: {
|
||||||
@@ -285,51 +288,25 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<strong>extractor 返回格式(所有字段均为可选):</strong>
|
<strong>{t("usageScript.extractorFormat")}</strong>
|
||||||
<ul className="mt-1 space-y-0.5 ml-2">
|
<ul className="mt-1 space-y-0.5 ml-2">
|
||||||
<li>
|
<li>{t("usageScript.fieldIsValid")}</li>
|
||||||
• <code>isValid</code>: 布尔值,套餐是否有效
|
<li>{t("usageScript.fieldInvalidMessage")}</li>
|
||||||
</li>
|
<li>{t("usageScript.fieldRemaining")}</li>
|
||||||
<li>
|
<li>{t("usageScript.fieldUnit")}</li>
|
||||||
• <code>invalidMessage</code>: 字符串,失效原因说明(当
|
<li>{t("usageScript.fieldPlanName")}</li>
|
||||||
isValid 为 false 时显示)
|
<li>{t("usageScript.fieldTotal")}</li>
|
||||||
</li>
|
<li>{t("usageScript.fieldUsed")}</li>
|
||||||
<li>
|
<li>{t("usageScript.fieldExtra")}</li>
|
||||||
• <code>remaining</code>: 数字,剩余额度
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
• <code>unit</code>: 字符串,单位(如 "USD")
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
• <code>planName</code>: 字符串,套餐名称
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
• <code>total</code>: 数字,总额度
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
• <code>used</code>: 数字,已用额度
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
• <code>extra</code>:
|
|
||||||
字符串,扩展字段,可自由补充需要展示的文本
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-gray-600 dark:text-gray-400">
|
<div className="text-gray-600 dark:text-gray-400">
|
||||||
<strong>💡 提示:</strong>
|
<strong>{t("usageScript.tips")}</strong>
|
||||||
<ul className="mt-1 space-y-0.5 ml-2">
|
<ul className="mt-1 space-y-0.5 ml-2">
|
||||||
<li>
|
<li>{t("usageScript.tip1", { apiKey: "{{apiKey}}", baseUrl: "{{baseUrl}}" })}</li>
|
||||||
• 变量 <code>{"{{apiKey}}"}</code> 和{" "}
|
<li>{t("usageScript.tip2")}</li>
|
||||||
<code>{"{{baseUrl}}"}</code> 会自动替换
|
<li>{t("usageScript.tip3")}</li>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
• extractor 函数在沙箱环境中执行,支持 ES2020+ 语法
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
• 整个配置必须用 <code>()</code>{" "}
|
|
||||||
包裹,形成对象字面量表达式
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -349,27 +326,27 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
disabled={!script.enabled || testing}
|
disabled={!script.enabled || testing}
|
||||||
>
|
>
|
||||||
<Play size={14} />
|
<Play size={14} />
|
||||||
{testing ? "测试中..." : "测试脚本"}
|
{testing ? t("usageScript.testing") : t("usageScript.testScript")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleFormat}
|
onClick={handleFormat}
|
||||||
disabled={!script.enabled}
|
disabled={!script.enabled}
|
||||||
title="格式化代码 (Prettier)"
|
title={t("usageScript.format")}
|
||||||
>
|
>
|
||||||
<Wand2 size={14} />
|
<Wand2 size={14} />
|
||||||
格式化
|
{t("usageScript.format")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right side - Cancel and Save buttons */}
|
{/* Right side - Cancel and Save buttons */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button variant="ghost" size="sm" onClick={onClose}>
|
<Button variant="ghost" size="sm" onClick={onClose}>
|
||||||
取消
|
{t("common.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="default" size="sm" onClick={handleSave}>
|
<Button variant="default" size="sm" onClick={handleSave}>
|
||||||
保存配置
|
{t("usageScript.saveConfig")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|||||||
@@ -135,8 +135,8 @@ export function AddProviderDialog({
|
|||||||
|
|
||||||
const submitLabel =
|
const submitLabel =
|
||||||
appType === "claude"
|
appType === "claude"
|
||||||
? t("provider.addClaudeProvider", { defaultValue: "添加 Claude 供应商" })
|
? t("provider.addClaudeProvider")
|
||||||
: t("provider.addCodexProvider", { defaultValue: "添加 Codex 供应商" });
|
: t("provider.addCodexProvider");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
@@ -144,16 +144,14 @@ export function AddProviderDialog({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{submitLabel}</DialogTitle>
|
<DialogTitle>{submitLabel}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{t("provider.addDescription", {
|
{t("provider.addProviderHint")}
|
||||||
defaultValue: "填写信息后即可在列表中快速切换供应商。",
|
|
||||||
})}
|
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto px-6 py-4">
|
<div className="flex-1 overflow-y-auto px-6 py-4">
|
||||||
<ProviderForm
|
<ProviderForm
|
||||||
appType={appType}
|
appType={appType}
|
||||||
submitLabel={t("common.add", { defaultValue: "添加" })}
|
submitLabel={t("common.add")}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onCancel={() => onOpenChange(false)}
|
onCancel={() => onOpenChange(false)}
|
||||||
showButtons={false}
|
showButtons={false}
|
||||||
@@ -162,11 +160,11 @@ export function AddProviderDialog({
|
|||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
{t("common.cancel", { defaultValue: "取消" })}
|
{t("common.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" form="provider-form">
|
<Button type="submit" form="provider-form">
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
{t("common.add", { defaultValue: "添加" })}
|
{t("common.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -66,19 +66,17 @@ export function EditProviderDialog({
|
|||||||
<DialogContent className="max-w-2xl max-h-[90vh] flex flex-col">
|
<DialogContent className="max-w-2xl max-h-[90vh] flex flex-col">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{t("provider.editProvider", { defaultValue: "编辑供应商" })}
|
{t("provider.editProvider")}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{t("provider.editDescription", {
|
{t("provider.editProviderHint")}
|
||||||
defaultValue: "更新配置后将立即应用到当前供应商。",
|
|
||||||
})}
|
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto px-6 py-4">
|
<div className="flex-1 overflow-y-auto px-6 py-4">
|
||||||
<ProviderForm
|
<ProviderForm
|
||||||
appType={appType}
|
appType={appType}
|
||||||
submitLabel={t("common.save", { defaultValue: "保存" })}
|
submitLabel={t("common.save")}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onCancel={() => onOpenChange(false)}
|
onCancel={() => onOpenChange(false)}
|
||||||
initialData={{
|
initialData={{
|
||||||
@@ -92,11 +90,11 @@ export function EditProviderDialog({
|
|||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
{t("common.cancel", { defaultValue: "取消" })}
|
{t("common.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" form="provider-form">
|
<Button type="submit" form="provider-form">
|
||||||
<Save className="h-4 w-4" />
|
<Save className="h-4 w-4" />
|
||||||
{t("common.save", { defaultValue: "保存" })}
|
{t("common.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -35,25 +35,25 @@ export function ProviderActions({
|
|||||||
{isCurrent ? (
|
{isCurrent ? (
|
||||||
<>
|
<>
|
||||||
<Check className="h-4 w-4" />
|
<Check className="h-4 w-4" />
|
||||||
{t("provider.inUse", { defaultValue: "已启用" })}
|
{t("provider.inUse")}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Play className="h-4 w-4" />
|
<Play className="h-4 w-4" />
|
||||||
{t("provider.enable", { defaultValue: "启用" })}
|
{t("provider.enable")}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button size="sm" variant="outline" onClick={onEdit}>
|
<Button size="sm" variant="outline" onClick={onEdit}>
|
||||||
{t("common.edit", { defaultValue: "编辑" })}
|
{t("common.edit")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={onConfigureUsage}
|
onClick={onConfigureUsage}
|
||||||
title={t("provider.configureUsage", { defaultValue: "配置用量查询" })}
|
title={t("provider.configureUsage")}
|
||||||
>
|
>
|
||||||
<BarChart3 className="h-4 w-4" />
|
<BarChart3 className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export function ProviderCard({
|
|||||||
"mt-1 flex h-8 w-8 items-center justify-center rounded-md border border-transparent text-muted-foreground transition-colors hover:border-muted hover:text-foreground",
|
"mt-1 flex h-8 w-8 items-center justify-center rounded-md border border-transparent text-muted-foreground transition-colors hover:border-muted hover:text-foreground",
|
||||||
dragHandleProps?.isDragging && "border-primary text-primary",
|
dragHandleProps?.isDragging && "border-primary text-primary",
|
||||||
)}
|
)}
|
||||||
aria-label={t("provider.dragHandle", { defaultValue: "拖拽排序" })}
|
aria-label={t("provider.dragHandle")}
|
||||||
{...(dragHandleProps?.attributes ?? {})}
|
{...(dragHandleProps?.attributes ?? {})}
|
||||||
{...(dragHandleProps?.listeners ?? {})}
|
{...(dragHandleProps?.listeners ?? {})}
|
||||||
>
|
>
|
||||||
@@ -118,7 +118,7 @@ export function ProviderCard({
|
|||||||
</h3>
|
</h3>
|
||||||
{isCurrent && (
|
{isCurrent && (
|
||||||
<span className="rounded-full bg-green-500/10 px-2 py-0.5 text-xs font-medium text-green-500 dark:text-green-400">
|
<span className="rounded-full bg-green-500/10 px-2 py-0.5 text-xs font-medium text-green-500 dark:text-green-400">
|
||||||
{t("provider.currentlyUsing", { defaultValue: "当前使用" })}
|
{t("provider.currentlyUsing")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,16 +15,14 @@ export function ProviderEmptyState({ onCreate }: ProviderEmptyStateProps) {
|
|||||||
<Users className="h-7 w-7 text-muted-foreground" />
|
<Users className="h-7 w-7 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-lg font-semibold">
|
<h3 className="text-lg font-semibold">
|
||||||
{t("provider.noProviders", { defaultValue: "暂无供应商" })}
|
{t("provider.noProviders")}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-2 max-w-sm text-sm text-muted-foreground">
|
<p className="mt-2 max-w-sm text-sm text-muted-foreground">
|
||||||
{t("provider.noProvidersDescription", {
|
{t("provider.noProvidersDescription")}
|
||||||
defaultValue: "开始添加一个供应商以快速完成切换。",
|
|
||||||
})}
|
|
||||||
</p>
|
</p>
|
||||||
{onCreate && (
|
{onCreate && (
|
||||||
<Button className="mt-6" onClick={onCreate}>
|
<Button className="mt-6" onClick={onCreate}>
|
||||||
{t("provider.addProvider", { defaultValue: "添加供应商" })}
|
{t("provider.addProvider")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,14 +25,12 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t("provider.name", { defaultValue: "供应商名称" })}
|
{t("provider.name")}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("provider.namePlaceholder", {
|
placeholder={t("provider.namePlaceholder")}
|
||||||
defaultValue: "例如:Claude 官方",
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -46,7 +44,7 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t("provider.websiteUrl", { defaultValue: "官网链接" })}
|
{t("provider.websiteUrl")}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} placeholder="https://" />
|
<Input {...field} placeholder="https://" />
|
||||||
|
|||||||
@@ -137,15 +137,11 @@ export function ClaudeFormFields({
|
|||||||
{shouldShowSpeedTest && (
|
{shouldShowSpeedTest && (
|
||||||
<EndpointField
|
<EndpointField
|
||||||
id="baseUrl"
|
id="baseUrl"
|
||||||
label={t("providerForm.apiEndpoint", { defaultValue: "API 端点" })}
|
label={t("providerForm.apiEndpoint")}
|
||||||
value={baseUrl}
|
value={baseUrl}
|
||||||
onChange={onBaseUrlChange}
|
onChange={onBaseUrlChange}
|
||||||
placeholder={t("providerForm.apiEndpointPlaceholder", {
|
placeholder={t("providerForm.apiEndpointPlaceholder")}
|
||||||
defaultValue: "https://api.example.com",
|
hint={t("providerForm.apiHint")}
|
||||||
})}
|
|
||||||
hint={t("providerForm.apiHint", {
|
|
||||||
defaultValue: "API 端点地址用于连接服务器",
|
|
||||||
})}
|
|
||||||
onManageClick={() => onEndpointModalToggle(true)}
|
onManageClick={() => onEndpointModalToggle(true)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -68,15 +68,11 @@ export function CodexFormFields({
|
|||||||
{shouldShowSpeedTest && (
|
{shouldShowSpeedTest && (
|
||||||
<EndpointField
|
<EndpointField
|
||||||
id="codexBaseUrl"
|
id="codexBaseUrl"
|
||||||
label={t("codexConfig.apiUrlLabel", { defaultValue: "API 端点" })}
|
label={t("codexConfig.apiUrlLabel")}
|
||||||
value={codexBaseUrl}
|
value={codexBaseUrl}
|
||||||
onChange={onBaseUrlChange}
|
onChange={onBaseUrlChange}
|
||||||
placeholder={t("providerForm.codexApiEndpointPlaceholder", {
|
placeholder={t("providerForm.codexApiEndpointPlaceholder")}
|
||||||
defaultValue: "https://api.example.com/v1",
|
hint={t("providerForm.codexApiHint")}
|
||||||
})}
|
|
||||||
hint={t("providerForm.codexApiHint", {
|
|
||||||
defaultValue: "Codex API 端点地址",
|
|
||||||
})}
|
|
||||||
onManageClick={() => onEndpointModalToggle(true)}
|
onManageClick={() => onEndpointModalToggle(true)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function CommonConfigEditor({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label htmlFor="settingsConfig">
|
<Label htmlFor="settingsConfig">
|
||||||
{t("provider.configJson", { defaultValue: "配置 JSON" })}
|
{t("provider.configJson")}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="inline-flex items-center gap-2 text-sm text-muted-foreground cursor-pointer">
|
<label className="inline-flex items-center gap-2 text-sm text-muted-foreground cursor-pointer">
|
||||||
|
|||||||
@@ -554,7 +554,7 @@ export function ProviderForm({
|
|||||||
{showButtons && (
|
{showButtons && (
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<Button variant="outline" type="button" onClick={onCancel}>
|
<Button variant="outline" type="button" onClick={onCancel}>
|
||||||
{t("common.cancel", { defaultValue: "取消" })}
|
{t("common.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit">{submitLabel}</Button>
|
<Button type="submit">{submitLabel}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function ProviderPresetSelector({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t("providerPreset.label", { defaultValue: "预设供应商" })}
|
{t("providerPreset.label")}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{/* 自定义按钮 */}
|
{/* 自定义按钮 */}
|
||||||
@@ -41,7 +41,7 @@ export function ProviderPresetSelector({
|
|||||||
: "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
|
: "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{t("providerPreset.custom", { defaultValue: "自定义配置" })}
|
{t("providerPreset.custom")}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* 预设按钮 */}
|
{/* 预设按钮 */}
|
||||||
|
|||||||
@@ -73,13 +73,15 @@
|
|||||||
"sortUpdateFailed": "Failed to update sort order",
|
"sortUpdateFailed": "Failed to update sort order",
|
||||||
"configureUsage": "Configure usage query",
|
"configureUsage": "Configure usage query",
|
||||||
"name": "Provider Name",
|
"name": "Provider Name",
|
||||||
|
"namePlaceholder": "e.g., Claude Official",
|
||||||
"websiteUrl": "Website URL",
|
"websiteUrl": "Website URL",
|
||||||
"configJson": "Config JSON",
|
"configJson": "Config JSON",
|
||||||
"writeCommonConfig": "Write common config",
|
"writeCommonConfig": "Write common config",
|
||||||
"editCommonConfigButton": "Edit common config",
|
"editCommonConfigButton": "Edit common config",
|
||||||
"configJsonHint": "Please fill in complete Claude Code configuration",
|
"configJsonHint": "Please fill in complete Claude Code configuration",
|
||||||
"editCommonConfigTitle": "Edit common config snippet",
|
"editCommonConfigTitle": "Edit common config snippet",
|
||||||
"editCommonConfigHint": "Common config snippet will be merged into all providers that enable it"
|
"editCommonConfigHint": "Common config snippet will be merged into all providers that enable it",
|
||||||
|
"addProvider": "Add Provider"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"providerSaved": "Provider configuration saved",
|
"providerSaved": "Provider configuration saved",
|
||||||
@@ -297,6 +299,48 @@
|
|||||||
"other": "Other",
|
"other": "Other",
|
||||||
"hint": "You can continue to adjust the fields below after selecting a preset."
|
"hint": "You can continue to adjust the fields below after selecting a preset."
|
||||||
},
|
},
|
||||||
|
"usage": {
|
||||||
|
"queryFailed": "Query failed",
|
||||||
|
"refreshUsage": "Refresh usage",
|
||||||
|
"planUsage": "Plan usage",
|
||||||
|
"invalid": "Expired",
|
||||||
|
"total": "Total:",
|
||||||
|
"used": "Used:",
|
||||||
|
"remaining": "Remaining:"
|
||||||
|
},
|
||||||
|
"usageScript": {
|
||||||
|
"title": "Configure Usage Query",
|
||||||
|
"enableUsageQuery": "Enable usage query",
|
||||||
|
"presetTemplate": "Preset template",
|
||||||
|
"queryScript": "Query script (JavaScript)",
|
||||||
|
"timeoutSeconds": "Timeout (seconds)",
|
||||||
|
"scriptHelp": "Script writing instructions:",
|
||||||
|
"configFormat": "Configuration format:",
|
||||||
|
"extractorFormat": "Extractor return format (all fields optional):",
|
||||||
|
"tips": "💡 Tips:",
|
||||||
|
"testing": "Testing...",
|
||||||
|
"testScript": "Test script",
|
||||||
|
"format": "Format",
|
||||||
|
"saveConfig": "Save config",
|
||||||
|
"scriptEmpty": "Script configuration cannot be empty",
|
||||||
|
"mustHaveReturn": "Script must contain return statement",
|
||||||
|
"testSuccess": "Test successful!",
|
||||||
|
"testFailed": "Test failed",
|
||||||
|
"formatSuccess": "Format successful",
|
||||||
|
"formatFailed": "Format failed",
|
||||||
|
"variablesHint": "Supported variables: {{apiKey}}, {{baseUrl}} | extractor function receives API response JSON object",
|
||||||
|
"fieldIsValid": "• isValid: Boolean, whether plan is valid",
|
||||||
|
"fieldInvalidMessage": "• invalidMessage: String, reason for expiration (shown when isValid is false)",
|
||||||
|
"fieldRemaining": "• remaining: Number, remaining quota",
|
||||||
|
"fieldUnit": "• unit: String, unit (e.g., \"USD\")",
|
||||||
|
"fieldPlanName": "• planName: String, plan name",
|
||||||
|
"fieldTotal": "• total: Number, total quota",
|
||||||
|
"fieldUsed": "• used: Number, used quota",
|
||||||
|
"fieldExtra": "• extra: String, custom display text",
|
||||||
|
"tip1": "• Variables {{apiKey}} and {{baseUrl}} are automatically replaced",
|
||||||
|
"tip2": "• Extractor function runs in sandbox environment, supports ES2020+ syntax",
|
||||||
|
"tip3": "• Entire config must be wrapped in () to form object literal expression"
|
||||||
|
},
|
||||||
"kimiSelector": {
|
"kimiSelector": {
|
||||||
"modelConfig": "Model Configuration",
|
"modelConfig": "Model Configuration",
|
||||||
"mainModel": "Main Model",
|
"mainModel": "Main Model",
|
||||||
|
|||||||
@@ -73,13 +73,15 @@
|
|||||||
"sortUpdateFailed": "排序更新失败",
|
"sortUpdateFailed": "排序更新失败",
|
||||||
"configureUsage": "配置用量查询",
|
"configureUsage": "配置用量查询",
|
||||||
"name": "供应商名称",
|
"name": "供应商名称",
|
||||||
|
"namePlaceholder": "例如:Claude 官方",
|
||||||
"websiteUrl": "官网链接",
|
"websiteUrl": "官网链接",
|
||||||
"configJson": "配置 JSON",
|
"configJson": "配置 JSON",
|
||||||
"writeCommonConfig": "写入通用配置",
|
"writeCommonConfig": "写入通用配置",
|
||||||
"editCommonConfigButton": "编辑通用配置",
|
"editCommonConfigButton": "编辑通用配置",
|
||||||
"configJsonHint": "请填写完整的 Claude Code 配置",
|
"configJsonHint": "请填写完整的 Claude Code 配置",
|
||||||
"editCommonConfigTitle": "编辑通用配置片段",
|
"editCommonConfigTitle": "编辑通用配置片段",
|
||||||
"editCommonConfigHint": "通用配置片段将合并到所有启用它的供应商配置中"
|
"editCommonConfigHint": "通用配置片段将合并到所有启用它的供应商配置中",
|
||||||
|
"addProvider": "添加供应商"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"providerSaved": "供应商配置已保存",
|
"providerSaved": "供应商配置已保存",
|
||||||
@@ -297,6 +299,48 @@
|
|||||||
"other": "其他",
|
"other": "其他",
|
||||||
"hint": "选择预设后可继续调整下方字段。"
|
"hint": "选择预设后可继续调整下方字段。"
|
||||||
},
|
},
|
||||||
|
"usage": {
|
||||||
|
"queryFailed": "查询失败",
|
||||||
|
"refreshUsage": "刷新用量",
|
||||||
|
"planUsage": "套餐用量",
|
||||||
|
"invalid": "已失效",
|
||||||
|
"total": "总:",
|
||||||
|
"used": "使用:",
|
||||||
|
"remaining": "剩余:"
|
||||||
|
},
|
||||||
|
"usageScript": {
|
||||||
|
"title": "配置用量查询",
|
||||||
|
"enableUsageQuery": "启用用量查询",
|
||||||
|
"presetTemplate": "预设模板",
|
||||||
|
"queryScript": "查询脚本(JavaScript)",
|
||||||
|
"timeoutSeconds": "超时时间(秒)",
|
||||||
|
"scriptHelp": "脚本编写说明:",
|
||||||
|
"configFormat": "配置格式:",
|
||||||
|
"extractorFormat": "extractor 返回格式(所有字段均为可选):",
|
||||||
|
"tips": "💡 提示:",
|
||||||
|
"testing": "测试中...",
|
||||||
|
"testScript": "测试脚本",
|
||||||
|
"format": "格式化",
|
||||||
|
"saveConfig": "保存配置",
|
||||||
|
"scriptEmpty": "脚本配置不能为空",
|
||||||
|
"mustHaveReturn": "脚本必须包含 return 语句",
|
||||||
|
"testSuccess": "测试成功!",
|
||||||
|
"testFailed": "测试失败",
|
||||||
|
"formatSuccess": "格式化成功",
|
||||||
|
"formatFailed": "格式化失败",
|
||||||
|
"variablesHint": "支持变量: {{apiKey}}, {{baseUrl}} | extractor 函数接收 API 响应的 JSON 对象",
|
||||||
|
"fieldIsValid": "• isValid: 布尔值,套餐是否有效",
|
||||||
|
"fieldInvalidMessage": "• invalidMessage: 字符串,失效原因说明(当 isValid 为 false 时显示)",
|
||||||
|
"fieldRemaining": "• remaining: 数字,剩余额度",
|
||||||
|
"fieldUnit": "• unit: 字符串,单位(如 \"USD\")",
|
||||||
|
"fieldPlanName": "• planName: 字符串,套餐名称",
|
||||||
|
"fieldTotal": "• total: 数字,总额度",
|
||||||
|
"fieldUsed": "• used: 数字,已用额度",
|
||||||
|
"fieldExtra": "• extra: 字符串,扩展字段,可自由补充需要展示的文本",
|
||||||
|
"tip1": "• 变量 {{apiKey}} 和 {{baseUrl}} 会自动替换",
|
||||||
|
"tip2": "• extractor 函数在沙箱环境中执行,支持 ES2020+ 语法",
|
||||||
|
"tip3": "• 整个配置必须用 () 包裹,形成对象字面量表达式"
|
||||||
|
},
|
||||||
"kimiSelector": {
|
"kimiSelector": {
|
||||||
"modelConfig": "模型配置",
|
"modelConfig": "模型配置",
|
||||||
"mainModel": "主模型",
|
"mainModel": "主模型",
|
||||||
|
|||||||
Reference in New Issue
Block a user