cc-zh-watcher
v0.3.3
Published
Side-channel Chinese ↔ English bridge for Claude Code (single-watcher TUI). Type Chinese, Claude sees English; Claude replies in English, you see Chinese.
Maintainers
Readme
cc-zh-watcher
A side-channel Chinese ↔ English bridge for Claude Code. Type Chinese, Claude sees English. Claude replies in English, you see Chinese.
让中文母语开发者用 Claude Code 时,输入和输出都是中文,但 Claude 内部全程只看英文 —— 节省 token、对齐英文工具生态、提升复杂推理质量。
为什么需要它
Claude Code 在英文场景表现最好:tokenizer 对英文更紧凑(中文一句话约是英文 2× token)、内部系统 prompt 全是英文、复杂推理在英文上略胜一筹。但你想用中文。
cc-zh-watcher 是一个旁路 TUI 翻译工具:
- 你输的中文 → 自动翻译成英文 → 通过
/i命令喂给 Claude Code - Claude 的英文回复(text、thinking、AskUserQuestion、TodoWrite 计划、Agent 派遣等所有结构化内容)→ 自动翻译成中文 → 在 watcher 里逐字流式显示
- 不翻译的内容(Bash / Read / Edit / Write 等 tool 调用 + 它们的输出)→ 也按 Claude Code 同款样式显示出来 —— watcher 一个窗口里就有完整上下文
不动 Claude Code 的 TUI(菜单导航、文件选择器、权限对话框照常用)。
┌── Terminal 1:你的 Claude Code ──┐ ┌── Terminal 2:cc-zh-watcher TUI ──────────────┐
│ │ │ ● 我先看一下文件结构。 │
│ > /i ↵ │ │ │
│ ↑ 这条命令读 user-input.md │ │ ● Bash(ls -la /workspace) │
│ │ │ ⎿ total 168 │
│ Claude 用英文流式回复… │ │ drwxr-xr-x 18 sheldon staff │
│ │ │ … +12 lines │
│ [菜单 / 选项 / 权限对话框 │ │ │
│ 都在这个窗口操作] │ │ ● auth.py 的 42 行有个 bug。 │
│ │ │ │
│ │ │ ┌── StatusBar ───────────────────────────────┐ │
│ │ │ │ session 3f59...8985 · 24 turns · 5m ago │ │
│ │ │ │ ctx: on (123 chars) · api: ✓ 1.2s · 234 t │ │
│ │ │ └────────────────────────────────────────────┘ │
│ │ │ ┌── 中文输入 ──────────────────────────────┐ │
│ │ │ │ > 再加上对应的 PoP 校验| │ │
│ │ │ └────────────────────────────────────────────┘ │
└───────────────────────────────────┘ └────────────────────────────────────────────────┘安装
npm install -g cc-zh-watcher需要 Node.js ≥ 20。无其他依赖。
快速开始(3 步)
1. 在你项目里启动 Claude Code
cd <你的项目目录>
claude2. 另一个终端跑 watcher
cc-zh-watcher第一次启动会自动引导:
- 没配过 API key → 内联提问 4 个字段(
api_key/base_url/model/timeout_seconds),写到~/.config/cc-zh-watcher/config.json(自动 0o600 权限)。任何 OpenAI 兼容 endpoint 都行(OpenAI、DeepSeek、Together、Groq、本地 vLLM 等) - 让你选要监听哪个 Claude Code session(picker 列出所有项目的所有 session,按最近活跃排序;不限于当前目录)
- 选完后,watcher 从 session 的 JSONL 读出它对应的项目目录,静默在那里装
/islash command(不再问 Y/n —— 如果已存在就跳过,stderr 打印一行✓ installed /i …)
之后启动直接进 watcher,不再问。
3. 开始用中文聊天
在 watcher 里输中文 + 回车 → 翻译完成立刻写入 user-input.md。
切到 Claude Code 终端 → 输 /i + 回车 → Claude 收到英文 prompt 开始处理。
Claude 用英文流式回复时,watcher 同步翻译并显示中文,同时把 Bash / Edit / Read 等 tool 调用按 CC 同款样式显示出来。
macOS Claude 桌面应用用户:桌面应用不支持 custom slash command(
/i不可用)。watcher 检测到 session 来自桌面应用(cwd 在<repo>/.claude/worktrees/<name>/下)时会自动改提示成「粘贴@.claude/cache/user-input.md到 Claude 桌面应用」—— 桌面应用支持@mention语法把文件内容内联进 prompt。watcher 还提供Ctrl+Y一键复制这条命令、Ctrl+B一键复制英文翻译。想跳过引导直接编辑配置:
~/.config/cc-zh-watcher/config.json,schema 见 配置文件 段。要再次配置 endpoint,直接Ctrl+E进 watcher 设置面板。
翻译输出长什么样
每条内容前面有一个带颜色的 ●,颜色标识 kind:
| Bullet 颜色 | Kind | 显示内容 |
|---|---|---|
| 白 | text | Claude 的中文回复(最常见)|
| 紫 | thinking | Claude 的推理过程(默认关)|
| 黄 | question | AskUserQuestion 多选题 |
| 蓝 | todo | TodoWrite 计划列表 |
| 绿 | agent | Agent / Task 子代理派遣 |
| 青 | tool_use | Bash / Read / Edit 等 tool 调用 |
| (无) | tool_result | tool 输出(用 ⎿ 前缀缩进显示)|
实际样子:
● 我先看一下文件结构。
● Bash(ls -la /workspace)
⎿ total 168
drwxr-xr-x 18 sheldon staff
-rw-r--r-- 1 sheldon staff 257 May 10 08:13 .gitignore
… +12 lines
● Read(src/app.tsx, limit=50)
⎿ /**
* Top-level ink application.
*/
import React, { useState, useEffect, useRef } from "react";
… +47 lines
● 现在我看清楚结构了。先做以下几步:
● 📝 计划更新:
✓ 探查目录 ← 完成(绿 ✓ + 删除线 + dim)
⟳ 重构 translate.ts ← 进行中(黄 ⟳)
○ 跑测试 ← 待办(dim ○)
● 🤖 子代理 [claude-code-guide]:
任务: 研究 SSE 流式接入方式
指令:
请查阅最新文档,回答以下问题:
1. Claude Code 是否原生支持 stream:true...
● ❓ 提问:
改 translate.ts 还是新建独立文件?
① 改 translate.ts 接受 onDelta 回调(推荐)
最小侵入,调用方决定要不要流式
② 拆出独立 streamTranslate 函数
分层更清晰但调用面变两倍流式中:黄色光标 ▎ 在内容末尾,表示还在翻译。
设置面板(Ctrl+E)
按 Ctrl+E 弹出设置面板,所有配置都在一处:
┌── Settings (↑/↓ select · Enter edit · Esc save & close) ─────────────┐
│ ▸ session: 3f59…8985 (24 turns · active 5m ago; Enter 切换) │
│ │
│ api_key: sk-deep***ce91 │
│ base_url: https://api.deepseek.com/v1 │
│ model: deepseek-v4-pro │
│ timeout_seconds: 90 │
│ │
│ ── Toggles ── │
│ notify_on_complete: on (终端响铃;Enter 切换) │
│ translate_thinking: off (默认关;Enter 切换) │
│ translate_todos: on (译 TodoWrite 计划;Enter 切换) │
│ translate_agent: on (译 Agent/Task 子代理;Enter 切换) │
│ context_enabled: on (zh→en 用 Claude 上一条作 context;…) │
│ show_tool_calls: on (显示原始 tool 调用 + 结果(CC 同款);…) │
│ │
│ ── User prompt (empty = use default) ── │
│ user.zh-to-en: (empty — using default) │
│ user.zh-to-en-w/ctx: (empty — using default) │
│ user.en-to-zh: 用您而不是你。错误信息保留英文。 │
│ │
│ ── Default prompt (read-only) ── │
│ default.zh-to-en: You are a professional transla… │
│ default.zh-to-en-w/ctx: You are a professional transla… │
│ default.en-to-zh: You are a professional transla… │
└───────────────────────────────────────────────────────────────────────┘Session 行
Enter弹 SessionPicker,热切换 session(不用退 watcher 重启)- 切换后 scrollback 写一条 "已切换 session: A… → B…" 分隔条
API 字段(4 个)
- 单行 TextInput,Enter 进入编辑、Enter 提交、Esc 弃改
- 校验:
api_key非空、base_url是http(s)://开头、timeout_seconds是正整数
Toggles(6 个,Enter 切换)
| Toggle | 默认 | 说明 |
|---|:---:|---|
| notify_on_complete | on | Claude 一个 turn 结束(stop_reason=end_turn)且 en→zh 翻译队列耗尽时响一次终端 bell(\x07)。中间 tool 调用或 thinking 不响 |
| translate_thinking | off | 译 Claude 的 thinking 块(Opus 4.7 多为 redacted;空块自动跳过)|
| translate_todos | on | 译 TodoWrite 计划项(按当前状态自动选 content 或 activeForm)|
| translate_agent | off | 译 Agent/Task 子代理派遣(description + prompt)。默认 off —— Agent 派遣的 prompt 经常多段、译成中文有上百行,但 Claude Code 自身 UI 也不显示,所以 watcher 默认也跟着隐藏。需要审查派给子代理的 prompt 时再开 |
| context_enabled | on | zh→en 翻译时用 Claude 上一条 text 作消歧 context |
| show_tool_calls | session-aware ¹ | 显示原始 tool 调用 + 结果(Bash/Read/Edit 等,CC 同款样式)|
¹ show_tool_calls 默认值取决于 session 类型:CLI session 默认 on,macOS Claude 桌面应用 session 默认 off(桌面应用界面里已经能看到 tool 调用,watcher 再渲染一遍是噪音)。在 config 里显式写 "show_tool_calls": true/false 始终覆盖这个 session-aware 默认值。
User prompt(3 个,每翻译方向一个)
- Enter 弹满屏多行编辑器,Ctrl+S 保存、Esc 取消
- 留空 = 用默认 prompt
Default prompt(3 个,只读)
- Enter 弹满屏只读查看器,看完整内置 base prompt
保存
Esc在导航态 = 保存所有改动 + 关闭面板- 改完即热生效,不用重启 watcher
字段校验失败显示在面板底部。
CLI 命令
cc-zh-watcher 启动 TUI(首次自动引导 API key + /i 安装)
cc-zh-watcher --pick 启动 TUI,强制弹 session picker
cc-zh-watcher --session <uuid|前缀> 启动 TUI,直接指定 session(≥4 字符前缀)
cc-zh-watcher --config <path> 指定 config 文件
cc-zh-watcher uninstall 移除 /i 命令(可选清缓存:--wipe-cache)
cc-zh-watcher sessions 列出本项目所有 session
cc-zh-watcher history [opts] 查看 / 搜索翻译历史
--grep <pat> 子串过滤
--tail N 最后 N 条(默认 20)
--direction <dir> zh-to-en | en-to-zh
--json 机器可读
cc-zh-watcher prompts show [dir] 打印当前生效的 prompt(dir 省略 = 全部 3 个)
--base 忽略 user override,只看内置
cc-zh-watcher -v / --version
cc-zh-watcher -h / --help0.1.0 里的
cc-zh-watcher configure和cc-zh-watcher install子命令在 0.2.0 删掉了 —— 第一次启动 watcher 自动引导这两步,之后想改用Ctrl+E进设置面板。
TUI 键位
主界面(极简化 —— 之前的 Ctrl+T/P/N/L 都搬进 settings 了):
| 键 | 动作 |
|---|---|
| Enter | 翻译并发送(pre-flight check 触发警告时,再按一次 Enter 确认)|
| Ctrl+E | 打开设置面板 |
| Ctrl+Y | 复制「触发命令」到系统剪贴板:CLI 上是 /i,桌面应用 session 上是 @.claude/cache/user-input.md |
| Ctrl+B | 复制最近一次英文翻译到系统剪贴板(任意场合粘贴用)|
| Esc | 清空输入 + 已显示结果 + 错误状态 |
| Ctrl+C | 退出 watcher |
Ctrl+Y/Ctrl+B在没有翻译可复制时不动作。剪贴板调用走系统原生工具(macOSpbcopy/ Windowsclip/ Linuxxclip)—— Linux 上若没装 xclip / wl-copy,状态栏会提示。
Session picker(Ctrl+E → Enter on session row,或首次启动 / --pick):
| 键 | 动作 |
|---|---|
| ↑ / ↓ | 选 session |
| Enter | 确认 |
| / | 模糊搜索 |
| Esc | 取消 |
设置面板:
| 键 | 动作 |
|---|---|
| ↑ / ↓ | 选字段 |
| Enter(导航态) | 进入编辑(API 字段)/ 切换(toggle 行)/ 弹 SessionPicker(session 行)/ 弹多行编辑器(user prompt)/ 弹只读查看器(default prompt)|
| Enter(编辑态) | 提交(仅 API 字段;多行编辑器用 Ctrl+S)|
| Esc(编辑态) | 弃改回到导航态 |
| Esc(导航态) | 保存所有改动 + 关闭面板 |
多行编辑器(user prompt):
| 键 | 动作 |
|---|---|
| ↑ / ↓ / ← / → | 光标导航 |
| Enter | 在光标处插入换行 |
| Tab | 插入 4 个空格 |
| Backspace | 删字符(行首时合并上一行)|
| Delete | 删字符(行末时合并下一行)|
| Ctrl+S | 提交 |
| Esc | 弃改 |
Context 模式(zh→en 消歧)
context_enabled = on 时(默认),watcher 会保存"Claude 上一次回复的最后一段 text",作为下一次 zh→en 翻译时的消歧 context。
为什么有用:当 Claude 问"A 还是 B?",你回"选 A",没有 context 翻译会是 Choose A(字面),有 context 会是 Let's go with A / I'll go with option A(更自然)。
状态栏右侧实时显示:
| Badge | 含义 |
|---|---|
| ctx: on (123 chars) | 启用,有 123 字 context 可用 |
| ctx: on (waiting for Claude) | 启用,但 Claude 还没说过话 |
| ctx: off (Ctrl+E → toggle) | 禁用 |
什么时候关:
- 切全新话题时(避免被前一轮误导)
- 翻译"那个"、"再加一条"等本身已经独立成意思的字面命令
Ctrl+E → 选 context_enabled 行 → Enter 切换 → Esc 保存。
上下文检查(pre-flight)
context_enabled = on 时,watcher 还会让翻译模型在翻译之前做一次合理性检查 —— 用 prior reply + 你的中文输入,判断这条会不会让 Claude 摸不着头脑。
WARN 触发条件(模型自行判断,prompt 里要求"宁可漏不能错"):
- 你提到的东西在 PRIOR_REPLY 里没出现("那个文件"但 Claude 没提任何文件)
- 多选题答非所选(Claude 问 "A or B?",你答 "好的")
- 主题不匹配(Claude 问 "要不要 commit?",你说 "看一下 Y 的实现")
- 输入太破碎,连上下文都救不回来
触发时 watcher 不会立刻把英文写到 user-input.md,而是显示两步确认:
┌── 翻译结果 ──────────────────────────────────────┐
│ ⚠ 上下文检查发现可能的问题 │
│ 你说的"那个文件"在上下文里没出现 │
│ │
│ ↳ 翻译已就绪: That file │
│ Enter 再按一次确认发送 · Esc 取消 · 或直接改输入重译 │
└──────────────────────────────────────────────────┘三选一:
- 输入未改 + Enter → 仍然发送(你判断 OK)
- Esc → 全部清掉
- 改输入再 Enter → 用新输入重新翻译
模型也会静默修复明显的 IME typo(测式 → 测试、克立 → 克隆、提价 → 提交、在/再 同音字混用等),无需触发警告。
自定义翻译 prompt
每个翻译方向有一个 user prompt 和一个 default prompt:
| 翻译方向 | 用途 |
|---|---|
| zh-to-en | 用户中文输入 → Claude Code prompt(无上下文)|
| zh-to-en-with-context | 同上,但带上一轮 Claude 英文回复作消歧上下文(启用 pre-flight check)|
| en-to-zh | Claude 英文回复 → 显示给用户的中文 |
user prompt 非空时完全替换 default。设置面板里 Ctrl+E → 选 user.<方向> → Enter → 多行编辑器输入你的自定义 prompt → Ctrl+S 保存。
典型用例:
- 域专业术语词典:
Domain: traditional Chinese medicine. Glossary: 经络=meridians, 气=qi (do not translate). - 风格偏好:
用您而不是你。错误信息保留英文。React/Vue/CSS 术语不译。
要看默认 prompt 长什么样:在面板里选 default.<方向> → Enter,弹只读查看器看完整 base prompt。
或用 CLI:
cc-zh-watcher prompts show zh-to-en --base配置文件
存在用户主目录,优先级 env > project-local > user-global > 默认:
~/.config/cc-zh-watcher/config.json 用户级(默认)
<project>/.cc-zh-watcher.json 项目级(可选)
环境变量:
CC_ZH_API_KEY 覆盖 api_key
CC_ZH_BASE_URL 覆盖 base_url
CC_ZH_MODEL 覆盖 model
CC_ZH_TIMEOUT 覆盖 timeout_seconds完整 schema:
{
// ── API ──
"api_key": "sk-...", // 必填
"base_url": "https://api.deepseek.com/v1", // 默认 https://api.openai.com/v1
"model": "deepseek-v4-pro", // 默认 gpt-4o-mini
"timeout_seconds": 90, // 默认 30
// ── Toggles,全部 boolean ──
"notify_on_complete": true, // 默认 true
"translate_thinking": false, // 默认 false
"translate_todos": true, // 默认 true
"translate_agent": true, // 默认 false(agent prompt 太长,开关在 settings 面板)
"context_enabled": true, // 默认 true
"show_tool_calls": true, // 默认 session-aware:CLI on, 桌面应用 off
// ── 可选:user prompt(非空替换内置 default) ──
"prompts": {
"zh-to-en": "...",
"zh-to-en-with-context": "...",
"en-to-zh": "..."
}
}文件权限自动设为 0o600(owner-only),因为里面有 api_key。
可靠性
- 原子写:所有持久化文件(
user-input.md/ 状态文件 / config)都用 tempfile + rename。Claude Code 在另一终端cat user-input.md时绝不会读到半截内容;中途 Ctrl+C 也不会损坏文件 - 翻译队列:en→zh 翻译请求 FIFO 串行(concurrency=1),避免在一个 Claude turn 内同时迸发多个请求触发 429,且保证 scrollback 顺序与 JSONL 顺序严格一致
- Session 文件可无限增长:单一 UUID transcript 长达 128MB / 35k 行 / 5 天连续使用都正常 ——
/compact不会切新文件。tail 用 offset-based 读取,文件多大都和性能无关 - API key 文件 mode 0o600:
~/.config/cc-zh-watcher/config.json自动设成 owner-only
API 调用 metrics
状态栏第三行实时显示:
| 显示 | 含义 |
|---|---|
| api: idle | 还没发过请求 |
| api (zh-to-en): ⏱ 0.8s | 进行中(黄色,每 200ms 自刷新计时) |
| api (zh-to-en): ✓ 1.2s · 234 tok | 成功(绿色,耗时 + token 消耗)|
| api (en-to-zh): ✗ HTTP 401 (0.3s) | 失败(红色,错误代码 + 耗时)|
右侧累计:
session: 12 calls · 8.4k tok ← 全部成功
session: 12 calls · 8.4k tok · 3 err ← 有失败时红色高亮历史可查询:
cc-zh-watcher history --tail 5
cc-zh-watcher history --grep BLS
cc-zh-watcher history --direction zh-to-en --jsonFAQ
Ctrl+E 没反应?
老版本一度用 Ctrl+,(VS Code 习惯),但终端不发这个序列。现在是 Ctrl+E。如果你装的是 < 0.2.0 老版本,升级一下。
翻译超时?
DeepSeek 长 prompt 偶尔超过 30s 默认超时。改 timeout_seconds: 90 或更大。
Session 选错了?
按 Ctrl+E → 顶部 session: 行 → Enter → 弹 picker 重新选。或者外面跑 cc-zh-watcher --pick 重启 watcher。
/clear 之后 watcher 不动了?
/clear 创建新 UUID + 新 JSONL 文件,watcher 不会自动跟上。Ctrl+E → session: 行 → Enter → 选最新那个。
想看每次实际发送的英文 prompt?
cc-zh-watcher history --tail 1 看最近一条;cc-zh-watcher history --json 拿机器可读格式。
翻译质量不行?
- 换更强的 model(如
gpt-4o或deepseek-v4-pro) - 在设置面板加 user prompt 给翻译器更多上下文 / 术语
- 调高
timeout_seconds,更强的模型可能需要更长时间
Bash 输出 / 文件内容被截断了?
tool_result 显示截断到 5 行 + … +N lines。完整内容回 Claude Code 那边看(按 ctrl+o 展开)。
为什么默认不译 thinking?
Opus 4.7 的 thinking 几乎全是 signature-only redacted(空块),译了也是空气。需要的话设置面板把 translate_thinking 切到 on。
终端没听到响铃?
很多终端默认禁用音频 bell(用闪屏代替)。iTerm2: Profiles → Terminal → Silence terminal bell 取消勾选;Terminal.app: View → Allow Audible Bell。或者把 notify_on_complete 关掉。
注意:watcher 每个 turn 只响一次,时机是「Claude 给出最终输出(stop_reason=end_turn)+ 中文翻译队列耗尽」—— 中间的 tool 调用和 thinking 不会触发响铃。如果你 0.2.x 时代记得"每段都响"或"译到一半就响",是当时基于 2 秒 debounce 的旧策略,0.3.0 改了。
Claude Code 找不到 /i 命令?
watcher 启动时只要 <projectDir>/.claude/commands/i.md 不存在就会静默装上(stderr 打印一行 ✓ installed /i …)。如果还是找不到:
- 看启动日志里有没有
note: couldn't install /i …(写入失败时会打印原因,不阻塞启动) - 或者直接复制 assets/i.md 到项目的
.claude/commands/i.md - macOS Claude 桌面应用用户:桌面应用不支持 custom slash command —— 用
@.claude/cache/user-input.md替代(按Ctrl+Y一键复制这条命令,粘贴到桌面应用即可)
卸载?
# 在每个用过的项目里
cc-zh-watcher uninstall
# 删二进制
npm uninstall -g cc-zh-watcher
# 删 user-global config(按需)
rm -rf ~/.config/cc-zh-watcher限制
/i这一步是手动的:watcher 不能直接喂 prompt 给运行中的 Claude Code TUI(CC 没暴露 IPC)。每轮要切窗口 + 敲/i ↵。这是为了保留 Claude Code interactive TUI 的全部功能(slash command、permission prompt、/resumepicker 等)所做的权衡- Prompt 不能含字面
\n:单行 TextInput 没法输入换行。要嵌入换行:watcher 关掉,手编~/.config/cc-zh-watcher/config.json里prompts.<dir>字段 - 不重放历史:watcher 启动时只翻译之后的新 assistant text,已有对话历史不翻译。要回看历史用终端原生 scrollback
- 单 session 监听:watcher 一次只盯一个 Claude Code session。多 session 并发要开多个 watcher 实例。
/clear后需要手动 Ctrl+E → 切到新 session - 翻译延迟:每次输入需等翻译 API 返回(典型 0.5-3 秒),慢于直接英文输入。trade-off
License
MIT — 见 LICENSE
