@daylenjeez/ccm-switch 1.2.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.md ADDED
@@ -0,0 +1,195 @@
1
+ <div align="center">
2
+
3
+ # ccm
4
+
5
+ **Claude Code Model Switcher**
6
+
7
+ Switch Claude Code custom model configurations from the terminal in seconds.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/@daylenjeez/ccm-switch.svg?style=flat-square)](https://www.npmjs.com/package/@daylenjeez/ccm-switch)
10
+ [![license](https://img.shields.io/npm/l/@daylenjeez/ccm-switch.svg?style=flat-square)](https://github.com/daylenjeez/ccm-switch/blob/main/LICENSE)
11
+ [![node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
12
+
13
+ [中文文档](./README.zh-CN.md) | English
14
+
15
+ [Install](#-install) · [Quick Start](#-quick-start) · [Commands](#-commands) · [How It Works](#%EF%B8%8F-how-it-works)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## ✨ Highlights
22
+
23
+ - 🔌 **cc-switch Integration** — Reads [cc-switch](https://github.com/farion1231/cc-switch) database directly, zero migration
24
+ - 🧙 **Interactive Wizard** — `ccm add` guides you step by step, type `<` to go back
25
+ - ⚡ **One-command Switch** — `ccm use OpenRouter` or `ccm ls` with arrow keys
26
+ - 🛡️ **Safe Switching** — Preserves `language`, `permissions` and other personal settings
27
+ - 🚀 **Zero Config** — Just `ccm init` and follow the prompts, no docs needed
28
+ - 🌍 **i18n** — English / 中文 (`ccm locale set en/zh`)
29
+
30
+ ## 📦 Install
31
+
32
+ ```bash
33
+ npm install -g @daylenjeez/ccm-switch
34
+ ```
35
+
36
+ Or build from source:
37
+
38
+ ```bash
39
+ git clone git@github.com:daylenjeez/ccm-switch.git
40
+ cd ccm && npm install && npm run build && npm link
41
+ ```
42
+
43
+ ## 🚀 Quick Start
44
+
45
+ ```bash
46
+ ccm init # Auto-detects cc-switch or initializes standalone mode
47
+ ccm add # Interactive wizard to add a provider
48
+ ```
49
+
50
+ > **Without ccm**: Manually edit `~/.claude/settings.json`, copy-paste API keys, restart, hope the JSON isn't broken.
51
+ > **With ccm**: `ccm use OpenRouter` — done.
52
+
53
+ ## 🔌 cc-switch Integration
54
+
55
+ Already using [cc-switch](https://github.com/farion1231/cc-switch)? ccm reads its SQLite database directly:
56
+
57
+ ```bash
58
+ $ ccm init
59
+ cc-switch detected. Import configurations from it? (Y/n)
60
+ ✓ Initialized in cc-switch mode
61
+ ✓ Imported 4 configurations
62
+ Active: OpenRouter
63
+ ```
64
+
65
+ All configs sync both ways — add in ccm, see it in cc-switch UI, and vice versa.
66
+
67
+ ## ➕ Adding Configurations
68
+
69
+ ### Interactive wizard (recommended)
70
+
71
+ ```bash
72
+ $ ccm add
73
+ Provider name (e.g. OpenRouter): OpenRouter
74
+
75
+ Choose how to add:
76
+ 1) Step by step # guided prompts, type < to go back
77
+ 2) Write JSON # opens $EDITOR
78
+
79
+ ANTHROPIC_BASE_URL: https://openrouter.ai/api/v1
80
+ ANTHROPIC_AUTH_TOKEN: sk-or-xxx
81
+ ANTHROPIC_MODEL: anthropic/claude-opus-4.6
82
+ ANTHROPIC_DEFAULT_OPUS_MODEL (optional):
83
+ ANTHROPIC_DEFAULT_SONNET_MODEL (optional):
84
+ ANTHROPIC_DEFAULT_HAIKU_MODEL (optional):
85
+
86
+ ✓ Saved configuration "OpenRouter"
87
+ Switch to this configuration now? (Y/n)
88
+ ```
89
+
90
+ ### Edit JSON directly
91
+
92
+ <details>
93
+ <summary>Standalone mode: <code>~/.ccm/config.json</code></summary>
94
+
95
+ ```json
96
+ {
97
+ "profiles": {
98
+ "OpenRouter": {
99
+ "env": {
100
+ "ANTHROPIC_BASE_URL": "https://openrouter.ai/api/v1",
101
+ "ANTHROPIC_AUTH_TOKEN": "sk-or-...",
102
+ "ANTHROPIC_MODEL": "anthropic/claude-opus-4.6"
103
+ }
104
+ }
105
+ }
106
+ }
107
+ ```
108
+ </details>
109
+
110
+ Aliases are stored in `~/.ccm/rc.json`:
111
+
112
+ ```json
113
+ {
114
+ "aliases": {
115
+ "or": "OpenRouter"
116
+ }
117
+ }
118
+ ```
119
+
120
+ ## 📖 Commands
121
+
122
+ ### Core
123
+
124
+ | Command | Alias | Description |
125
+ |---|---|---|
126
+ | `ccm init` | | Initialize, auto-detect cc-switch |
127
+ | `ccm list` | `ls` | Interactive list & switch |
128
+ | `ccm use <name>` | | Switch by name |
129
+ | `ccm add` | `new` | Interactive add wizard |
130
+ | `ccm save <name>` | | Save current settings as profile |
131
+ | `ccm show [name]` | | View config details |
132
+ | `ccm modify [name]` | `edit` | Edit existing configuration |
133
+ | `ccm remove [name]` | `rm` | Interactive or named delete |
134
+ | `ccm current` | | Show active configuration |
135
+ | `ccm config` | | Switch storage mode |
136
+
137
+ ### Aliases
138
+
139
+ | Command | Description |
140
+ |---|---|
141
+ | `ccm alias set <short> <name>` | Create alias, e.g. `ccm alias set or OpenRouter` |
142
+ | `ccm alias rm <short>` | Remove alias |
143
+ | `ccm alias list` / `ls` | List all aliases |
144
+
145
+ ```bash
146
+ ccm alias set or OpenRouter
147
+ ccm use or # same as: ccm use OpenRouter
148
+ ```
149
+
150
+ ### Locale
151
+
152
+ | Command | Description |
153
+ |---|---|
154
+ | `ccm locale` / `ls` | List & switch language |
155
+ | `ccm locale set <lang>` | Set language (`zh` / `en`) |
156
+
157
+ ### Examples
158
+
159
+ ```bash
160
+ # Switch provider
161
+ $ ccm use OpenRouter
162
+ ✓ Switched to OpenRouter
163
+ Model: anthropic/claude-opus-4.6
164
+ Restart Claude Code to apply
165
+
166
+ # View current config
167
+ $ ccm current
168
+ Current configuration: OpenRouter
169
+ ANTHROPIC_BASE_URL: https://openrouter.ai/api/v1
170
+ ANTHROPIC_MODEL: anthropic/claude-opus-4.6
171
+ ANTHROPIC_AUTH_TOKEN: sk-or-v1...a3f2
172
+
173
+ # Save current settings.json as a new profile
174
+ $ ccm save my-backup
175
+ ✓ Saved current configuration as "my-backup"
176
+ ```
177
+
178
+ ## ⚙️ How It Works
179
+
180
+ Claude Code reads `~/.claude/settings.json` on startup. The `env` field controls the API provider and model:
181
+
182
+ | Variable | Description |
183
+ |---|---|
184
+ | `ANTHROPIC_BASE_URL` | API endpoint URL |
185
+ | `ANTHROPIC_AUTH_TOKEN` | Authentication token |
186
+ | `ANTHROPIC_MODEL` | Default model |
187
+ | `ANTHROPIC_DEFAULT_OPUS_MODEL` | Model used when selecting Opus via `/model` |
188
+ | `ANTHROPIC_DEFAULT_SONNET_MODEL` | Model used when selecting Sonnet via `/model` |
189
+ | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Model used when selecting Haiku via `/model` |
190
+
191
+ `ccm use` writes the selected profile into `settings.json` while preserving personal settings (`language`, `permissions`, etc.). Restart Claude Code to apply.
192
+
193
+ ## 📄 License
194
+
195
+ [MIT](./LICENSE)
@@ -0,0 +1,195 @@
1
+ <div align="center">
2
+
3
+ # ccm
4
+
5
+ **Claude Code Model Switcher**
6
+
7
+ 在终端几秒内完成 Claude Code 自定义模型配置的管理和切换。
8
+
9
+ [![npm version](https://img.shields.io/npm/v/@daylenjeez/ccm-switch.svg?style=flat-square)](https://www.npmjs.com/package/@daylenjeez/ccm-switch)
10
+ [![license](https://img.shields.io/npm/l/@daylenjeez/ccm-switch.svg?style=flat-square)](https://github.com/daylenjeez/ccm-switch/blob/main/LICENSE)
11
+ [![node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
12
+
13
+ English | [中文文档](./README.zh-CN.md)
14
+
15
+ [安装](#-安装) · [快速开始](#-快速开始) · [命令一览](#-命令一览) · [工作原理](#%EF%B8%8F-工作原理)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## ✨ 亮点
22
+
23
+ - 🔌 **cc-switch 无缝对接** — 直接读取 [cc-switch](https://github.com/farion1231/cc-switch) 数据库,无需迁移
24
+ - 🧙 **交互式向导** — `ccm add` 逐步引导,输入 `<` 可返回上一步
25
+ - ⚡ **一键切换** — `ccm use OpenRouter` 或 `ccm ls` 方向键选择
26
+ - 🛡️ **安全切换** — 自动保留 `language`、`permissions` 等个人设置
27
+ - 🚀 **零配置上手** — 直接 `ccm init`,跟着提示走,无需阅读文档
28
+ - 🌍 **中英双语** — `ccm locale set zh/en` 切换界面语言
29
+
30
+ ## 📦 安装
31
+
32
+ ```bash
33
+ npm install -g @daylenjeez/ccm-switch
34
+ ```
35
+
36
+ 或从源码构建:
37
+
38
+ ```bash
39
+ git clone git@github.com:daylenjeez/ccm-switch.git
40
+ cd ccm && npm install && npm run build && npm link
41
+ ```
42
+
43
+ ## 🚀 快速开始
44
+
45
+ ```bash
46
+ ccm init # 自动检测 cc-switch 或初始化独立模式
47
+ ccm add # 交互式向导添加供应商
48
+ ```
49
+
50
+ > **没有 ccm**: 手动编辑 `~/.claude/settings.json`,复制粘贴 API key,重启,祈祷 JSON 没写错。
51
+ > **使用 ccm**: `ccm use OpenRouter` — 搞定。
52
+
53
+ ## 🔌 cc-switch 集成
54
+
55
+ 已经在用 [cc-switch](https://github.com/farion1231/cc-switch)?ccm 直接读取它的 SQLite 数据库:
56
+
57
+ ```bash
58
+ $ ccm init
59
+ 检测到 cc-switch 已安装,是否从中导入配置?(Y/n)
60
+ ✓ 已初始化为 cc-switch 模式
61
+ ✓ 已导入 4 个配置
62
+ 当前激活: OpenRouter
63
+ ```
64
+
65
+ 双向同步 — 在 ccm 中添加的配置在 cc-switch UI 中也能看到,反之亦然。
66
+
67
+ ## ➕ 添加配置
68
+
69
+ ### 交互式向导(推荐)
70
+
71
+ ```bash
72
+ $ ccm add
73
+ 供应商名称 (如 OpenRouter): OpenRouter
74
+
75
+ 选择添加方式:
76
+ 1) 逐步填写 # 按步骤输入,输入 < 返回上一步
77
+ 2) 直接编写 JSON # 打开 $EDITOR 编辑
78
+
79
+ ANTHROPIC_BASE_URL: https://openrouter.ai/api/v1
80
+ ANTHROPIC_AUTH_TOKEN: sk-or-xxx
81
+ ANTHROPIC_MODEL: anthropic/claude-opus-4.6
82
+ ANTHROPIC_DEFAULT_OPUS_MODEL (可选):
83
+ ANTHROPIC_DEFAULT_SONNET_MODEL (可选):
84
+ ANTHROPIC_DEFAULT_HAIKU_MODEL (可选):
85
+
86
+ ✓ 已保存配置 "OpenRouter"
87
+ 是否立即切换到此配置?(Y/n)
88
+ ```
89
+
90
+ ### 直接编辑 JSON
91
+
92
+ <details>
93
+ <summary>独立模式:<code>~/.ccm/config.json</code></summary>
94
+
95
+ ```json
96
+ {
97
+ "profiles": {
98
+ "OpenRouter": {
99
+ "env": {
100
+ "ANTHROPIC_BASE_URL": "https://openrouter.ai/api/v1",
101
+ "ANTHROPIC_AUTH_TOKEN": "sk-or-...",
102
+ "ANTHROPIC_MODEL": "anthropic/claude-opus-4.6"
103
+ }
104
+ }
105
+ }
106
+ }
107
+ ```
108
+ </details>
109
+
110
+ 别名存储在 `~/.ccm/rc.json` 中:
111
+
112
+ ```json
113
+ {
114
+ "aliases": {
115
+ "or": "OpenRouter"
116
+ }
117
+ }
118
+ ```
119
+
120
+ ## 📖 命令一览
121
+
122
+ ### 核心命令
123
+
124
+ | 命令 | 缩写 | 说明 |
125
+ |---|---|---|
126
+ | `ccm init` | | 初始化,自动检测 cc-switch |
127
+ | `ccm list` | `ls` | 交互式列表 & 切换 |
128
+ | `ccm use <name>` | | 按名称切换 |
129
+ | `ccm add` | `new` | 交互式添加向导 |
130
+ | `ccm save <name>` | | 将当前设置保存为方案 |
131
+ | `ccm show [name]` | | 查看配置详情 |
132
+ | `ccm modify [name]` | `edit` | 修改已有配置 |
133
+ | `ccm remove [name]` | `rm` | 交互式或指定名称删除 |
134
+ | `ccm current` | | 显示当前激活配置 |
135
+ | `ccm config` | | 切换存储模式 |
136
+
137
+ ### 别名管理
138
+
139
+ | 命令 | 说明 |
140
+ |---|---|
141
+ | `ccm alias set <short> <name>` | 创建别名,如 `ccm alias set or OpenRouter` |
142
+ | `ccm alias rm <short>` | 删除别名 |
143
+ | `ccm alias list` / `ls` | 列出所有别名 |
144
+
145
+ ```bash
146
+ ccm alias set or OpenRouter
147
+ ccm use or # 等同于: ccm use OpenRouter
148
+ ```
149
+
150
+ ### 语言设置
151
+
152
+ | 命令 | 说明 |
153
+ |---|---|
154
+ | `ccm locale` / `ls` | 列出并切换语言 |
155
+ | `ccm locale set <lang>` | 设置语言(`zh` / `en`) |
156
+
157
+ ### 使用示例
158
+
159
+ ```bash
160
+ # 切换供应商
161
+ $ ccm use OpenRouter
162
+ ✓ 已切换到 OpenRouter
163
+ 模型: anthropic/claude-opus-4.6
164
+ 重启 Claude Code 生效
165
+
166
+ # 查看当前配置
167
+ $ ccm current
168
+ 当前配置: OpenRouter
169
+ ANTHROPIC_BASE_URL: https://openrouter.ai/api/v1
170
+ ANTHROPIC_MODEL: anthropic/claude-opus-4.6
171
+ ANTHROPIC_AUTH_TOKEN: sk-or-v1...a3f2
172
+
173
+ # 将当前 settings.json 保存为新方案
174
+ $ ccm save my-backup
175
+ ✓ 已保存当前配置为 "my-backup"
176
+ ```
177
+
178
+ ## ⚙️ 工作原理
179
+
180
+ Claude Code 启动时读取 `~/.claude/settings.json`,`env` 字段控制 API 供应商和模型:
181
+
182
+ | 变量 | 说明 |
183
+ |---|---|
184
+ | `ANTHROPIC_BASE_URL` | API 端点地址 |
185
+ | `ANTHROPIC_AUTH_TOKEN` | 认证令牌 |
186
+ | `ANTHROPIC_MODEL` | 默认模型 |
187
+ | `ANTHROPIC_DEFAULT_OPUS_MODEL` | 在 Claude Code 中通过 `/model` 选择 Opus 时使用的模型 |
188
+ | `ANTHROPIC_DEFAULT_SONNET_MODEL` | 选择 Sonnet 时使用的模型 |
189
+ | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | 选择 Haiku 时使用的模型 |
190
+
191
+ `ccm use` 将选中配置写入 `settings.json`,同时保留 `language`、`permissions` 等个人设置。重启 Claude Code 后生效。
192
+
193
+ ## 📄 License
194
+
195
+ [MIT](./LICENSE)
@@ -0,0 +1,3 @@
1
+ export declare function readClaudeSettings(): Record<string, unknown>;
2
+ export declare function applyProfile(settingsConfig: Record<string, unknown>): void;
3
+ export declare function getSettingsPath(): string;
package/dist/claude.js ADDED
@@ -0,0 +1,25 @@
1
+ import { homedir } from "os";
2
+ import { join } from "path";
3
+ import { existsSync, readFileSync, writeFileSync } from "fs";
4
+ const SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
5
+ export function readClaudeSettings() {
6
+ if (!existsSync(SETTINGS_PATH))
7
+ return {};
8
+ return JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
9
+ }
10
+ export function applyProfile(settingsConfig) {
11
+ const current = readClaudeSettings();
12
+ // 保留用户级字段,用 profile 的配置覆盖
13
+ const preserved = {};
14
+ const USER_FIELDS = ["language", "permissions"];
15
+ for (const key of USER_FIELDS) {
16
+ if (key in current) {
17
+ preserved[key] = current[key];
18
+ }
19
+ }
20
+ const merged = { ...preserved, ...settingsConfig };
21
+ writeFileSync(SETTINGS_PATH, JSON.stringify(merged, null, 2));
22
+ }
23
+ export function getSettingsPath() {
24
+ return SETTINGS_PATH;
25
+ }
@@ -0,0 +1,3 @@
1
+ import type { TranslationKey } from "./zh.js";
2
+ declare const en: Record<TranslationKey, string>;
3
+ export default en;
@@ -0,0 +1,124 @@
1
+ const en = {
2
+ // program
3
+ "program.description": "Claude Code Model Switcher - Quickly switch Claude Code custom model configurations",
4
+ // common
5
+ "common.not_init": "Not initialized yet. Run: ccm init",
6
+ "common.model": "Model",
7
+ "common.model_default": "default",
8
+ "common.source": "Source",
9
+ // error
10
+ "error.not_found": 'Configuration "{name}" not found',
11
+ "error.alias_target_missing": 'Alias "{alias}" points to "{target}", but it does not exist',
12
+ "error.invalid_choice": "Invalid choice",
13
+ // suggest
14
+ "suggest.did_you_mean": "Did you mean: {name}?",
15
+ "suggest.did_you_mean_header": "Did you mean:",
16
+ "suggest.use_list": "Use ccm list to see all available configurations",
17
+ // init
18
+ "init.description": "Initialize ccm",
19
+ "init.cc_switch_found": "cc-switch detected. Import configurations from it? (Y/n) ",
20
+ "init.imported": "✓ Imported {count} configurations",
21
+ "init.current": "Active: {name}",
22
+ "init.no_current": "No active configuration",
23
+ "init.done_cc_switch": "✓ Initialized in cc-switch mode",
24
+ "init.done_standalone": "✓ Initialized in standalone mode",
25
+ // config
26
+ "config.description": "View or switch data source mode",
27
+ "config.current_mode": "Current mode: {mode}",
28
+ "config.switch_confirm": "Switch mode? (y/N) ",
29
+ "config.cc_switch_not_installed": "cc-switch is not installed",
30
+ "config.switched": "✓ Switched to {mode} mode",
31
+ // list
32
+ "list.description": "List and select configurations",
33
+ "list.empty": "No configurations yet. Use ccm save <name> to save current config",
34
+ "list.header": "Available configurations:",
35
+ "list.select": "Select configuration:",
36
+ "list.current_marker": "(current)",
37
+ "list.cancelled": "Cancelled",
38
+ "list.choose_number": "Enter number to switch (Enter to skip): ",
39
+ // current
40
+ "current.description": "Show the currently active configuration",
41
+ "current.none": "No active configuration",
42
+ "current.settings_header": "Current settings.json:",
43
+ "current.not_exist": 'Current configuration "{name}" no longer exists',
44
+ "current.header": "Current configuration: {name}",
45
+ // use
46
+ "use.description": "Switch to a specified configuration",
47
+ "use.done": "✓ Switched to {name}",
48
+ "use.restart": "Restart Claude Code to apply",
49
+ // save
50
+ "save.description": "Save current settings.json as a new configuration",
51
+ "save.overwrite": 'Configuration "{name}" already exists, will overwrite',
52
+ "save.done": '✓ Saved current configuration as "{name}"',
53
+ // show
54
+ "show.description": "View configuration details (defaults to current)",
55
+ "show.no_current": "No active configuration. Specify a name: ccm show <name>",
56
+ // remove
57
+ "remove.description": "Delete a configuration",
58
+ "remove.select": "Select configuration to delete:",
59
+ "remove.confirm": 'Delete "{name}"? (y/N) ',
60
+ "remove.done": '✓ Deleted "{name}"',
61
+ // alias
62
+ "alias.description": "Manage aliases",
63
+ "alias.set_description": "Set alias, e.g.: ccm alias set or openrouter-opus4.6",
64
+ "alias.set_done": "✓ Alias set: {short} → {name}",
65
+ "alias.rm_description": "Remove an alias",
66
+ "alias.rm_not_found": 'Alias "{short}" not found',
67
+ "alias.rm_done": '✓ Removed alias "{short}"',
68
+ "alias.list_description": "List all aliases",
69
+ "alias.list_empty": "No aliases yet. Use ccm alias set <short> <name> to add one",
70
+ "alias.list_header": "Aliases:",
71
+ // locale
72
+ "locale.description": "Manage interface language",
73
+ "locale.current": "Current language: {locale}",
74
+ "locale.set_description": "Set language (zh/en)",
75
+ "locale.set_done": "✓ Language set to {locale}",
76
+ "locale.set_invalid": "Unsupported language: {locale}. Available: zh, en",
77
+ "locale.list_description": "List and select language",
78
+ "locale.list_header": "Supported languages:",
79
+ "locale.list_current_marker": "(current)",
80
+ "locale.select": "Select language:",
81
+ "locale.choose_number": "Enter number to switch (Enter to skip): ",
82
+ // add
83
+ "add.description": "Interactively add a new configuration",
84
+ "add.prompt_name": "Provider name (e.g. OpenRouter): ",
85
+ "add.prompt_base_url": "ANTHROPIC_BASE_URL: ",
86
+ "add.prompt_auth_token": "ANTHROPIC_AUTH_TOKEN: ",
87
+ "add.prompt_model": "ANTHROPIC_MODEL: ",
88
+ "add.prompt_default_opus": "ANTHROPIC_DEFAULT_OPUS_MODEL (press Enter to skip): ",
89
+ "add.prompt_default_sonnet": "ANTHROPIC_DEFAULT_SONNET_MODEL (press Enter to skip): ",
90
+ "add.prompt_default_haiku": "ANTHROPIC_DEFAULT_HAIKU_MODEL (press Enter to skip): ",
91
+ "add.mode_select": "Choose how to add:",
92
+ "add.mode_interactive": "Step by step",
93
+ "add.mode_json": "Write JSON directly",
94
+ "add.mode_choose": "Choose (1/2): ",
95
+ "add.json_template_hint": "Fill in the configuration in editor, save and exit",
96
+ "add.json_parse_error": "JSON parse error, please check format",
97
+ "add.back_hint": "Type < to go back",
98
+ "add.name_required": "Provider name cannot be empty",
99
+ "add.field_required": "{field} cannot be empty",
100
+ "add.already_exists": 'Configuration "{name}" already exists. Overwrite? (y/N) ',
101
+ "add.edit_confirm": "Edit configuration in editor? (y/N) ",
102
+ "add.preview_header": "Configuration preview:",
103
+ "add.done": '✓ Saved configuration "{name}"',
104
+ "add.switch_confirm": "Switch to this configuration now? (Y/n) ",
105
+ "add.cancelled": "Cancelled",
106
+ // modify
107
+ "modify.description": "Modify an existing configuration",
108
+ "modify.select": "Select configuration to modify:",
109
+ "modify.done": '✓ Updated configuration "{name}"',
110
+ "modify.no_change": "No changes made",
111
+ // alias conflict
112
+ "alias.is_alias": '"{name}" is an alias for "{target}"',
113
+ "alias.conflict": '"{name}" is both an alias (→ {target}) and a config name. Which one?',
114
+ "alias.conflict_alias": "Alias (→ {target})",
115
+ "alias.conflict_config": "Config {name}",
116
+ "alias.choose_conflict": "Choose (1/2): ",
117
+ "alias.rm_which": "Which one to delete?",
118
+ "alias.rm_alias": "Alias {name}",
119
+ "alias.rm_config": "Config {target}",
120
+ "alias.rm_choose": "Choose (1/2): ",
121
+ // store errors
122
+ "store.db_not_found": "cc-switch database not found: {path}",
123
+ };
124
+ export default en;
@@ -0,0 +1,5 @@
1
+ import { type TranslationKey } from "./zh.js";
2
+ export type Locale = "zh" | "en";
3
+ export declare function setLocale(locale: Locale): void;
4
+ export declare function t(key: TranslationKey, vars?: Record<string, string>): string;
5
+ export { type TranslationKey };
@@ -0,0 +1,35 @@
1
+ import zh from "./zh.js";
2
+ import en from "./en.js";
3
+ import { readRc } from "../utils.js";
4
+ const locales = { zh, en };
5
+ function detectLocale() {
6
+ // 1. rc.json locale setting takes priority
7
+ const rc = readRc();
8
+ if (rc?.locale && rc.locale in locales)
9
+ return rc.locale;
10
+ // 2. Fallback to system LANG/LC_ALL
11
+ const lang = process.env.LC_ALL || process.env.LANG || "";
12
+ if (lang.startsWith("en"))
13
+ return "en";
14
+ // 3. Default to Chinese
15
+ return "zh";
16
+ }
17
+ let currentLocale;
18
+ function getLocale() {
19
+ if (!currentLocale)
20
+ currentLocale = detectLocale();
21
+ return currentLocale;
22
+ }
23
+ export function setLocale(locale) {
24
+ currentLocale = locale;
25
+ }
26
+ export function t(key, vars) {
27
+ const locale = getLocale();
28
+ let text = locales[locale][key] ?? locales.zh[key] ?? key;
29
+ if (vars) {
30
+ for (const [k, v] of Object.entries(vars)) {
31
+ text = text.replace(new RegExp(`\\{${k}\\}`, "g"), v);
32
+ }
33
+ }
34
+ return text;
35
+ }