diff --git a/README.md b/README.md index b05e019..c2cb956 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ Issues

+English · **[中文](./README.zh-CN.md)** + Each profile is an isolated Claude Code config directory: its own MCP servers, skills, agents, slash commands, and settings. Work in one terminal, personal in another, both running Claude Code with different tools loaded. 647 curated plugins and 4,795 skills come built in.

diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..5479181 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,254 @@ +

+ ClaudeWorks +

+

ClaudeWorks

+

无需手动编辑配置,运行独立的 Claude Code 环境。

+ +

+ macOS + MIT + Version 1.0.0 +

+ +

+ 主页 +  ·  + 文档 +  ·  + 发布 +  ·  + 问题 +

+ +> **[English](./README.md)** · 中文 + +每个 Profile 是一个独立的 Claude Code 配置目录:拥有自己的 MCP servers、Skills、Agents、斜杠命令和设置。一个终端用于工作,另一个用于个人用途,两者都运行 Claude Code 但加载不同的工具。内置 647 个精选 Plugin 和 4,795 个 Skills。 + +

+ ClaudeWorks Profile 侧边栏,两个终端并排运行 Claude Code,各自加载不同的 Skills +

+ +--- + +## 为什么做这个 + +我一直在手动管理两套 Claude Code 环境:工作用的那套有自己的 MCP servers 和 Skills,个人用的那套有不同的 CLAUDE.md 和工具。切换意味着手动编辑 `~/.claude.json` 和 `mcp.json`,下一次会话再改回来。Claude Code 没有原生的多 Profile 支持,所以我做了这个。 + +Plugin 缓存通过符号链接在各 Profile 间共享,安装一次 Plugin,更新自动同步到所有地方。 + +## 它做什么 + +每个 Profile 运行在自己的 `CLAUDE_CONFIG_DIR` 下,这是 Claude Code 内置的配置根目录重定向方式。Profile A 的 MCP servers、Skills、Agents 和斜杠命令不会出现在 Profile B 的会话中。真正的进程级隔离,不是配置切换。 + +内置精选 Plugin Marketplace。浏览 114 个上游 Marketplace 和 647 个精选 Plugin,无需离开 ClaudeWorks。一个搜索栏同时查询 7,009 个条目(每个 Marketplace、Plugin、Skill、Command、Agent 和 MCP server)。 + +

+ ClaudeWorks 浏览标签页,展示精选 Plugin、集合标签和 Marketplace 列表 +

