babaofan-translate-cli 1.0.0 → 1.0.2-beta.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 +31 -42
- package/package.json +12 -3
- package/utils.js +244 -0
package/README.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# babaofan-translate-cli
|
|
2
2
|
|
|
3
|
+
> 当前版本为 **Beta 测试版**。功能还在快速迭代,存在已知问题与兼容性风险,生产项目请先使用 `--dry-run` 验证,再决定是否正式改写源码。
|
|
4
|
+
>
|
|
5
|
+
> Beta 安装方式:
|
|
6
|
+
>
|
|
7
|
+
> ```bash
|
|
8
|
+
> npm install -D babaofan-translate-cli@beta
|
|
9
|
+
> ```
|
|
10
|
+
>
|
|
11
|
+
> Beta 临时执行方式:
|
|
12
|
+
>
|
|
13
|
+
> ```bash
|
|
14
|
+
> npx babaofan-translate-cli@beta create --languages zh-cn,en --files "src/**/*.vue,src/**/*.ts" --dry-run
|
|
15
|
+
> ```
|
|
16
|
+
>
|
|
17
|
+
> GitHub 仓库地址:
|
|
18
|
+
>
|
|
19
|
+
> ```text
|
|
20
|
+
> https://github.com/2285907229/babaofan-translate-cli
|
|
21
|
+
> ```
|
|
22
|
+
|
|
3
23
|
一个本地运行的 i18n 翻译 CLI。
|
|
4
24
|
|
|
5
25
|
它的目标很直接:扫描前端项目中的 `t("...")`、`$t("...")` 这类国际化调用,把中文词条提取出来,自动生成稳定的 i18n key,把源码里的中文调用改写成 key 调用,然后直接在本地通过 Google AI Studio 的 Gemini 模型完成翻译,最后生成对应的多语言文件。
|
|
@@ -89,56 +109,19 @@ npm install
|
|
|
89
109
|
- 已安装当前项目依赖
|
|
90
110
|
- 能访问 Google AI Studio / Gemini 接口
|
|
91
111
|
|
|
92
|
-
## 发布到 npm
|
|
93
|
-
|
|
94
|
-
发布前建议先确认:
|
|
95
|
-
|
|
96
|
-
- [package.json](./package.json) 中的 `name`、`version`、`description` 已经正确
|
|
97
|
-
- [config.js](./config.js) 里不要保留你自己的真实 API Key
|
|
98
|
-
- 本地已经执行过测试命令
|
|
99
|
-
|
|
100
|
-
### 1. 登录 npm
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
npm login
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### 2. 检查最终会发布哪些文件
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
npm pack --dry-run
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### 3. 发布
|
|
113
|
-
|
|
114
|
-
首次发布:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
npm publish
|
|
118
|
-
```
|
|
119
112
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
npm version patch
|
|
124
|
-
npm publish
|
|
125
|
-
```
|
|
113
|
+
### 安装
|
|
126
114
|
|
|
127
|
-
|
|
115
|
+
建议作为开发依赖安装:
|
|
128
116
|
|
|
129
117
|
```bash
|
|
130
|
-
npm
|
|
131
|
-
npm version major
|
|
118
|
+
npm install -D babaofan-translate-cli
|
|
132
119
|
```
|
|
133
120
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
### 安装
|
|
137
|
-
|
|
138
|
-
建议作为开发依赖安装:
|
|
121
|
+
如果你想明确安装测试版,建议直接指定 `beta` tag:
|
|
139
122
|
|
|
140
123
|
```bash
|
|
141
|
-
npm install -D babaofan-translate-cli
|
|
124
|
+
npm install -D babaofan-translate-cli@beta
|
|
142
125
|
```
|
|
143
126
|
|
|
144
127
|
或者:
|
|
@@ -159,6 +142,12 @@ yarn add -D babaofan-translate-cli
|
|
|
159
142
|
npx babaofan-translate create --languages zh-cn,en --files "src/**/*.vue,src/**/*.tsx" --dry-run
|
|
160
143
|
```
|
|
161
144
|
|
|
145
|
+
如果你想强制使用 Beta 版本测试:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx babaofan-translate-cli@beta create --languages zh-cn,en --files "src/**/*.vue,src/**/*.tsx" --dry-run
|
|
149
|
+
```
|
|
150
|
+
|
|
162
151
|
### 方式 2:写进项目 scripts
|
|
163
152
|
|
|
164
153
|
在目标项目的 `package.json` 里加:
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "babaofan-translate-cli",
|
|
4
|
-
"version": "1.0.0",
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "1.0.2-beta.0",
|
|
5
|
+
"description": "Beta local CLI for scanning i18n calls, generating semantic keys, translating with Google AI Studio, and updating locale files.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"babaofan-translate": "./bin/i18n.js"
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"bin",
|
|
15
15
|
"config.js",
|
|
16
|
-
"README.md"
|
|
16
|
+
"README.md",
|
|
17
|
+
"utils.js"
|
|
17
18
|
],
|
|
18
19
|
"keywords": [
|
|
19
20
|
"i18n",
|
|
@@ -28,6 +29,14 @@
|
|
|
28
29
|
],
|
|
29
30
|
"author": "",
|
|
30
31
|
"license": "MIT",
|
|
32
|
+
"homepage": "https://github.com/2285907229/babaofan-translate-cli#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/2285907229/babaofan-translate-cli/issues"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/2285907229/babaofan-translate-cli.git"
|
|
39
|
+
},
|
|
31
40
|
"dependencies": {
|
|
32
41
|
"@google/genai": "^1.24.0",
|
|
33
42
|
"@babel/parser": "^7.28.6",
|
package/utils.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 公共工具函数模块
|
|
5
|
+
* 提供跨脚本使用的通用功能
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import os from "os";
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// 路径工具
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 获取项目根目录
|
|
20
|
+
* @returns {string} 项目根目录路径
|
|
21
|
+
*/
|
|
22
|
+
function getProjectRoot() {
|
|
23
|
+
// return "/Users/jiangshuai/Desktop/coding/cool/uniapp/cool-unix";
|
|
24
|
+
return process.cwd();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 获取相对于项目根目录的路径
|
|
29
|
+
* @param {string} absolutePath - 绝对路径
|
|
30
|
+
* @param {string} projectRoot - 项目根目录(可选)
|
|
31
|
+
* @returns {string} 相对路径
|
|
32
|
+
*/
|
|
33
|
+
function getRelativePath(absolutePath, projectRoot = getProjectRoot()) {
|
|
34
|
+
return absolutePath.replace(projectRoot, "").replace(/^\//, "");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// 文件系统工具
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 确保目录存在,不存在则创建
|
|
43
|
+
* @param {string} filePath - 文件路径或目录路径
|
|
44
|
+
*/
|
|
45
|
+
function ensureDirectoryExists(filePath) {
|
|
46
|
+
const dirPath = path.dirname(filePath);
|
|
47
|
+
if (!fs.existsSync(dirPath)) {
|
|
48
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 递归复制目录/文件(覆盖模式)
|
|
54
|
+
* @param {string} src - 源路径
|
|
55
|
+
* @param {string} dest - 目标路径
|
|
56
|
+
*/
|
|
57
|
+
function copyRecursive(src, dest) {
|
|
58
|
+
const stat = fs.statSync(src);
|
|
59
|
+
|
|
60
|
+
if (fs.existsSync(dest)) {
|
|
61
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (stat.isDirectory()) {
|
|
65
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
66
|
+
const items = fs.readdirSync(src);
|
|
67
|
+
for (const item of items) {
|
|
68
|
+
const srcPath = path.join(src, item);
|
|
69
|
+
const destPath = path.join(dest, item);
|
|
70
|
+
copyRecursive(srcPath, destPath);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
fs.copyFileSync(src, dest);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 清理目录
|
|
79
|
+
* @param {string} dirPath - 目录路径
|
|
80
|
+
* @returns {boolean} 是否成功清理
|
|
81
|
+
*/
|
|
82
|
+
function cleanDirectory(dirPath) {
|
|
83
|
+
if (fs.existsSync(dirPath)) {
|
|
84
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// 终端日志工具
|
|
92
|
+
// ============================================================================
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 统一的日志工具类
|
|
96
|
+
*/
|
|
97
|
+
const logger = {
|
|
98
|
+
/**
|
|
99
|
+
* 打印标题
|
|
100
|
+
* @param {string} text - 标题文本
|
|
101
|
+
*/
|
|
102
|
+
title(text) {
|
|
103
|
+
console.log();
|
|
104
|
+
console.log(chalk.white.bold(text));
|
|
105
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 打印信息
|
|
110
|
+
* @param {string} label - 标签
|
|
111
|
+
* @param {string} value - 值
|
|
112
|
+
*/
|
|
113
|
+
info(label, value) {
|
|
114
|
+
console.log(chalk.gray(" ●"), chalk.white(label), chalk.cyan(value));
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 打印成功消息
|
|
119
|
+
* @param {string} text - 消息文本
|
|
120
|
+
*/
|
|
121
|
+
success(text) {
|
|
122
|
+
console.log(chalk.green(" ✓"), chalk.white(text));
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 打印警告消息
|
|
127
|
+
* @param {string} text - 警告文本
|
|
128
|
+
*/
|
|
129
|
+
warn(text) {
|
|
130
|
+
console.log(chalk.yellow(" ⚠"), chalk.white(text));
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 打印错误消息
|
|
135
|
+
* @param {string} text - 错误文本
|
|
136
|
+
*/
|
|
137
|
+
error(text) {
|
|
138
|
+
console.error(chalk.red(" ✗"), chalk.white(text));
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 打印文件路径
|
|
143
|
+
* @param {string} filePath - 文件路径
|
|
144
|
+
* @param {string} projectRoot - 项目根目录(可选)
|
|
145
|
+
*/
|
|
146
|
+
file(filePath, projectRoot = getProjectRoot()) {
|
|
147
|
+
const relativePath = getRelativePath(filePath, projectRoot);
|
|
148
|
+
console.log(chalk.gray(" +"), chalk.green(relativePath));
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 打印分隔线
|
|
153
|
+
*/
|
|
154
|
+
divider() {
|
|
155
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 打印空行
|
|
160
|
+
*/
|
|
161
|
+
newline() {
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// 命令行工具
|
|
168
|
+
// ============================================================================
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 跨平台检测命令是否存在
|
|
172
|
+
* @param {string} command - 要检测的命令
|
|
173
|
+
* @returns {boolean} 命令是否存在
|
|
174
|
+
*/
|
|
175
|
+
function commandExists(command) {
|
|
176
|
+
const isWindows = os.platform() === "win32";
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
if (isWindows) {
|
|
180
|
+
try {
|
|
181
|
+
execSync(`where ${command}`, { stdio: "ignore", shell: "cmd.exe" });
|
|
182
|
+
return true;
|
|
183
|
+
} catch {
|
|
184
|
+
execSync(`${command} --version`, {
|
|
185
|
+
stdio: "ignore",
|
|
186
|
+
shell: "cmd.exe",
|
|
187
|
+
timeout: 3000
|
|
188
|
+
});
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
try {
|
|
193
|
+
execSync(`which ${command}`, { stdio: "ignore" });
|
|
194
|
+
return true;
|
|
195
|
+
} catch {
|
|
196
|
+
execSync(`${command} --version`, {
|
|
197
|
+
stdio: "ignore",
|
|
198
|
+
timeout: 3000
|
|
199
|
+
});
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ============================================================================
|
|
209
|
+
// 字符串工具
|
|
210
|
+
// ============================================================================
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 将连字符命名转换为驼峰命名
|
|
214
|
+
* @param {string} str - 连字符字符串
|
|
215
|
+
* @returns {string} 驼峰命名字符串
|
|
216
|
+
*/
|
|
217
|
+
function toCamelCase(str) {
|
|
218
|
+
return str.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 将驼峰命名转换为连字符命名
|
|
223
|
+
* @param {string} str - 驼峰字符串
|
|
224
|
+
* @returns {string} 连字符命名字符串
|
|
225
|
+
*/
|
|
226
|
+
function toKebabCase(str) {
|
|
227
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// 导出
|
|
232
|
+
// ============================================================================
|
|
233
|
+
|
|
234
|
+
export {
|
|
235
|
+
getProjectRoot,
|
|
236
|
+
getRelativePath,
|
|
237
|
+
ensureDirectoryExists,
|
|
238
|
+
copyRecursive,
|
|
239
|
+
cleanDirectory,
|
|
240
|
+
logger,
|
|
241
|
+
commandExists,
|
|
242
|
+
toCamelCase,
|
|
243
|
+
toKebabCase
|
|
244
|
+
};
|