@ownlate/cli 1.0.3 → 1.0.4
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/dist/index.cjs +226 -135
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,20 +5,37 @@ let node_fs_promises = require("node:fs/promises");
|
|
|
5
5
|
let node_os = require("node:os");
|
|
6
6
|
let node_path = require("node:path");
|
|
7
7
|
|
|
8
|
+
//#region src/lib/global-config.ts
|
|
9
|
+
const CONFIG_PATH = (0, node_path.join)((0, node_os.homedir)(), ".ownlate");
|
|
10
|
+
const API_URL = process.env.DEPLOY_ENV === "dev" ? "http://127.0.0.1:3000" : "https://api.ownlate.com";
|
|
11
|
+
async function readGlobalConfig() {
|
|
12
|
+
try {
|
|
13
|
+
const raw = await (0, node_fs_promises.readFile)(CONFIG_PATH, "utf-8");
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function writeGlobalConfig(config) {
|
|
20
|
+
await (0, node_fs_promises.writeFile)(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
21
|
+
}
|
|
22
|
+
async function requireGlobalConfig() {
|
|
23
|
+
const config = await readGlobalConfig();
|
|
24
|
+
if (!config?.apiKey) throw new Error("Not logged in. Run \"ownlate login\" first.");
|
|
25
|
+
return config;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
8
29
|
//#region src/lib/api-client.ts
|
|
9
30
|
var ApiClient = class {
|
|
10
|
-
constructor(
|
|
11
|
-
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
31
|
+
constructor(apiKey) {
|
|
12
32
|
this.apiKey = apiKey;
|
|
13
|
-
this.workspaceId = workspaceId;
|
|
14
33
|
}
|
|
15
34
|
authHeaders() {
|
|
16
|
-
|
|
17
|
-
if (this.workspaceId) headers["X-Tenant-Id"] = this.workspaceId;
|
|
18
|
-
return headers;
|
|
35
|
+
return { Authorization: `Bearer ${this.apiKey}` };
|
|
19
36
|
}
|
|
20
37
|
async request(path, init) {
|
|
21
|
-
const res = await fetch(`${
|
|
38
|
+
const res = await fetch(`${API_URL}${path}`, {
|
|
22
39
|
...init,
|
|
23
40
|
headers: {
|
|
24
41
|
...this.authHeaders(),
|
|
@@ -37,6 +54,27 @@ var ApiClient = class {
|
|
|
37
54
|
async listProjects(workspaceId) {
|
|
38
55
|
return this.request(`/v1/projects?workspaceId=${encodeURIComponent(workspaceId)}`);
|
|
39
56
|
}
|
|
57
|
+
async createProject(dto) {
|
|
58
|
+
return this.request("/v1/projects", {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
body: JSON.stringify(dto)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async getProgress(projectId, targetLanguages) {
|
|
65
|
+
const query = new URLSearchParams({
|
|
66
|
+
projectId,
|
|
67
|
+
targetLanguages: targetLanguages.join(",")
|
|
68
|
+
});
|
|
69
|
+
return this.request(`/v1/segments/progress?${query}`);
|
|
70
|
+
}
|
|
71
|
+
async preTranslate(params) {
|
|
72
|
+
await this.request("/v1/segments/pre-translate", {
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: { "Content-Type": "application/json" },
|
|
75
|
+
body: JSON.stringify(params)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
40
78
|
async upload(dto) {
|
|
41
79
|
return this.request("/v1/translation-files/upload", {
|
|
42
80
|
method: "POST",
|
|
@@ -52,7 +90,7 @@ var ApiClient = class {
|
|
|
52
90
|
});
|
|
53
91
|
if (params.fileId) query.set("fileId", params.fileId);
|
|
54
92
|
if (params.sourceLanguage) query.set("sourceLanguage", params.sourceLanguage);
|
|
55
|
-
const res = await fetch(`${
|
|
93
|
+
const res = await fetch(`${API_URL}/v1/segments/export?${query}`, { headers: this.authHeaders() });
|
|
56
94
|
if (!res.ok) {
|
|
57
95
|
const text = await res.text();
|
|
58
96
|
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
@@ -61,46 +99,21 @@ var ApiClient = class {
|
|
|
61
99
|
}
|
|
62
100
|
};
|
|
63
101
|
|
|
64
|
-
//#endregion
|
|
65
|
-
//#region src/lib/global-config.ts
|
|
66
|
-
const CONFIG_PATH = (0, node_path.join)((0, node_os.homedir)(), ".ownlate");
|
|
67
|
-
const DEFAULT_API_URL = "https://api.ownlate.com";
|
|
68
|
-
async function readGlobalConfig() {
|
|
69
|
-
try {
|
|
70
|
-
const raw = await (0, node_fs_promises.readFile)(CONFIG_PATH, "utf-8");
|
|
71
|
-
return JSON.parse(raw);
|
|
72
|
-
} catch {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
async function writeGlobalConfig(config) {
|
|
77
|
-
await (0, node_fs_promises.writeFile)(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
78
|
-
}
|
|
79
|
-
async function requireGlobalConfig() {
|
|
80
|
-
const config = await readGlobalConfig();
|
|
81
|
-
if (!config?.apiKey) throw new Error("Not logged in. Run \"ownlate login\" first.");
|
|
82
|
-
return config;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
102
|
//#endregion
|
|
86
103
|
//#region src/commands/login.ts
|
|
87
|
-
async function
|
|
88
|
-
return (await rl.question(question)).trim() || fallback;
|
|
89
|
-
}
|
|
90
|
-
async function loginCommand(options) {
|
|
104
|
+
async function loginCommand() {
|
|
91
105
|
const rl = (0, node_readline_promises.createInterface)({
|
|
92
106
|
input: process.stdin,
|
|
93
107
|
output: process.stdout
|
|
94
108
|
});
|
|
95
109
|
try {
|
|
96
|
-
const
|
|
97
|
-
const apiKey = await ask$1(rl, "API Key: ");
|
|
110
|
+
const apiKey = (await rl.question("API Key: ")).trim();
|
|
98
111
|
if (!apiKey) {
|
|
99
112
|
console.error("API Key is required.");
|
|
100
113
|
process.exit(1);
|
|
101
114
|
}
|
|
102
|
-
process.stdout.write("
|
|
103
|
-
const client = new ApiClient(
|
|
115
|
+
process.stdout.write("Connecting...");
|
|
116
|
+
const client = new ApiClient(apiKey);
|
|
104
117
|
let workspaces;
|
|
105
118
|
try {
|
|
106
119
|
workspaces = await client.listWorkspaces();
|
|
@@ -110,26 +123,17 @@ async function loginCommand(options) {
|
|
|
110
123
|
console.error(`\n${err.message}`);
|
|
111
124
|
process.exit(1);
|
|
112
125
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
currentWorkspaceId = workspaces[0].id;
|
|
117
|
-
console.log(`\nUsing workspace: ${workspaces[0].name}`);
|
|
118
|
-
} else {
|
|
119
|
-
console.log("\nAvailable workspaces:");
|
|
120
|
-
workspaces.forEach((w, i) => console.log(` ${i + 1}. ${w.name}`));
|
|
121
|
-
const choice = await ask$1(rl, "\nSelect workspace [1]: ", "1");
|
|
122
|
-
const idx = parseInt(choice, 10) - 1;
|
|
123
|
-
const picked = workspaces[Math.max(0, Math.min(idx, workspaces.length - 1))];
|
|
124
|
-
currentWorkspaceId = picked.id;
|
|
125
|
-
console.log(`Using: ${picked.name}`);
|
|
126
|
+
if (workspaces.length === 0) {
|
|
127
|
+
console.error("\nNo workspace found for this API key.");
|
|
128
|
+
process.exit(1);
|
|
126
129
|
}
|
|
130
|
+
const workspace = workspaces[0];
|
|
127
131
|
await writeGlobalConfig({
|
|
128
|
-
apiUrl,
|
|
129
132
|
apiKey,
|
|
130
|
-
|
|
133
|
+
workspaceId: workspace.id
|
|
131
134
|
});
|
|
132
|
-
console.log(
|
|
135
|
+
console.log(`\nLogged in to workspace: ${workspace.name}`);
|
|
136
|
+
console.log("Run \"ownlate project list\" to see your projects.");
|
|
133
137
|
} finally {
|
|
134
138
|
rl.close();
|
|
135
139
|
}
|
|
@@ -147,48 +151,45 @@ async function logoutCommand() {
|
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
//#endregion
|
|
150
|
-
//#region src/commands/
|
|
151
|
-
async function
|
|
154
|
+
//#region src/commands/project.ts
|
|
155
|
+
async function projectListCommand() {
|
|
152
156
|
const auth = await requireGlobalConfig();
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
console.log("No
|
|
157
|
+
const projects = await new ApiClient(auth.apiKey).listProjects(auth.workspaceId);
|
|
158
|
+
if (projects.length === 0) {
|
|
159
|
+
console.log("No projects found.");
|
|
156
160
|
return;
|
|
157
161
|
}
|
|
158
|
-
for (const
|
|
159
|
-
const marker = w.id === auth.currentWorkspaceId ? "* " : " ";
|
|
160
|
-
console.log(`${marker}${w.name} ${w.id}`);
|
|
161
|
-
}
|
|
162
|
+
for (const p of projects) console.log(` ${p.name.padEnd(32)} ${p.id}`);
|
|
162
163
|
}
|
|
163
|
-
async function
|
|
164
|
+
async function projectCreateCommand(name, options) {
|
|
164
165
|
const auth = await requireGlobalConfig();
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
console.log("Run \"ownlate workspace list\" to see available workspaces.");
|
|
169
|
-
process.exit(1);
|
|
170
|
-
}
|
|
171
|
-
await writeGlobalConfig({
|
|
172
|
-
...auth,
|
|
173
|
-
currentWorkspaceId: found.id
|
|
166
|
+
const rl = (0, node_readline_promises.createInterface)({
|
|
167
|
+
input: process.stdin,
|
|
168
|
+
output: process.stdout
|
|
174
169
|
});
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
170
|
+
try {
|
|
171
|
+
const ask = async (question, fallback = "") => {
|
|
172
|
+
return (await rl.question(question)).trim() || fallback;
|
|
173
|
+
};
|
|
174
|
+
const projectName = name ?? await ask("Project name: ");
|
|
175
|
+
if (!projectName) {
|
|
176
|
+
console.error("Project name is required.");
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const sourceLanguage = options.source ?? await ask("Source language [en]: ", "en");
|
|
180
|
+
const targetLanguages = (options.langs ?? await ask("Target languages, comma-separated [fr,de]: ", "fr,de")).split(",").map((l) => l.trim()).filter(Boolean);
|
|
181
|
+
const result = await new ApiClient(auth.apiKey).createProject({
|
|
182
|
+
workspaceId: auth.workspaceId,
|
|
183
|
+
name: projectName,
|
|
184
|
+
sourceLanguage,
|
|
185
|
+
targetLanguages
|
|
186
|
+
});
|
|
187
|
+
console.log(`\nCreated project: ${projectName}`);
|
|
188
|
+
console.log(`ID: ${result.id}`);
|
|
189
|
+
console.log(`\nRun "ownlate init" in your project directory to set it up.`);
|
|
190
|
+
} finally {
|
|
191
|
+
rl.close();
|
|
190
192
|
}
|
|
191
|
-
for (const p of projects) console.log(` ${p.name} ${p.id}`);
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
//#endregion
|
|
@@ -204,37 +205,29 @@ async function initCommand(options) {
|
|
|
204
205
|
process.exit(1);
|
|
205
206
|
} catch {}
|
|
206
207
|
const auth = await requireGlobalConfig();
|
|
207
|
-
const client = new ApiClient(auth.
|
|
208
|
+
const client = new ApiClient(auth.apiKey);
|
|
208
209
|
const rl = (0, node_readline_promises.createInterface)({
|
|
209
210
|
input: process.stdin,
|
|
210
211
|
output: process.stdout
|
|
211
212
|
});
|
|
212
213
|
try {
|
|
213
|
-
|
|
214
|
+
let projects = [];
|
|
215
|
+
try {
|
|
216
|
+
projects = await client.listProjects(auth.workspaceId);
|
|
217
|
+
} catch {}
|
|
214
218
|
let projectId;
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const choice = await ask(rl, "Select project: ");
|
|
225
|
-
const idx = parseInt(choice, 10) - 1;
|
|
226
|
-
if (idx >= 0 && idx < projects.length) {
|
|
227
|
-
projectId = projects[idx].id;
|
|
228
|
-
console.log(`Using: ${projects[idx].name}`);
|
|
229
|
-
} else projectId = await ask(rl, "Project ID: ");
|
|
230
|
-
} else projectId = await ask(rl, "Project ID: ");
|
|
231
|
-
} else {
|
|
232
|
-
console.log("No workspace selected. Run \"ownlate workspace use <id>\" to pick one, or enter the project ID manually.\n");
|
|
233
|
-
projectId = await ask(rl, "Project ID: ");
|
|
234
|
-
}
|
|
219
|
+
if (projects.length > 0) {
|
|
220
|
+
console.log("Projects:\n");
|
|
221
|
+
projects.forEach((p, i) => console.log(` ${i + 1}. ${p.name} (${p.id})`));
|
|
222
|
+
console.log(` ${projects.length + 1}. Enter ID manually\n`);
|
|
223
|
+
const choice = await ask(rl, "Select project: ");
|
|
224
|
+
const idx = parseInt(choice, 10) - 1;
|
|
225
|
+
if (idx >= 0 && idx < projects.length) projectId = projects[idx].id;
|
|
226
|
+
else projectId = await ask(rl, "Project ID: ");
|
|
227
|
+
} else projectId = await ask(rl, "Project ID: ");
|
|
235
228
|
const sourceLanguage = await ask(rl, "Source language [en]: ", "en");
|
|
236
229
|
const targetLangsRaw = await ask(rl, "Target languages, comma-separated [fr,de]: ", "fr,de");
|
|
237
|
-
const sourceFile = await ask(rl, "Source file
|
|
230
|
+
const sourceFile = await ask(rl, "Source file [locales/en.json]: ", "locales/en.json");
|
|
238
231
|
const outputPattern = await ask(rl, "Output pattern [locales/{{language}}.json]: ", "locales/{{language}}.json");
|
|
239
232
|
const config = {
|
|
240
233
|
projectId,
|
|
@@ -247,9 +240,8 @@ async function initCommand(options) {
|
|
|
247
240
|
};
|
|
248
241
|
await (0, node_fs_promises.writeFile)(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
249
242
|
console.log(`\nCreated ${configPath}`);
|
|
250
|
-
console.log("\
|
|
251
|
-
console.log(" ownlate
|
|
252
|
-
console.log(" ownlate pull — download translations to local files");
|
|
243
|
+
console.log("\n ownlate push — upload source strings");
|
|
244
|
+
console.log(" ownlate pull — download translations");
|
|
253
245
|
} finally {
|
|
254
246
|
rl.close();
|
|
255
247
|
}
|
|
@@ -287,24 +279,21 @@ function detectFormat$1(filePath) {
|
|
|
287
279
|
if (ext === ".po") return "po";
|
|
288
280
|
throw new Error(`Unsupported extension "${ext}". Use .json, .yaml, .yml or .po`);
|
|
289
281
|
}
|
|
290
|
-
async function
|
|
291
|
-
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(options.config)]);
|
|
292
|
-
const client = new ApiClient(auth.apiUrl, auth.apiKey, auth.currentWorkspaceId);
|
|
282
|
+
async function pushAll(client, config, configPath, prefix = "") {
|
|
293
283
|
let configUpdated = false;
|
|
294
|
-
console.log(`Pushing to ${auth.apiUrl} (project: ${config.projectId})\n`);
|
|
295
284
|
for (const file of config.files) {
|
|
296
285
|
const absPath = (0, node_path.resolve)(file.source);
|
|
297
286
|
let content;
|
|
298
287
|
try {
|
|
299
288
|
content = await (0, node_fs_promises.readFile)(absPath, "utf-8");
|
|
300
289
|
} catch {
|
|
301
|
-
console.error(
|
|
290
|
+
console.error(`${prefix} [skip] ${file.source} — file not found (${absPath})`);
|
|
302
291
|
continue;
|
|
303
292
|
}
|
|
304
293
|
const format = detectFormat$1(file.source);
|
|
305
294
|
const name = (0, node_path.basename)(file.source);
|
|
306
295
|
const dir = (0, node_path.dirname)(file.source);
|
|
307
|
-
process.stdout.write(
|
|
296
|
+
process.stdout.write(`${prefix} pushing ${file.source} ...`);
|
|
308
297
|
try {
|
|
309
298
|
const result = await client.upload({
|
|
310
299
|
projectId: config.projectId,
|
|
@@ -321,12 +310,41 @@ async function pushCommand(options) {
|
|
|
321
310
|
console.log(` ${result.segmentsCreated} segments`);
|
|
322
311
|
} catch (err) {
|
|
323
312
|
console.log(" failed");
|
|
324
|
-
console.error(
|
|
313
|
+
console.error(`${prefix} [!] ${err.message}`);
|
|
325
314
|
}
|
|
326
315
|
}
|
|
327
|
-
if (configUpdated)
|
|
328
|
-
|
|
329
|
-
|
|
316
|
+
if (configUpdated) await writeProjectConfig(configPath, config);
|
|
317
|
+
}
|
|
318
|
+
async function pushCommand(options) {
|
|
319
|
+
const configPath = (0, node_path.resolve)(options.config);
|
|
320
|
+
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(configPath)]);
|
|
321
|
+
const client = new ApiClient(auth.apiKey);
|
|
322
|
+
console.log(`project: ${config.projectId}`);
|
|
323
|
+
console.log(`config: ${configPath}\n`);
|
|
324
|
+
await pushAll(client, config, configPath);
|
|
325
|
+
if (!options.watch) return;
|
|
326
|
+
const sources = config.files.map((f) => (0, node_path.resolve)(f.source));
|
|
327
|
+
console.log(`\nWatching ${sources.map((s) => s.split("/").pop()).join(", ")}...\n`);
|
|
328
|
+
const timers = /* @__PURE__ */ new Map();
|
|
329
|
+
for (const absPath of sources) watchFile(absPath, client, config, configPath, timers);
|
|
330
|
+
}
|
|
331
|
+
async function watchFile(absPath, client, config, configPath, timers) {
|
|
332
|
+
try {
|
|
333
|
+
const watcher = (0, node_fs_promises.watch)(absPath);
|
|
334
|
+
for await (const event of watcher) {
|
|
335
|
+
if (event.eventType !== "change") continue;
|
|
336
|
+
const existing = timers.get(absPath);
|
|
337
|
+
if (existing) clearTimeout(existing);
|
|
338
|
+
timers.set(absPath, setTimeout(async () => {
|
|
339
|
+
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
340
|
+
process.stdout.write(`[${time}] ${absPath.split("/").pop()} changed → `);
|
|
341
|
+
await pushAll(client, config, configPath, "");
|
|
342
|
+
}, 300));
|
|
343
|
+
}
|
|
344
|
+
} catch {
|
|
345
|
+
setTimeout(() => {
|
|
346
|
+
watchFile(absPath, client, config, configPath, timers);
|
|
347
|
+
}, 1e3);
|
|
330
348
|
}
|
|
331
349
|
}
|
|
332
350
|
|
|
@@ -340,10 +358,12 @@ function detectFormat(pattern) {
|
|
|
340
358
|
throw new Error(`Cannot detect format from output pattern "${pattern}"`);
|
|
341
359
|
}
|
|
342
360
|
async function pullCommand(options) {
|
|
343
|
-
const
|
|
344
|
-
const
|
|
361
|
+
const configPath = (0, node_path.resolve)(options.config);
|
|
362
|
+
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(configPath)]);
|
|
363
|
+
const client = new ApiClient(auth.apiKey);
|
|
345
364
|
const languages = options.lang ? [options.lang] : config.targetLanguages;
|
|
346
|
-
console.log(`
|
|
365
|
+
console.log(`project: ${config.projectId}`);
|
|
366
|
+
console.log(`config: ${configPath}\n`);
|
|
347
367
|
for (const file of config.files) {
|
|
348
368
|
const format = detectFormat(file.output);
|
|
349
369
|
for (const language of languages) {
|
|
@@ -369,24 +389,95 @@ async function pullCommand(options) {
|
|
|
369
389
|
}
|
|
370
390
|
}
|
|
371
391
|
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region src/commands/status.ts
|
|
394
|
+
const BAR_WIDTH = 24;
|
|
395
|
+
function bar(progress) {
|
|
396
|
+
const filled = Math.round(progress / 100 * BAR_WIDTH);
|
|
397
|
+
return "█".repeat(filled) + "░".repeat(BAR_WIDTH - filled);
|
|
398
|
+
}
|
|
399
|
+
function pad(s, n) {
|
|
400
|
+
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
401
|
+
}
|
|
402
|
+
async function statusCommand(options) {
|
|
403
|
+
const configPath = (0, node_path.resolve)(options.config);
|
|
404
|
+
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(configPath)]);
|
|
405
|
+
const client = new ApiClient(auth.apiKey);
|
|
406
|
+
let items;
|
|
407
|
+
try {
|
|
408
|
+
items = await client.getProgress(config.projectId, config.targetLanguages);
|
|
409
|
+
} catch (err) {
|
|
410
|
+
console.error(`Failed: ${err.message}`);
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
if (items.length === 0) {
|
|
414
|
+
console.log("No segments found for this project.");
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
console.log(`project: ${config.projectId}\n`);
|
|
418
|
+
for (const item of items) {
|
|
419
|
+
const pct = `${item.progress}%`.padStart(4);
|
|
420
|
+
const detail = `(${item.approved}/${item.total} approved)`;
|
|
421
|
+
console.log(` ${pad(item.language, 6)} ${bar(item.progress)} ${pct} ${detail}`);
|
|
422
|
+
}
|
|
423
|
+
const minArg = options.min ? parseInt(options.min, 10) : void 0;
|
|
424
|
+
if (minArg !== void 0) {
|
|
425
|
+
const below = items.filter((i) => i.progress < minArg);
|
|
426
|
+
if (below.length > 0) {
|
|
427
|
+
console.error(`\nFailed: ${below.map((i) => i.language).join(", ")} below ${minArg}%`);
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region src/commands/translate.ts
|
|
435
|
+
async function translateCommand(options) {
|
|
436
|
+
const configPath = (0, node_path.resolve)(options.config);
|
|
437
|
+
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(configPath)]);
|
|
438
|
+
const client = new ApiClient(auth.apiKey);
|
|
439
|
+
const languages = options.lang ? [options.lang] : config.targetLanguages;
|
|
440
|
+
const minTmScore = options.score ? parseInt(options.score, 10) : 100;
|
|
441
|
+
console.log(`project: ${config.projectId}`);
|
|
442
|
+
console.log(`score: ≥${minTmScore}%\n`);
|
|
443
|
+
for (const lang of languages) {
|
|
444
|
+
process.stdout.write(` pre-translating ${lang} ...`);
|
|
445
|
+
try {
|
|
446
|
+
await client.preTranslate({
|
|
447
|
+
projectId: config.projectId,
|
|
448
|
+
workspaceId: auth.workspaceId,
|
|
449
|
+
targetLanguage: lang,
|
|
450
|
+
sourceLanguage: config.sourceLanguage,
|
|
451
|
+
minTmScore
|
|
452
|
+
});
|
|
453
|
+
console.log(" queued");
|
|
454
|
+
} catch (err) {
|
|
455
|
+
console.log(" failed");
|
|
456
|
+
console.error(` [!] ${err.message}`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
console.log("\nTranslation runs in background. Use \"ownlate status\" to check progress.");
|
|
460
|
+
}
|
|
461
|
+
|
|
372
462
|
//#endregion
|
|
373
463
|
//#region src/index.ts
|
|
374
464
|
const program = new commander.Command();
|
|
375
|
-
program.name("ownlate").description("Sync translation files with your ownlate project").version("1.0.
|
|
376
|
-
program.command("login").description("Log in with your API key").
|
|
465
|
+
program.name("ownlate").description("Sync translation files with your ownlate project").version("1.0.4");
|
|
466
|
+
program.command("login").description("Log in with your API key").action(loginCommand);
|
|
377
467
|
program.command("logout").description("Remove stored credentials").action(logoutCommand);
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
program.command("project").description("Manage projects").command("list").description("List projects in the current workspace").action(projectListCommand);
|
|
468
|
+
const project = program.command("project").description("Manage projects");
|
|
469
|
+
project.command("list").description("List projects in your workspace").action(projectListCommand);
|
|
470
|
+
project.command("create [name]").description("Create a new project").option("-s, --source <lang>", "source language (e.g. en)").option("-l, --langs <codes>", "target languages, comma-separated (e.g. fr,de)").action(projectCreateCommand);
|
|
382
471
|
const configOpt = [
|
|
383
472
|
"-c, --config <path>",
|
|
384
473
|
"path to project config file",
|
|
385
474
|
".ownlate.json"
|
|
386
475
|
];
|
|
387
476
|
program.command("init").description("Create .ownlate.json for this project").option(...configOpt).action(initCommand);
|
|
388
|
-
program.command("push").description("Upload source files
|
|
389
|
-
program.command("pull").description("Download
|
|
477
|
+
program.command("push").description("Upload source files to ownlate").option(...configOpt).option("-w, --watch", "watch source files and push on change").action(pushCommand);
|
|
478
|
+
program.command("pull").description("Download translations to local files").option(...configOpt).option("-l, --lang <code>", "pull a single language only").action(pullCommand);
|
|
479
|
+
program.command("status").description("Show translation progress for each language").option(...configOpt).option("--min <percent>", "exit with code 1 if any language is below this threshold (for CI)").action(statusCommand);
|
|
480
|
+
program.command("translate").description("Pre-translate untranslated segments using Translation Memory").option(...configOpt).option("-l, --lang <code>", "translate a single language only").option("-s, --score <n>", "minimum TM match score 0–100 (default: 100)").action(translateCommand);
|
|
390
481
|
program.parseAsync(process.argv).catch((err) => {
|
|
391
482
|
console.error(err.message);
|
|
392
483
|
process.exit(1);
|