cctrans 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.zh-Hant.md CHANGED
@@ -1,8 +1,20 @@
1
- # cctranslate
1
+ <div align="center">
2
+
3
+ # cctrans
4
+
5
+ **用母語讀 Claude Code——token 按英文計費。**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/cctrans?color=cb3837&logo=npm)](https://www.npmjs.com/package/cctrans)
8
+ [![npm downloads](https://img.shields.io/npm/dm/cctrans?color=blue)](https://www.npmjs.com/package/cctrans)
9
+ [![GitHub stars](https://img.shields.io/github/stars/roy-jiang-opus/cctrans?style=flat&logo=github)](https://github.com/roy-jiang-opus/cctrans)
10
+ [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
11
+ [![node](https://img.shields.io/node/v/cctrans)](package.json)
2
12
 
3
13
  [English](README.md) | [简体中文](README.zh-Hans.md) | **繁體中文** | [日本語](README.ja.md) | [한국어](README.ko.md) | [Русский](README.ru.md) | [हिन्दी](README.hi.md)
4
14
 
5
- 為 Claude Code 加上一層**雙語對照**:每則回覆會在原始英文行下方自動補上一行譯文(中/日/韓/俄/印地),**就在對話裡**,一行英文一行譯文。
15
+ </div>
16
+
17
+ ---
6
18
 
7
19
  ```
8
20
  ● I will refactor the auth module to use async tokens.
@@ -11,11 +23,41 @@
11
23
  ↳ 這會影響 3 個檔案並加入重試層。
12
24
  ```
13
25
 
14
- - **非破壞性**:畫面上多了譯文,但轉錄檔與模型看到的上下文**仍是純英文**——技術文件、skills、程式碼都不受影響。
15
- - **不污染歷史、不耗主對話 token**:翻譯由一個**獨立的低成本後端**完成,與你的 Claude Code 工作階段完全無關。
16
- - **一鍵開關**:預設常開;想讀純英文/程式碼時一鍵關閉。
26
+ Claude Code 加上一層**雙語對照**:每行英文下方一行譯文(中/日/韓/俄/印地),**就在對話裡**——僅作顯示,轉錄、模型上下文和你的 token 帳單 100% 保持英文。
27
+
28
+ ## ✨ 特性
29
+
30
+ - 🪞 **行內雙語顯示** —— 譯文隨回覆串流出現在每行英文下方,就在對話裡
31
+ - 🧾 **非破壞性** —— 轉錄與模型上下文保持純英文;skills、文件、程式碼不受影響
32
+ - 🆓 **主對話零 token** —— 翻譯走獨立低成本後端(也有免費選項),完全在 Claude Code 工作階段之外
33
+ - ⌨️ **輸入翻譯(beta)** —— 用母語打字,模型按英文工作、按英文回覆(`cctrans input on`)
34
+ - 🌏 **6 種目標語言** —— `zh-Hans` `zh-Hant` `ja` `ko` `ru` `hi`
35
+ - 🔌 **6 個後端自動降級** —— OpenAI / Anthropic / DeepL / Azure / 免費 Google / 你自己的 Claude 訂閱
36
+ - 🔒 **金鑰隔離** —— API key 只存在 chmod-600 的檔案裡,從不讀終端機環境變數
37
+ - 🛟 **故障安全** —— 任何錯誤或逾時都回退為純英文,絕不卡住工作階段
38
+
39
+ ## 🚀 快速開始
40
+
41
+ ```bash
42
+ npm install -g cctrans && cctrans install
43
+ ```
44
+
45
+ 安裝會註冊鉤子並引導你完成設定(語言 → 後端 → API key → 即時驗證)。然後**重新啟動 Claude Code**——回覆變成雙語。隨時在 Claude Code 輸入框輸入 `!cctrans off` / `!cctrans on` 開關(`!` 是 CC 內建 bash 模式,不呼叫模型、不花 token)。
46
+
47
+ <details>
48
+ <summary>從原始碼安裝</summary>
49
+
50
+ ```bash
51
+ git clone https://github.com/roy-jiang-opus/cctrans.git
52
+ cd cctrans
53
+ node bin/cctrans.js install
54
+ ```
55
+
56
+ 需要 `~/.local/bin` 在 PATH 中,或使用別名:`alias cctrans='node /path/to/cctrans/bin/cctrans.js'`
57
+
58
+ </details>
17
59
 
18
- ## 為什麼做這個
60
+ ## 🤔 為什麼做這個
19
61
 
20
62
  兩個痛點,一個架構解決:
21
63
 
@@ -33,7 +75,7 @@ Anthropic 關於按語言調整額度的 issue([#26401](https://github.com/anthr
33
75
 
34
76
  完整調研資料與來源:[MOTIVATION.md](MOTIVATION.md)。
35
77
 
36
- ## 運作原理
78
+ ## ⚙️ 運作原理
37
79
 
38
80
  利用 Claude Code 原生的 **`MessageDisplay` 鉤子**(v2.1.152+):它在每則助理訊息渲染時觸發,把完成的文字片段(`delta`)交給鉤子;鉤子回傳的 `displayContent` **只替換螢幕顯示**,不改變儲存的訊息。
39
81
 
@@ -50,23 +92,7 @@ Claude 串流輸出英文
50
92
 
51
93
  > 已在 CC 2.1.169 實測:`delta` 是**互不重疊**的已完成片段(不是累積文字),普通 `\n` 即可讓兩種語言分行顯示,程式碼區塊/路徑/已是目標語言的行會自動跳過。
52
94
 
53
- ## 安裝
54
-
55
- ```bash
56
- npm install -g cctrans && cctrans install
57
-
58
- # from source:
59
- git clone https://github.com/roy-jiang-opus/cctranslate.git
60
- cd cctranslate
61
- node bin/cctrans.js install # 註冊鉤子、連結 cctrans 到 ~/.local/bin,然後執行 setup 精靈
62
- ```
63
-
64
- 接著**重新啟動 Claude Code**(開新工作階段)讓鉤子生效。送出任意訊息,回覆就會雙語對照。
65
-
66
- > 需要 `~/.local/bin` 在 PATH 中;否則使用別名:
67
- > `alias cctrans='node /path/to/cctranslate/bin/cctrans.js'`
68
-
69
- ## 使用
95
+ ## 🎛 指令
70
96
 
71
97
  | 指令 | 作用 |
72
98
  |------|------|
@@ -77,14 +103,13 @@ node bin/cctrans.js install # 註冊鉤子、連結 cctrans 到 ~/.local/bi
77
103
  | `cctrans backends` | 列出所有引擎及其可用性 |
78
104
  | `cctrans setup` | 互動式精靈:語言、後端、API key |
79
105
  | `cctrans key [id] [value]` | 管理 `~/.cc-translate/keys.json` 中的 API key |
80
- | `cctrans input on` / `cctrans input off` | 把非英文輸入翻譯成英文(作為上下文傳給模型) |
106
+ | `cctrans input on` / `cctrans input off` | **(beta)** 把非英文輸入翻譯成英文(作為上下文傳給模型) |
107
+ | `cctrans input threshold <n>` | 觸發輸入翻譯的非拉丁字元數(預設 4) |
81
108
  | `cctrans last [N]` | 把最近(或往前第 N 則)回覆翻譯到終端機 |
82
109
  | `cctrans test <文字>` | 翻譯一段文字,驗證引擎 |
83
110
  | `cctrans install` / `cctrans uninstall` | 註冊 / 移除鉤子 |
84
111
 
85
- **最快的開關方式**:在 Claude Code 輸入框直接輸入 `!cctrans off` 或 `!cctrans on`(`!` 是 CC 內建的 bash 模式,不呼叫模型、不花 token)。
86
-
87
- ## 翻譯後端
112
+ ## 🌐 翻譯後端
88
113
 
89
114
  | 後端 | 前提 | 速度 | 品質 | 說明 |
90
115
  |------|------|------|------|------|
@@ -101,7 +126,7 @@ API key **只**存放在 `~/.cc-translate/keys.json`(chmod 600)——用 `cctran
101
126
 
102
127
  其餘設定(後端、語言、標記、模型、Azure 端點)都在 `~/.cc-translate/state.json` 中——用 `cctrans` 指令修改或直接編輯檔案。
103
128
 
104
- ## 多語言
129
+ ## 🗣 多語言
105
130
 
106
131
  目標語言支援 **CJK + 俄語 + 印地語**(非拉丁文字,可按 Unicode 區間零成本判斷「該行已是目標語言」並跳過):
107
132
 
@@ -116,19 +141,27 @@ cctrans lang zh-Hans # 簡體中文(預設)
116
141
 
117
142
  中文採用 BCP-47 **文字碼**(`zh-Hans`/`zh-Hant`)——繁體是文字系統而非地區;`zh-CN` / `zh-TW` 仍可作為別名使用,會自動正規化。切換語言立即生效(鉤子每次呼叫都讀取狀態),不同語言的快取相互獨立。
118
143
 
119
- ## 輸入翻譯
144
+ ## ⌨️ 輸入翻譯(beta)
145
+
146
+ `cctrans input on` 啟用 `UserPromptSubmit` 鉤子:當你的輸入包含足夠多的非拉丁字元時(預設 4 個以上——按絕對數量計,檔案路徑和識別字不會稀釋觸發條件;用 `cctrans input threshold <n>` 調整),英文譯文會作為上下文附給模型並被視為權威指令,同時要求模型**用英文回覆**——這樣雙語 overlay 持續生效,對話上下文全程保持英文。(已在 CC 2.1.169 核實:鉤子無法改寫 prompt 本身,所以原文仍在歷史中,英文隨附。)英文輸入原樣通過;任何錯誤都安全回退為原樣送出。
120
147
 
121
- `cctrans input on` 啟用 `UserPromptSubmit` 鉤子:當你的輸入大多是非英文時,英文譯文會作為上下文附給模型並被視為權威指令——你繼續用母語打字,模型按英文工作。(已在 CC 2.1.169 核實:鉤子無法改寫 prompt 本身,所以原文仍在歷史中,英文隨附。)英文輸入原樣通過;任何錯誤都安全回退為原樣送出。
148
+ > **Beta**:翻譯呼叫會在每條非英文輸入送出前阻塞約 0.5–1.5 秒。預設關閉;setup 精靈會詢問一次。回饋 → [issues](https://github.com/roy-jiang-opus/cctrans/issues)
122
149
 
123
- ## 行為與限制(已核實)
150
+ ## 📏 行為與限制(已核實)
124
151
 
125
152
  - 鉤子在**串流輸出中**按片段觸發,每段單獨翻譯並就地替換——所以譯文會隨英文逐段出現。
126
153
  - 鉤子有 **10 秒**逾時;本工具內部 9 秒保底。任何錯誤/逾時/超長(>9000 字元)都會**安全回退成原始英文**,絕不卡住工作階段。
127
154
  - 每行譯文按內容雜湊**快取**(`~/.cc-translate/cache`),重繪與重複文字零成本。
128
155
  - 用 `openai` 時每段約一次 API 呼叫(~$0.0001),串流輸出會比純英文多約 1 秒/段的延遲;`google` 較快但品質略低。
129
156
 
130
- ## 解除安裝
157
+ ## 🔗 關注專案
131
158
 
132
- ```bash
133
- node bin/cctrans.js uninstall # 移除鉤子;重新啟動 Claude Code 生效
134
- ```
159
+ - ⭐ **Star / Watch** [github.com/roy-jiang-opus/cctrans](https://github.com/roy-jiang-opus/cctrans),第一時間獲取版本更新
160
+ - 📦 **npm** —— [npmjs.com/package/cctrans](https://www.npmjs.com/package/cctrans) · 升級:`npm update -g cctrans`
161
+ - 🗺 **路線圖** —— [ROADMAP.md](ROADMAP.md):已完成與計劃中的功能
162
+ - 📚 **調研** —— [MOTIVATION.md](MOTIVATION.md):本專案背後的非英語 token 稅資料
163
+ - 🐛 **Issue / 新語言請求** —— [github.com/roy-jiang-opus/cctrans/issues](https://github.com/roy-jiang-opus/cctrans/issues)
164
+
165
+ ## 📄 授權條款
166
+
167
+ [MIT](LICENSE) © Roy Jiang
package/bin/cctrans.js CHANGED
@@ -104,7 +104,7 @@ function uninstall() {
104
104
  if (Object.keys(s.hooks).length === 0) delete s.hooks;
105
105
  writeSettings(s);
106
106
  }
107
- console.log(C.green('✓') + ' removed cctranslate hooks. Restart Claude Code to take effect.');
107
+ console.log(C.green('✓') + ' removed cctrans hooks. Restart Claude Code to take effect.');
108
108
  }
109
109
 
110
110
  function status() {
@@ -112,12 +112,12 @@ function status() {
112
112
  const installed = hookInstalled();
113
113
  const b = getBackend(st.backend);
114
114
  const lang = getLang(st.target);
115
- console.log(C.bold('cctranslate status'));
115
+ console.log(C.bold('cctrans status'));
116
116
  console.log(' enabled : ' + (st.enabled ? C.green('ON') : C.red('OFF')));
117
117
  console.log(' hook : ' + (installed ? C.green('installed') : C.red('not installed') + C.dim(' (run: cctrans install)')));
118
118
  console.log(' backend : ' + st.backend + (b ? (b.available() ? C.green(' (ready)') : C.red(' (missing: ' + b.needs + ')')) : C.red(' (unknown backend)')));
119
119
  console.log(' lang : ' + st.target + (lang ? C.dim(' (' + lang.name + ')') : C.red(' (unsupported — see: cctrans lang)')));
120
- console.log(' input : ' + (st.inputEn ? C.green('ON') : 'off') + C.dim(' (prompt -> English; toggle: cctrans input on|off)'));
120
+ console.log(' input : ' + (st.inputEn ? C.green('ON') : 'off') + C.dim(' (beta; prompt -> English; toggle: cctrans input on|off; triggers at ' + st.inputMinChars + '+ non-Latin chars)'));
121
121
  console.log(' keys : ' + Object.keys(keys.readKeys()).length + ' in ' + keys.KEYS_FILE + C.dim(' (manage: cctrans key)'));
122
122
  console.log(' state : ' + STATE_FILE);
123
123
  }
@@ -179,7 +179,8 @@ function help() {
179
179
 
180
180
  ${C.bold('Control')}
181
181
  cctrans on | off | toggle turn the inline translation on/off
182
- cctrans input on | off translate non-English input to English (as context)
182
+ cctrans input on | off (beta) translate non-English input to English (as context)
183
+ cctrans input threshold <n> non-Latin chars that trigger input translation (default 4)
183
184
  cctrans status show current state
184
185
  cctrans lang [code] show/set target language (zh-Hans, zh-Hant, ja, ko, ru, hi)
185
186
  cctrans backend <id> choose translation engine
@@ -188,7 +189,7 @@ ${C.bold('Control')}
188
189
  ${C.bold('Setup')}
189
190
  cctrans install register hooks (+ link cctrans), then run setup
190
191
  cctrans setup interactive wizard: language, backend, API keys
191
- (flags: --lang --backend --key --yes)
192
+ (flags: --lang --backend --key --input --yes)
192
193
  cctrans key [id] [value] manage API keys in ~/.cc-translate/keys.json
193
194
  (ids: openai, anthropic, deepl, azure, azure-region)
194
195
  cctrans uninstall remove the hooks
@@ -242,6 +243,7 @@ async function main() {
242
243
  lang: flag('--lang'),
243
244
  backend: flag('--backend'),
244
245
  key: flag('--key'),
246
+ input: flag('--input'),
245
247
  yes: rest.includes('--yes'),
246
248
  });
247
249
  break;
@@ -251,8 +253,17 @@ async function main() {
251
253
  const sub = rest[0];
252
254
  if (sub === 'on' || sub === 'off') setState({ inputEn: sub === 'on' });
253
255
  else if (sub === 'toggle') setState({ inputEn: !getState().inputEn });
256
+ else if (sub === 'threshold' && rest[1] !== undefined) {
257
+ const n = parseInt(rest[1], 10);
258
+ if (!Number.isInteger(n) || n < 1) {
259
+ console.error('usage: cctrans input threshold <n> (non-Latin chars in a prompt that trigger translation; n >= 1)');
260
+ process.exit(1);
261
+ }
262
+ setState({ inputMinChars: n });
263
+ }
254
264
  const st = getState();
255
- console.log('input translation (prompt -> English): ' + (st.inputEn ? C.green('ON') : C.red('OFF')) +
265
+ console.log('input translation ' + C.dim('(beta)') + ' (prompt -> English): ' + (st.inputEn ? C.green('ON') : C.red('OFF')) +
266
+ C.dim(' threshold: ' + st.inputMinChars + ' non-Latin chars (set: cctrans input threshold <n>)') +
256
267
  (inputHookInstalled() ? '' : C.red(' (hook not installed — run: cctrans install)')));
257
268
  break;
258
269
  }
@@ -16,7 +16,7 @@
16
16
 
17
17
  const { getState } = require('../src/config');
18
18
  const { translateLines } = require('../src/translate');
19
- const { nonLatinRatio } = require('../src/langs');
19
+ const { nonLatinCount } = require('../src/langs');
20
20
 
21
21
  function passThrough() { process.exit(0); }
22
22
 
@@ -37,8 +37,11 @@ process.stdin.on('end', async () => {
37
37
  try { st = getState(); } catch (e) { return passThrough(); }
38
38
  if (!st.inputEn) return passThrough();
39
39
 
40
- // Only act on prompts that are substantially non-English.
41
- if (nonLatinRatio(prompt) < 0.2) return passThrough();
40
+ // Trigger on an absolute count of non-Latin chars (configurable:
41
+ // `cctrans input threshold <n>`), NOT a ratio — coding prompts are mostly
42
+ // Latin paths/identifiers, which would dilute a ratio below any threshold.
43
+ const min = Number.isInteger(st.inputMinChars) && st.inputMinChars > 0 ? st.inputMinChars : 4;
44
+ if (nonLatinCount(prompt) < min) return passThrough();
42
45
 
43
46
  const guard = setTimeout(passThrough, 9000);
44
47
  try {
@@ -52,7 +55,8 @@ process.stdin.on('end', async () => {
52
55
  hookEventName: 'UserPromptSubmit',
53
56
  additionalContext:
54
57
  'English translation of the user\'s prompt (translated by a local tool; ' +
55
- 'treat it as the canonical instruction):\n' + en,
58
+ 'treat it as the canonical instruction). Respond in English — a local ' +
59
+ 'overlay renders your reply bilingually for the user:\n' + en,
56
60
  },
57
61
  }));
58
62
  process.exit(0);
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "cctrans",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Bilingual inline translation overlay for Claude Code: a translated line (zh/ja/ko/ru/hi) under each English line, right in the conversation — non-destructive, zero main-loop tokens.",
5
5
  "license": "MIT",
6
6
  "author": "Roy Jiang",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/roy-jiang-opus/cctranslate.git"
9
+ "url": "git+https://github.com/roy-jiang-opus/cctrans.git"
10
10
  },
11
- "homepage": "https://github.com/roy-jiang-opus/cctranslate#readme",
11
+ "homepage": "https://github.com/roy-jiang-opus/cctrans#readme",
12
12
  "bugs": {
13
- "url": "https://github.com/roy-jiang-opus/cctranslate/issues"
13
+ "url": "https://github.com/roy-jiang-opus/cctrans/issues"
14
14
  },
15
15
  "keywords": [
16
16
  "claude-code",
package/src/config.js CHANGED
@@ -29,7 +29,8 @@ function defaults() {
29
29
  anthropicModel: 'claude-haiku-4-5', // anthropic backend model
30
30
  azureEndpoint: 'https://api.cognitive.microsofttranslator.com',
31
31
  marker: '↳ ', // prefix on each translated line
32
- inputEn: false, // input translation (prompt -> English) off until enabled
32
+ inputEn: false, // input translation (beta, prompt -> English) off until enabled
33
+ inputMinChars: 4, // non-Latin chars in a prompt that trigger input translation
33
34
  };
34
35
  }
35
36
 
@@ -52,6 +53,7 @@ function setState(patch) {
52
53
  azureEndpoint: next.azureEndpoint,
53
54
  marker: next.marker,
54
55
  inputEn: next.inputEn,
56
+ inputMinChars: next.inputMinChars,
55
57
  };
56
58
  const tmp = STATE_FILE + '.' + process.pid + '.tmp';
57
59
  fs.writeFileSync(tmp, JSON.stringify(persist, null, 2));
package/src/langs.js CHANGED
@@ -52,10 +52,10 @@ const LANGS = {
52
52
  // supported non-English languages?" Used by the input-translation hook.
53
53
  const NON_LATIN = /[一-鿿㐀-䶿぀-ゟ゠-ヿ가-힯ᄀ-ᇿЀ-ӿऀ-ॿ]/g;
54
54
 
55
- function nonLatinRatio(text) {
56
- const hits = (text.match(NON_LATIN) || []).length;
57
- const nonspace = text.replace(/\s/g, '').length;
58
- return nonspace === 0 ? 0 : hits / nonspace;
55
+ // Absolute count, not a ratio: coding prompts are full of Latin paths and
56
+ // identifiers that would dilute any ratio below a usable threshold.
57
+ function nonLatinCount(text) {
58
+ return (text.match(NON_LATIN) || []).length;
59
59
  }
60
60
 
61
61
  // Region-code (and bare-zh) aliases -> canonical script codes.
@@ -91,4 +91,4 @@ function isProbablyTarget(line, code) {
91
91
  return nonspace > 0 && hits / nonspace >= 0.3;
92
92
  }
93
93
 
94
- module.exports = { LANGS, getLang, listLangs, isProbablyTarget, normalizeLang, nonLatinRatio };
94
+ module.exports = { LANGS, getLang, listLangs, isProbablyTarget, normalizeLang, nonLatinCount };
package/src/setup.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
- // Interactive setup wizard: language -> backend -> API-key entry -> live
3
- // verification -> save. Re-runnable via `cctrans setup`; non-interactive with
4
- // flags (--lang, --backend, --key, --yes). Keys go to keys.json only — the
5
- // shell environment is never read.
2
+ // Interactive setup wizard: language -> backend -> API-key entry -> input
3
+ // translation (beta) -> live verification -> save. Re-runnable via `cctrans
4
+ // setup`; non-interactive with flags (--lang, --backend, --key, --input,
5
+ // --yes). Keys go to keys.json only — the shell environment is never read.
6
6
 
7
7
  const readline = require('node:readline/promises');
8
8
  const { getState, setState } = require('./config');
@@ -32,7 +32,7 @@ async function runSetup(opts) {
32
32
  };
33
33
 
34
34
  try {
35
- console.log(C.bold('cctranslate setup') + C.dim(' (re-run anytime: cctrans setup)'));
35
+ console.log(C.bold('cctrans setup') + C.dim(' (re-run anytime: cctrans setup)'));
36
36
 
37
37
  // 1. Target language
38
38
  let lang = opts.lang;
@@ -73,12 +73,24 @@ async function runSetup(opts) {
73
73
  }
74
74
  }
75
75
 
76
- // 4. Save config
77
- setState({ target: lang, backend });
76
+ // 4. Input translation (beta, opt-in): prompt -> English as context
77
+ let inputEn = typeof opts.input === 'string' ? opts.input === 'on' : getState().inputEn;
78
+ if (opts.input === undefined && rl) {
79
+ console.log('\n' + C.bold('Input translation') + ' ' + C.dim('(beta)') +
80
+ ' — type prompts in your language; an English translation is\n' +
81
+ 'attached as context and the model is asked to reply in English (the original\n' +
82
+ 'prompt stays in your history; adds ~0.5–1.5s per non-English prompt).');
83
+ const a = await ask('Enable input translation? (y/N)', inputEn ? 'y' : 'n');
84
+ inputEn = /^y(es)?$/i.test(a);
85
+ }
86
+
87
+ // 5. Save config
88
+ setState({ target: lang, backend, inputEn });
78
89
  console.log('\n' + C.green('✓') + ' saved: lang=' + lang + ' (' + getLang(lang).name + '), backend=' + backend +
90
+ ', input=' + (inputEn ? 'on' : 'off') +
79
91
  (b.available() ? '' : C.red(' (no key yet — will fall back to google)')));
80
92
 
81
- // 5. Live verification
93
+ // 6. Live verification
82
94
  process.stdout.write(C.dim('verifying… '));
83
95
  try {
84
96
  const { displayContent } = await buildDisplayContent('Setup verification: translation works.\n', {
@@ -89,7 +101,10 @@ async function runSetup(opts) {
89
101
  console.log(C.red('verification failed: ' + e.message));
90
102
  }
91
103
 
92
- console.log(C.dim('\nNext: restart Claude Code (new session). Toggle with `!cctrans off` / `!cctrans on`; input translation: `cctrans input on`.'));
104
+ console.log(C.dim('\nNext: restart Claude Code (new session). Toggle with `!cctrans off` / `!cctrans on`. ' +
105
+ (inputEn
106
+ ? 'Input translation (beta) is ON — disable: `cctrans input off`.'
107
+ : 'Input translation (beta): `cctrans input on`.')));
93
108
  return true;
94
109
  } finally {
95
110
  if (rl) rl.close();