@ownlate/cli 1.0.2 → 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 +225 -131
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,17 +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
33
|
}
|
|
14
34
|
authHeaders() {
|
|
15
35
|
return { Authorization: `Bearer ${this.apiKey}` };
|
|
16
36
|
}
|
|
17
37
|
async request(path, init) {
|
|
18
|
-
const res = await fetch(`${
|
|
38
|
+
const res = await fetch(`${API_URL}${path}`, {
|
|
19
39
|
...init,
|
|
20
40
|
headers: {
|
|
21
41
|
...this.authHeaders(),
|
|
@@ -34,6 +54,27 @@ var ApiClient = class {
|
|
|
34
54
|
async listProjects(workspaceId) {
|
|
35
55
|
return this.request(`/v1/projects?workspaceId=${encodeURIComponent(workspaceId)}`);
|
|
36
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
|
+
}
|
|
37
78
|
async upload(dto) {
|
|
38
79
|
return this.request("/v1/translation-files/upload", {
|
|
39
80
|
method: "POST",
|
|
@@ -49,7 +90,7 @@ var ApiClient = class {
|
|
|
49
90
|
});
|
|
50
91
|
if (params.fileId) query.set("fileId", params.fileId);
|
|
51
92
|
if (params.sourceLanguage) query.set("sourceLanguage", params.sourceLanguage);
|
|
52
|
-
const res = await fetch(`${
|
|
93
|
+
const res = await fetch(`${API_URL}/v1/segments/export?${query}`, { headers: this.authHeaders() });
|
|
53
94
|
if (!res.ok) {
|
|
54
95
|
const text = await res.text();
|
|
55
96
|
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
@@ -58,46 +99,21 @@ var ApiClient = class {
|
|
|
58
99
|
}
|
|
59
100
|
};
|
|
60
101
|
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region src/lib/global-config.ts
|
|
63
|
-
const CONFIG_PATH = (0, node_path.join)((0, node_os.homedir)(), ".ownlate");
|
|
64
|
-
const DEFAULT_API_URL = "https://api.ownlate.com";
|
|
65
|
-
async function readGlobalConfig() {
|
|
66
|
-
try {
|
|
67
|
-
const raw = await (0, node_fs_promises.readFile)(CONFIG_PATH, "utf-8");
|
|
68
|
-
return JSON.parse(raw);
|
|
69
|
-
} catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async function writeGlobalConfig(config) {
|
|
74
|
-
await (0, node_fs_promises.writeFile)(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
75
|
-
}
|
|
76
|
-
async function requireGlobalConfig() {
|
|
77
|
-
const config = await readGlobalConfig();
|
|
78
|
-
if (!config?.apiKey) throw new Error("Not logged in. Run \"ownlate login\" first.");
|
|
79
|
-
return config;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
102
|
//#endregion
|
|
83
103
|
//#region src/commands/login.ts
|
|
84
|
-
async function
|
|
85
|
-
return (await rl.question(question)).trim() || fallback;
|
|
86
|
-
}
|
|
87
|
-
async function loginCommand(options) {
|
|
104
|
+
async function loginCommand() {
|
|
88
105
|
const rl = (0, node_readline_promises.createInterface)({
|
|
89
106
|
input: process.stdin,
|
|
90
107
|
output: process.stdout
|
|
91
108
|
});
|
|
92
109
|
try {
|
|
93
|
-
const
|
|
94
|
-
const apiKey = await ask$1(rl, "API Key: ");
|
|
110
|
+
const apiKey = (await rl.question("API Key: ")).trim();
|
|
95
111
|
if (!apiKey) {
|
|
96
112
|
console.error("API Key is required.");
|
|
97
113
|
process.exit(1);
|
|
98
114
|
}
|
|
99
|
-
process.stdout.write("
|
|
100
|
-
const client = new ApiClient(
|
|
115
|
+
process.stdout.write("Connecting...");
|
|
116
|
+
const client = new ApiClient(apiKey);
|
|
101
117
|
let workspaces;
|
|
102
118
|
try {
|
|
103
119
|
workspaces = await client.listWorkspaces();
|
|
@@ -107,26 +123,17 @@ async function loginCommand(options) {
|
|
|
107
123
|
console.error(`\n${err.message}`);
|
|
108
124
|
process.exit(1);
|
|
109
125
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
currentWorkspaceId = workspaces[0].id;
|
|
114
|
-
console.log(`\nUsing workspace: ${workspaces[0].name}`);
|
|
115
|
-
} else {
|
|
116
|
-
console.log("\nAvailable workspaces:");
|
|
117
|
-
workspaces.forEach((w, i) => console.log(` ${i + 1}. ${w.name}`));
|
|
118
|
-
const choice = await ask$1(rl, "\nSelect workspace [1]: ", "1");
|
|
119
|
-
const idx = parseInt(choice, 10) - 1;
|
|
120
|
-
const picked = workspaces[Math.max(0, Math.min(idx, workspaces.length - 1))];
|
|
121
|
-
currentWorkspaceId = picked.id;
|
|
122
|
-
console.log(`Using: ${picked.name}`);
|
|
126
|
+
if (workspaces.length === 0) {
|
|
127
|
+
console.error("\nNo workspace found for this API key.");
|
|
128
|
+
process.exit(1);
|
|
123
129
|
}
|
|
130
|
+
const workspace = workspaces[0];
|
|
124
131
|
await writeGlobalConfig({
|
|
125
|
-
apiUrl,
|
|
126
132
|
apiKey,
|
|
127
|
-
|
|
133
|
+
workspaceId: workspace.id
|
|
128
134
|
});
|
|
129
|
-
console.log(
|
|
135
|
+
console.log(`\nLogged in to workspace: ${workspace.name}`);
|
|
136
|
+
console.log("Run \"ownlate project list\" to see your projects.");
|
|
130
137
|
} finally {
|
|
131
138
|
rl.close();
|
|
132
139
|
}
|
|
@@ -144,48 +151,45 @@ async function logoutCommand() {
|
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
//#endregion
|
|
147
|
-
//#region src/commands/
|
|
148
|
-
async function
|
|
154
|
+
//#region src/commands/project.ts
|
|
155
|
+
async function projectListCommand() {
|
|
149
156
|
const auth = await requireGlobalConfig();
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
152
|
-
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.");
|
|
153
160
|
return;
|
|
154
161
|
}
|
|
155
|
-
for (const
|
|
156
|
-
const marker = w.id === auth.currentWorkspaceId ? "* " : " ";
|
|
157
|
-
console.log(`${marker}${w.name} ${w.id}`);
|
|
158
|
-
}
|
|
162
|
+
for (const p of projects) console.log(` ${p.name.padEnd(32)} ${p.id}`);
|
|
159
163
|
}
|
|
160
|
-
async function
|
|
164
|
+
async function projectCreateCommand(name, options) {
|
|
161
165
|
const auth = await requireGlobalConfig();
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
console.log("Run \"ownlate workspace list\" to see available workspaces.");
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
await writeGlobalConfig({
|
|
169
|
-
...auth,
|
|
170
|
-
currentWorkspaceId: found.id
|
|
166
|
+
const rl = (0, node_readline_promises.createInterface)({
|
|
167
|
+
input: process.stdin,
|
|
168
|
+
output: process.stdout
|
|
171
169
|
});
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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();
|
|
187
192
|
}
|
|
188
|
-
for (const p of projects) console.log(` ${p.name} ${p.id}`);
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
//#endregion
|
|
@@ -201,37 +205,29 @@ async function initCommand(options) {
|
|
|
201
205
|
process.exit(1);
|
|
202
206
|
} catch {}
|
|
203
207
|
const auth = await requireGlobalConfig();
|
|
204
|
-
const client = new ApiClient(auth.
|
|
208
|
+
const client = new ApiClient(auth.apiKey);
|
|
205
209
|
const rl = (0, node_readline_promises.createInterface)({
|
|
206
210
|
input: process.stdin,
|
|
207
211
|
output: process.stdout
|
|
208
212
|
});
|
|
209
213
|
try {
|
|
210
|
-
|
|
214
|
+
let projects = [];
|
|
215
|
+
try {
|
|
216
|
+
projects = await client.listProjects(auth.workspaceId);
|
|
217
|
+
} catch {}
|
|
211
218
|
let projectId;
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const choice = await ask(rl, "Select project: ");
|
|
222
|
-
const idx = parseInt(choice, 10) - 1;
|
|
223
|
-
if (idx >= 0 && idx < projects.length) {
|
|
224
|
-
projectId = projects[idx].id;
|
|
225
|
-
console.log(`Using: ${projects[idx].name}`);
|
|
226
|
-
} else projectId = await ask(rl, "Project ID: ");
|
|
227
|
-
} else projectId = await ask(rl, "Project ID: ");
|
|
228
|
-
} else {
|
|
229
|
-
console.log("No workspace selected. Run \"ownlate workspace use <id>\" to pick one, or enter the project ID manually.\n");
|
|
230
|
-
projectId = await ask(rl, "Project ID: ");
|
|
231
|
-
}
|
|
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: ");
|
|
232
228
|
const sourceLanguage = await ask(rl, "Source language [en]: ", "en");
|
|
233
229
|
const targetLangsRaw = await ask(rl, "Target languages, comma-separated [fr,de]: ", "fr,de");
|
|
234
|
-
const sourceFile = await ask(rl, "Source file
|
|
230
|
+
const sourceFile = await ask(rl, "Source file [locales/en.json]: ", "locales/en.json");
|
|
235
231
|
const outputPattern = await ask(rl, "Output pattern [locales/{{language}}.json]: ", "locales/{{language}}.json");
|
|
236
232
|
const config = {
|
|
237
233
|
projectId,
|
|
@@ -244,9 +240,8 @@ async function initCommand(options) {
|
|
|
244
240
|
};
|
|
245
241
|
await (0, node_fs_promises.writeFile)(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
246
242
|
console.log(`\nCreated ${configPath}`);
|
|
247
|
-
console.log("\
|
|
248
|
-
console.log(" ownlate
|
|
249
|
-
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");
|
|
250
245
|
} finally {
|
|
251
246
|
rl.close();
|
|
252
247
|
}
|
|
@@ -284,24 +279,21 @@ function detectFormat$1(filePath) {
|
|
|
284
279
|
if (ext === ".po") return "po";
|
|
285
280
|
throw new Error(`Unsupported extension "${ext}". Use .json, .yaml, .yml or .po`);
|
|
286
281
|
}
|
|
287
|
-
async function
|
|
288
|
-
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(options.config)]);
|
|
289
|
-
const client = new ApiClient(auth.apiUrl, auth.apiKey);
|
|
282
|
+
async function pushAll(client, config, configPath, prefix = "") {
|
|
290
283
|
let configUpdated = false;
|
|
291
|
-
console.log(`Pushing to ${auth.apiUrl} (project: ${config.projectId})\n`);
|
|
292
284
|
for (const file of config.files) {
|
|
293
285
|
const absPath = (0, node_path.resolve)(file.source);
|
|
294
286
|
let content;
|
|
295
287
|
try {
|
|
296
288
|
content = await (0, node_fs_promises.readFile)(absPath, "utf-8");
|
|
297
289
|
} catch {
|
|
298
|
-
console.error(
|
|
290
|
+
console.error(`${prefix} [skip] ${file.source} — file not found (${absPath})`);
|
|
299
291
|
continue;
|
|
300
292
|
}
|
|
301
293
|
const format = detectFormat$1(file.source);
|
|
302
294
|
const name = (0, node_path.basename)(file.source);
|
|
303
295
|
const dir = (0, node_path.dirname)(file.source);
|
|
304
|
-
process.stdout.write(
|
|
296
|
+
process.stdout.write(`${prefix} pushing ${file.source} ...`);
|
|
305
297
|
try {
|
|
306
298
|
const result = await client.upload({
|
|
307
299
|
projectId: config.projectId,
|
|
@@ -318,12 +310,41 @@ async function pushCommand(options) {
|
|
|
318
310
|
console.log(` ${result.segmentsCreated} segments`);
|
|
319
311
|
} catch (err) {
|
|
320
312
|
console.log(" failed");
|
|
321
|
-
console.error(
|
|
313
|
+
console.error(`${prefix} [!] ${err.message}`);
|
|
322
314
|
}
|
|
323
315
|
}
|
|
324
|
-
if (configUpdated)
|
|
325
|
-
|
|
326
|
-
|
|
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);
|
|
327
348
|
}
|
|
328
349
|
}
|
|
329
350
|
|
|
@@ -337,10 +358,12 @@ function detectFormat(pattern) {
|
|
|
337
358
|
throw new Error(`Cannot detect format from output pattern "${pattern}"`);
|
|
338
359
|
}
|
|
339
360
|
async function pullCommand(options) {
|
|
340
|
-
const
|
|
341
|
-
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);
|
|
342
364
|
const languages = options.lang ? [options.lang] : config.targetLanguages;
|
|
343
|
-
console.log(`
|
|
365
|
+
console.log(`project: ${config.projectId}`);
|
|
366
|
+
console.log(`config: ${configPath}\n`);
|
|
344
367
|
for (const file of config.files) {
|
|
345
368
|
const format = detectFormat(file.output);
|
|
346
369
|
for (const language of languages) {
|
|
@@ -366,24 +389,95 @@ async function pullCommand(options) {
|
|
|
366
389
|
}
|
|
367
390
|
}
|
|
368
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
|
+
|
|
369
462
|
//#endregion
|
|
370
463
|
//#region src/index.ts
|
|
371
464
|
const program = new commander.Command();
|
|
372
|
-
program.name("ownlate").description("Sync translation files with your ownlate project").version("1.0.
|
|
373
|
-
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);
|
|
374
467
|
program.command("logout").description("Remove stored credentials").action(logoutCommand);
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
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);
|
|
379
471
|
const configOpt = [
|
|
380
472
|
"-c, --config <path>",
|
|
381
473
|
"path to project config file",
|
|
382
474
|
".ownlate.json"
|
|
383
475
|
];
|
|
384
476
|
program.command("init").description("Create .ownlate.json for this project").option(...configOpt).action(initCommand);
|
|
385
|
-
program.command("push").description("Upload source files
|
|
386
|
-
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);
|
|
387
481
|
program.parseAsync(process.argv).catch((err) => {
|
|
388
482
|
console.error(err.message);
|
|
389
483
|
process.exit(1);
|