修复缺少的i18n汉化
Some checks are pending
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Linux os:ubuntu-latest rust-target:x86_64-unknown-linux-gnu]) (push) Waiting to run
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Windows os:windows-latest rust-target:x86_64-pc-windows-msvc]) (push) Waiting to run
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:macOS os:macos-latest rust-target:x86_64-apple-darwin]) (push) Waiting to run
Build Test / Build Test Summary (push) Blocked by required conditions
Some checks are pending
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Linux os:ubuntu-latest rust-target:x86_64-unknown-linux-gnu]) (push) Waiting to run
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Windows os:windows-latest rust-target:x86_64-pc-windows-msvc]) (push) Waiting to run
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:macOS os:macos-latest rust-target:x86_64-apple-darwin]) (push) Waiting to run
Build Test / Build Test Summary (push) Blocked by required conditions
This commit is contained in:
13
bun.lock
13
bun.lock
@@ -33,10 +33,13 @@
|
||||
"diff": "^8.0.2",
|
||||
"framer-motion": "^12.0.0-alpha.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"i18next": "^25.3.0",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"lucide-react": "^0.468.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-markdown": "^9.0.3",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"recharts": "^2.14.1",
|
||||
@@ -671,12 +674,18 @@
|
||||
|
||||
"highlightjs-vue": ["highlightjs-vue@1.0.0", "", {}, "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA=="],
|
||||
|
||||
"html-parse-stringify": ["html-parse-stringify@3.0.1", "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", { "dependencies": { "void-elements": "3.1.0" } }, "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg=="],
|
||||
|
||||
"html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
|
||||
|
||||
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||
|
||||
"html2canvas": ["html2canvas@1.4.1", "", { "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" } }, "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA=="],
|
||||
|
||||
"i18next": ["i18next@25.3.0", "https://registry.npmmirror.com/i18next/-/i18next-25.3.0.tgz", { "dependencies": { "@babel/runtime": "^7.27.6" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-ZSQIiNGfqSG6yoLHaCvrkPp16UejHI8PCDxFYaNG/1qxtmqNmqEg4JlWKlxkrUmrin2sEjsy+Mjy1TRozBhOgw=="],
|
||||
|
||||
"i18next-browser-languagedetector": ["i18next-browser-languagedetector@8.2.0", "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g=="],
|
||||
|
||||
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
|
||||
|
||||
"internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
|
||||
@@ -881,6 +890,8 @@
|
||||
|
||||
"react-hook-form": ["react-hook-form@7.58.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-Lml/KZYEEFfPhUVgE0RdCVpnC4yhW+PndRhbiTtdvSlQTL8IfVR+iQkBjLIvmmc6+GGoVeM11z37ktKFPAb0FA=="],
|
||||
|
||||
"react-i18next": ["react-i18next@15.5.3", "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.5.3.tgz", { "dependencies": { "@babel/runtime": "^7.27.6", "html-parse-stringify": "^3.0.1" }, "peerDependencies": { "i18next": ">= 23.2.3", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-ypYmOKOnjqPEJZO4m1BI0kS8kWqkBNsKYyhVUfij0gvjy9xJNoG/VcGkxq5dRlVwzmrmY1BQMAmpbbUBLwC4Kw=="],
|
||||
|
||||
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||
|
||||
"react-markdown": ["react-markdown@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw=="],
|
||||
@@ -1013,6 +1024,8 @@
|
||||
|
||||
"vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||
|
||||
"void-elements": ["void-elements@3.1.0", "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="],
|
||||
|
||||
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
||||
|
||||
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||
|
@@ -39,10 +39,13 @@
|
||||
"diff": "^8.0.2",
|
||||
"framer-motion": "^12.0.0-alpha.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"i18next": "^25.3.0",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"lucide-react": "^0.468.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-markdown": "^9.0.3",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"recharts": "^2.14.1",
|
||||
|
30
src/App.tsx
30
src/App.tsx
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { Plus, Loader2, Bot, FolderCode } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { api, type Project, type Session, type ClaudeMdFile } from "@/lib/api";
|
||||
import { OutputCacheProvider } from "@/lib/outputCache";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -26,6 +27,7 @@ type View = "welcome" | "projects" | "agents" | "editor" | "settings" | "claude-
|
||||
* Main App component - Manages the Claude directory browser UI
|
||||
*/
|
||||
function App() {
|
||||
const { t } = useTranslation();
|
||||
const [view, setView] = useState<View>("welcome");
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
|
||||
@@ -81,7 +83,7 @@ function App() {
|
||||
setProjects(projectList);
|
||||
} catch (err) {
|
||||
console.error("Failed to load projects:", err);
|
||||
setError("加载项目失败。请确保 ~/.claude 目录存在。");
|
||||
setError(t('errors.loadProjectsFailed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -99,7 +101,7 @@ function App() {
|
||||
setSelectedProject(project);
|
||||
} catch (err) {
|
||||
console.error("Failed to load sessions:", err);
|
||||
setError("加载此项目的会话失败。");
|
||||
setError(t('errors.loadSessionsFailed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -143,11 +145,7 @@ function App() {
|
||||
const handleViewChange = (newView: View) => {
|
||||
// Check if we're navigating away from an active Claude session
|
||||
if (view === "claude-code-session" && isClaudeStreaming && activeClaudeSessionId) {
|
||||
const shouldLeave = window.confirm(
|
||||
"Claude 仍在响应中。如果您离开此页面,Claude 将继续在后台运行。\n\n" +
|
||||
"您可以从项目视图返回到此会话。\n\n" +
|
||||
"是否继续?"
|
||||
);
|
||||
const shouldLeave = window.confirm(t('confirmations.leaveActiveSession'));
|
||||
|
||||
if (!shouldLeave) {
|
||||
return;
|
||||
@@ -172,7 +170,7 @@ function App() {
|
||||
>
|
||||
<h1 className="text-4xl font-bold tracking-tight">
|
||||
<span className="rotating-symbol"></span>
|
||||
欢迎使用 Claudia
|
||||
{t('welcome.title')}
|
||||
</h1>
|
||||
</motion.div>
|
||||
|
||||
@@ -190,7 +188,7 @@ function App() {
|
||||
>
|
||||
<div className="h-full flex flex-col items-center justify-center p-8">
|
||||
<Bot className="h-16 w-16 mb-4 text-primary" />
|
||||
<h2 className="text-xl font-semibold">CC 智能助手</h2>
|
||||
<h2 className="text-xl font-semibold">{t('welcome.ccAgents')}</h2>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
@@ -207,7 +205,7 @@ function App() {
|
||||
>
|
||||
<div className="h-full flex flex-col items-center justify-center p-8">
|
||||
<FolderCode className="h-16 w-16 mb-4 text-primary" />
|
||||
<h2 className="text-xl font-semibold">CC 项目</h2>
|
||||
<h2 className="text-xl font-semibold">{t('welcome.ccProjects')}</h2>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
@@ -255,12 +253,12 @@ function App() {
|
||||
onClick={() => handleViewChange("welcome")}
|
||||
className="mb-4"
|
||||
>
|
||||
← 返回主页
|
||||
{t('projects.backToHome')}
|
||||
</Button>
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl font-bold tracking-tight">CC 项目</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight">{t('welcome.ccProjects')}</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
浏览您的 Claude Code 会话
|
||||
{t('welcome.browseProjects')}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
@@ -322,7 +320,7 @@ function App() {
|
||||
className="w-full"
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
新建 Claude Code 会话
|
||||
{t('welcome.newSession')}
|
||||
</Button>
|
||||
</motion.div>
|
||||
|
||||
@@ -338,7 +336,7 @@ function App() {
|
||||
) : (
|
||||
<div className="py-8 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
在 ~/.claude/projects 中未找到项目
|
||||
{t('projects.noProjects')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -413,7 +411,7 @@ function App() {
|
||||
open={showClaudeBinaryDialog}
|
||||
onOpenChange={setShowClaudeBinaryDialog}
|
||||
onSuccess={() => {
|
||||
setToast({ message: "Claude 二进制文件路径保存成功", type: "success" });
|
||||
setToast({ message: t('success.claudeBinaryPathSaved'), type: "success" });
|
||||
// Trigger a refresh of the Claude version check
|
||||
window.location.reload();
|
||||
}}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import {
|
||||
Plus,
|
||||
@@ -65,6 +66,7 @@ export type AgentIconName = keyof typeof AGENT_ICONS;
|
||||
* <CCAgents onBack={() => setView('home')} />
|
||||
*/
|
||||
export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
const { t } = useTranslation();
|
||||
const [agents, setAgents] = useState<Agent[]>([]);
|
||||
const [runs, setRuns] = useState<AgentRunWithMetrics[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -96,8 +98,8 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
setAgents(agentsList);
|
||||
} catch (err) {
|
||||
console.error("Failed to load agents:", err);
|
||||
setError("加载智能体失败");
|
||||
setToast({ message: "加载智能体失败", type: "error" });
|
||||
setError(t('agents.loadFailed'));
|
||||
setToast({ message: t('agents.loadFailed'), type: "error" });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -134,12 +136,12 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
try {
|
||||
setIsDeleting(true);
|
||||
await api.deleteAgent(agentToDelete.id);
|
||||
setToast({ message: "智能体删除成功", type: "success" });
|
||||
setToast({ message: t('agents.deleteSuccess'), type: "success" });
|
||||
await loadAgents();
|
||||
await loadRuns(); // Reload runs as they might be affected
|
||||
} catch (err) {
|
||||
console.error("Failed to delete agent:", err);
|
||||
setToast({ message: "删除智能体失败", type: "error" });
|
||||
setToast({ message: t('agents.deleteFailed'), type: "error" });
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
setShowDeleteDialog(false);
|
||||
@@ -168,13 +170,13 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
const handleAgentCreated = async () => {
|
||||
setView("list");
|
||||
await loadAgents();
|
||||
setToast({ message: "智能体创建成功", type: "success" });
|
||||
setToast({ message: t('agents.createSuccess'), type: "success" });
|
||||
};
|
||||
|
||||
const handleAgentUpdated = async () => {
|
||||
setView("list");
|
||||
await loadAgents();
|
||||
setToast({ message: "智能体更新成功", type: "success" });
|
||||
setToast({ message: t('agents.updateSuccess'), type: "success" });
|
||||
};
|
||||
|
||||
// const handleRunClick = (run: AgentRunWithMetrics) => {
|
||||
@@ -211,10 +213,10 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
filePath
|
||||
});
|
||||
|
||||
setToast({ message: `智能体 "${agent.name}" 导出成功`, type: "success" });
|
||||
setToast({ message: t('agents.exportSuccess', { name: agent.name }), type: "success" });
|
||||
} catch (err) {
|
||||
console.error("Failed to export agent:", err);
|
||||
setToast({ message: "导出智能体失败", type: "error" });
|
||||
setToast({ message: t('agents.exportFailed'), type: "error" });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -237,11 +239,11 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
// Import the agent from the selected file
|
||||
await api.importAgentFromFile(filePath as string);
|
||||
|
||||
setToast({ message: "智能体导入成功", type: "success" });
|
||||
setToast({ message: t('agents.importSuccess'), type: "success" });
|
||||
await loadAgents();
|
||||
} catch (err) {
|
||||
console.error("Failed to import agent:", err);
|
||||
const errorMessage = err instanceof Error ? err.message : "Failed to import agent";
|
||||
const errorMessage = err instanceof Error ? err.message : t('agents.importFailed');
|
||||
setToast({ message: errorMessage, type: "error" });
|
||||
}
|
||||
};
|
||||
@@ -310,9 +312,9 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">CC 智能体</h1>
|
||||
<h1 className="text-2xl font-bold">{t('agents.title')}</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
管理您的 Claude Code 智能体
|
||||
{t('agents.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -325,18 +327,18 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
导入
|
||||
{t('common.import')}
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={handleImportAgent}>
|
||||
<FileJson className="h-4 w-4 mr-2" />
|
||||
从文件
|
||||
{t('agents.fromFile')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setShowGitHubBrowser(true)}>
|
||||
<Globe className="h-4 w-4 mr-2" />
|
||||
从 GitHub
|
||||
{t('agents.fromGitHub')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -346,7 +348,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
创建 CC 智能体
|
||||
{t('agents.createAgent')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -377,7 +379,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Bot className="h-4 w-4" />
|
||||
智能体
|
||||
{t('navigation.agents')}
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
@@ -391,7 +393,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Play className="h-4 w-4" />
|
||||
运行中的会话
|
||||
{t('agents.runningSessions')}
|
||||
</div>
|
||||
</button>
|
||||
</nav>
|
||||
@@ -418,13 +420,13 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
) : agents.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-64 text-center">
|
||||
<Bot className="h-16 w-16 text-muted-foreground mb-4" />
|
||||
<h3 className="text-lg font-medium mb-2">暂无智能体</h3>
|
||||
<h3 className="text-lg font-medium mb-2">{t('agents.noAgents')}</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
创建您的第一个 CC 智能体开始使用
|
||||
{t('agents.createFirstAgent')}
|
||||
</p>
|
||||
<Button onClick={() => setView("create")} size="default">
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
创建 CC 智能体
|
||||
{t('agents.createAgent')}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
@@ -448,7 +450,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
{agent.name}
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
创建时间:{new Date(agent.created_at).toLocaleDateString()}
|
||||
{t('agents.created')}{new Date(agent.created_at).toLocaleDateString()}
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter className="p-4 pt-0 flex justify-center gap-1 flex-wrap">
|
||||
@@ -460,7 +462,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
title="Execute agent"
|
||||
>
|
||||
<Play className="h-3 w-3" />
|
||||
执行
|
||||
{t('common.execute')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -470,7 +472,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
title="Edit agent"
|
||||
>
|
||||
<Edit className="h-3 w-3" />
|
||||
编辑
|
||||
{t('common.edit')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -480,7 +482,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
title="Export agent to .claudia.json"
|
||||
>
|
||||
<Upload className="h-3 w-3" />
|
||||
导出
|
||||
{t('common.export')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -490,7 +492,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
title="Delete agent"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
删除
|
||||
{t('common.delete')}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
@@ -508,10 +510,10 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
上一页
|
||||
{t('common.previous')}
|
||||
</Button>
|
||||
<span className="flex items-center px-3 text-sm">
|
||||
第 {currentPage} 页,共 {totalPages} 页
|
||||
{t('common.page', { current: currentPage, total: totalPages })}
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -519,7 +521,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
下一页
|
||||
{t('common.next')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -532,7 +534,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
<div className="overflow-hidden">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<History className="h-5 w-5 text-muted-foreground" />
|
||||
<h2 className="text-lg font-semibold">最近执行</h2>
|
||||
<h2 className="text-lg font-semibold">{t('agents.recentExecutions')}</h2>
|
||||
</div>
|
||||
{runsLoading ? (
|
||||
<div className="flex items-center justify-center h-32">
|
||||
@@ -582,7 +584,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
onImportSuccess={async () => {
|
||||
setShowGitHubBrowser(false);
|
||||
await loadAgents();
|
||||
setToast({ message: "从 GitHub 导入智能体成功", type: "success" });
|
||||
setToast({ message: t('agents.importFromGitHubSuccess'), type: "success" });
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -592,11 +594,11 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Trash2 className="h-5 w-5 text-destructive" />
|
||||
删除智能体
|
||||
{t('agents.deleteAgent')}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
您确定要删除智能体 "{agentToDelete?.name}" 吗?
|
||||
此操作无法撤销,将永久删除该智能体及其所有相关数据。
|
||||
{t('agents.deleteConfirmation', { name: agentToDelete?.name })}
|
||||
{t('agents.deleteWarning')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter className="flex flex-col-reverse sm:flex-row sm:justify-end gap-2">
|
||||
@@ -606,7 +608,7 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
disabled={isDeleting}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
取消
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
@@ -617,12 +619,12 @@ export const CCAgents: React.FC<CCAgentsProps> = ({ onBack, className }) => {
|
||||
{isDeleting ? (
|
||||
<>
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" />
|
||||
删除中...
|
||||
{t('common.deleting')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
删除智能体
|
||||
{t('agents.deleteAgent')}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
61
src/components/LanguageSwitcher.tsx
Normal file
61
src/components/LanguageSwitcher.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Globe } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
|
||||
interface LanguageSwitcherProps {
|
||||
/**
|
||||
* Optional className for styling
|
||||
*/
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* LanguageSwitcher component for switching between languages
|
||||
*/
|
||||
export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({ className }) => {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const languages = [
|
||||
{ code: 'en-US', name: 'English', nativeName: 'English' },
|
||||
{ code: 'zh-CN', name: 'Chinese', nativeName: '简体中文' },
|
||||
];
|
||||
|
||||
const currentLanguage = languages.find(lang => lang.code === i18n.language) || languages[0];
|
||||
|
||||
const handleLanguageChange = (languageCode: string) => {
|
||||
i18n.changeLanguage(languageCode);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={className}
|
||||
>
|
||||
<Globe className="h-4 w-4 mr-2" />
|
||||
{currentLanguage.nativeName}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{languages.map((language) => (
|
||||
<DropdownMenuItem
|
||||
key={language.code}
|
||||
onClick={() => handleLanguageChange(language.code)}
|
||||
className={language.code === i18n.language ? 'bg-accent' : ''}
|
||||
>
|
||||
{language.nativeName}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
@@ -1,4 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Download, Upload, FileText, Loader2, Info, Network, Settings2 } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
@@ -24,6 +25,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
onImportCompleted,
|
||||
onError,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [importingDesktop, setImportingDesktop] = useState(false);
|
||||
const [importingJson, setImportingJson] = useState(false);
|
||||
const [importScope, setImportScope] = useState("local");
|
||||
@@ -43,7 +45,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
const failedServers = result.servers.filter(s => !s.success);
|
||||
|
||||
if (successfulServers.length > 0) {
|
||||
const successMessage = `Successfully imported: ${successfulServers.map(s => s.name).join(", ")}`;
|
||||
const successMessage = t("mcp.successfullyImported", { servers: successfulServers.map(s => s.name).join(", ") });
|
||||
onImportCompleted(result.imported_count, result.failed_count);
|
||||
// Show success details
|
||||
if (failedServers.length === 0) {
|
||||
@@ -53,16 +55,16 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
|
||||
if (failedServers.length > 0) {
|
||||
const failureDetails = failedServers
|
||||
.map(s => `${s.name}: ${s.error || "Unknown error"}`)
|
||||
.map(s => `${s.name}: ${s.error || t("common.unknown")}`)
|
||||
.join("\n");
|
||||
onError(`Failed to import some servers:\n${failureDetails}`);
|
||||
onError(`${t("mcp.failedToImportSome")}\n${failureDetails}`);
|
||||
}
|
||||
} else {
|
||||
onImportCompleted(result.imported_count, result.failed_count);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Failed to import from Claude Desktop:", error);
|
||||
onError(error.toString() || "Failed to import from Claude Desktop");
|
||||
onError(error.toString() || t("mcp.failedToImportFromDesktop"));
|
||||
} finally {
|
||||
setImportingDesktop(false);
|
||||
}
|
||||
@@ -84,7 +86,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
try {
|
||||
jsonData = JSON.parse(content);
|
||||
} catch (e) {
|
||||
onError("Invalid JSON file. Please check the format.");
|
||||
onError(t("mcp.invalidJsonFile"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +119,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
onImportCompleted(imported, failed);
|
||||
} else if (jsonData.type && jsonData.command) {
|
||||
// Single server format
|
||||
const name = prompt("Enter a name for this server:");
|
||||
const name = prompt(t("mcp.enterServerName"));
|
||||
if (!name) return;
|
||||
|
||||
const result = await api.mcpAddJson(name, content, importScope);
|
||||
@@ -127,11 +129,11 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
onError(result.message);
|
||||
}
|
||||
} else {
|
||||
onError("Unrecognized JSON format. Expected MCP server configuration.");
|
||||
onError(t("mcp.unrecognizedJsonFormat"));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to import JSON:", error);
|
||||
onError("Failed to import JSON file");
|
||||
onError(t("mcp.failedToImportJson"));
|
||||
} finally {
|
||||
setImportingJson(false);
|
||||
// Reset the input
|
||||
@@ -144,7 +146,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
*/
|
||||
const handleExport = () => {
|
||||
// TODO: Implement export functionality
|
||||
onError("Export functionality coming soon!");
|
||||
onError(t("mcp.exportFunctionalityComingSoon"));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -153,19 +155,19 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
const handleStartMCPServer = async () => {
|
||||
try {
|
||||
await api.mcpServe();
|
||||
onError("Claude Code MCP server started. You can now connect to it from other applications.");
|
||||
onError(t("mcp.mcpServerStarted"));
|
||||
} catch (error) {
|
||||
console.error("Failed to start MCP server:", error);
|
||||
onError("Failed to start Claude Code as MCP server");
|
||||
onError(t("mcp.failedToStartMcpServer"));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 space-y-6">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold">Import & Export</h3>
|
||||
<h3 className="text-base font-semibold">{t("mcp.importExportTitle")}</h3>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Import MCP servers from other sources or export your configuration
|
||||
{t("mcp.importExportDescription")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -175,19 +177,19 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Settings2 className="h-4 w-4 text-slate-500" />
|
||||
<Label className="text-sm font-medium">Import Scope</Label>
|
||||
<Label className="text-sm font-medium">{t("mcp.importScope")}</Label>
|
||||
</div>
|
||||
<SelectComponent
|
||||
value={importScope}
|
||||
onValueChange={(value: string) => setImportScope(value)}
|
||||
options={[
|
||||
{ value: "local", label: "Local (this project only)" },
|
||||
{ value: "project", label: "Project (shared via .mcp.json)" },
|
||||
{ value: "user", label: "User (all projects)" },
|
||||
{ value: "local", label: t("mcp.localThisProjectOnly") },
|
||||
{ value: "project", label: t("mcp.projectSharedViaMcp") },
|
||||
{ value: "user", label: t("mcp.userAllProjects") },
|
||||
]}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Choose where to save imported servers from JSON files
|
||||
{t("mcp.importScopeDescription")}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -200,9 +202,9 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
<Download className="h-5 w-5 text-blue-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium">Import from Claude Desktop</h4>
|
||||
<h4 className="text-sm font-medium">{t("mcp.importFromClaudeDesktop")}</h4>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Automatically imports all MCP servers from Claude Desktop. Installs to user scope (available across all projects).
|
||||
{t("mcp.importFromClaudeDesktopDesc")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -214,12 +216,12 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
{importingDesktop ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Importing...
|
||||
{t("mcp.importing")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Download className="h-4 w-4" />
|
||||
Import from Claude Desktop
|
||||
{t("mcp.importFromClaudeDesktop")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
@@ -234,9 +236,9 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
<FileText className="h-5 w-5 text-purple-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium">Import from JSON</h4>
|
||||
<h4 className="text-sm font-medium">{t("mcp.importFromJson")}</h4>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Import server configuration from a JSON file
|
||||
{t("mcp.importFromJsonDesc")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -263,7 +265,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
) : (
|
||||
<>
|
||||
<FileText className="h-4 w-4" />
|
||||
Choose JSON File
|
||||
{t("mcp.chooseJsonFile")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
@@ -279,9 +281,9 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
<Upload className="h-5 w-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium">Export Configuration</h4>
|
||||
<h4 className="text-sm font-medium">{t("mcp.exportConfiguration")}</h4>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Export your MCP server configuration
|
||||
{t("mcp.exportConfigurationDesc")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -292,7 +294,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
className="w-full gap-2"
|
||||
>
|
||||
<Upload className="h-4 w-4" />
|
||||
Export (Coming Soon)
|
||||
{t("mcp.exportComingSoon")}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -305,9 +307,9 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
<Network className="h-5 w-5 text-green-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium">Use Claude Code as MCP Server</h4>
|
||||
<h4 className="text-sm font-medium">{t("mcp.useClaudeCodeAsMcp")}</h4>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Start Claude Code as an MCP server that other applications can connect to
|
||||
{t("mcp.useClaudeCodeAsMcpDesc")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -317,7 +319,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
className="w-full gap-2 border-green-500/20 hover:bg-green-500/10 hover:text-green-600 hover:border-green-500/50"
|
||||
>
|
||||
<Network className="h-4 w-4" />
|
||||
Start MCP Server
|
||||
{t("mcp.startMcpServer")}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -328,11 +330,11 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 text-sm font-medium">
|
||||
<Info className="h-4 w-4 text-primary" />
|
||||
<span>JSON Format Examples</span>
|
||||
<span>{t("mcp.jsonFormatExamples")}</span>
|
||||
</div>
|
||||
<div className="space-y-3 text-xs">
|
||||
<div>
|
||||
<p className="font-medium text-muted-foreground mb-1">Single server:</p>
|
||||
<p className="font-medium text-muted-foreground mb-1">{t("mcp.singleServer")}</p>
|
||||
<pre className="bg-background p-3 rounded-lg overflow-x-auto">
|
||||
{`{
|
||||
"type": "stdio",
|
||||
@@ -343,7 +345,7 @@ export const MCPImportExport: React.FC<MCPImportExportProps> = ({
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-muted-foreground mb-1">Multiple servers (.mcp.json format):</p>
|
||||
<p className="font-medium text-muted-foreground mb-1">{t("mcp.multipleServers")}</p>
|
||||
<pre className="bg-background p-3 rounded-lg overflow-x-auto">
|
||||
{`{
|
||||
"mcpServers": {
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { motion } from "framer-motion";
|
||||
import { Circle, FileText, Settings, ExternalLink, BarChart3, Network, Info } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Popover } from "@/components/ui/popover";
|
||||
import { api, type ClaudeVersionStatus } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { LanguageSwitcher } from "./LanguageSwitcher";
|
||||
|
||||
interface TopbarProps {
|
||||
/**
|
||||
@@ -52,6 +54,7 @@ export const Topbar: React.FC<TopbarProps> = ({
|
||||
onInfoClick,
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [versionStatus, setVersionStatus] = useState<ClaudeVersionStatus | null>(null);
|
||||
const [checking, setChecking] = useState(true);
|
||||
|
||||
@@ -87,7 +90,7 @@ export const Topbar: React.FC<TopbarProps> = ({
|
||||
return (
|
||||
<div className="flex items-center space-x-2 text-xs">
|
||||
<Circle className="h-3 w-3 animate-pulse text-muted-foreground" />
|
||||
<span className="text-muted-foreground">检查中...</span>
|
||||
<span className="text-muted-foreground">{t('topbar.checking')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -125,7 +128,7 @@ export const Topbar: React.FC<TopbarProps> = ({
|
||||
trigger={statusContent}
|
||||
content={
|
||||
<div className="space-y-3 max-w-xs">
|
||||
<p className="text-sm font-medium">未找到 Claude Code</p>
|
||||
<p className="text-sm font-medium">{t('topbar.claudeNotFound')}</p>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<pre className="text-xs font-mono whitespace-pre-wrap">
|
||||
{versionStatus.output}
|
||||
@@ -137,7 +140,7 @@ export const Topbar: React.FC<TopbarProps> = ({
|
||||
className="w-full"
|
||||
onClick={onSettingsClick}
|
||||
>
|
||||
选择 Claude 安装
|
||||
{t('topbar.selectInstallation')}
|
||||
</Button>
|
||||
<a
|
||||
href="https://www.anthropic.com/claude-code"
|
||||
@@ -145,7 +148,7 @@ export const Topbar: React.FC<TopbarProps> = ({
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center space-x-1 text-xs text-primary hover:underline"
|
||||
>
|
||||
<span>安装 Claude Code</span>
|
||||
<span>{t('topbar.installClaude')}</span>
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</a>
|
||||
</div>
|
||||
@@ -180,7 +183,7 @@ export const Topbar: React.FC<TopbarProps> = ({
|
||||
className="text-xs"
|
||||
>
|
||||
<BarChart3 className="mr-2 h-3 w-3" />
|
||||
使用情况仪表板
|
||||
{t('navigation.usageDashboard')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@@ -210,15 +213,17 @@ export const Topbar: React.FC<TopbarProps> = ({
|
||||
className="text-xs"
|
||||
>
|
||||
<Settings className="mr-2 h-3 w-3" />
|
||||
设置
|
||||
{t('navigation.settings')}
|
||||
</Button>
|
||||
|
||||
<LanguageSwitcher />
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onInfoClick}
|
||||
className="h-8 w-8"
|
||||
title="关于"
|
||||
title={t('navigation.about')}
|
||||
>
|
||||
<Info className="h-4 w-4" />
|
||||
</Button>
|
||||
|
41
src/i18n/index.ts
Normal file
41
src/i18n/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
// 导入语言资源
|
||||
import enUS from './locales/en-US.json';
|
||||
import zhCN from './locales/zh-CN.json';
|
||||
|
||||
const resources = {
|
||||
'en-US': {
|
||||
translation: enUS,
|
||||
},
|
||||
'zh-CN': {
|
||||
translation: zhCN,
|
||||
},
|
||||
};
|
||||
|
||||
i18n
|
||||
// 检测用户语言
|
||||
.use(LanguageDetector)
|
||||
// 将i18n实例传递给react-i18next
|
||||
.use(initReactI18next)
|
||||
// 初始化i18next
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'en-US', // 默认语言
|
||||
lng: 'zh-CN', // 初始语言设置为中文
|
||||
debug: false,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // React已经自动转义了
|
||||
},
|
||||
|
||||
detection: {
|
||||
order: ['localStorage', 'navigator', 'htmlTag'],
|
||||
caches: ['localStorage'],
|
||||
lookupLocalStorage: 'claudia-language',
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
362
src/i18n/locales/en-US.json
Normal file
362
src/i18n/locales/en-US.json
Normal file
@@ -0,0 +1,362 @@
|
||||
{
|
||||
"common": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"create": "Create",
|
||||
"import": "Import",
|
||||
"export": "Export",
|
||||
"execute": "Execute",
|
||||
"stop": "Stop",
|
||||
"close": "Close",
|
||||
"copy": "Copy",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading...",
|
||||
"saving": "Saving...",
|
||||
"deleting": "Deleting...",
|
||||
"next": "Next",
|
||||
"previous": "Previous",
|
||||
"back": "Back",
|
||||
"settings": "Settings",
|
||||
"page": "Page {{current}} of {{total}}",
|
||||
"unknown": "Unknown",
|
||||
"success": "Success",
|
||||
"error": "Error",
|
||||
"warning": "Warning"
|
||||
},
|
||||
"navigation": {
|
||||
"home": "Home",
|
||||
"projects": "Projects",
|
||||
"agents": "Agents",
|
||||
"usageDashboard": "Usage Dashboard",
|
||||
"claudeMd": "CLAUDE.md",
|
||||
"mcp": "MCP",
|
||||
"about": "About"
|
||||
},
|
||||
"welcome": {
|
||||
"title": "Welcome to Claudia",
|
||||
"ccAgents": "CC Agents",
|
||||
"ccProjects": "CC Projects",
|
||||
"browseProjects": "Browse your Claude Code sessions",
|
||||
"newSession": "New Claude Code session"
|
||||
},
|
||||
"agents": {
|
||||
"title": "CC Agents",
|
||||
"description": "Manage your Claude Code agents",
|
||||
"createAgent": "Create CC Agent",
|
||||
"noAgents": "No agents yet",
|
||||
"createFirstAgent": "Create your first CC Agent to get started",
|
||||
"recentExecutions": "Recent Executions",
|
||||
"runningSessions": "Running Sessions",
|
||||
"agentName": "Agent Name",
|
||||
"agentIcon": "Agent Icon",
|
||||
"model": "Model",
|
||||
"defaultTask": "Default Task (Optional)",
|
||||
"systemPrompt": "System Prompt",
|
||||
"basicInformation": "Basic Information",
|
||||
"nameRequired": "Agent name is required",
|
||||
"systemPromptRequired": "System prompt is required",
|
||||
"created": "Created:",
|
||||
"deleteConfirm": "Are you sure you want to delete the agent \"{{name}}\"? This action cannot be undone and will permanently remove the agent and all its associated data.",
|
||||
"deleteSuccess": "Agent deleted successfully",
|
||||
"deleteFailed": "Failed to delete agent",
|
||||
"createSuccess": "Agent created successfully",
|
||||
"updateSuccess": "Agent updated successfully",
|
||||
"importSuccess": "Agent imported successfully",
|
||||
"exportSuccess": "Agent \"{{name}}\" exported successfully",
|
||||
"fromFile": "From File",
|
||||
"fromGitHub": "From GitHub",
|
||||
"importFromGitHubSuccess": "Agent imported successfully from GitHub",
|
||||
"loadFailed": "Failed to load agents",
|
||||
"exportFailed": "Failed to export agent",
|
||||
"placeholderTask": "This will be used as the default task placeholder when executing the agent",
|
||||
"definePrompt": "Define the behavior and capabilities of your CC Agent",
|
||||
"unsavedChanges": "You have unsaved changes. Are you sure you want to leave?"
|
||||
},
|
||||
"agentExecution": {
|
||||
"title": "Execute CC Agent",
|
||||
"readyToExecute": "Ready to Execute",
|
||||
"running": "Running",
|
||||
"initializing": "Initializing agent...",
|
||||
"projectPath": "Project Path",
|
||||
"task": "Task",
|
||||
"selectProjectPath": "Select or enter project path",
|
||||
"enterTask": "Enter the task for the agent",
|
||||
"selectProject": "Select a project path and enter a task to run the agent",
|
||||
"fullscreen": "Fullscreen",
|
||||
"copyOutput": "Copy Output",
|
||||
"copyAsJsonl": "Copy as JSONL",
|
||||
"copyAsMarkdown": "Copy as Markdown",
|
||||
"executionFailed": "Agent execution failed",
|
||||
"executionCancelled": "Agent execution was cancelled",
|
||||
"executionStopped": "Execution stopped by user",
|
||||
"stopFailed": "Failed to stop execution",
|
||||
"selectDirectoryFailed": "Failed to select directory",
|
||||
"navigationWarning": "An agent is currently running. If you navigate away, the agent will continue running in the background. You can view running sessions in the 'Running Sessions' tab within CC Agents.\n\nDo you want to continue?"
|
||||
},
|
||||
"models": {
|
||||
"sonnet": "Claude 4 Sonnet",
|
||||
"opus": "Claude 4 Opus",
|
||||
"sonnetDescription": "Faster, efficient for most tasks",
|
||||
"opusDescription": "More capable, better for complex tasks"
|
||||
},
|
||||
"projects": {
|
||||
"sessions": "{{count}} session",
|
||||
"sessions_plural": "{{count}} sessions",
|
||||
"backToHome": "← Back to Home",
|
||||
"noProjects": "No projects found in ~/.claude/projects",
|
||||
"loadFailed": "Failed to load projects. Please ensure ~/.claude directory exists.",
|
||||
"sessionLoadFailed": "Failed to load sessions for this project."
|
||||
},
|
||||
"sessions": {
|
||||
"firstMessage": "First message:",
|
||||
"hasTodo": "Has todo",
|
||||
"loadingHistory": "Loading session history...",
|
||||
"initializingClaude": "Initializing Claude Code...",
|
||||
"queuedPrompts": "Queued Prompts ({{count}})",
|
||||
"selectProjectFirst": "Please select a project directory first",
|
||||
"sessionCancelled": "Session cancelled by user",
|
||||
"loadHistoryFailed": "Failed to load session history",
|
||||
"forkSession": "Fork Session",
|
||||
"createBranch": "Create a new session branch from the selected checkpoint.",
|
||||
"newSessionName": "New Session Name",
|
||||
"createFork": "Create Fork",
|
||||
"projectDirectory": "Project Directory",
|
||||
"selectProjectDirectory": "Select Project Directory",
|
||||
"claudeCodeSession": "Claude Code Session",
|
||||
"resumingSession": "Resuming session {{sessionId}}...",
|
||||
"interactiveSession": "Interactive session",
|
||||
"timeline": "Timeline",
|
||||
"preview": "Preview",
|
||||
"closePreview": "Close Preview",
|
||||
"outputCopiedAsJsonl": "Output copied as JSONL",
|
||||
"outputRefreshed": "Output refreshed",
|
||||
"agentExecutionCompleted": "Agent execution completed"
|
||||
},
|
||||
"usageDashboard": {
|
||||
"title": "Usage Dashboard",
|
||||
"description": "Track your Claude Code usage and costs",
|
||||
"allTime": "All Time",
|
||||
"last7Days": "Last 7 Days",
|
||||
"last30Days": "Last 30 Days",
|
||||
"totalCost": "Total Cost",
|
||||
"totalSessions": "Total Sessions",
|
||||
"totalTokens": "Total Tokens",
|
||||
"avgCostPerSession": "Avg Cost/Session",
|
||||
"overview": "Overview",
|
||||
"byModel": "By Model",
|
||||
"byProject": "By Project",
|
||||
"bySession": "By Session",
|
||||
"timeline": "Timeline",
|
||||
"tokenBreakdown": "Token Breakdown",
|
||||
"inputTokens": "Input Tokens",
|
||||
"outputTokens": "Output Tokens",
|
||||
"cacheWrite": "Cache Write",
|
||||
"cacheRead": "Cache Read",
|
||||
"mostUsedModels": "Most Used Models",
|
||||
"topProjects": "Top Projects",
|
||||
"dailyUsage": "Daily Usage",
|
||||
"dailyUsageOverTime": "Daily Usage Over Time",
|
||||
"loadingStats": "Loading usage statistics...",
|
||||
"loadFailed": "Failed to load usage statistics. Please try again.",
|
||||
"tryAgain": "Try Again",
|
||||
"noDataAvailable": "No usage data available for the selected period",
|
||||
"sessions": "sessions",
|
||||
"tokens": "tokens",
|
||||
"models": "models",
|
||||
"input": "Input:",
|
||||
"output": "Output:",
|
||||
"cacheW": "Cache W:",
|
||||
"cacheR": "Cache R:",
|
||||
"cost": "Cost:",
|
||||
"perSession": "/session"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"description": "Configure Claude Code preferences",
|
||||
"general": "General",
|
||||
"permissions": "Permissions",
|
||||
"environment": "Environment",
|
||||
"advanced": "Advanced",
|
||||
"saveSettings": "Save Settings",
|
||||
"generalSettings": "General Settings",
|
||||
"includeCoAuthor": "Include \"Co-authored by Claude\"",
|
||||
"includeCoAuthorDesc": "Add Claude attribution to git commits and pull requests",
|
||||
"verboseOutput": "Verbose Output",
|
||||
"verboseOutputDesc": "Show full bash and command outputs",
|
||||
"chatRetention": "Chat Transcript Retention (days)",
|
||||
"chatRetentionDesc": "How long to retain chat transcripts locally (default: 30 days)",
|
||||
"claudeInstallation": "Claude Code Installation",
|
||||
"claudeInstallationDesc": "Select which Claude Code installation to use",
|
||||
"permissionRules": "Permission Rules",
|
||||
"permissionRulesDesc": "Control which tools Claude Code can use without manual approval",
|
||||
"allowRules": "Allow Rules",
|
||||
"denyRules": "Deny Rules",
|
||||
"addRule": "Add Rule",
|
||||
"noAllowRules": "No allow rules configured. Claude will ask for approval for all tools.",
|
||||
"noDenyRules": "No deny rules configured.",
|
||||
"environmentVariables": "Environment Variables",
|
||||
"environmentVariablesDesc": "Environment variables applied to every Claude Code session",
|
||||
"addVariable": "Add Variable",
|
||||
"noEnvironmentVars": "No environment variables configured.",
|
||||
"advancedSettings": "Advanced Settings",
|
||||
"advancedSettingsDesc": "Additional configuration options for advanced users",
|
||||
"apiKeyHelper": "API Key Helper Script",
|
||||
"apiKeyHelperDesc": "Custom script to generate auth values for API requests",
|
||||
"rawSettings": "Raw Settings (JSON)",
|
||||
"rawSettingsDesc": "This shows the raw JSON that will be saved to ~/.claude/settings.json",
|
||||
"saveSuccess": "Settings saved successfully!",
|
||||
"saveFailed": "Failed to save settings",
|
||||
"loadFailed": "Failed to load settings. Please ensure ~/.claude directory exists.",
|
||||
"binaryPathChanged": "⚠️ Claude binary path has been changed. Remember to save your settings."
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCP Servers",
|
||||
"description": "Manage Model Context Protocol servers",
|
||||
"servers": "Servers",
|
||||
"addServer": "Add Server",
|
||||
"importExport": "Import/Export",
|
||||
"configuredServers": "Configured Servers",
|
||||
"running": "Running",
|
||||
"noServers": "No MCP servers configured",
|
||||
"getStartedText": "Add a server to get started with Model Context Protocol",
|
||||
"serverName": "Server Name",
|
||||
"command": "Command",
|
||||
"arguments": "Arguments (optional)",
|
||||
"scope": "Scope",
|
||||
"environmentVariables": "Environment Variables",
|
||||
"localScope": "Local (this project only)",
|
||||
"projectScope": "Project (shared via .mcp.json)",
|
||||
"userScope": "User (all projects)",
|
||||
"addStdioServer": "Add Stdio Server",
|
||||
"addSseServer": "Add SSE Server",
|
||||
"addingServer": "Adding Server...",
|
||||
"serverNameRequired": "Server name is required",
|
||||
"commandRequired": "Command is required",
|
||||
"addServerFailed": "Failed to add server",
|
||||
"exampleCommands": "Example Commands",
|
||||
"showFull": "Show full",
|
||||
"hide": "Hide",
|
||||
"environmentVars": "Environment variables: {{count}}",
|
||||
"loadFailed": "Failed to load MCP servers",
|
||||
"serverAddSuccess": "MCP server added successfully!",
|
||||
"serverRemoveSuccess": "Server removed successfully!",
|
||||
"importSuccess": "Successfully imported {{count}} server(s)",
|
||||
"importFailed": "Failed to import servers",
|
||||
"importExportTitle": "Import & Export",
|
||||
"importExportDescription": "Import MCP servers from other sources or export your configuration",
|
||||
"importScope": "Import Scope",
|
||||
"importScopeDescription": "Choose where to save imported servers from JSON files",
|
||||
"localThisProjectOnly": "Local (this project only)",
|
||||
"projectSharedViaMcp": "Project (shared via .mcp.json)",
|
||||
"userAllProjects": "User (all projects)",
|
||||
"importFromClaudeDesktop": "Import from Claude Desktop",
|
||||
"importFromClaudeDesktopDesc": "Automatically imports all MCP servers from Claude Desktop. Installs to user scope (available across all projects).",
|
||||
"importing": "Importing...",
|
||||
"importFromJson": "Import from JSON",
|
||||
"importFromJsonDesc": "Import server configuration from a JSON file",
|
||||
"chooseJsonFile": "Choose JSON File",
|
||||
"exportConfiguration": "Export Configuration",
|
||||
"exportConfigurationDesc": "Export your MCP server configuration",
|
||||
"exportComingSoon": "Export (Coming Soon)",
|
||||
"useClaudeCodeAsMcp": "Use Claude Code as MCP Server",
|
||||
"useClaudeCodeAsMcpDesc": "Start Claude Code as an MCP server that other applications can connect to",
|
||||
"startMcpServer": "Start MCP Server",
|
||||
"jsonFormatExamples": "JSON Format Examples",
|
||||
"singleServer": "Single server:",
|
||||
"multipleServers": "Multiple servers (.mcp.json format):",
|
||||
"successfullyImported": "Successfully imported: {{servers}}",
|
||||
"failedToImportSome": "Failed to import some servers:",
|
||||
"failedToImportFromDesktop": "Failed to import from Claude Desktop",
|
||||
"invalidJsonFile": "Invalid JSON file. Please check the format.",
|
||||
"unrecognizedJsonFormat": "Unrecognized JSON format. Expected MCP server configuration.",
|
||||
"failedToImportJson": "Failed to import JSON file",
|
||||
"exportFunctionalityComingSoon": "Export functionality coming soon!",
|
||||
"mcpServerStarted": "Claude Code MCP server started. You can now connect to it from other applications.",
|
||||
"failedToStartMcpServer": "Failed to start Claude Code as MCP server",
|
||||
"enterServerName": "Enter a name for this server:"
|
||||
},
|
||||
"topbar": {
|
||||
"checking": "Checking...",
|
||||
"claudeNotFound": "Claude Code not found",
|
||||
"selectInstallation": "Select Claude Installation",
|
||||
"installClaude": "Install Claude Code"
|
||||
},
|
||||
"runningSessions": {
|
||||
"title": "Running Agent Sessions",
|
||||
"noSessions": "No agent sessions are currently running",
|
||||
"loadFailed": "Failed to load running sessions",
|
||||
"refreshSuccess": "Running sessions list has been updated",
|
||||
"refreshFailed": "Failed to refresh sessions",
|
||||
"sessionStopped": "{{session}} has been stopped",
|
||||
"sessionMayFinished": "Session may have already finished",
|
||||
"terminateFailed": "Failed to terminate session",
|
||||
"loadingSessions": "Loading running sessions...",
|
||||
"task": "Task",
|
||||
"duration": "Duration",
|
||||
"projectPath": "Project Path",
|
||||
"sessionId": "Session ID",
|
||||
"viewOutput": "View Output",
|
||||
"pending": "Pending"
|
||||
},
|
||||
"output": {
|
||||
"title": "Output",
|
||||
"live": "Live",
|
||||
"messages": "{{count}} messages",
|
||||
"loadingOutput": "Loading output...",
|
||||
"waitingOutput": "Waiting for output...",
|
||||
"agentRunningNoOutput": "Agent is running but no output received yet",
|
||||
"noOutputAvailable": "No output available",
|
||||
"enterFullscreen": "Enter fullscreen",
|
||||
"exitFullscreen": "Exit fullscreen",
|
||||
"refreshOutput": "Refresh output"
|
||||
},
|
||||
"claudeBinary": {
|
||||
"title": "Select Claude Code Installation",
|
||||
"searching": "Searching for Claude installations...",
|
||||
"multipleFound": "Multiple Claude Code installations were found on your system. Please select which one you'd like to use.",
|
||||
"notFound": "Claude Code was not found in any of the common installation locations. Please install Claude Code to continue.",
|
||||
"installationGuide": "Installation Guide",
|
||||
"validating": "Validating...",
|
||||
"saveSelection": "Save Selection",
|
||||
"noInstallations": "No Installations Found",
|
||||
"selectInstallation": "Please select a Claude installation",
|
||||
"saveFailed": "Failed to save Claude binary path",
|
||||
"searchedLocations": "Searched locations:",
|
||||
"tip": "Tip:",
|
||||
"saveSuccess": "Claude binary path saved successfully"
|
||||
},
|
||||
"thinking": {
|
||||
"auto": "Auto",
|
||||
"autoDesc": "Let Claude decide",
|
||||
"think": "Think",
|
||||
"thinkDesc": "Basic reasoning",
|
||||
"thinkHard": "Think Hard",
|
||||
"thinkHardDesc": "Deeper analysis",
|
||||
"thinkHarder": "Think Harder",
|
||||
"thinkHarderDesc": "Extensive reasoning",
|
||||
"ultrathink": "Ultrathink",
|
||||
"ultrathinkDesc": "Maximum computation"
|
||||
},
|
||||
"prompts": {
|
||||
"typePrompt": "Type your prompt here...",
|
||||
"askClaude": "Ask Claude anything...",
|
||||
"dropImages": "Drop images here...",
|
||||
"composePrompt": "Compose your prompt",
|
||||
"pressEnterToSend": "Press Enter to send, Shift+Enter for new line, @ to mention files, drag & drop images",
|
||||
"send": "Send",
|
||||
"model": "Model:",
|
||||
"thinking": "Thinking:"
|
||||
},
|
||||
"errors": {
|
||||
"loadProjectsFailed": "Failed to load projects. Please ensure ~/.claude directory exists.",
|
||||
"loadSessionsFailed": "Failed to load sessions for this project."
|
||||
},
|
||||
"confirmations": {
|
||||
"leaveActiveSession": "Claude is still responding. If you navigate away, Claude will continue running in the background.\n\nYou can return to this session from the projects view.\n\nDo you want to continue?"
|
||||
},
|
||||
"success": {
|
||||
"claudeBinaryPathSaved": "Claude binary path saved successfully"
|
||||
}
|
||||
}
|
362
src/i18n/locales/zh-CN.json
Normal file
362
src/i18n/locales/zh-CN.json
Normal file
@@ -0,0 +1,362 @@
|
||||
{
|
||||
"common": {
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"create": "创建",
|
||||
"import": "导入",
|
||||
"export": "导出",
|
||||
"execute": "执行",
|
||||
"stop": "停止",
|
||||
"close": "关闭",
|
||||
"copy": "复制",
|
||||
"refresh": "刷新",
|
||||
"loading": "加载中...",
|
||||
"saving": "保存中...",
|
||||
"deleting": "删除中...",
|
||||
"next": "下一页",
|
||||
"previous": "上一页",
|
||||
"back": "返回",
|
||||
"settings": "设置",
|
||||
"page": "第 {{current}} 页,共 {{total}} 页",
|
||||
"unknown": "未知",
|
||||
"success": "成功",
|
||||
"error": "错误",
|
||||
"warning": "警告"
|
||||
},
|
||||
"navigation": {
|
||||
"home": "主页",
|
||||
"projects": "项目",
|
||||
"agents": "智能体",
|
||||
"usageDashboard": "使用情况仪表板",
|
||||
"claudeMd": "CLAUDE.md",
|
||||
"mcp": "MCP",
|
||||
"about": "关于"
|
||||
},
|
||||
"welcome": {
|
||||
"title": "欢迎使用 Claudia",
|
||||
"ccAgents": "CC 智能体",
|
||||
"ccProjects": "CC 项目",
|
||||
"browseProjects": "浏览您的 Claude Code 会话",
|
||||
"newSession": "新建 Claude Code 会话"
|
||||
},
|
||||
"agents": {
|
||||
"title": "CC 智能体",
|
||||
"description": "管理您的 Claude Code 智能体",
|
||||
"createAgent": "创建 CC 智能体",
|
||||
"noAgents": "暂无智能体",
|
||||
"createFirstAgent": "创建您的第一个 CC 智能体开始使用",
|
||||
"recentExecutions": "最近执行",
|
||||
"runningSessions": "运行中的会话",
|
||||
"agentName": "智能体名称",
|
||||
"agentIcon": "智能体图标",
|
||||
"model": "模型",
|
||||
"defaultTask": "默认任务(可选)",
|
||||
"systemPrompt": "系统提示词",
|
||||
"basicInformation": "基本信息",
|
||||
"nameRequired": "智能体名称为必填项",
|
||||
"systemPromptRequired": "系统提示词为必填项",
|
||||
"created": "创建时间:",
|
||||
"deleteConfirm": "您确定要删除智能体 \"{{name}}\" 吗?此操作无法撤销,将永久删除该智能体及其所有相关数据。",
|
||||
"deleteSuccess": "智能体删除成功",
|
||||
"deleteFailed": "删除智能体失败",
|
||||
"createSuccess": "智能体创建成功",
|
||||
"updateSuccess": "智能体更新成功",
|
||||
"importSuccess": "智能体导入成功",
|
||||
"exportSuccess": "智能体 \"{{name}}\" 导出成功",
|
||||
"fromFile": "从文件",
|
||||
"fromGitHub": "从 GitHub",
|
||||
"importFromGitHubSuccess": "从 GitHub 导入智能体成功",
|
||||
"loadFailed": "加载智能体失败",
|
||||
"exportFailed": "导出智能体失败",
|
||||
"placeholderTask": "这将作为执行智能体时的默认任务占位符",
|
||||
"definePrompt": "定义您的 CC 智能体的行为和能力",
|
||||
"unsavedChanges": "您有未保存的更改。您确定要离开吗?"
|
||||
},
|
||||
"agentExecution": {
|
||||
"title": "执行 CC 智能体",
|
||||
"readyToExecute": "准备执行",
|
||||
"running": "运行中",
|
||||
"initializing": "初始化智能体中...",
|
||||
"projectPath": "项目路径",
|
||||
"task": "任务",
|
||||
"selectProjectPath": "选择或输入项目路径",
|
||||
"enterTask": "输入智能体的任务",
|
||||
"selectProject": "选择项目路径并输入任务以运行智能体",
|
||||
"fullscreen": "全屏",
|
||||
"copyOutput": "复制输出",
|
||||
"copyAsJsonl": "复制为 JSONL",
|
||||
"copyAsMarkdown": "复制为 Markdown",
|
||||
"executionFailed": "智能体执行失败",
|
||||
"executionCancelled": "智能体执行已取消",
|
||||
"executionStopped": "执行已被用户停止",
|
||||
"stopFailed": "停止执行失败",
|
||||
"selectDirectoryFailed": "选择目录失败",
|
||||
"navigationWarning": "智能体正在运行中。如果您离开此页面,智能体将在后台继续运行。您可以在 CC 智能体的「运行中的会话」标签页中查看运行中的会话。\n\n您确定要继续吗?"
|
||||
},
|
||||
"models": {
|
||||
"sonnet": "Claude 4 Sonnet",
|
||||
"opus": "Claude 4 Opus",
|
||||
"sonnetDescription": "速度快,适用于大多数任务",
|
||||
"opusDescription": "功能更强,适用于复杂任务"
|
||||
},
|
||||
"projects": {
|
||||
"sessions": "{{count}} 个会话",
|
||||
"sessions_plural": "{{count}} 个会话",
|
||||
"backToHome": "← 返回主页",
|
||||
"noProjects": "在 ~/.claude/projects 中未找到项目",
|
||||
"loadFailed": "加载项目失败。请确保 ~/.claude 目录存在。",
|
||||
"sessionLoadFailed": "加载此项目的会话失败。"
|
||||
},
|
||||
"sessions": {
|
||||
"firstMessage": "首条消息:",
|
||||
"hasTodo": "有待办事项",
|
||||
"loadingHistory": "正在加载会话历史...",
|
||||
"initializingClaude": "正在初始化 Claude Code...",
|
||||
"queuedPrompts": "排队提示({{count}})",
|
||||
"selectProjectFirst": "请先选择项目目录",
|
||||
"sessionCancelled": "会话已被用户取消",
|
||||
"loadHistoryFailed": "加载会话历史失败",
|
||||
"forkSession": "分叉会话",
|
||||
"createBranch": "从选定的检查点创建新的会话分支。",
|
||||
"newSessionName": "新会话名称",
|
||||
"createFork": "创建分叉",
|
||||
"projectDirectory": "项目目录",
|
||||
"selectProjectDirectory": "选择项目目录",
|
||||
"claudeCodeSession": "Claude Code 会话",
|
||||
"resumingSession": "恢复会话 {{sessionId}}...",
|
||||
"interactiveSession": "交互式会话",
|
||||
"timeline": "时间线",
|
||||
"preview": "预览",
|
||||
"closePreview": "关闭预览",
|
||||
"outputCopiedAsJsonl": "输出已复制为 JSONL",
|
||||
"outputRefreshed": "输出已刷新",
|
||||
"agentExecutionCompleted": "智能体执行完成"
|
||||
},
|
||||
"usageDashboard": {
|
||||
"title": "使用情况仪表板",
|
||||
"description": "跟踪您的 Claude Code 使用情况和费用",
|
||||
"allTime": "全部时间",
|
||||
"last7Days": "最近 7 天",
|
||||
"last30Days": "最近 30 天",
|
||||
"totalCost": "总费用",
|
||||
"totalSessions": "总会话数",
|
||||
"totalTokens": "总令牌数",
|
||||
"avgCostPerSession": "平均会话费用",
|
||||
"overview": "概览",
|
||||
"byModel": "按模型",
|
||||
"byProject": "按项目",
|
||||
"bySession": "按会话",
|
||||
"timeline": "时间线",
|
||||
"tokenBreakdown": "令牌分布",
|
||||
"inputTokens": "输入令牌",
|
||||
"outputTokens": "输出令牌",
|
||||
"cacheWrite": "缓存写入",
|
||||
"cacheRead": "缓存读取",
|
||||
"mostUsedModels": "常用模型",
|
||||
"topProjects": "热门项目",
|
||||
"dailyUsage": "每日使用情况",
|
||||
"dailyUsageOverTime": "每日使用趋势",
|
||||
"loadingStats": "正在加载使用统计...",
|
||||
"loadFailed": "加载使用统计失败,请重试。",
|
||||
"tryAgain": "重试",
|
||||
"noDataAvailable": "所选时间段内没有可用的使用数据",
|
||||
"sessions": "个会话",
|
||||
"tokens": "个令牌",
|
||||
"models": "个模型",
|
||||
"input": "输入:",
|
||||
"output": "输出:",
|
||||
"cacheW": "缓存写:",
|
||||
"cacheR": "缓存读:",
|
||||
"cost": "费用:",
|
||||
"perSession": "/会话"
|
||||
},
|
||||
"settings": {
|
||||
"title": "设置",
|
||||
"description": "配置 Claude Code 偏好设置",
|
||||
"general": "常规",
|
||||
"permissions": "权限",
|
||||
"environment": "环境",
|
||||
"advanced": "高级",
|
||||
"saveSettings": "保存设置",
|
||||
"generalSettings": "常规设置",
|
||||
"includeCoAuthor": "包含'Co-authored by Claude'",
|
||||
"includeCoAuthorDesc": "在 git 提交和拉取请求中添加 Claude 署名",
|
||||
"verboseOutput": "详细输出",
|
||||
"verboseOutputDesc": "显示完整的 bash 和命令输出",
|
||||
"chatRetention": "聊天记录保留期(天)",
|
||||
"chatRetentionDesc": "本地保留聊天记录的时长(默认:30天)",
|
||||
"claudeInstallation": "Claude Code 安装",
|
||||
"claudeInstallationDesc": "选择要使用的 Claude Code 安装",
|
||||
"permissionRules": "权限规则",
|
||||
"permissionRulesDesc": "控制 Claude Code 无需手动批准即可使用的工具",
|
||||
"allowRules": "允许规则",
|
||||
"denyRules": "拒绝规则",
|
||||
"addRule": "添加规则",
|
||||
"noAllowRules": "未配置允许规则。Claude 将对所有工具请求批准。",
|
||||
"noDenyRules": "未配置拒绝规则。",
|
||||
"environmentVariables": "环境变量",
|
||||
"environmentVariablesDesc": "应用于每个 Claude Code 会话的环境变量",
|
||||
"addVariable": "添加变量",
|
||||
"noEnvironmentVars": "未配置环境变量。",
|
||||
"advancedSettings": "高级设置",
|
||||
"advancedSettingsDesc": "面向高级用户的额外配置选项",
|
||||
"apiKeyHelper": "API 密钥助手脚本",
|
||||
"apiKeyHelperDesc": "用于生成 API 请求认证值的自定义脚本",
|
||||
"rawSettings": "原始设置(JSON)",
|
||||
"rawSettingsDesc": "显示将保存到 ~/.claude/settings.json 的原始 JSON",
|
||||
"saveSuccess": "设置保存成功!",
|
||||
"saveFailed": "保存设置失败",
|
||||
"loadFailed": "加载设置失败。请确保 ~/.claude 目录存在。",
|
||||
"binaryPathChanged": "⚠️ Claude 二进制文件路径已更改。请记得保存您的设置。"
|
||||
},
|
||||
"mcp": {
|
||||
"title": "MCP 服务器",
|
||||
"description": "管理 Model Context Protocol 服务器",
|
||||
"servers": "服务器",
|
||||
"addServer": "添加服务器",
|
||||
"importExport": "导入/导出",
|
||||
"configuredServers": "已配置的服务器",
|
||||
"running": "运行中",
|
||||
"noServers": "没有配置 MCP 服务器",
|
||||
"getStartedText": "添加服务器以开始使用 Model Context Protocol",
|
||||
"serverName": "服务器名称",
|
||||
"command": "命令",
|
||||
"arguments": "参数(可选)",
|
||||
"scope": "作用域",
|
||||
"environmentVariables": "环境变量",
|
||||
"localScope": "本地(仅限此项目)",
|
||||
"projectScope": "项目(通过 .mcp.json 共享)",
|
||||
"userScope": "用户(所有项目)",
|
||||
"addStdioServer": "添加 Stdio 服务器",
|
||||
"addSseServer": "添加 SSE 服务器",
|
||||
"addingServer": "正在添加服务器...",
|
||||
"serverNameRequired": "服务器名称为必填项",
|
||||
"commandRequired": "命令为必填项",
|
||||
"addServerFailed": "添加服务器失败",
|
||||
"exampleCommands": "命令示例",
|
||||
"showFull": "展开全部",
|
||||
"hide": "隐藏",
|
||||
"environmentVars": "环境变量:{{count}}",
|
||||
"loadFailed": "无法加载 MCP 服务器",
|
||||
"serverAddSuccess": "MCP 服务器添加成功!",
|
||||
"serverRemoveSuccess": "服务器删除成功!",
|
||||
"importSuccess": "成功导入 {{count}} 个服务器",
|
||||
"importFailed": "导入服务器失败",
|
||||
"importExportTitle": "导入/导出",
|
||||
"importExportDescription": "从其他来源导入 MCP 服务器或导出您的配置",
|
||||
"importScope": "导入作用域",
|
||||
"importScopeDescription": "选择从 JSON 文件导入服务器的保存位置",
|
||||
"localThisProjectOnly": "本地(仅限此项目)",
|
||||
"projectSharedViaMcp": "项目(通过 .mcp.json 共享)",
|
||||
"userAllProjects": "用户(所有项目)",
|
||||
"importFromClaudeDesktop": "从 Claude Desktop 导入",
|
||||
"importFromClaudeDesktopDesc": "自动导入 Claude Desktop 中的所有 MCP 服务器。安装到用户作用域(在所有项目中可用)。",
|
||||
"importing": "导入中...",
|
||||
"importFromJson": "从 JSON 导入",
|
||||
"importFromJsonDesc": "从 JSON 文件导入服务器配置",
|
||||
"chooseJsonFile": "选择 JSON 文件",
|
||||
"exportConfiguration": "导出配置",
|
||||
"exportConfigurationDesc": "导出您的 MCP 服务器配置",
|
||||
"exportComingSoon": "导出(即将推出)",
|
||||
"useClaudeCodeAsMcp": "将 Claude Code 用作 MCP 服务器",
|
||||
"useClaudeCodeAsMcpDesc": "启动 Claude Code 作为 MCP 服务器,其他应用程序可以连接到它",
|
||||
"startMcpServer": "启动 MCP 服务器",
|
||||
"jsonFormatExamples": "JSON 格式示例",
|
||||
"singleServer": "单个服务器:",
|
||||
"multipleServers": "多个服务器(.mcp.json 格式):",
|
||||
"successfullyImported": "成功导入:{{servers}}",
|
||||
"failedToImportSome": "部分服务器导入失败:",
|
||||
"failedToImportFromDesktop": "从 Claude Desktop 导入失败",
|
||||
"invalidJsonFile": "无效的 JSON 文件。请检查格式。",
|
||||
"unrecognizedJsonFormat": "无法识别的 JSON 格式。期望是 MCP 服务器配置。",
|
||||
"failedToImportJson": "导入 JSON 文件失败",
|
||||
"exportFunctionalityComingSoon": "导出功能即将推出!",
|
||||
"mcpServerStarted": "Claude Code MCP 服务器已启动。您现在可以从其他应用程序连接到它。",
|
||||
"failedToStartMcpServer": "启动 Claude Code 作为 MCP 服务器失败",
|
||||
"enterServerName": "为此服务器输入名称:"
|
||||
},
|
||||
"topbar": {
|
||||
"checking": "检查中...",
|
||||
"claudeNotFound": "未找到 Claude Code",
|
||||
"selectInstallation": "选择 Claude 安装",
|
||||
"installClaude": "安装 Claude Code"
|
||||
},
|
||||
"runningSessions": {
|
||||
"title": "运行中的代理会话",
|
||||
"noSessions": "当前没有正在运行的代理会话",
|
||||
"loadFailed": "加载运行中的会话失败",
|
||||
"refreshSuccess": "运行中的会话列表已更新",
|
||||
"refreshFailed": "刷新会话失败",
|
||||
"sessionStopped": "{{session}} 会话已停止",
|
||||
"sessionMayFinished": "会话可能已经结束",
|
||||
"terminateFailed": "终止会话失败",
|
||||
"loadingSessions": "正在加载运行中的会话...",
|
||||
"task": "任务",
|
||||
"duration": "持续时间",
|
||||
"projectPath": "项目路径",
|
||||
"sessionId": "会话ID",
|
||||
"viewOutput": "查看输出",
|
||||
"pending": "等待中"
|
||||
},
|
||||
"output": {
|
||||
"title": "输出",
|
||||
"live": "实时",
|
||||
"messages": "{{count}} 条消息",
|
||||
"loadingOutput": "正在加载输出...",
|
||||
"waitingOutput": "等待输出...",
|
||||
"agentRunningNoOutput": "智能体正在运行但尚未收到输出",
|
||||
"noOutputAvailable": "暂无输出",
|
||||
"enterFullscreen": "进入全屏",
|
||||
"exitFullscreen": "退出全屏",
|
||||
"refreshOutput": "刷新输出"
|
||||
},
|
||||
"claudeBinary": {
|
||||
"title": "选择 Claude Code 安装",
|
||||
"searching": "正在搜索 Claude 安装...",
|
||||
"multipleFound": "在您的系统中发现了多个 Claude Code 安装。请选择您想要使用的版本。",
|
||||
"notFound": "在常见安装位置未找到 Claude Code。请安装 Claude Code 以继续。",
|
||||
"installationGuide": "安装指南",
|
||||
"validating": "验证中...",
|
||||
"saveSelection": "保存选择",
|
||||
"noInstallations": "未找到安装",
|
||||
"selectInstallation": "请选择一个 Claude 安装",
|
||||
"saveFailed": "保存 Claude 二进制路径失败",
|
||||
"searchedLocations": "搜索位置:",
|
||||
"tip": "提示:",
|
||||
"saveSuccess": "Claude 二进制文件路径保存成功"
|
||||
},
|
||||
"thinking": {
|
||||
"auto": "自动",
|
||||
"autoDesc": "让 Claude 决定",
|
||||
"think": "基础思考",
|
||||
"thinkDesc": "基本推理",
|
||||
"thinkHard": "深度思考",
|
||||
"thinkHardDesc": "更深入的分析",
|
||||
"thinkHarder": "专业思考",
|
||||
"thinkHarderDesc": "广泛的推理",
|
||||
"ultrathink": "超级思考",
|
||||
"ultrathinkDesc": "最大计算能力"
|
||||
},
|
||||
"prompts": {
|
||||
"typePrompt": "请输入您的提示词...",
|
||||
"askClaude": "向 Claude 提问任何问题...",
|
||||
"dropImages": "将图片拖放至此...",
|
||||
"composePrompt": "编写您的提示词",
|
||||
"pressEnterToSend": "按回车发送,Shift+回车换行,@ 符号提及文件,拖放图片",
|
||||
"send": "发送",
|
||||
"model": "模型:",
|
||||
"thinking": "思考:"
|
||||
},
|
||||
"errors": {
|
||||
"loadProjectsFailed": "加载项目失败。请确保 ~/.claude 目录存在。",
|
||||
"loadSessionsFailed": "加载此项目的会话失败。"
|
||||
},
|
||||
"confirmations": {
|
||||
"leaveActiveSession": "Claude 仍在响应中。如果您离开此页面,Claude 将继续在后台运行。\n\n您可以从项目视图返回到此会话。\n\n是否继续?"
|
||||
},
|
||||
"success": {
|
||||
"claudeBinaryPathSaved": "Claude 二进制文件路径保存成功"
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ import App from "./App";
|
||||
import { ErrorBoundary } from "./components/ErrorBoundary";
|
||||
import "./assets/shimmer.css";
|
||||
import "./styles.css";
|
||||
import "./i18n";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
|
Reference in New Issue
Block a user