+ +每个 Profile 可以配置任意 Shell 别名。每个别名会 cd 到指定目录,并可选地自动运行 `/workflow` 或已保存的提示词。一个 Profile 可以声明 `claude` 命令,这样在任何终端输入 `claude` 都会进入你的默认 Profile,而不会加载其他 Profile 的内容。 + +``` +ship-api → cd ~/code/api, 然后 /workflow +debug-mobile → cd ~/code/mobile, 然后运行预填提示词 +claude → 打开默认 Profile(bare-claude 拦截) +``` + +
+更多功能 + +每个 Profile 的 `/workflow` 命令(以及命名变体如 `/workflow-debug`、`/workflow-deploy`)会成为该 Profile 会话中的真实 Claude Code 命令。变体可以限定到特定的启动目录。 + +多 Agent Teams _(实验性,需设置 `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`)_:从现有 Profile 拖拽组建团队,分配角色,指定负责人,然后启动。负责人启动时会生成一个 `/start-team` 命令来创建每个队友。 + +按目录管理 MCP:在敏感仓库中禁用特定服务器,同时在其他地方保持可用。 + +状态栏构建器:17 个组件,拖拽重排,命名保存/加载,按 Profile 覆盖。 + +Profile 导出/导入:自包含 JSON 文件,精确列出目标机器缺少哪些 Plugin。 + +Profile Doctor:跨 Profile、Plugin 和别名脚本的只读健康检查。修复模式需要明确确认,且每次修改前都会创建 `.bak-<时间戳>` 备份。 + +本地 Skill 来源:`~/.claude/skills/` 中的 Skills 在来源可检测时会显示 `skillfish` 或 `git` 来源标签。 + +
+ +## 它不做什么 + +目前仅支持 macOS。Windows 计划中,Linux 可能随后支持。Electron 壳已经是跨平台的,但终端启动和 Keychain 同步需要移植。 + +Profile 存储在本地,没有云同步。使用 `exportProfile` / `importProfile` 来迁移。 + +不需要 GitHub token。ClaudeWorks 优先尝试 `gh` CLI,然后 `GITHUB_TOKEN`,最后回退到匿名请求(60 次/小时)。参见[架构](#架构)了解回退链。 + +Teams 需要 Anthropic 的实验性标志(`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`)。该功能今天可用,但随着上游成熟,行为可能会变化。 + +我们不生成或管理 Claude API 密钥。请先运行 `claude login`。应用会从你现有的登录中复制凭据。 + +## 安装 + +### 从发布版本 + +1. 从 [Releases](https://github.com/Mduffy37/claudeworks/releases) 页面下载最新的 `.dmg`。 +2. 打开 DMG,将 ClaudeWorks 拖到 Applications。 +3. 首次启动时,macOS 会标记应用未签名;右键点击,打开,确认。 +4. 如果还没登录,运行 `gh auth login`。ClaudeWorks 使用 `gh` CLI 进行 GitHub API 调用,可以获得 5,000 次/小时的配额而不是 60 次。 + +### 从源码 + +```bash +git clone https://github.com/Mduffy37/claudeworks.git +cd claudeworks +npm install +npm run build +npm start +``` + +需要 Node 20+、Electron 33,以及 `PATH` 中的 `gh` CLI(Marketplace 功能需要)。运行 `npm run dev` 启动 Vite 开发服务器 + Electron 监听循环。 + +## 快速开始 + +### 创建你的第一个 Profile + +1. 启动 ClaudeWorks。侧边栏显示你的 Profile(应用会预置一个 `profile-creator` 工作区来帮助你创建)。 +2. 点击侧边栏中的 **新建 Profile**。 +3. 选择此 Profile 要加载的 Plugin、Skills、Agents 和 MCP servers。默认全部关闭,按需启用。 +4. 点击 **保存**。 + +### 启动 Profile + +点击 Profile 卡片上的 **启动** 按钮。一个终端会打开,`CLAUDE_CONFIG_DIR` 已自动设置。默认使用 Terminal.app;可以在设置中选择 iTerm2,更多终端选项即将推出。 + +或者输入你的 Shell 别名。如果你在 Profile 中添加了 `aliases: [{ name: "ship-api", directory: "~/code/api" }]`,在任何终端输入 `ship-api` 就会 cd 到该目录并以该 Profile 启动 Claude Code 会话。 + +### 发现 Plugin + +打开 **配置 Claude → Plugin → 浏览**。全局搜索栏同时查询每个 Marketplace、Plugin、Skill、Command、Agent 和 MCP server(114 个 Marketplace 中的 7,009 个条目)。点击任何 Plugin 卡片查看详情:上游 README 内联渲染,旁边是相关 Plugin 列表。 + +## 架构 + +### Profile 作为配置目录 + +每个 Profile 存储在 `~/.claudeworks/profiles//config/`,启动时设置 `CLAUDE_CONFIG_DIR` 指向它。这是 Claude Code 内置的按会话重定向配置根目录的方式,因此每个 Profile 的 MCP servers 和 Skills 都在自己的命名空间中。 + +`~/.claude/plugins/cache/` 下的 Plugin 缓存通过符号链接在各 Profile 间共享。安装一次 Plugin 即可在所有地方使用,上游更新自动同步。当 Profile 排除特定 Plugin 项目时,ClaudeWorks 只为受影响的目录创建一个小的覆盖层;其他内容保持符号链接。 + +### 精选 Marketplace + +配套仓库 [`claudeworks-marketplace`](https://github.com/Mduffy37/claudeworks-marketplace) 发布 v2 schema: + +- `marketplaces[]`:要在浏览标签页中展示为可点击卡片的完整上游 Marketplace。 +- `plugins[]`:单独精选的 Plugin。 +- `collections[]`:共享分类(如"测试"、"可观测性")。 +- `index.json`:所有 Marketplace 中每个项目的扁平化快照。支持全局搜索栏。 + +当前快照(2026-04-17): + +| 项目 | 数量 | +|---|---| +| Marketplace | 114 | +| Plugin | 647 | +| Skills | 4,795 | +| Agents | 802 | +| Command | 628 | +| MCP servers | 23 | +| **总条目** | **7,009** | + +### GitHub 后端与优雅回退 + +每个 GitHub 请求都经过 3 级后端: + +1. **`gh` CLI**:如果你运行过 `gh auth login`,使用 `execFileAsync("gh", ["api", ...])` 获得 5,000 次/小时配额和私有仓库访问。 +2. **`GITHUB_TOKEN` 请求**:如果设置了 `$GITHUB_TOKEN`,使用它访问 `api.github.com`,5,000 次/小时。 +3. **匿名请求**:60 次/小时回退。可以浏览,只是较慢。 + +所有请求都有 LRU 缓存(大小 50),重新访问 Plugin 详情面板是即时的。 + +## 功能详情 + +### 多 Agent Teams _(实验性)_ + +ClaudeWorks 可以从现有 Profile 组建团队并一起启动: + +1. 从侧边栏创建团队。拖入 Profile,分配角色,指定负责人。 +2. 设置每个成员的指令。每个队友获得自己的 CLAUDE.md 和所有权标签,负责人的委派会路由到拥有该能力的成员。 +3. 启动。负责人在新终端中打开,带有一个生成的 `/start-team` 命令,通过 Claude Code 原生的 Agent Teams 支持创建其他队友。 + +Teams 标记为 **实验性**,因为底层 Anthropic 功能仍在 `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` 之后。随着上游功能成熟,行为可能会变化。 + +### 每个 Profile 的 `/workflow` 命令 + +每个 Profile 可以携带一个可选的 `/workflow` 命令内容。Profile 组装时,ClaudeWorks 将其写入 `/commands/workflow.md`。从该 Profile 启动的任何会话都可以输入 `/workflow` 获得该 Profile 的定制运行手册。 + +命名变体让一个 Profile 可以拥有多个 Workflow 命令(`/workflow-debug`、`/workflow-deploy`、`/workflow-lint`),每个限定到特定启动目录。如果你用一个 Profile 维护三个有不同运行手册的仓库,每次 `cd` 都能进入正确的 Workflow。 + +### 多别名启动 + +每个 Profile 可以配置任意 Shell 别名。每个别名有自己的可选 `directory`(终端在启动前 cd 到那里)和自己的 `launchAction`: + +- `workflow`:会话启动后自动运行 `/workflow`。 +- `prompt`:向 `claude -p ""` 传递预填提示词,会话打开时已在工作中。 + +冲突检测在保存时捕获跨 Profile 的别名冲突。一个 Profile 可以标记 `isDefault: true`,这会安装一个 bare-`claude` 别名,让任何 Shell 中的 `claude` 都在该 Profile 的配置目录中运行。 + +### 状态栏构建器 + +拖拽重排的 Claude Code 状态栏构建器。17 个组件(模型、分支、上下文窗口、用量、成本、速率限制、7 天 Sonnet 用量等),分隔符,组件选项,实时预览。按名称保存配置,稍后加载,并可按 Profile 覆盖。 + +### Profile 导出/导入 + +`exportProfile` 写入一个自包含 JSON,包括 Plugin 列表、排除项、别名、Workflow、MCP 覆盖、环境变量和设置。在另一台机器上,`importProfile` 读取文件并显示 `missingPlugins[]` 列表(Profile 完全加载前新机器需要哪些 Plugin)。 + +### Profile Doctor + +跨 `profiles.json`、`teams.json`、`installed_plugins.json`、`~/.claudeworks/bin/` 下的别名脚本和 Plugin 引用完整性的诊断检查。检测模式是只读的。修复模式需要明确确认,且每次修改前都会创建 `.bak-<时间戳>` 备份。`exportDiagnostics` 打包一个脱敏快照用于错误报告。 + +## 常见问题 + +**"`CLAUDE_CONFIG_DIR` 是怎么工作的?"** + +`CLAUDE_CONFIG_DIR` 是 Claude Code 启动时读取的环境变量,用于重定向配置根目录。设置为 `~/.claudeworks/profiles/work/config/` 后,Claude Code 会从那里读取 `profiles.json`、`mcp.json`、`commands/`、`agents/`、`skills/` 和 `plugins/`,而不是 `~/.claude/`。ClaudeWorks 在启动时自动设置。这是 Claude Code 的一级功能,不是 ClaudeWorks 的发明。 + +**"需要 GitHub token 吗?"** + +不需要。应用优先使用 `gh` CLI(如果已认证),然后是环境中的 `GITHUB_TOKEN`,最后是匿名 HTTP。匿名请求被 GitHub 限速为 60 次/小时,所以第一次浏览标签页会很快用完。运行一次 `gh auth login` 即可。 + +**"有遥测吗?"** + +没有。唯一的网络调用是 Marketplace 内容的 GitHub 请求和你主动触发的发布版本检查。两者都可以在 `src/electron/marketplace.ts` 和 `src/electron/diagnostics.ts` 中查看。 + +**"Windows / Linux 呢?"** + +计划中。Electron 壳已经是跨平台的;终端启动和 macOS Keychain 同步需要移植。如果你想参与,欢迎开 issue。乐意分享架构笔记。 + +**"可以不用 Marketplace 吗?"** + +可以。每个 Profile 功能都独立工作。Marketplace 只是一个模态框中的一个标签页。忽略它,应用的其余部分是一个离线的 Profile 管理器。 + +## 文档 + +完整文档:[mduffy37.github.io/claudeworks/docs](https://mduffy37.github.io/claudeworks/docs/)。 + +- [安装](https://mduffy37.github.io/claudeworks/docs/getting-started.html) +- [快速开始](https://mduffy37.github.io/claudeworks/docs/quick-start.html) +- [Profile 编辑器](https://mduffy37.github.io/claudeworks/docs/profile-editor.html)(每个标签页) +- [配置 Claude](https://mduffy37.github.io/claudeworks/docs/configure-claude.html) +- [别名与启动](https://mduffy37.github.io/claudeworks/docs/aliases.html) +- [Teams](https://mduffy37.github.io/claudeworks/docs/team-editor.html) +- [Profile Doctor](https://mduffy37.github.io/claudeworks/docs/doctor.html) + +## 参与贡献 + +Issue 和功能请求是最好的参与方式。我阅读每一条内容,它们影响我接下来的工作。如果有什么坏了或者你有想法,开一个 issue。 + +要向精选 Marketplace 添加 Plugin,请向 [`claudeworks-marketplace`](https://github.com/Mduffy37/claudeworks-marketplace)(不是本仓库)提交 PR。那里的 `README.md` 记录了 v2 schema。 + +## 许可证 + +MIT。参见 [LICENSE](LICENSE)。 + +## 致谢 + +- Anthropic,发布了 Claude Code 并使 `CLAUDE_CONFIG_DIR` 成为一级功能。 +- 每个上游 Plugin / Skill / Agent 作者,精选 Marketplace 展示了你们的工作。Marketplace 有用是因为你们发布了它。 +- 铺路的开源工具:cctm、claudectx、ccp、CCO。 diff --git a/package-lock.json b/package-lock.json index 095b04d..6818276 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,13 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "i18next": "^26.3.1", + "i18next-browser-languagedetector": "^8.2.1", + "i18next-fs-backend": "^2.6.6", + "i18next-resources-to-backend": "^1.2.1", "react": "^18.3.0", "react-dom": "^18.3.0", + "react-i18next": "^17.0.8", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1" }, @@ -23,7 +28,7 @@ "@types/react-dom": "^18.2.0", "@vitejs/plugin-react": "^4.2.0", "concurrently": "^8.2.0", - "electron": "^33.0.0", + "electron": "^33.4.11", "electron-builder": "^25.0.0", "typescript": "^5.4.0", "vite": "^5.4.0", @@ -268,7 +273,6 @@ "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1542,9 +1546,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1559,9 +1560,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1576,9 +1574,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1593,9 +1588,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1610,9 +1602,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1627,9 +1616,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1644,9 +1630,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1661,9 +1644,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1678,9 +1658,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1695,9 +1672,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1712,9 +1686,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1729,9 +1700,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1746,9 +1714,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3981,7 +3946,7 @@ }, "node_modules/electron": { "version": "33.4.11", - "resolved": "https://registry.npmjs.org/electron/-/electron-33.4.11.tgz", + "resolved": "https://registry.npmmirror.com/electron/-/electron-33.4.11.tgz", "integrity": "sha512-xmdAs5QWRkInC7TpXGNvzo/7exojubk+72jn1oJL7keNeIlw7xNglf8TGtJtkR4rWC5FJq0oXiIXPS9BcK2Irg==", "dev": true, "hasInstallScript": true, @@ -5014,6 +4979,15 @@ "dev": true, "license": "ISC" }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -5083,6 +5057,58 @@ "ms": "^2.0.0" } }, + "node_modules/i18next": { + "version": "26.3.1", + "resolved": "https://registry.npmmirror.com/i18next/-/i18next-26.3.1.tgz", + "integrity": "sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.1", + "resolved": "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.1.tgz", + "integrity": "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-fs-backend": { + "version": "2.6.6", + "resolved": "https://registry.npmmirror.com/i18next-fs-backend/-/i18next-fs-backend-2.6.6.tgz", + "integrity": "sha512-mYGu6Nt8RIp3X/U8Y+Gej1wo5xmYWmGKLqBGMCC2OCAou5rW5epeHgHmVcw20mJs9Z9+DAPHIxQPNCgFyPRMeg==", + "license": "MIT" + }, + "node_modules/i18next-resources-to-backend": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz", + "integrity": "sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -7395,6 +7421,33 @@ "react": "^18.3.1" } }, + "node_modules/react-i18next": { + "version": "17.0.8", + "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-17.0.8.tgz", + "integrity": "sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "html-parse-stringify": "^3.0.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "i18next": ">= 26.2.0", + "react": ">= 16.8.0", + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-markdown": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", @@ -8467,7 +8520,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -8648,6 +8701,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/utf8-byte-length": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", @@ -8862,6 +8924,15 @@ } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index c66b751..d534dbd 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@types/react-dom": "^18.2.0", "@vitejs/plugin-react": "^4.2.0", "concurrently": "^8.2.0", - "electron": "^33.0.0", + "electron": "^33.4.11", "electron-builder": "^25.0.0", "typescript": "^5.4.0", "vite": "^5.4.0", @@ -37,8 +37,13 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "i18next": "^26.3.1", + "i18next-browser-languagedetector": "^8.2.1", + "i18next-fs-backend": "^2.6.6", + "i18next-resources-to-backend": "^1.2.1", "react": "^18.3.0", "react-dom": "^18.3.0", + "react-i18next": "^17.0.8", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1" } diff --git a/src/electron/i18n.ts b/src/electron/i18n.ts new file mode 100644 index 0000000..50b3df6 --- /dev/null +++ b/src/electron/i18n.ts @@ -0,0 +1,45 @@ +/** + * i18n utilities for the Electron main process. + * + * Self-contained version that doesn't import from src/i18n/ (which is + * outside the electron tsconfig's rootDir). Uses i18next-fs-backend to + * load the same translation JSON files shared with the React frontend. + */ + +import i18next from 'i18next'; +import Backend from 'i18next-fs-backend'; +import path from 'path'; + +export function detectSystemLang(): string { + const envLang = process.env.CLAUDEWORKS_LANG || process.env.LANG || process.env.LC_ALL || ''; + if (envLang.startsWith('zh')) return 'zh'; + return 'en'; +} + +let initialized = false; + +export async function initNodeI18n(lang?: string): Promise { + if (initialized) return i18next; + + await i18next.use(Backend).init({ + lng: lang || detectSystemLang(), + fallbackLng: 'en', + defaultNS: 'common', + ns: ['common', 'scripts', 'profile', 'team', 'plugin', 'settings'], + backend: { + // At runtime: dist/electron/i18n.js → ../../src/i18n/locales/… + // In dev: src/electron/i18n.ts → ../i18n/locales/… + loadPath: path.join(__dirname, '..', '..', 'src', 'i18n', 'locales', '{{lng}}', '{{ns}}.json'), + }, + interpolation: { escapeValue: false }, + }); + + initialized = true; + return i18next; +} + +export function changeNodeLang(lang: string): void { + i18next.changeLanguage(lang); +} + +export { i18next as i18n }; diff --git a/src/i18n/i18next.d.ts b/src/i18n/i18next.d.ts new file mode 100644 index 0000000..3d2e609 --- /dev/null +++ b/src/i18n/i18next.d.ts @@ -0,0 +1,22 @@ +import type common from './locales/en/common.json'; +import type profile from './locales/en/profile.json'; +import type team from './locales/en/team.json'; +import type plugin from './locales/en/plugin.json'; +import type marketplace from './locales/en/marketplace.json'; +import type settings from './locales/en/settings.json'; +import type scripts from './locales/en/scripts.json'; + +declare module 'i18next' { + interface CustomTypeOptions { + defaultNS: 'common'; + resources: { + common: typeof common; + profile: typeof profile; + team: typeof team; + plugin: typeof plugin; + marketplace: typeof marketplace; + settings: typeof settings; + scripts: typeof scripts; + }; + } +} diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..0b16dd7 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,22 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import Backend from 'i18next-resources-to-backend'; + +i18n + .use(Backend((lang: string, ns: string) => import(`./locales/${lang}/${ns}.json`))) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + fallbackLng: 'en', + defaultNS: 'common', + ns: ['common', 'profile', 'team', 'plugin', 'marketplace', 'settings', 'scripts'], + interpolation: { escapeValue: false }, + detection: { + order: ['localStorage', 'navigator'], + caches: ['localStorage'], + lookupLocalStorage: 'claudeworks-lang', + }, + }); + +export default i18n; diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json new file mode 100644 index 0000000..16b8c2b --- /dev/null +++ b/src/i18n/locales/en/common.json @@ -0,0 +1,229 @@ +{ + "buttons": { + "save": "Save", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "close": "Close", + "back": "Back", + "next": "Next", + "done": "Done", + "launch": "Launch", + "newSession": "New session", + "viewRelease": "View release ↗", + "showLess": "Show less", + "showMore": "Show more", + "viewAll": "View all {{count}}", + "allTime": "All Time", + "7days": "7 Days", + "30days": "30 Days", + "allProjects": "All Projects", + "import": "Import", + "overview": "Overview", + "add": "Add", + "override": "Override", + "remove": "Remove", + "addPlugin": "Add plugin", + "openInEditor": "Open in Editor ↗", + "insertPrompt": "Insert Prompt", + "saveAsPrompt": "Save as Prompt", + "addToPath": "Add to PATH", + "openInFinder": "Open Config Directory in Finder", + "previewMerge": "Preview Merge", + "setAsLead": "Set as lead", + "confirm": "Confirm", + "retry": "Retry", + "export": "Export", + "clearAll": "Clear all", + "reloadApp": "Reload app", + "create": "Create", + "refresh": "Refresh", + "apply": "Apply", + "update": "Update", + "runCheck": "Run Check", + "saveCurrent": "Save current", + "loadPreset": "Load preset", + "addSource": "Add source", + "selectAll": "Select all", + "uninstall": "Uninstall", + "install": "Install", + "reScan": "Re-scan", + "scan": "Scan", + "updateAll": "Update All", + "applyRepairs": "Apply Repairs", + "openProject": "Open Project", + "openClaudeDir": "Open .claude/" + }, + "status": { + "loading": "Loading...", + "loadingAnalytics": "Loading analytics...", + "saving": "Saving...", + "saved": "Saved", + "error": "Error", + "success": "Success", + "justNow": "just now", + "minutesAgo": "{{count}}m ago", + "hoursAgo": "{{count}}h ago", + "daysAgo": "{{count}}d ago", + "updateAvailable": "Update available: v{{version}}", + "current": "Current: v{{version}}", + "unsavedChanges": "Unsaved changes", + "launchingEllipsis": "Launching…", + "savedCheck": "✓ Saved", + "lookingUp": "Looking up…", + "updating": "Updating...", + "removing": "Removing...", + "installing": "Installing...", + "adding": "Adding...", + "applying": "Applying...", + "refreshing": "Refreshing...", + "scanning": "Scanning...", + "running": "Running...", + "loadingConfig": "Loading status bar config…", + "runningDiagnostics": "Running diagnostic checks…", + "applyingRepairs": "Applying repairs…", + "loadingCurated": "Loading curated plugins...", + "loadingAvailable": "Loading available plugins...", + "loadingPluginList": "Loading plugin list…", + "loadingReadme": "Loading README…", + "noReadme": "No README available for this repository.", + "cannotResolveSource": "Cannot resolve GitHub source for this entry.", + "savedToDownloads": "Saved to Downloads", + "updated": "Updated" + }, + "errors": { + "networkFailed": "Network error", + "unknownError": "Unknown error", + "saveFailed": "Save failed:", + "launchFailed": "Launch failed:", + "teamLaunchFailed": "Team launch failed", + "invalidName": "Invalid name", + "nameRequired": "Name is required", + "nameAlreadyExists": "Name already exists", + "installationFailed": "Installation failed", + "updateFailed": "Update failed", + "uninstallFailed": "Uninstall failed", + "doctorFailed": "Doctor failed:", + "invalidJson": "Invalid JSON", + "claudeNotFound": "Could not find the claude binary. Tried: {{tried}}. Is Claude Code installed?", + "refusingToLaunch": "Refusing to launch: {{source}} contains shell metacharacters (; & | ` $ < >). Remove them and try again.", + "invalidProfileName": "Invalid profile name: \"{{name}}\". Names must not contain path separators, \"..\", or null bytes.", + "invalidProfileNameOutside": "Invalid profile name: \"{{name}}\" resolves outside the profiles directory.", + "profileNotFound": "Profile \"{{name}}\" not found", + "profileNameAlreadyExists": "A profile named \"{{name}}\" already exists" + }, + "emptyStates": { + "noResults": "No results", + "noMatches": "No matches", + "noItems": "No items", + "selectPlugin": "Select a plugin to view details", + "selectProject": "Select a project", + "noPluginsInstalled": "No plugins installed", + "noConfigurableItems": "No configurable items", + "noItemsYet": "No items in .claude/ yet. Create one below.", + "noProjectsImported": "No projects imported. Click + to add a project directory.", + "noEnvVars": "No environment variables configured.", + "noMarketplaceSources": "No marketplace sources registered", + "noPluginsFromSource": "No plugins from this source", + "noPromptsYet": "No prompts yet", + "selectPrompt": "Select a prompt", + "noPluginsAvailable": "No plugins available", + "noGlobalPlugins": "No global plugins installed", + "noMatchingEntries": "No matching entries", + "noCuratedAvailable": "No curated marketplaces or plugins available" + }, + "sections": { + "favourites": "Favourites", + "activeSessions": "Active Sessions ({{count}})", + "activity": "Activity", + "topProjects": "Top Projects", + "recentSessions": "Recent Sessions", + "profileActivity": "Profile Activity" + }, + "stats": { + "sessions": "Sessions", + "messages": "Messages", + "projects": "Projects", + "profiles": "Profiles", + "msgs": "{{count}} msgs", + "project": "Project", + "profile": "Profile", + "date": "Date" + }, + "plurals": { + "plugin_one": "{{count}} plugin", + "plugin_other": "{{count}} plugins", + "session_one": "{{count}} session", + "session_other": "{{count}} sessions", + "message_one": "{{count}} message", + "message_other": "{{count}} messages", + "item_one": "{{count}} item", + "item_other": "{{count}} items", + "profile_one": "{{count}} profile", + "profile_other": "{{count}} profiles", + "team_one": "{{count}} team", + "team_other": "{{count}} teams", + "member_one": "{{count}} member", + "member_other": "{{count}} members", + "install_one": "{{count}} install", + "install_other": "{{count}} installs", + "match_one": "{{count}} match", + "match_other": "{{count}} matches", + "kind_one": "{{count}} kind", + "kind_other": "{{count}} kinds", + "update_one": "{{count}} update", + "update_other": "{{count}} updates", + "issue_one": "{{count}} issue", + "issue_other": "{{count}} issues" + }, + "labels": { + "moreActions": "More actions", + "chooseDirectory": "Choose directory...", + "launchSettings": "Launch settings", + "commaSeparated": "Comma-separated", + "charsLines": "{{chars}} chars · {{lines}} lines", + "internal": "internal", + "global": "global", + "managed": "managed", + "lead": "LEAD", + "missing": "Missing", + "dragToAdd": "Drag to add to team", + "inTeam": "In team ✓", + "all": "All", + "enabled": "Enabled", + "disabled": "Disabled", + "installed": "Installed", + "added": "Added", + "preview": "Preview", + "health": "Health", + "untitled": "Untitled", + "enabledOnly": "Enabled only", + "showAll": "Show all plugins", + "showEnabled": "Show only plugins enabled in this profile", + "name": "Name", + "source": "Source", + "popular": "Popular", + "popularSort": "Popular", + "allSources": "All sources", + "from": "from", + "by": "by", + "addedDate": "added", + "plugins": "plugins", + "members": "members", + "defaultSettings": "Default settings", + "searchPlugins": "Search plugins...", + "searchPrompts": "Search prompts...", + "searchAvailablePlugins": "Search available plugins...", + "dangerousMode": "Dangerous mode" + }, + "fields": { + "name": "Name", + "description": "Description", + "tags": "Tags", + "role": "Role", + "instructions": "Instructions", + "directory": "Directory", + "value": "value", + "voice": "Voice" + } +} diff --git a/src/i18n/locales/en/marketplace.json b/src/i18n/locales/en/marketplace.json new file mode 100644 index 0000000..9507219 --- /dev/null +++ b/src/i18n/locales/en/marketplace.json @@ -0,0 +1,58 @@ +{ + "discover": { + "title": "Discover", + "searchPlaceholder": "Search plugins, skills, commands...", + "collections": "Collections", + "plugins": "Plugins", + "skills": "Skills", + "commands": "Commands", + "agents": "Agents", + "noResults": "No results found", + "install": "Install", + "viewDetails": "View details", + "searchAvailable": "Search available plugins...", + "popular": "Popular", + "sortAZ": "A-Z", + "source": "Source", + "allSources": "All sources", + "installs": "{{count}} installs", + "noPlugins": "No plugins available", + "noMatches": "No matches", + "installed": "Installed" + }, + "detail": { + "by": "by {{author}}", + "version": "Version {{version}}", + "install": "Install", + "installed": "Installed", + "viewOnGitHub": "View on GitHub", + "readme": "README", + "items": "Items", + "dependencies": "Dependencies", + "installTitle": "Install {{name}}?", + "installDesc": "Install \"{{name}}\" from {{marketplace}}? This will download and install the plugin globally.", + "installing": "Installing...", + "source": "Source" + }, + "curated": { + "title": "Curated", + "featured": "Featured", + "popular": "Popular", + "new": "New", + "categories": "Categories", + "by": "by", + "added": "added", + "source": "source", + "plugins": "plugins", + "pluginsInMarketplace": "Plugins in this marketplace", + "loadingPluginList": "Loading plugin list…", + "remove": "Remove", + "install": "Install", + "installPlugin": "Install plugin", + "addMarketplace": "Add marketplace", + "adding": "Adding…", + "installing": "Installing…", + "addedLabel": "Added", + "installedLabel": "Installed" + } +} diff --git a/src/i18n/locales/en/plugin.json b/src/i18n/locales/en/plugin.json new file mode 100644 index 0000000..e107303 --- /dev/null +++ b/src/i18n/locales/en/plugin.json @@ -0,0 +1,309 @@ +{ + "list": { + "title": "Plugins", + "installed": "Installed", + "available": "Available", + "searchPlaceholder": "Search plugins...", + "global": "Global", + "project": "Project", + "noMatches": "No matches", + "noInstalled": "No plugins installed", + "updateAvailable": "Update available" + }, + "manager": { + "title": "Plugin Manager", + "install": "Install", + "uninstall": "Uninstall", + "update": "Update", + "updatesAvailable": "Updates available", + "noPlugins": "No plugins installed", + "confirmUninstall": "Uninstall plugin \"{{name}}\"?", + "selectPlugin": "Select a plugin to view details", + "updating": "Updating...", + "updateTo": "Update to v{{version}}", + "removing": "Removing...", + "usedBy": "Used by", + "contents": "Contents", + "skills": "Skills", + "agents": "Agents", + "commands": "Commands", + "hooks": "Hooks", + "mcpServers": "MCP Servers", + "noConfigurable": "No configurable items", + "uninstallConfirmTitle": "Uninstall {{name}}?", + "uninstallUsedBy": "This plugin is used by {{count}} profile(s) ({{profiles}}). They will show as unhealthy after removal.", + "uninstallNotUsed": "This plugin is not used by any profiles." + }, + "picker": { + "title": "Add Plugin", + "searchPlaceholder": "Search plugins...", + "installing": "Installing...", + "installed": "Installed", + "enabledOnly": "Enabled only", + "showAll": "Show all plugins", + "showEnabled": "Show only plugins enabled in this profile", + "globalPlugins": "Global Plugins", + "frameworks": "Frameworks", + "local": "Local", + "projectPlugins": "Project Plugins", + "noGlobal": "No global plugins installed", + "noProjectFor": "No plugins installed for {{dir}}" + }, + "manage": { + "title": "Manage Plugin", + "skills": "Skills", + "commands": "Commands", + "agents": "Agents", + "hooks": "Hooks", + "mcpServers": "MCP Servers", + "enableAll": "Enable all", + "disableAll": "Disable all" + }, + "marketplace": { + "title": "Marketplaces", + "add": "Add marketplace", + "remove": "Remove", + "update": "Update", + "updateAll": "Update all", + "lastUpdated": "Last updated: {{date}}", + "installed": "Installed marketplaces", + "addSource": "Add source", + "updateAllSources": "Update All Sources", + "githubPlaceholder": "GitHub repo (e.g. owner/repo)", + "refreshSource": "Refresh source", + "removeSource": "Remove source", + "lastSynced": "Last synced {{time}}", + "installedOfTotal": "{{installed}} / {{total}} Installed" + }, + "mcp": { + "noServers": "No MCP servers found. Install a plugin that provides MCP servers, or configure servers in ~/.claude.json.", + "showingProjectMcps": "Showing project MCPs for `{{dir}}`", + "noDirSelected": "No directory selected — select one in the topbar to see project-specific MCP servers", + "fromPlugins": "From plugins ({{count}})", + "fromPluginsHint": "Toggling a row disables the source plugin for this profile.", + "userLabel": "User ({{count}}) `(~/.claude.json)`", + "userHint": "Global servers. Toggling disables for this profile only.", + "globalLabel": "global", + "projectLabel": "Project · {{dir}} ({{count}})", + "projectTogglesHint": "Toggles persist per directory.", + "disableMcpForProfile": "Disable MCP server for this profile", + "enableMcpForProfile": "Enable MCP server for this profile", + "disableMcp": "Disable MCP server", + "enableMcp": "Enable MCP server" + }, + "dialog": { + "tabs": { + "plugins": "Plugins", + "projects": "Projects", + "prompts": "Prompts", + "global": "Global", + "statusbar": "Status Bar", + "health": "Health" + }, + "browse": "Browse", + "sources": "Sources", + "installed": "Installed", + "defaultNudge": "Running `claude` loads all {{count}} installed plugin(s).", + "noDefaultProfile": "No default profile.", + "createDefaultProfile": "Create Default Profile", + "configureClaude": "Configure Claude", + "browseCurated": "Browse Curated Marketplaces", + "searchCuratedSub": "Search curated marketplaces, plugins, skills, agents, and commands.", + "searchPluginsPlaceholder": "Search plugins, skills, agents, commands…", + "refreshCurated": "Refresh curated list", + "featured": "Featured", + "collections": "Collections", + "moreCollections": "+ {{count}} more", + "showLess": "Show less", + "marketplacePlugins": "marketplace · {{count}} plugins", + "bundle": "Bundle", + "pluginsAvailable": "Plugins available from installed marketplaces", + "noResultsFor": "No results for \"{{query}}\" across {{total}} entries", + "matchSummary": "{{total}} match(es) across {{kindCount}} kind(s)", + "anonymousAccess": "Anonymous GitHub access — {{rateLimit}} rate limit.", + "addToProfile": "Add to PATH", + "itemTypes": { + "skill": "Skill", + "agent": "Agent", + "command": "Command" + }, + "models": { + "opus": "Opus", + "sonnet": "Sonnet", + "haiku": "Haiku" + }, + "effortLevels": { + "low": "Low", + "medium": "Medium", + "high": "High", + "xhigh": "XHigh", + "max": "Max" + }, + "terminal": { + "terminal": "Terminal.app", + "plain": "Plain tmux", + "none": "No tmux" + }, + "claudeMd": "CLAUDE.md", + "noDefaultBanner": "Running `claude` loads all {{count}} installed plugin(s).", + "invalidJson": "Invalid JSON", + "removeMarketplace": "Remove marketplace", + "add": "Add", + "install": "Install", + "adding": "Adding...", + "installing": "Installing...", + "usedByProfile": "This {thing} is used by {count} profile(s)", + "itWillShow": "It will show as unhealthy after {action}.", + "theyWillShow": "They will show as unhealthy after {action}.", + "dangerousMode": "Dangerous mode" + }, + "project": { + "title": "Projects", + "addProject": "Add project", + "noProjects": "No projects imported. Click + to add a project directory.", + "importedProjects": "Imported projects", + "git": "Git", + "uncommittedChanges": "uncommitted changes", + "directory": "Directory", + "openProject": "Open Project", + "openClaudeDir": "Open .claude/", + "claudeMd": "CLAUDE.md", + "insertPrompt": "Insert Prompt", + "skillsAgentsCommands": "Skills, Agents & Commands", + "noItems": "No items in .claude/ yet. Create one below.", + "skills": "Skills", + "agents": "Agents", + "commands": "Commands", + "mcpServers": "MCP Servers", + "mcpServersHint": "Edit .mcp.json — raw JSON for project-level MCP server configuration.", + "projectSettings": "Project Settings", + "projectSettingsHint": "Saved to .claude/settings.json — applies to all sessions in this directory.", + "model": "Model", + "effortLevel": "Effort Level", + "envVars": "Environment Variables", + "envVarsHint": "Project-level env vars override global settings for sessions launched in this directory.", + "inheritedFromGlobal": "Inherited from global settings", + "removeProject": "Remove project {{name}}", + "selectProject": "Select a project", + "selectProjectDesc": "Choose a project from the list, or add one to manage its CLAUDE.md, skills, MCP servers, and settings.", + "namePlaceholder": "Name...", + "contentPlaceholder": "{{type}} content...", + "instructionsPlaceholder": "Project-level instructions for Claude Code...", + "openInDefaultEditor": "Open in default editor", + "global": "Global", + "override": "Override" + }, + "global": { + "globalConfig": "Global Config", + "openInFinder": "Open in Finder", + "globalClaudeMd": "Global CLAUDE.md", + "globalClaudeMdHint": "Instructions that apply to every Claude Code session, regardless of profile.", + "globalClaudeMdPlaceholder": "Global instructions for all Claude Code sessions...", + "defaultModelEffort": "Default Model & Effort", + "defaultModelEffortHint": "Fallback values used when a profile doesn't specify its own.", + "model": "Model", + "opusContext": "Opus Context", + "sonnetContext": "Sonnet Context", + "effortLevel": "Effort Level", + "envVars": "Environment Variables", + "envVarsHint": "From ~/.claude/settings.json — applied to all sessions. Per-profile env vars override these.", + "defaultCliFlags": "Default CLI Flags", + "defaultCliFlagsHint": "Flags passed to `claude` on every launch. Per-profile flags are appended after these.", + "hooks": "Hooks", + "hooksHint": "Shell commands that run in response to Claude Code events. Saved to ~/.claude/settings.json and inherited by all profiles.", + "launchDefaults": "Launch Defaults", + "launchDefaultsHint": "Default terminal and tmux settings used by the launch popover.", + "terminalApp": "Terminal App", + "tmuxMode": "tmux Mode", + "tmuxNotInstalled": "tmux not installed — defaulting to no tmux", + "systemDefault": "System default" + }, + "health": { + "credentials": "Credentials", + "globalCredentials": "Global Credentials", + "profileCredentials": "Profile Credentials", + "active": "Active", + "notFound": "Not found", + "storedInKeychain": "stored in macOS Keychain as \"Claude Code-credentials\"", + "profileIssues": "Profile Issues", + "noMissingPlugins": "No missing plugins detected", + "staleProfiles": "Stale Profiles (30+ days)", + "neverLaunched": "Never Launched", + "unusedPlugins": "Unused Plugins", + "unusedPluginsHint": "Installed plugins not used by any profile.", + "systemDiagnostics": "System Diagnostics", + "version": "Version", + "configDir": "Config Dir", + "claudeHome": "Claude Home", + "profilesTeams": "Profiles / Teams", + "issues": "Issues ({{count}})", + "noIssuesDetected": "No issues detected", + "loadingCredentialStatus": "Loading credential status...", + "runningDiagnostics": "Running diagnostics..." + }, + "prompts": { + "title": "Prompts", + "importPrompt": "Import prompt", + "newPrompt": "New prompt", + "searchPrompts": "Search prompts...", + "savedPrompts": "Saved prompts", + "noPromptsYet": "No prompts yet. Click + to create one.", + "selectPrompt": "Select a prompt", + "prompt": "Prompt", + "namePlaceholder": "Prompt name...", + "descPlaceholder": "What this prompt is for...", + "addTag": "Add tag...", + "content": "Content", + "contentPlaceholder": "Prompt content — this text gets inserted into CLAUDE.md editors...", + "exportPrompt": "Export", + "reusableSnippets": "Reusable snippets you can drop into profile instructions or CLAUDE.md files.", + "pickOne": "Pick one from the list to edit, or create a new prompt.", + "newPromptBtn": "+ New prompt", + "untitled": "Untitled" + }, + "bulk": { + "title": "Manage", + "profiles": "Profiles ({{count}})", + "teams": "Teams ({{count}})", + "noItems": "No {{type}} yet.", + "name": "Name", + "modelEffortPlugins": "Model / Effort / Plugins", + "members": "Members", + "selected": "{{count}} {{type}} selected", + "chooseAction": "Choose action...", + "deleteSelected": "Delete selected", + "manageTags": "Manage tags", + "manageProjects": "Manage projects", + "setModel": "Set model", + "setEffort": "Set effort level", + "togglePlugin": "Toggle plugin", + "toggleAuth": "Toggle auth", + "addTag": "Add tag", + "removeTag": "Remove tag", + "tagNamePlaceholder": "Tag name...", + "addProject": "Add project", + "removeProject": "Remove project", + "noImportedProjects": "No imported projects", + "chooseProject": "Choose project...", + "defaultClear": "Default (clear)", + "choosePlugin": "Choose plugin...", + "enable": "Enable", + "disable": "Disable", + "useDefaultAuth": "Use default auth", + "separateAuth": "Separate auth", + "applying": "Applying...", + "apply": "Apply", + "deleteConfirm": "Delete {{count}} {{type}}(s)?", + "deleteDescription": "This will permanently delete: {{names}}" + }, + "errors": { + "failedToLoadCurated": "Failed to load curated plugins", + "failedToRefreshCurated": "Failed to refresh curated plugins", + "failedToAddMarketplace": "Failed to add marketplace", + "failedToRemoveMarketplace": "Failed to remove marketplace", + "failedToRefreshMarketplace": "Failed to refresh marketplace", + "failedToUpdateMarketplaces": "Failed to update marketplaces", + "failedToLoadPlugins": "Failed to load available plugins", + "failedToInstallPlugin": "Failed to install plugin" + } +} diff --git a/src/i18n/locales/en/profile.json b/src/i18n/locales/en/profile.json new file mode 100644 index 0000000..bd40e19 --- /dev/null +++ b/src/i18n/locales/en/profile.json @@ -0,0 +1,230 @@ +{ + "list": { + "title": "Profiles", + "newProfile": "New profile", + "searchPlaceholder": "Search profiles...", + "allTags": "All tags", + "allProjects": "All projects", + "addTag": "+ Tag", + "addProject": "+ Project", + "pickProject": "Pick project…", + "sortBy": { + "name": "A-Z", + "plugins": "Plugins", + "recent": "Recent", + "favourites": "Favourites" + }, + "defaultBadge": "DEFAULT", + "emptyTitle": "No profiles yet", + "emptyBody": "Profiles save named presets of plugins, skills, and settings for Claude Code sessions. Click + above to create your first profile.", + "noMatches": "No matches", + "launchTitle": "Launch \"{{name}}\"", + "launchTitleInDir": "Launch \"{{name}}\" in {{dir}}", + "healthBadge": "{{count}} missing plugin(s)" + }, + "editor": { + "title": "Edit Profile", + "newTitle": "New Profile", + "save": "Save", + "discard": "Discard changes", + "name": "Name", + "description": "Description", + "descriptionPlaceholder": "What this profile is for", + "model": "Model", + "effortLevel": "Effort Level", + "plugins": "Plugins", + "skills": "Skills", + "commands": "Commands", + "agents": "Agents", + "excludedItems": "Excluded Items", + "customClaudeMd": "Custom CLAUDE.md", + "workflow": "Workflow", + "launchPrompt": "Launch Prompt", + "tags": "Tags", + "projects": "Projects", + "directories": "Directories", + "envVars": "Environment Variables", + "aliases": "Aliases", + "mcpServers": "MCP Servers", + "launchFlags": "Launch Flags", + "dangerouslySkipPermissions": "Dangerously skip permissions", + "verbose": "Verbose", + "customFlags": "Custom flags", + "useDefaultAuth": "Use default auth", + "isDefault": "Set as default", + "disabledHooks": "Disabled Hooks", + "disabledMcpServers": "Disabled MCP Servers", + "statusLine": "Status Line" + }, + "topBar": { + "launch": "Launch", + "export": "Export", + "duplicate": "Duplicate", + "delete": "Delete", + "confirmDelete": "Delete profile \"{{name}}\"?", + "overview": "Overview", + "createProfile": "Create Profile", + "namePlaceholder": "Profile name...", + "configureSubtitle": "Configure plugins and skills for this profile", + "noPluginsEnabled": "No plugins enabled", + "pluginsEnabled": "{{count}} plugin(s) enabled", + "importResult": "Profile imported as \"{{name}}\".\n\n{{count}} plugin(s) need installing:\n{{plugins}}\n\nInstall them from Configure Claude > Plugins > Browse." + }, + "overview": { + "title": "Profile Overview", + "closeAriaLabel": "Close overview", + "description": "Summary of what this profile will load when launched. Click a category to see details.", + "enabledPlugins": "Enabled Plugins", + "enabledSkills": "Enabled Skills", + "enabledAgents": "Enabled Agents", + "enabledCommands": "Enabled Commands", + "enabledMcpServers": "Enabled MCP Servers", + "itemsCount": "{{count}} items", + "goToTab": "Go to {{tab}} tab →", + "modelLabel": "Model: {{model}}", + "effortLabel": "Effort: {{level}}" + }, + "emptyState": { + "noProfileSelected": "No profile selected", + "chooseProfile": "Choose a profile from the sidebar, or create a new one to get started." + }, + "defaultBanner": "This is your default profile. Running `claude` in any terminal launches with these plugins and settings. Add only what you need for everyday use.", + "health": { + "missingPlugins": "{{count}} missing plugin(s) — choose \"Add plugin\" to find it in the marketplace or \"Remove\" to drop it from this profile.", + "lookingUp": "Looking up…", + "addPlugin": "Add plugin", + "remove": "Remove" + }, + "tabs": { + "projectItems": "Project Items", + "sections": "Profile sections" + }, + "filter": { + "skillsPlaceholder": "Search skills by name or plugin…", + "agentsPlaceholder": "Filter agents by name or plugin…", + "commandsPlaceholder": "Search commands by name or plugin…" + }, + "noItems": "No {{tab}} available. Install plugins to see {{tab}} here.", + "group": { + "expand": "Expand {{name}} group", + "collapse": "Collapse {{name}} group", + "selectTitle": "Select all in group", + "deselectTitle": "Deselect all in group", + "none": "None", + "all": "All" + }, + "internal": "internal", + "local": { + "itemHeader": "{{type}}: {{name}}", + "selectDir": "Select a project directory", + "selectDirHint": "Choose a directory in the topbar to see project-specific skills, agents, and commands.", + "noItemsInDir": "No items found in {{dir}}/.claude/", + "noItemsHint": "Add skills, agents, or commands to your project's `.claude/` directory and they'll appear here.", + "openClaudeDir": "Open .claude/ directory", + "itemsFromDir": "Items from `{{dir}}`/.claude/ — these are loaded automatically when launching into this directory, independent of profile settings.", + "addMoreItems": "Add more project items", + "addMoreItemsHint": "Drop skills, agents, or commands into this project's `.claude/` directory and they'll appear here alongside profile-level items.", + "openClaude": "Open .claude/", + "editTitle": "Edit {{name}}", + "sectionLabel": "{{type}} ({{count}})", + "contentPlaceholder": "{{type}} content..." + }, + "instructions": { + "alwaysOn": "Always on", + "claudeMdHint": "Appended to CLAUDE.md — Claude reads this every turn.", + "onDemand": "On demand", + "workflowHint": "Invoked on demand — runs when you type `/workflow` in a session.", + "workflowPlaceholder": "Describe how this profile should orchestrate its tools — e.g. “First run the code-explorer agent, then invoke systematic-debugging, then produce a bulleted report.”", + "placeholder": "Additional instructions for this profile...", + "charStats": "{{chars}} chars · {{lines}} lines", + "variants": { + "title": "Workflow Variants", + "hint": "Named variants become `/workflow-name` commands. Optionally scope to a specific project directory.", + "namePlaceholder": "name", + "thisProjectOnly": "This project only", + "projectOnly": "{{project}} only", + "addVariant": "+ Add Variant", + "variantPlaceholder": "Describe the {{name}} workflow...", + "remove": "Remove" + } + }, + "dialogs": { + "deleteTitle": "Delete Profile", + "deleteConfirm": "Are you sure you want to delete `{{name}}`?", + "deleteDefaultWarning": "This is your default profile. Deleting it means running `claude` will load all installed plugins.", + "deleteCannotUndo": "This will remove the profile configuration and its assembled config directory. This cannot be undone.", + "pluginNotFound": "Plugin not found", + "pluginNotFoundDesc": "`{{name}}` isn't available in any curated marketplace we can see", + "pluginNotFoundSearched": "(searched for `{{id}}`)", + "pluginNotFoundRemove": "It may have been removed, renamed, or only lives in a private marketplace you haven't added. Remove it from this profile?", + "removeFromProfile": "Remove from profile", + "cannotSave": "Cannot Save", + "savePromptTitle": "Save as Prompt", + "namePlaceholder": "My prompt", + "descriptionPlaceholder": "Optional description", + "tagsPlaceholder": "tag1, tag2, tag3" + }, + "settings": { + "sessionBehavior": "Session Behavior", + "defaultInheritGlobal": "Default (inherit global)", + "opusContextDefault": "1M (default)", + "opusContextHint": "Opus 1M context is included in your plan.", + "sonnetContextDefault": "200k (default)", + "sonnetContextExtra": "1M — billed as extra usage", + "sonnetContextHint": "Sonnet 1M context is billed as extra usage outside your plan.", + "effortLow": "Low", + "effortMedium": "Medium", + "effortHigh": "High", + "effortXhigh": "X-High", + "effortMax": "Max", + "defaultVoice": "Default", + "enabledVoice": "Enabled", + "disabledVoice": "Disabled", + "statusBar": "Status Bar", + "overrideGlobalStatusBar": "Override global status bar for this profile", + "statusBarOverrideHint": "This profile uses its own widget config, seeded from the global status bar. Edits made in Configure Claude → Status Bar apply to the global config only; per-profile overrides persist independently and take effect for sessions launched via this profile.", + "statusBarDefaultHint": "When off, sessions launched via this profile use the global status bar from Configure Claude → Status Bar.", + "authentication": "Authentication", + "useDefaultAuth": "Use default authentication", + "authSharedHint": "This profile shares credentials with your default Claude Code installation.", + "authOwnHint": "This profile will use its own credentials. You'll need to authenticate separately on first launch.", + "envVars": "Environment Variables", + "inheritedFromGlobal": "Inherited from global settings", + "overrideInProfile": "Override", + "envVarsHint": "Environment variables set when this profile launches.", + "hooks": "Hooks", + "hooksHint": "Global hooks inherited from ~/.claude/settings.json. Toggle off to disable for this profile.", + "launchConfig": "Launch Configuration", + "disableClaudeOverride": "Disable `claude` override (not recommended)", + "claudeOverrideDisabledHint": "`claude` will load with all {{count}} installed addon(s) — no profile filtering applied", + "claudeOverrideEnabledHint": "Profile controls which of the {{count}} addon(s) load into `claude` sessions", + "cliAliases": "CLI Aliases", + "addAliasesHint": "Add aliases to launch this profile from the terminal.", + "removeAlias": "Remove", + "aliasNamePlaceholder": "e.g. claude-research", + "defaultProfileDir": "Default (profile directory)", + "launchAction": "Launch Action", + "launchActionNone": "None", + "launchActionWorkflow": "/workflow", + "launchActionPrompt": "Custom prompt", + "aliasPromptPlaceholder": "Enter the prompt to send on launch...", + "aliasesSavedHint": "Aliases are saved to ~/.claudeworks/bin/. `Add to PATH` to use from any terminal.", + "runAliasHint": "Run any alias name from your terminal to launch this profile.", + "removeAsDefault": "Remove as Default Profile", + "setAsDefault": "Set as Default Profile", + "removeAsDefaultHint": "Clears default status. Running `claude` will fall back to vanilla Claude (no profile), and another profile can take the default slot.", + "setAsDefaultHint": "Makes this profile the default. Running `claude` will launch with this profile's plugins and settings.", + "launchPromptPlaceholder": "e.g. /workflow | summarise the repo", + "launchPromptHint": "Fires automatically when launching this profile (no alias invoked). Supports slash commands like `/workflow` or a free-form prompt. Leave empty to launch without an initial prompt.", + "customFlagsPlaceholder": "Additional flags, e.g. --max-turns 10", + "customFlagsHint": "Flags passed to `claude` when launching this profile", + "profileConfig": "Profile Config", + "openConfigDir": "Open Config Directory in Finder", + "configDirHint": "View this profile's assembled settings, plugins, and CLAUDE.md", + "duplicateAlias": "Duplicate — already used above" + }, + "descriptions": { + "default": "Your default profile. Running `claude` launches with these plugins and settings.", + "profileCreator": "Dedicated workspace for creating and managing ClaudeWorks profiles." + } +} diff --git a/src/i18n/locales/en/scripts.json b/src/i18n/locales/en/scripts.json new file mode 100644 index 0000000..9ddcde0 --- /dev/null +++ b/src/i18n/locales/en/scripts.json @@ -0,0 +1,48 @@ +{ + "listAddons": { + "notRunningUnderProfile": "Not running under a profile (no CLAUDE_CONFIG_DIR)", + "profilesJsonNotFound": "profiles.json not found", + "profileNotFound": "Profile not found: {{name}}", + "profile": "Profile", + "plugins": "Plugins", + "skills": "Skills", + "commands": "Commands", + "agents": "Agents", + "active": "{{count}} active", + "activeExcluded": "{{count}} active, {{excluded}} excluded", + "localItems": "Local (from {{dir}}/.claude/)" + }, + "writeProfile": { + "success": "Profile saved: {{name}}", + "error": "Failed to save: {{error}}" + }, + "writeTeam": { + "success": "Team saved: {{name}}", + "error": "Failed to save: {{error}}", + "nameRequired": "T_NAME is not set or empty", + "invalidName": "T_NAME contains invalid characters", + "membersRequired": "T_MEMBERS cannot be empty", + "duplicateProfiles": "Duplicate profile(s) in T_MEMBERS: {{profiles}}", + "profileNotExists": "Team member references profile \"{{name}}\" which does not exist" + }, + "installPlugins": { + "claudeNotFound": "Real claude binary not found. Is Claude Code installed?", + "installing": "Installing {{name}}...", + "success": "Installed: {{name}}", + "failed": "Failed to install {{name}}: {{error}}" + }, + "listLocalPlugins": { + "title": "Local Plugins" + }, + "listPluginItems": { + "missingId": "Missing plugin id argument", + "skills": "Skills", + "commands": "Commands", + "agents": "Agents" + }, + "inferProject": { + "analyzing": "Analyzing project...", + "detected": "Detected: {{type}}", + "unknown": "Unknown project type" + } +} diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json new file mode 100644 index 0000000..36aac30 --- /dev/null +++ b/src/i18n/locales/en/settings.json @@ -0,0 +1,119 @@ +{ + "dialog": { + "title": "Settings", + "appTitle": "App Settings", + "tabs": { + "global": "Global Config", + "projects": "Projects", + "plugins": "Plugins", + "prompts": "Prompts", + "health": "Health", + "statusbar": "Status Bar" + }, + "searchPluginsPlaceholder": "Search plugins...", + "browseCurated": "Browse curated", + "refreshCurated": "Refresh", + "featured": "Featured", + "installed": "Installed", + "sources": "Sources", + "bundle": "Bundle", + "anonymousAccess": "Anonymous access", + "browse": "Browse", + "createDefaultProfile": "Create default profile", + "searchCuratedSub": "Search curated marketplace" + }, + "tabs": { + "general": "General", + "globalClaudeMd": "Global CLAUDE.md", + "prompts": "Prompts", + "envVars": "Environment Variables", + "hooks": "Hooks", + "defaults": "Defaults", + "credentials": "Credentials", + "diagnostics": "Diagnostics", + "statusLine": "Status Line" + }, + "language": { + "label": "Language", + "description": "Display language for the application" + }, + "general": { + "fontSize": "Font size", + "theme": "Theme", + "textSize": "Text Size", + "binInPath": "BIN in PATH", + "addBinToPath": "Add to PATH", + "binInPathDescription": "ClaudeWorks bin directory is in your PATH", + "binNotInPath": "ClaudeWorks bin directory is not in your PATH", + "diagnostics": "Diagnostics", + "profilesDoctor": "Profiles Doctor", + "runCheck": "Run Check", + "running": "Running...", + "allChecksPassed": "All checks passed", + "clickRunCheck": "Click \"Run Check\" to verify config directories, symlinks, and settings.", + "appVersion": "App Version", + "configDirectory": "Config Directory", + "claudeHome": "Claude Home", + "summary": "Summary", + "health": "Health" + }, + "doctor": { + "title": "Doctor", + "profilesTitle": "Profiles Doctor", + "subtitleError": "The app couldn't load its data. Run a diagnostic to find and repair the issue.", + "subtitleNormal": "Check your profiles store and related config files for known issues.", + "runDiagnostics": "Run diagnostics", + "runRepair": "Run repair", + "detect": "Detect issues", + "repair": "Repair issues", + "noIssues": "No issues found", + "issuesFound": "{{count}} issue(s) found", + "fixed": "Fixed", + "notFixed": "Not fixed", + "healthy": "Healthy", + "detected": "Detected", + "unfixable": "Unfixable", + "skipped": "Skipped", + "repairedLabel": "Repairs applied. Reload the app to re-read the healed store.", + "reloadApp": "Reload app", + "openConfigFolder": "Open config folder", + "exportDiagnostics": "Export diagnostics", + "savedLabel": "Saved: {{path}}", + "applyRepairs": "Apply Repairs", + "applying": "Applying…", + "backup": "Backup:", + "runningDetect": "Running diagnostic checks…", + "runningRepair": "Applying repairs…", + "failed": "Doctor failed:" + }, + "statusLine": { + "title": "Status Bar Widgets", + "hint": "Click a widget on the left to configure it in the inspector. Drag to reorder. Drop in a section break to split a long bar into groups. Changes apply on the next Claude Code session restart.", + "configuredWidgets": "Configured widgets", + "global": "Global", + "masterColor": "Master color", + "fieldSeparator": "Field separator", + "sectionSeparator": "Section separator", + "inspector": "Inspector", + "sectionBreakHint": "Section breaks have no options — they just split widgets into groups.", + "selectWidgetHint": "Select a widget on the left to configure it.", + "addWidget": "+ Add widget", + "sectionBreak": "— Section break —", + "clearAll": "Clear all", + "clearAllConfirm": "Clear all widgets?", + "clearAllDesc": "Remove all widgets and reset to just the model. This can't be undone.", + "loadPreset": "Load preset ▾", + "saveCurrent": "Save current", + "configName": "Config name…", + "noOptions": "No options for this widget.", + "preview": "Preview", + "exampleData": "Example data:", + "realData": "Real data:", + "exampleHint": "model, context%, cost, uptime, lines, burn, and ctx-to-full use a mock session (Opus, 25% context, 30m, $0.50).", + "realHint": "time, git, cwd, 5h/7d usage, active profile, and plugin count are queried live.", + "dragBreak": "Drag break to reorder", + "removeBreak": "Remove break", + "reorder": "Reorder {{label}}", + "remove": "Remove {{label}}" + } +} diff --git a/src/i18n/locales/en/team.json b/src/i18n/locales/en/team.json new file mode 100644 index 0000000..c873089 --- /dev/null +++ b/src/i18n/locales/en/team.json @@ -0,0 +1,89 @@ +{ + "list": { + "title": "Teams", + "newTeam": "New team", + "searchPlaceholder": "Search teams...", + "emptyTitle": "No teams yet", + "emptyBody": "Teams group profiles into coordinated multi-agent sessions. Click + above to create a team.", + "members": "{{count}} member(s)", + "launchTitle": "Launch team \"{{name}}\"", + "launchLabel": "Launch", + "allTags": "All tags", + "addTag": "+ Tag", + "allProjects": "All projects", + "addProject": "+ Project", + "noMatches": "No matches", + "sortBy": { + "name": "A-Z", + "members": "Members", + "favourites": "Favourites" + }, + "experimentalBanner": "Experimental.", + "experimentalDesc": "Teams are actively evolving — behaviour and data shape may change between releases." + }, + "editor": { + "title": "Edit Team", + "newTitle": "New Team", + "save": "Save", + "discard": "Discard changes", + "name": "Name", + "description": "Description", + "members": "Members", + "addMember": "Add member", + "removeMember": "Remove member", + "role": "Role", + "instructions": "Instructions", + "isLead": "Lead", + "colour": "Colour", + "model": "Model", + "effortLevel": "Effort Level", + "tags": "Tags", + "projects": "Projects", + "customFlags": "Custom flags", + "createTeam": "Create Team", + "namePlaceholder": "Team name...", + "membersSubtitle": "{{count}} member(s)", + "previewMerge": "Preview Merge", + "deleteTeam": "Delete Team", + "availableProfiles": "Available Profiles", + "searchPlaceholder": "Search...", + "teamMembersHeader": "Team Members ({{count}})", + "dragToAdd": "Drag a profile here to add", + "noTeamSelected": "No team selected", + "chooseTeam": "Choose a team from the sidebar, or create a new one to get started.", + "deleteTitle": "Delete {{name}}?", + "deleteDescription": "This team will be permanently deleted.", + "envVarTitle": "Teams Environment Variable Required", + "envVarDescription": "Teams require the environment variable `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` to be set to 1. Where would you like to add it?", + "addGlobalSettings": "Add to Global Settings", + "addProjectLabel": "Add to Project: {{dir}}" + }, + "merge": { + "title": "Merge Preview", + "description": "This will merge the following profiles into a single team launch:", + "combinedPlugins": "Combined Plugins ({{count}})", + "noPlugins": "No plugins", + "members": "Members ({{count}})", + "from": "from {{profile}}", + "settings": "Settings ({{source}})", + "model": "Model: {{model}}", + "effort": "Effort: {{level}}", + "flags": "Flags: {{flags}}", + "defaultSettings": "Default settings", + "conflicts": "Conflicts ({{count}})" + }, + "member": { + "lead": "LEAD", + "missing": "Missing", + "pluginCount": "{{count}} plugin(s)", + "setAsLead": "Set as lead", + "removeFromTeam": "Remove {{name}} from team", + "rolePlaceholder": "e.g. Lead Researcher", + "instructionsPlaceholder": "Instructions for this agent in the team context...", + "goToProfile": "Go to {{name}}", + "dragToAdd": "Drag to add to team", + "inTeam": "In team ✓", + "addToTeam": "Add to team", + "addProfileToTeam": "Add {{name}} to team" + } +} diff --git a/src/i18n/locales/zh/common.json b/src/i18n/locales/zh/common.json new file mode 100644 index 0000000..8465f63 --- /dev/null +++ b/src/i18n/locales/zh/common.json @@ -0,0 +1,229 @@ +{ + "buttons": { + "save": "保存", + "cancel": "取消", + "delete": "删除", + "edit": "编辑", + "close": "关闭", + "back": "返回", + "next": "下一步", + "done": "完成", + "launch": "启动", + "newSession": "新建会话", + "viewRelease": "查看更新 ↗", + "showLess": "收起", + "showMore": "展开", + "viewAll": "查看全部 {{count}} 条", + "allTime": "全部时间", + "7days": "7 天", + "30days": "30 天", + "allProjects": "所有项目", + "import": "导入", + "overview": "概览", + "add": "添加", + "override": "覆盖", + "remove": "移除", + "addPlugin": "添加 Plugin", + "openInEditor": "在编辑器中打开 ↗", + "insertPrompt": "插入提示词", + "saveAsPrompt": "另存为提示词", + "addToPath": "添加到 PATH", + "openInFinder": "在 Finder 中打开配置目录", + "previewMerge": "预览合并", + "setAsLead": "设为负责人", + "confirm": "确认", + "retry": "重试", + "export": "导出", + "clearAll": "全部清除", + "reloadApp": "重新加载应用", + "create": "创建", + "refresh": "刷新", + "apply": "应用", + "update": "更新", + "runCheck": "运行检查", + "saveCurrent": "保存当前", + "loadPreset": "加载预设", + "addSource": "添加源", + "selectAll": "全选", + "uninstall": "卸载", + "install": "安装", + "reScan": "重新扫描", + "scan": "扫描", + "updateAll": "全部更新", + "applyRepairs": "应用修复", + "openProject": "打开项目", + "openClaudeDir": "打开 .claude/" + }, + "status": { + "loading": "加载中...", + "loadingAnalytics": "正在加载分析数据...", + "saving": "保存中...", + "saved": "已保存", + "error": "错误", + "success": "成功", + "justNow": "刚刚", + "minutesAgo": "{{count}} 分钟前", + "hoursAgo": "{{count}} 小时前", + "daysAgo": "{{count}} 天前", + "updateAvailable": "有可用更新:v{{version}}", + "current": "当前版本:v{{version}}", + "unsavedChanges": "未保存的更改", + "launchingEllipsis": "正在启动…", + "savedCheck": "✓ 已保存", + "lookingUp": "正在查找…", + "updating": "更新中...", + "removing": "移除中...", + "installing": "安装中...", + "adding": "添加中...", + "applying": "应用中...", + "refreshing": "刷新中...", + "scanning": "扫描中...", + "running": "运行中...", + "loadingConfig": "正在加载状态栏配置…", + "runningDiagnostics": "正在运行诊断检查…", + "applyingRepairs": "正在应用修复…", + "loadingCurated": "正在加载精选 Plugin...", + "loadingAvailable": "正在加载可用 Plugin...", + "loadingPluginList": "正在加载 Plugin 列表…", + "loadingReadme": "正在加载 README…", + "noReadme": "该仓库没有 README。", + "cannotResolveSource": "无法解析此条目的 GitHub 源。", + "savedToDownloads": "已保存到下载", + "updated": "已更新" + }, + "errors": { + "networkFailed": "网络错误", + "unknownError": "未知错误", + "saveFailed": "保存失败:", + "launchFailed": "启动失败:", + "teamLaunchFailed": "Team 启动失败", + "invalidName": "名称无效", + "nameRequired": "名称不能为空", + "nameAlreadyExists": "名称已存在", + "installationFailed": "安装失败", + "updateFailed": "更新失败", + "uninstallFailed": "卸载失败", + "doctorFailed": "诊断失败:", + "invalidJson": "JSON 无效", + "claudeNotFound": "未找到 claude 二进制文件。已尝试:{{tried}}。是否已安装 Claude Code?", + "refusingToLaunch": "拒绝启动:{{source}} 包含 shell 元字符 (; & | ` $ < >)。请移除后重试。", + "invalidProfileName": "Profile 名称无效:\"{{name}}\"。名称不得包含路径分隔符、\"..\" 或空字节。", + "invalidProfileNameOutside": "Profile 名称无效:\"{{name}}\" 解析到了配置目录之外。", + "profileNotFound": "未找到 Profile:\"{{name}}\"", + "profileNameAlreadyExists": "已存在名为 \"{{name}}\" 的 Profile" + }, + "emptyStates": { + "noResults": "无结果", + "noMatches": "无匹配", + "noItems": "暂无项目", + "selectPlugin": "选择一个 Plugin 查看详情", + "selectProject": "选择一个项目", + "noPluginsInstalled": "未安装 Plugin", + "noConfigurableItems": "无可配置项", + "noItemsYet": ".claude/ 中暂无项目。请在下方创建。", + "noProjectsImported": "未导入项目。点击 + 添加项目目录。", + "noEnvVars": "未配置环境变量。", + "noMarketplaceSources": "未注册 Marketplace 源", + "noPluginsFromSource": "该源没有 Plugin", + "noPromptsYet": "暂无提示词", + "selectPrompt": "选择一个提示词", + "noPluginsAvailable": "无可用 Plugin", + "noGlobalPlugins": "未安装全局 Plugin", + "noMatchingEntries": "无匹配条目", + "noCuratedAvailable": "暂无精选 Marketplace 或 Plugin" + }, + "sections": { + "favourites": "收藏夹", + "activeSessions": "活跃会话({{count}})", + "activity": "活动", + "topProjects": "热门项目", + "recentSessions": "最近会话", + "profileActivity": "Profile 活动" + }, + "stats": { + "sessions": "会话", + "messages": "消息", + "projects": "项目", + "profiles": "Profile", + "msgs": "{{count}} 条消息", + "project": "项目", + "profile": "Profile", + "date": "日期" + }, + "plurals": { + "plugin_one": "{{count}} 个 Plugin", + "plugin_other": "{{count}} 个 Plugin", + "session_one": "{{count}} 个会话", + "session_other": "{{count}} 个会话", + "message_one": "{{count}} 条消息", + "message_other": "{{count}} 条消息", + "item_one": "{{count}} 个项目", + "item_other": "{{count}} 个项目", + "profile_one": "{{count}} 个 Profile", + "profile_other": "{{count}} 个 Profile", + "team_one": "{{count}} 个 Team", + "team_other": "{{count}} 个 Team", + "member_one": "{{count}} 个成员", + "member_other": "{{count}} 个成员", + "install_one": "{{count}} 次安装", + "install_other": "{{count}} 次安装", + "match_one": "{{count}} 个匹配", + "match_other": "{{count}} 个匹配", + "kind_one": "{{count}} 种", + "kind_other": "{{count}} 种", + "update_one": "{{count}} 个更新", + "update_other": "{{count}} 个更新", + "issue_one": "{{count}} 个问题", + "issue_other": "{{count}} 个问题" + }, + "labels": { + "moreActions": "更多操作", + "chooseDirectory": "选择目录...", + "launchSettings": "启动设置", + "commaSeparated": "逗号分隔", + "charsLines": "{{chars}} 个字符 · {{lines}} 行", + "internal": "内部", + "global": "全局", + "managed": "已托管", + "lead": "负责人", + "missing": "缺失", + "dragToAdd": "拖拽以添加到 Team", + "inTeam": "已在 Team 中 ✓", + "all": "全部", + "enabled": "已启用", + "disabled": "已禁用", + "installed": "已安装", + "added": "已添加", + "preview": "预览", + "health": "健康", + "untitled": "无标题", + "enabledOnly": "仅已启用", + "showAll": "显示所有 Plugin", + "showEnabled": "仅显示此 Profile 中启用的 Plugin", + "name": "名称", + "source": "来源", + "popular": "热门", + "popularSort": "热门", + "allSources": "所有来源", + "from": "来自", + "by": "作者", + "addedDate": "添加于", + "plugins": "Plugin", + "members": "成员", + "defaultSettings": "默认设置", + "searchPlugins": "搜索 Plugin...", + "searchPrompts": "搜索提示词...", + "searchAvailablePlugins": "搜索可用 Plugin...", + "dangerousMode": "危险模式" + }, + "fields": { + "name": "名称", + "description": "描述", + "tags": "标签", + "role": "角色", + "instructions": "指令", + "directory": "目录", + "value": "值", + "voice": "语音" + } +} diff --git a/src/i18n/locales/zh/marketplace.json b/src/i18n/locales/zh/marketplace.json new file mode 100644 index 0000000..a4515de --- /dev/null +++ b/src/i18n/locales/zh/marketplace.json @@ -0,0 +1,58 @@ +{ + "discover": { + "title": "发现", + "searchPlaceholder": "搜索 Plugin、Skills、命令...", + "collections": "合集", + "plugins": "Plugin", + "skills": "Skills", + "commands": "命令", + "agents": "代理", + "noResults": "未找到结果", + "install": "安装", + "viewDetails": "查看详情", + "searchAvailable": "搜索可用 Plugin...", + "popular": "热门", + "sortAZ": "A-Z", + "source": "来源", + "allSources": "所有来源", + "installs": "{{count}} 次安装", + "noPlugins": "无可用 Plugin", + "noMatches": "无匹配", + "installed": "已安装" + }, + "detail": { + "by": "作者:{{author}}", + "version": "版本 {{version}}", + "install": "安装", + "installed": "已安装", + "viewOnGitHub": "在 GitHub 查看", + "readme": "说明文档", + "items": "项目", + "dependencies": "依赖", + "installTitle": "安装 {{name}}?", + "installDesc": "从 {{marketplace}} 安装 \"{{name}}\"?将全局下载并安装该 Plugin。", + "installing": "安装中...", + "source": "来源" + }, + "curated": { + "title": "精选", + "featured": "推荐", + "popular": "热门", + "new": "最新", + "categories": "分类", + "by": "作者", + "added": "添加于", + "source": "来源", + "plugins": "Plugin", + "pluginsInMarketplace": "此 Marketplace 中的 Plugin", + "loadingPluginList": "正在加载 Plugin 列表…", + "remove": "移除", + "install": "安装", + "installPlugin": "安装 Plugin", + "addMarketplace": "添加 Marketplace", + "adding": "添加中…", + "installing": "安装中…", + "addedLabel": "已添加", + "installedLabel": "已安装" + } +} diff --git a/src/i18n/locales/zh/plugin.json b/src/i18n/locales/zh/plugin.json new file mode 100644 index 0000000..79d43c8 --- /dev/null +++ b/src/i18n/locales/zh/plugin.json @@ -0,0 +1,309 @@ +{ + "list": { + "title": "Plugin", + "installed": "已安装", + "available": "可用", + "searchPlaceholder": "搜索 Plugin...", + "global": "全局", + "project": "项目", + "noMatches": "无匹配", + "noInstalled": "未安装 Plugin", + "updateAvailable": "有可用更新" + }, + "manager": { + "title": "Plugin 管理", + "install": "安装", + "uninstall": "卸载", + "update": "更新", + "updatesAvailable": "有可用更新", + "noPlugins": "未安装 Plugin", + "confirmUninstall": "确认卸载 Plugin \"{{name}}\"?", + "selectPlugin": "选择一个 Plugin 查看详情", + "updating": "更新中...", + "updateTo": "更新到 v{{version}}", + "removing": "移除中...", + "usedBy": "使用者", + "contents": "内容", + "skills": "Skills", + "agents": "代理", + "commands": "命令", + "hooks": "Hooks", + "mcpServers": "MCP 服务器", + "noConfigurable": "无可配置项", + "uninstallConfirmTitle": "卸载 {{name}}?", + "uninstallUsedBy": "该 Plugin 被 {{count}} 个 Profile 使用({{profiles}})。移除后它们将显示为不健康。", + "uninstallNotUsed": "该 Plugin 未被任何配置使用。" + }, + "picker": { + "title": "添加 Plugin", + "searchPlaceholder": "搜索 Plugin...", + "installing": "安装中...", + "installed": "已安装", + "enabledOnly": "仅已启用", + "showAll": "显示所有 Plugin", + "showEnabled": "仅显示此 Profile 中启用的 Plugin", + "globalPlugins": "全局 Plugin", + "frameworks": "框架", + "local": "本地", + "projectPlugins": "项目 Plugin", + "noGlobal": "未安装全局 Plugin", + "noProjectFor": "未在 {{dir}} 安装 Plugin" + }, + "manage": { + "title": "管理 Plugin", + "skills": "Skills", + "commands": "命令", + "agents": "代理", + "hooks": "Hooks", + "mcpServers": "MCP 服务器", + "enableAll": "全部启用", + "disableAll": "全部禁用" + }, + "marketplace": { + "title": "Marketplace", + "add": "添加 Marketplace", + "remove": "移除", + "update": "更新", + "updateAll": "全部更新", + "lastUpdated": "最后更新:{{date}}", + "installed": "已安装 Marketplace", + "addSource": "添加源", + "updateAllSources": "更新所有源", + "githubPlaceholder": "GitHub 仓库(如 owner/repo)", + "refreshSource": "刷新源", + "removeSource": "移除源", + "lastSynced": "上次同步 {{time}}", + "installedOfTotal": "{{installed}} / {{total}} 已安装" + }, + "mcp": { + "noServers": "未找到 MCP 服务器。安装提供 MCP 服务器的 Plugin,或在 ~/.claude.json 中配置服务器。", + "showingProjectMcps": "显示 `{{dir}}` 的项目 MCP", + "noDirSelected": "未选择目录 — 在顶部栏选择一个目录以查看项目特定的 MCP 服务器", + "fromPlugins": "来自 Plugin({{count}})", + "fromPluginsHint": "切换行会禁用此 Profile 的源 Plugin。", + "userLabel": "用户({{count}})`(~/.claude.json)`", + "userHint": "全局服务器。切换仅对此 Profile 禁用。", + "globalLabel": "全局", + "projectLabel": "项目 · {{dir}}({{count}})", + "projectTogglesHint": "切换按目录持久化。", + "disableMcpForProfile": "禁用此 Profile 的 MCP 服务器", + "enableMcpForProfile": "启用此 Profile 的 MCP 服务器", + "disableMcp": "禁用 MCP 服务器", + "enableMcp": "启用 MCP 服务器" + }, + "dialog": { + "tabs": { + "plugins": "Plugin", + "projects": "项目", + "prompts": "提示词", + "global": "全局", + "statusbar": "状态栏", + "health": "健康" + }, + "browse": "浏览", + "sources": "来源", + "installed": "已安装", + "defaultNudge": "运行 `claude` 将加载全部 {{count}} 个已安装 Plugin。", + "noDefaultProfile": "无默认 Profile。", + "createDefaultProfile": "创建默认 Profile", + "configureClaude": "配置 Claude", + "browseCurated": "浏览精选 Marketplace", + "searchCuratedSub": "搜索精选 Marketplace、Plugin、Skills、代理和命令。", + "searchPluginsPlaceholder": "搜索 Plugin、Skills、代理、命令…", + "refreshCurated": "刷新精选列表", + "featured": "推荐", + "collections": "合集", + "moreCollections": "+ {{count}} 个更多", + "showLess": "收起", + "marketplacePlugins": "Marketplace · {{count}} 个 Plugin", + "bundle": "套件", + "pluginsAvailable": "已安装 Marketplace 中的可用 Plugin", + "noResultsFor": "在 {{total}} 条目中未找到 \"{{query}}\" 的结果", + "matchSummary": "{{total}} 个匹配,{{kindCount}} 种类型", + "anonymousAccess": "匿名 GitHub 访问 — {{rateLimit}} 速率限制。", + "addToProfile": "添加到 PATH", + "itemTypes": { + "skill": "Skills", + "agent": "代理", + "command": "命令" + }, + "models": { + "opus": "Opus", + "sonnet": "Sonnet", + "haiku": "Haiku" + }, + "effortLevels": { + "low": "低", + "medium": "中", + "high": "高", + "xhigh": "极高", + "max": "最大" + }, + "terminal": { + "terminal": "Terminal.app", + "plain": "Plain tmux", + "none": "不使用 tmux" + }, + "claudeMd": "CLAUDE.md", + "noDefaultBanner": "运行 `claude` 将加载所有 {{count}} 个已安装的 Plugin。", + "invalidJson": "JSON 格式无效", + "removeMarketplace": "移除 Marketplace", + "add": "添加", + "install": "安装", + "adding": "添加中...", + "installing": "安装中...", + "usedByProfile": "此 {thing} 被 {count} 个 Profile 使用", + "itWillShow": "操作后它将显示为不健康。", + "theyWillShow": "操作后它们将显示为不健康。", + "dangerousMode": "危险模式" + }, + "project": { + "title": "项目", + "addProject": "添加项目", + "noProjects": "未导入项目。点击 + 添加项目目录。", + "importedProjects": "已导入项目", + "git": "Git", + "uncommittedChanges": "未提交的更改", + "directory": "目录", + "openProject": "打开项目", + "openClaudeDir": "打开 .claude/", + "claudeMd": "CLAUDE.md", + "insertPrompt": "插入提示词", + "skillsAgentsCommands": "Skills、代理和命令", + "noItems": ".claude/ 中暂无项目。请在下方创建。", + "skills": "Skills", + "agents": "代理", + "commands": "命令", + "mcpServers": "MCP 服务器", + "mcpServersHint": "编辑 .mcp.json — 项目级 MCP 服务器配置的原始 JSON。", + "projectSettings": "项目设置", + "projectSettingsHint": "保存到 .claude/settings.json — 应用于此目录的所有会话。", + "model": "模型", + "effortLevel": "努力程度", + "envVars": "环境变量", + "envVarsHint": "项目级环境变量覆盖此目录中启动会话的全局设置。", + "inheritedFromGlobal": "继承自全局设置", + "removeProject": "移除项目 {{name}}", + "selectProject": "选择一个项目", + "selectProjectDesc": "从列表中选择一个项目,或添加一个来管理其 CLAUDE.md、Skills、MCP 服务器和设置。", + "namePlaceholder": "名称...", + "contentPlaceholder": "{{type}} 内容...", + "instructionsPlaceholder": "Claude Code 的项目级指令...", + "openInDefaultEditor": "在默认编辑器中打开", + "global": "全局", + "override": "覆盖" + }, + "global": { + "globalConfig": "全局配置", + "openInFinder": "在 Finder 中打开", + "globalClaudeMd": "全局 CLAUDE.md", + "globalClaudeMdHint": "适用于所有 Claude Code 会话的指令,与配置无关。", + "globalClaudeMdPlaceholder": "所有 Claude Code 会话的全局指令...", + "defaultModelEffort": "默认模型和努力程度", + "defaultModelEffortHint": "当配置未指定时使用的回退值。", + "model": "模型", + "opusContext": "Opus 上下文", + "sonnetContext": "Sonnet 上下文", + "effortLevel": "努力程度", + "envVars": "环境变量", + "envVarsHint": "来自 ~/.claude/settings.json — 应用于所有会话。每 Profile 的环境变量覆盖这些。", + "defaultCliFlags": "默认 CLI 标志", + "defaultCliFlagsHint": "每次启动时传递给 `claude` 的标志。每 Profile 的标志追加在这些之后。", + "hooks": "Hooks", + "hooksHint": "响应 Claude Code 事件的 Shell 命令。保存到 ~/.claude/settings.json 并被所有 Profile 继承。", + "launchDefaults": "启动默认值", + "launchDefaultsHint": "启动弹窗使用的默认终端和 tmux 设置。", + "terminalApp": "终端应用", + "tmuxMode": "tmux 模式", + "tmuxNotInstalled": "tmux 未安装 — 默认不使用 tmux", + "systemDefault": "系统默认" + }, + "health": { + "credentials": "凭据", + "globalCredentials": "全局凭据", + "profileCredentials": "Profile 凭据", + "active": "有效", + "notFound": "未找到", + "storedInKeychain": "存储在 macOS 钥匙串中为 \"Claude Code-credentials\"", + "profileIssues": "Profile 问题", + "noMissingPlugins": "未检测到缺失 Plugin", + "staleProfiles": "过期 Profile(30+ 天)", + "neverLaunched": "从未启动", + "unusedPlugins": "未使用的 Plugin", + "unusedPluginsHint": "已安装但未被任何配置使用的 Plugin。", + "systemDiagnostics": "系统诊断", + "version": "版本", + "configDir": "配置目录", + "claudeHome": "Claude 主目录", + "profilesTeams": "配置 / Team", + "issues": "问题({{count}})", + "noIssuesDetected": "未检测到问题", + "loadingCredentialStatus": "正在加载凭据状态...", + "runningDiagnostics": "正在运行诊断..." + }, + "prompts": { + "title": "提示词", + "importPrompt": "导入提示词", + "newPrompt": "新建提示词", + "searchPrompts": "搜索提示词...", + "savedPrompts": "已保存提示词", + "noPromptsYet": "暂无提示词。点击 + 创建。", + "selectPrompt": "选择一个提示词", + "prompt": "提示词", + "namePlaceholder": "提示词名称...", + "descPlaceholder": "此提示词的用途...", + "addTag": "添加标签...", + "content": "内容", + "contentPlaceholder": "提示词内容 — 此文本将插入到 CLAUDE.md 编辑器中...", + "exportPrompt": "导出", + "reusableSnippets": "可复用的片段,可以拖入 Profile 指令或 CLAUDE.md 文件中。", + "pickOne": "从列表中选择一个来编辑,或创建新的提示词。", + "newPromptBtn": "+ 新建提示词", + "untitled": "无标题" + }, + "bulk": { + "title": "管理", + "profiles": "Profile({{count}})", + "teams": "Team({{count}})", + "noItems": "暂无{{type}}。", + "name": "名称", + "modelEffortPlugins": "模型 / 努力程度 / Plugin", + "members": "成员", + "selected": "已选择 {{count}} 个{{type}}", + "chooseAction": "选择操作...", + "deleteSelected": "删除所选", + "manageTags": "管理标签", + "manageProjects": "管理项目", + "setModel": "设置模型", + "setEffort": "设置努力程度", + "togglePlugin": "切换 Plugin", + "toggleAuth": "切换认证", + "addTag": "添加标签", + "removeTag": "移除标签", + "tagNamePlaceholder": "标签名称...", + "addProject": "添加项目", + "removeProject": "移除项目", + "noImportedProjects": "无已导入项目", + "chooseProject": "选择项目...", + "defaultClear": "默认(清除)", + "choosePlugin": "选择 Plugin...", + "enable": "启用", + "disable": "禁用", + "useDefaultAuth": "使用默认认证", + "separateAuth": "独立认证", + "applying": "应用中...", + "apply": "应用", + "deleteConfirm": "删除 {{count}} 个{{type}}?", + "deleteDescription": "将永久删除:{{names}}" + }, + "errors": { + "failedToLoadCurated": "加载精选 Plugin 失败", + "failedToRefreshCurated": "刷新精选 Plugin 失败", + "failedToAddMarketplace": "添加 Marketplace 失败", + "failedToRemoveMarketplace": "移除 Marketplace 失败", + "failedToRefreshMarketplace": "刷新 Marketplace 失败", + "failedToUpdateMarketplaces": "更新 Marketplace 失败", + "failedToLoadPlugins": "加载可用 Plugin 失败", + "failedToInstallPlugin": "安装 Plugin 失败" + } +} diff --git a/src/i18n/locales/zh/profile.json b/src/i18n/locales/zh/profile.json new file mode 100644 index 0000000..e97a3a3 --- /dev/null +++ b/src/i18n/locales/zh/profile.json @@ -0,0 +1,230 @@ +{ + "list": { + "title": "Profile", + "newProfile": "新建 Profile", + "searchPlaceholder": "搜索 Profile...", + "allTags": "所有标签", + "allProjects": "所有项目", + "addTag": "+ 标签", + "addProject": "+ 项目", + "pickProject": "选择项目…", + "sortBy": { + "name": "按名称", + "plugins": "按 Plugin 数", + "recent": "最近使用", + "favourites": "收藏夹" + }, + "defaultBadge": "默认", + "emptyTitle": "暂无 Profile", + "emptyBody": "Profile 可以保存 Claude Code 会话的 Plugin、Skills 和设置预设。点击上方 + 创建你的第一个 Profile。", + "noMatches": "无匹配结果", + "launchTitle": "启动 \"{{name}}\"", + "launchTitleInDir": "在 {{dir}} 中启动 \"{{name}}\"", + "healthBadge": "缺少 {{count}} 个 Plugin" + }, + "editor": { + "title": "编辑 Profile", + "newTitle": "新建 Profile", + "save": "保存", + "discard": "放弃更改", + "name": "名称", + "description": "描述", + "descriptionPlaceholder": "此 Profile 的用途", + "model": "模型", + "effortLevel": "努力程度", + "plugins": "Plugin", + "skills": "Skills", + "commands": "命令", + "agents": "代理", + "excludedItems": "已排除项目", + "customClaudeMd": "自定义 CLAUDE.md", + "workflow": "Workflow", + "launchPrompt": "启动提示词", + "tags": "标签", + "projects": "项目", + "directories": "目录", + "envVars": "环境变量", + "aliases": "别名", + "mcpServers": "MCP 服务器", + "launchFlags": "启动标志", + "dangerouslySkipPermissions": "跳过权限检查(危险)", + "verbose": "详细模式", + "customFlags": "自定义标志", + "useDefaultAuth": "使用默认认证", + "isDefault": "设为默认", + "disabledHooks": "已禁用的 Hooks", + "disabledMcpServers": "已禁用的 MCP 服务器", + "statusLine": "状态栏" + }, + "topBar": { + "launch": "启动", + "export": "导出", + "duplicate": "复制", + "delete": "删除", + "confirmDelete": "确认删除 Profile \"{{name}}\"?", + "overview": "概览", + "createProfile": "创建 Profile", + "namePlaceholder": "Profile 名称...", + "configureSubtitle": "为此 Profile 设置 Plugin 和 Skills", + "noPluginsEnabled": "未启用 Plugin", + "pluginsEnabled": "已启用 {{count}} 个 Plugin", + "importResult": "Profile 已导入为 \"{{name}}\"。\n\n需要安装 {{count}} 个 Plugin:\n{{plugins}}\n\n请从 配置 Claude > Plugin > 浏览 中安装。" + }, + "overview": { + "title": "Profile 概览", + "closeAriaLabel": "关闭概览", + "description": "此 Profile 启动时加载内容的摘要。点击类别查看详情。", + "enabledPlugins": "已启用 Plugin", + "enabledSkills": "已启用 Skills", + "enabledAgents": "已启用代理", + "enabledCommands": "已启用命令", + "enabledMcpServers": "已启用 MCP 服务器", + "itemsCount": "{{count}} 个项目", + "goToTab": "前往{{tab}}标签页 →", + "modelLabel": "模型:{{model}}", + "effortLabel": "努力程度:{{level}}" + }, + "emptyState": { + "noProfileSelected": "未选择 Profile", + "chooseProfile": "从侧边栏选择一个 Profile,或创建一个新的开始使用。" + }, + "defaultBanner": "这是你的默认 Profile。在任何终端运行 `claude` 都会使用这些 Plugin 和设置启动。只添加日常所需的内容。", + "health": { + "missingPlugins": "缺少 {{count}} 个 Plugin — 选择「添加 Plugin」在 Marketplace 中查找,或选择「移除」从配置中删除。", + "lookingUp": "正在查找…", + "addPlugin": "添加 Plugin", + "remove": "移除" + }, + "tabs": { + "projectItems": "项目内容", + "sections": "Profile 分区" + }, + "filter": { + "skillsPlaceholder": "按名称或 Plugin 搜索 Skills…", + "agentsPlaceholder": "按名称或 Plugin 筛选代理…", + "commandsPlaceholder": "按名称或 Plugin 搜索命令…" + }, + "noItems": "暂无可用{{tab}}。安装 Plugin 后可在此查看{{tab}}。", + "group": { + "expand": "展开 {{name}} 分组", + "collapse": "收起 {{name}} 分组", + "selectTitle": "全选此分组", + "deselectTitle": "取消全选此分组", + "none": "无", + "all": "全" + }, + "internal": "内部", + "local": { + "itemHeader": "{{type}}:{{name}}", + "selectDir": "选择项目目录", + "selectDirHint": "在顶部栏选择目录以查看项目特定的 Skills、代理和命令。", + "noItemsInDir": "在 {{dir}}/.claude/ 中未找到项目", + "noItemsHint": "将 Skills、代理或命令添加到项目的 `.claude/` 目录中,它们将在此处显示。", + "openClaudeDir": "打开 .claude/ 目录", + "itemsFromDir": "来自 `{{dir}}`/.claude/ 的项目 — 启动到此目录时会自动加载,独立于 Profile 设置。", + "addMoreItems": "添加更多项目内容", + "addMoreItemsHint": "将 Skills、代理或命令放入此项目的 `.claude/` 目录,它们将与 Profile 级别的项目一起显示。", + "openClaude": "打开 .claude/", + "editTitle": "编辑 {{name}}", + "sectionLabel": "{{type}}({{count}})", + "contentPlaceholder": "{{type}} 内容..." + }, + "instructions": { + "alwaysOn": "始终启用", + "claudeMdHint": "追加到 CLAUDE.md — Claude 每次对话都会读取。", + "onDemand": "按需调用", + "workflowHint": "按需调用 — 在会话中输入 `/workflow` 时运行。", + "workflowPlaceholder": "描述此 Profile 如何编排工具 — 例如「首先运行 code-explorer 代理,然后调用 systematic-debugging,最后生成项目符号报告。」", + "placeholder": "此 Profile 的附加指令...", + "charStats": "{{chars}} 个字符 · {{lines}} 行", + "variants": { + "title": "Workflow 变体", + "hint": "命名变体会成为 `/workflow-name` 命令。可选择限定到特定项目目录。", + "namePlaceholder": "名称", + "thisProjectOnly": "仅此项目", + "projectOnly": "仅 {{project}}", + "addVariant": "+ 添加变体", + "variantPlaceholder": "描述 {{name}} Workflow...", + "remove": "移除" + } + }, + "dialogs": { + "deleteTitle": "删除 Profile", + "deleteConfirm": "确定要删除 `{{name}}` 吗?", + "deleteDefaultWarning": "这是你的默认 Profile。删除后运行 `claude` 将加载所有已安装的 Plugin。", + "deleteCannotUndo": "这将删除 Profile 文件及其组装的配置目录。此操作不可撤销。", + "pluginNotFound": "未找到 Plugin", + "pluginNotFoundDesc": "`{{name}}` 在我们能看到的任何精选 Marketplace 中都不可用", + "pluginNotFoundSearched": "(已搜索 `{{id}}`)", + "pluginNotFoundRemove": "该 Plugin 可能已被移除、重命名,或仅存在于你未添加的私有 Marketplace 中。是否从配置中移除?", + "removeFromProfile": "从配置中移除", + "cannotSave": "无法保存", + "savePromptTitle": "另存为提示词", + "namePlaceholder": "我的提示词", + "descriptionPlaceholder": "可选描述", + "tagsPlaceholder": "标签 1, 标签 2, 标签 3" + }, + "settings": { + "sessionBehavior": "会话行为", + "defaultInheritGlobal": "默认(继承全局)", + "opusContextDefault": "1M(默认)", + "opusContextHint": "Opus 1M 上下文包含在你的计划中。", + "sonnetContextDefault": "200k(默认)", + "sonnetContextExtra": "1M — 按额外用量计费", + "sonnetContextHint": "Sonnet 1M 上下文按计划外的额外用量计费。", + "effortLow": "低", + "effortMedium": "中", + "effortHigh": "高", + "effortXhigh": "超高", + "effortMax": "最大", + "defaultVoice": "默认", + "enabledVoice": "已启用", + "disabledVoice": "已禁用", + "statusBar": "状态栏", + "overrideGlobalStatusBar": "为此 Profile 文件覆盖全局状态栏", + "statusBarOverrideHint": "此 Profile 文件使用自己的小组件配置,从全局状态栏初始化。在 配置 Claude → 状态栏 中所做的编辑仅适用于全局配置;每个 Profile 文件的覆盖独立持久化,并对此 Profile 启动的会话生效。", + "statusBarDefaultHint": "关闭时,通过此 Profile 启动的会话使用 配置 Claude → 状态栏 中的全局状态栏。", + "authentication": "认证", + "useDefaultAuth": "使用默认认证", + "authSharedHint": "此 Profile 文件与默认 Claude Code 安装共享凭据。", + "authOwnHint": "此 Profile 文件将使用自己的凭据。首次启动时需要单独认证。", + "envVars": "环境变量", + "inheritedFromGlobal": "从全局设置继承", + "overrideInProfile": "覆盖", + "envVarsHint": "此 Profile 启动时设置的环境变量。", + "hooks": "Hooks", + "hooksHint": "从 ~/.claude/settings.json 继承的全局 Hooks。关闭以禁用此 Profile 的 Hooks。", + "launchConfig": "启动 Profile 配置", + "disableClaudeOverride": "禁用 `claude` 覆盖(不推荐)", + "claudeOverrideDisabledHint": "`claude` 将加载全部 {{count}} 个已安装的 Plugin — 不应用配置过滤", + "claudeOverrideEnabledHint": "配置控制 {{count}} 个 Plugin 中的哪些加载到 `claude` 会话中", + "cliAliases": "CLI 别名", + "addAliasesHint": "添加别名以从终端启动此 Profile。", + "removeAlias": "移除", + "aliasNamePlaceholder": "例如 claude-research", + "defaultProfileDir": "默认(配置目录)", + "launchAction": "启动动作", + "launchActionNone": "无", + "launchActionWorkflow": "/workflow", + "launchActionPrompt": "自定义提示词", + "aliasPromptPlaceholder": "输入启动时发送的提示词...", + "aliasesSavedHint": "别名保存到 ~/.claudeworks/bin/。`添加到 PATH` 以从任何终端使用。", + "runAliasHint": "从终端运行任何别名即可启动此 Profile。", + "removeAsDefault": "移除默认 Profile", + "setAsDefault": "设为默认 Profile", + "removeAsDefaultHint": "清除默认状态。运行 `claude` 将回退到原版 Claude(无 Profile),其他 Profile 可以接管默认位置。", + "setAsDefaultHint": "将此 Profile 设为默认。运行 `claude` 将使用此 Profile 的 Plugin 和设置启动。", + "launchPromptPlaceholder": "例如 /workflow | 总结仓库", + "launchPromptHint": "启动此 Profile 时自动触发(不调用别名时)。支持 `/workflow` 等斜杠命令或自由格式提示词。留空则不带初始提示词启动。", + "customFlagsPlaceholder": "附加标志,例如 --max-turns 10", + "customFlagsHint": "启动此 Profile 时传递给 `claude` 的标志", + "profileConfig": "Profile 设置", + "openConfigDir": "在 Finder 中打开配置目录", + "configDirHint": "查看此 Profile 的组装设置、Plugin 和 CLAUDE.md", + "duplicateAlias": "重复 — 已在上方使用" + }, + "descriptions": { + "default": "你的默认 Profile。运行 `claude` 将使用这些 Plugin 和设置启动。", + "profileCreator": "用于创建和管理 ClaudeWorks Profile 的专用工作区。" + } +} diff --git a/src/i18n/locales/zh/scripts.json b/src/i18n/locales/zh/scripts.json new file mode 100644 index 0000000..3a1540b --- /dev/null +++ b/src/i18n/locales/zh/scripts.json @@ -0,0 +1,48 @@ +{ + "listAddons": { + "notRunningUnderProfile": "未在 Profile 下运行(无 CLAUDE_CONFIG_DIR)", + "profilesJsonNotFound": "未找到 profiles.json", + "profileNotFound": "未找到 Profile:{{name}}", + "profile": "Profile", + "plugins": "Plugin", + "skills": "Skills", + "commands": "命令", + "agents": "代理", + "active": "{{count}} 个已启用", + "activeExcluded": "{{count}} 个已启用,{{excluded}} 个已排除", + "localItems": "本地(来自 {{dir}}/.claude/)" + }, + "writeProfile": { + "success": "配置已保存:{{name}}", + "error": "保存失败:{{error}}" + }, + "writeTeam": { + "success": "Team 已保存:{{name}}", + "error": "保存失败:{{error}}", + "nameRequired": "T_NAME 未设置或为空", + "invalidName": "T_NAME 包含无效字符", + "membersRequired": "T_MEMBERS 不能为空", + "duplicateProfiles": "T_MEMBERS 中存在重复的 Profile:{{profiles}}", + "profileNotExists": "Team 成员引用的 Profile \"{{name}}\" 不存在" + }, + "installPlugins": { + "claudeNotFound": "未找到 claude 二进制文件。是否已安装 Claude Code?", + "installing": "正在安装 {{name}}...", + "success": "已安装:{{name}}", + "failed": "安装失败 {{name}}:{{error}}" + }, + "listLocalPlugins": { + "title": "本地 Plugin" + }, + "listPluginItems": { + "missingId": "缺少 Plugin ID 参数", + "skills": "Skills", + "commands": "命令", + "agents": "代理" + }, + "inferProject": { + "analyzing": "正在分析项目...", + "detected": "检测到:{{type}}", + "unknown": "未知项目类型" + } +} diff --git a/src/i18n/locales/zh/settings.json b/src/i18n/locales/zh/settings.json new file mode 100644 index 0000000..136c255 --- /dev/null +++ b/src/i18n/locales/zh/settings.json @@ -0,0 +1,119 @@ +{ + "dialog": { + "title": "设置", + "appTitle": "应用设置", + "tabs": { + "global": "全局配置", + "projects": "项目", + "plugins": "Plugin", + "prompts": "提示词", + "health": "健康", + "statusbar": "状态栏" + }, + "searchPluginsPlaceholder": "搜索 Plugin...", + "browseCurated": "浏览精选", + "refreshCurated": "刷新", + "featured": "推荐", + "installed": "已安装", + "sources": "来源", + "bundle": "套件", + "anonymousAccess": "匿名访问", + "browse": "浏览", + "createDefaultProfile": "创建默认 Profile", + "searchCuratedSub": "搜索精选 Marketplace" + }, + "tabs": { + "general": "通用", + "globalClaudeMd": "全局 CLAUDE.md", + "prompts": "提示词", + "envVars": "环境变量", + "hooks": "Hooks", + "defaults": "默认值", + "credentials": "凭据", + "diagnostics": "诊断", + "statusLine": "状态栏" + }, + "language": { + "label": "语言", + "description": "应用程序的显示语言" + }, + "general": { + "fontSize": "字体大小", + "theme": "主题", + "textSize": "文字大小", + "binInPath": "BIN 路径", + "addBinToPath": "添加到 PATH", + "binInPathDescription": "ClaudeWorks bin 目录已在 PATH 中", + "binNotInPath": "ClaudeWorks bin 目录不在 PATH 中", + "diagnostics": "诊断", + "profilesDoctor": "Profile 诊断工具", + "runCheck": "运行检查", + "running": "运行中...", + "allChecksPassed": "所有检查通过", + "clickRunCheck": "点击“运行检查”以验证配置目录、符号链接和设置。", + "appVersion": "应用版本", + "configDirectory": "配置目录", + "claudeHome": "Claude 主目录", + "summary": "概要", + "health": "健康" + }, + "doctor": { + "title": "诊断工具", + "profilesTitle": "Profile 诊断工具", + "subtitleError": "应用无法加载数据。运行诊断以查找并修复问题。", + "subtitleNormal": "检查你的配置存储和相关 Profile 是否有已知问题。", + "runDiagnostics": "运行诊断", + "runRepair": "运行修复", + "detect": "检测问题", + "repair": "修复问题", + "noIssues": "未发现问题", + "issuesFound": "发现 {{count}} 个问题", + "fixed": "已修复", + "notFixed": "未修复", + "healthy": "健康", + "detected": "已检测", + "unfixable": "无法修复", + "skipped": "已跳过", + "repairedLabel": "修复已应用。重新加载应用以重新读取修复后的存储。", + "reloadApp": "重新加载应用", + "openConfigFolder": "打开 Profile 夹", + "exportDiagnostics": "导出诊断", + "savedLabel": "已保存:{{path}}", + "applyRepairs": "应用修复", + "applying": "正在应用…", + "backup": "备份:", + "runningDetect": "正在运行诊断检查…", + "runningRepair": "正在应用修复…", + "failed": "诊断失败:" + }, + "statusLine": { + "title": "状态栏组件", + "hint": "点击左侧组件在检查器中配置。拖拽重新排列。放入分隔符将长栏分成小组。更改将在下次 Claude Code 会话重启时生效。", + "configuredWidgets": "已配置组件", + "global": "全局", + "masterColor": "主颜色", + "fieldSeparator": "字段分隔符", + "sectionSeparator": "节分隔符", + "inspector": "检查器", + "sectionBreakHint": "分隔符没有选项 — 它们只是将组件分成组。", + "selectWidgetHint": "选择左侧的组件以进行配置。", + "addWidget": "+ 添加组件", + "sectionBreak": "— 分隔符 —", + "clearAll": "全部清除", + "clearAllConfirm": "清除所有组件?", + "clearAllDesc": "移除所有组件并重置为仅模型。此操作无法撤销。", + "loadPreset": "加载预设 ▾", + "saveCurrent": "保存当前", + "configName": "Profile 名称…", + "noOptions": "此组件没有选项。", + "preview": "预览", + "exampleData": "示例数据:", + "realData": "真实数据:", + "exampleHint": "模型、上下文%、成本、运行时间、行数、消耗率和上下文满期使用模拟会话(Opus,25% 上下文,30 分钟,$0.50)。", + "realHint": "时间、Git、工作目录、5 小时/7 天使用量、活跃配置和 Plugin 数量为实时查询。", + "dragBreak": "拖拽分隔符以重新排列", + "removeBreak": "移除分隔符", + "reorder": "重新排列 {{label}}", + "remove": "移除 {{label}}" + } +} diff --git a/src/i18n/locales/zh/team.json b/src/i18n/locales/zh/team.json new file mode 100644 index 0000000..130dc28 --- /dev/null +++ b/src/i18n/locales/zh/team.json @@ -0,0 +1,89 @@ +{ + "list": { + "title": "Team", + "newTeam": "新建 Team", + "searchPlaceholder": "搜索 Team...", + "emptyTitle": "暂无 Team", + "emptyBody": "Team 可以同时启动多个 Profile,并协调各自的角色。点击上方 + 创建你的第一个 Team。", + "members": "{{count}} 个成员", + "launchTitle": "启动 Team \"{{name}}\"", + "launchLabel": "启动", + "allTags": "所有标签", + "addTag": "+ 标签", + "allProjects": "所有项目", + "addProject": "+ 项目", + "noMatches": "无匹配结果", + "sortBy": { + "name": "A-Z", + "members": "成员", + "favourites": "收藏夹" + }, + "experimentalBanner": "实验性功能。", + "experimentalDesc": "Team 功能正在积极开发中 — 行为和数据结构可能在版本间发生变化。" + }, + "editor": { + "title": "编辑 Team", + "newTitle": "新建 Team", + "save": "保存", + "discard": "放弃更改", + "name": "名称", + "description": "描述", + "members": "成员", + "addMember": "添加成员", + "removeMember": "移除成员", + "role": "角色", + "instructions": "指令", + "isLead": "负责人", + "colour": "颜色", + "model": "模型", + "effortLevel": "努力程度", + "tags": "标签", + "projects": "项目", + "customFlags": "自定义标志", + "createTeam": "创建 Team", + "namePlaceholder": "Team 名称...", + "membersSubtitle": "{{count}} 个成员", + "previewMerge": "预览合并", + "deleteTeam": "删除 Team", + "availableProfiles": "可用配置", + "searchPlaceholder": "搜索...", + "teamMembersHeader": "Team 成员({{count}})", + "dragToAdd": "拖拽 Profile 到此处添加", + "noTeamSelected": "未选择 Team", + "chooseTeam": "从侧边栏选择一个 Team,或创建一个新的开始使用。", + "deleteTitle": "删除 {{name}}?", + "deleteDescription": "此 Team 将被永久删除。", + "envVarTitle": "需要 Team 环境变量", + "envVarDescription": "Team 需要将环境变量 `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` 设置为 1。你想添加到哪里?", + "addGlobalSettings": "添加到全局设置", + "addProjectLabel": "添加到项目:{{dir}}" + }, + "merge": { + "title": "合并预览", + "description": "以下 Profile 将合并为一次 Team 启动:", + "combinedPlugins": "合并 Plugin({{count}})", + "noPlugins": "无 Plugin", + "members": "成员({{count}})", + "from": "来自 {{profile}}", + "settings": "设置({{source}})", + "model": "模型:{{model}}", + "effort": "努力程度:{{level}}", + "flags": "标志:{{flags}}", + "defaultSettings": "默认设置", + "conflicts": "冲突({{count}})" + }, + "member": { + "lead": "负责人", + "missing": "缺失", + "pluginCount": "{{count}} 个 Plugin", + "setAsLead": "设为负责人", + "removeFromTeam": "从 Team 中移除 {{name}}", + "rolePlaceholder": "例如 首席研究员", + "instructionsPlaceholder": "此代理在 Team 上下文中的指令...", + "goToProfile": "前往 {{name}}", + "dragToAdd": "拖拽以添加到 Team", + "inTeam": "已在 Team 中 ✓", + "addToTeam": "添加到 Team", + "addProfileToTeam": "将 {{name}} 添加到 Team" + } +} diff --git a/src/i18n/node.ts b/src/i18n/node.ts new file mode 100644 index 0000000..9f8bfc9 --- /dev/null +++ b/src/i18n/node.ts @@ -0,0 +1,35 @@ +import i18n from 'i18next'; +import Backend from 'i18next-fs-backend'; +import path from 'path'; + +export function detectSystemLang(): string { + const envLang = process.env.CLAUDEWORKS_LANG || process.env.LANG || process.env.LC_ALL || ''; + if (envLang.startsWith('zh')) return 'zh'; + return 'en'; +} + +let initialized = false; + +export async function initNodeI18n(lang?: string): Promise { + if (initialized) return i18n; + + await i18n.use(Backend).init({ + lng: lang || detectSystemLang(), + fallbackLng: 'en', + defaultNS: 'scripts', + ns: ['common', 'scripts', 'profile', 'team', 'plugin', 'settings'], + backend: { + loadPath: path.join(__dirname, 'locales/{{lng}}/{{ns}}.json'), + }, + interpolation: { escapeValue: false }, + }); + + initialized = true; + return i18n; +} + +export function changeNodeLang(lang: string): void { + i18n.changeLanguage(lang); +} + +export { i18n }; diff --git a/src/ui/components/AppSettingsDialog.tsx b/src/ui/components/AppSettingsDialog.tsx index ba8b61c..918f6af 100644 --- a/src/ui/components/AppSettingsDialog.tsx +++ b/src/ui/components/AppSettingsDialog.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect, useRef } from "react"; +import { useTranslation } from "react-i18next"; interface DiagResult { version: string; @@ -31,6 +32,7 @@ interface Props { } export function AppSettingsDialog({ onClose, onOpenDoctor }: Props) { + const { t, i18n } = useTranslation(["settings", "common"]); const [scale, setScale] = useState(1); const [theme, setTheme] = useState("dark"); const [diag, setDiag] = useState(null); @@ -93,7 +95,7 @@ export function AppSettingsDialog({ onClose, onOpenDoctor }: Props) { onClick={(e) => e.stopPropagation()} >
- App Settings + {t("dialog.appTitle")} + ))} +
+ + {/* Diagnostics */}
- Diagnostics + {t("general.diagnostics")}
-
{diag ? (
- +
{diag.version}
- +
{diag.configDir}
- +
{diag.claudeHome}
- +
{diag.profileCount} profile{diag.profileCount !== 1 ? "s" : ""}, {diag.teamCount} team{diag.teamCount !== 1 ? "s" : ""}
- + {diag.issues.length === 0 ? ( -
All checks passed
+
{t("general.allChecksPassed")}
) : (
{diag.issues.map((issue, i) => ( @@ -216,7 +245,7 @@ export function AppSettingsDialog({ onClose, onOpenDoctor }: Props) {
) : ( -
Click "Run Check" to verify config directories, symlinks, and settings.
+
{t("general.clickRunCheck")}
)}
diff --git a/src/ui/components/BulkManageDialog.tsx b/src/ui/components/BulkManageDialog.tsx index c2bff1c..2621f41 100644 --- a/src/ui/components/BulkManageDialog.tsx +++ b/src/ui/components/BulkManageDialog.tsx @@ -1,7 +1,20 @@ import React, { useState, useEffect, useRef, useMemo } from "react"; +import { useTranslation } from "react-i18next"; import type { Profile, Team, PluginWithItems } from "../../electron/types"; import { ConfirmDialog } from "./shared/ConfirmDialog"; +// ─── Translate known profile descriptions at display time ─────────────────── + +function translateDescription(desc: string, t: any): string { + if (desc === "Your default profile. Running `claude` launches with these plugins and settings.") { + return t("profile:descriptions.default"); + } + if (desc === "Dedicated workspace for creating and managing ClaudeWorks profiles.") { + return t("profile:descriptions.profileCreator"); + } + return desc; +} + function ItemCheckbox({ checked, onChange, label }: { checked: boolean; onChange: () => void; label?: string }) { return (
(defaultTab); const [selectedProfiles, setSelectedProfiles] = useState>(new Set()); const [selectedTeams, setSelectedTeams] = useState>(new Set()); @@ -252,21 +266,21 @@ export function BulkManageDialog({ }; const profileActions: { value: BulkAction; label: string }[] = [ - { value: "none", label: "Choose action..." }, - { value: "delete", label: "Delete selected" }, - { value: "tags", label: "Manage tags" }, - { value: "projects", label: "Manage projects" }, - { value: "model", label: "Set model" }, - { value: "effort", label: "Set effort level" }, - { value: "plugin", label: "Toggle plugin" }, - { value: "auth", label: "Toggle auth" }, + { value: "none", label: t("bulk.chooseAction") }, + { value: "delete", label: t("bulk.deleteSelected") }, + { value: "tags", label: t("bulk.manageTags") }, + { value: "projects", label: t("bulk.manageProjects") }, + { value: "model", label: t("bulk.setModel") }, + { value: "effort", label: t("bulk.setEffort") }, + { value: "plugin", label: t("bulk.togglePlugin") }, + { value: "auth", label: t("bulk.toggleAuth") }, ]; const teamActions: { value: BulkAction; label: string }[] = [ - { value: "none", label: "Choose action..." }, - { value: "delete", label: "Delete selected" }, - { value: "tags", label: "Manage tags" }, - { value: "projects", label: "Manage projects" }, + { value: "none", label: t("bulk.chooseAction") }, + { value: "delete", label: t("bulk.deleteSelected") }, + { value: "tags", label: t("bulk.manageTags") }, + { value: "projects", label: t("bulk.manageProjects") }, ]; const actions = activeTab === "profiles" ? profileActions : teamActions; @@ -298,13 +312,13 @@ export function BulkManageDialog({ className={`manage-dialog-tab${activeTab === "profiles" ? " active" : ""}`} onClick={() => setActiveTab("profiles")} > - Profiles ({profiles.length}) + {t("bulk.profiles", { count: profiles.length })}