locai 2.3.0 → 2.4.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/dist/cli/helpers.d.ts +2 -0
- package/dist/cli/helpers.js +344 -0
- package/dist/cli.js +38 -519
- package/dist/commands/translator.d.ts +1 -1
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +26 -0
- package/dist/config/setup.d.ts +10 -0
- package/dist/config/setup.js +79 -0
- package/dist/services/language-processor.d.ts +22 -0
- package/dist/services/language-processor.js +239 -0
- package/dist/services/translation-service.d.ts +1 -49
- package/dist/services/translation-service.js +3 -215
- package/dist/types/index.d.ts +58 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/ui-manager.js +2 -2
- package/package.json +15 -7
package/dist/cli.js
CHANGED
|
@@ -1,147 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import dotenv from "dotenv";
|
|
3
|
-
import { promises as fs } from "fs";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import os from "os";
|
|
6
2
|
import { createRequire } from "module";
|
|
7
3
|
import { program } from "commander";
|
|
8
|
-
import { translateFile, findLocaleFiles, validateAndFixExistingTranslations, } from "./commands/translator.js";
|
|
9
|
-
import ProviderFactory from "./core/provider-factory.js";
|
|
10
|
-
import { FileManager } from "./utils/file-manager.js";
|
|
11
|
-
import rateLimiter from "./utils/rate-limiter.js";
|
|
12
|
-
import Orchestrator from "./core/orchestrator.js";
|
|
13
|
-
import InputValidator from "./utils/input-validator.js";
|
|
14
4
|
import ErrorHelper from "./utils/error-helper.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
5
|
+
import { loadConfig, validateEnvironment } from "./config/index.js";
|
|
6
|
+
import { loadEnvironmentVariables, configureComponents } from "./config/setup.js";
|
|
7
|
+
import { autoDetectConfig, runCommand } from "./cli/helpers.js";
|
|
8
|
+
import InputValidator from "./utils/input-validator.js";
|
|
19
9
|
// Use createRequire to load package.json in ESM context
|
|
20
10
|
const require = createRequire(import.meta.url);
|
|
21
11
|
const { version } = require("../package.json");
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// First load .env (base defaults), then .env.local (local overrides)
|
|
29
|
-
// This ensures .env.local values take precedence over .env values
|
|
30
|
-
const envFiles = [
|
|
31
|
-
{ file: ".env", override: false },
|
|
32
|
-
{ file: ".env.local", override: true },
|
|
33
|
-
];
|
|
34
|
-
let loadedCount = 0;
|
|
35
|
-
for (const { file: envFile, override } of envFiles) {
|
|
36
|
-
const envPath = path.resolve(process.cwd(), envFile);
|
|
37
|
-
try {
|
|
38
|
-
await fs.access(envPath);
|
|
39
|
-
const result = dotenv.config({ path: envPath, override });
|
|
40
|
-
if (!result.error) {
|
|
41
|
-
loadedCount++;
|
|
42
|
-
// Only log in verbose/debug mode to reduce noise
|
|
43
|
-
if (process.env.VERBOSE || process.env.DEBUG) {
|
|
44
|
-
console.log(`Loaded environment variables from ${envFile}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
// ENOENT is expected when files don't exist - silently skip
|
|
50
|
-
if (error.code !== "ENOENT") {
|
|
51
|
-
console.warn(`Warning: Could not load ${envFile}: ${error.message}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Informative message only if verbose/debug and at least one file was loaded
|
|
56
|
-
if ((process.env.VERBOSE || process.env.DEBUG) && loadedCount > 0) {
|
|
57
|
-
console.log(`Environment: ${loadedCount} file(s) loaded`);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
// Local config loader replaced by src/config/index.ts
|
|
61
|
-
/**
|
|
62
|
-
* Configure global components (logger, rate limiter, etc.).
|
|
63
|
-
* @param {any} config - Configuration object.
|
|
64
|
-
*/
|
|
65
|
-
const configureComponents = (config) => {
|
|
66
|
-
// Configure FileManager with config settings
|
|
67
|
-
if (config.fileOperations) {
|
|
68
|
-
FileManager.configure(config.fileOperations);
|
|
69
|
-
}
|
|
70
|
-
// Initialize logger with config
|
|
71
|
-
if (config.logging) {
|
|
72
|
-
const logger = getLogger(config.logging);
|
|
73
|
-
if (config.logging.verbose || config.debug) {
|
|
74
|
-
logger.info("Logger initialized", {
|
|
75
|
-
saveErrorLogs: config.logging.saveErrorLogs,
|
|
76
|
-
logDirectory: config.logging.logDirectory,
|
|
77
|
-
rotationEnabled: config.logging.logRotation?.enabled,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (config.rateLimiter) {
|
|
82
|
-
rateLimiter.updateConfig({
|
|
83
|
-
queueStrategy: config.rateLimiter.queueStrategy || "priority",
|
|
84
|
-
queueTimeout: config.rateLimiter.queueTimeout || 30000,
|
|
85
|
-
adaptiveThrottling: config.rateLimiter.adaptiveThrottling !== false,
|
|
86
|
-
providerLimits: config.rateLimiter.providerLimits,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
if (config.advanced?.debug) {
|
|
90
|
-
process.env.DEBUG = "true";
|
|
91
|
-
}
|
|
92
|
-
if (config.logging?.verbose) {
|
|
93
|
-
process.env.VERBOSE = "true";
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* Configure and run the CLI program.
|
|
98
|
-
* @param {any} defaultConfig - Default configuration.
|
|
99
|
-
* @returns {Promise<any>} - Final configuration.
|
|
100
|
-
*/
|
|
101
|
-
const configureCLI = async (defaultConfig) => {
|
|
102
|
-
// Auto-detect framework and configuration if not explicitly set
|
|
103
|
-
// This makes the tool framework-agnostic
|
|
104
|
-
const detected = await FrameworkDetector.detect(process.cwd());
|
|
105
|
-
if (detected) {
|
|
106
|
-
// Set locales directory if not configured
|
|
107
|
-
if (!defaultConfig.localesDir && detected.localesDir) {
|
|
108
|
-
defaultConfig.localesDir = detected.localesDir;
|
|
109
|
-
if (process.env.VERBOSE) {
|
|
110
|
-
console.log(`Auto-detected locales directory: ${detected.localesDir}`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Set file format if not configured
|
|
114
|
-
if (!defaultConfig.fileOperations)
|
|
115
|
-
defaultConfig.fileOperations = {};
|
|
116
|
-
if ((!defaultConfig.fileOperations.format ||
|
|
117
|
-
defaultConfig.fileOperations.format === "auto") &&
|
|
118
|
-
detected.fileFormat) {
|
|
119
|
-
defaultConfig.fileOperations.format = detected.fileFormat;
|
|
120
|
-
if (process.env.VERBOSE) {
|
|
121
|
-
console.log(`Auto-detected file format: ${detected.fileFormat}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Auto-detect file structure (flat vs nested)
|
|
126
|
-
if (!defaultConfig.fileOperations)
|
|
127
|
-
defaultConfig.fileOperations = {};
|
|
128
|
-
if (!defaultConfig.fileOperations.fileStructure ||
|
|
129
|
-
defaultConfig.fileOperations.fileStructure === "auto") {
|
|
130
|
-
const targetDir = defaultConfig.localesDir || "./locales";
|
|
131
|
-
const structure = await StructureDetector.detect(targetDir);
|
|
132
|
-
if (structure !== LocaleStructure.UNKNOWN) {
|
|
133
|
-
defaultConfig.fileOperations.fileStructure = structure;
|
|
134
|
-
if (process.env.VERBOSE) {
|
|
135
|
-
console.log(`Auto-detected file structure: ${structure}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
// Default to flat if unknown
|
|
140
|
-
defaultConfig.fileOperations.fileStructure = LocaleStructure.FLAT;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
12
|
+
const main = async () => {
|
|
13
|
+
await loadEnvironmentVariables();
|
|
14
|
+
// Load configuration
|
|
15
|
+
const loaded = await loadConfig();
|
|
16
|
+
let defaultConfig = loaded.config || {};
|
|
17
|
+
defaultConfig = await autoDetectConfig(defaultConfig);
|
|
143
18
|
configureComponents(defaultConfig);
|
|
144
|
-
|
|
19
|
+
validateEnvironment(defaultConfig);
|
|
145
20
|
const toolVersion = defaultConfig.version || version;
|
|
146
21
|
program
|
|
147
22
|
.name("locai")
|
|
@@ -161,278 +36,6 @@ const configureCLI = async (defaultConfig) => {
|
|
|
161
36
|
process.env.VERBOSE = "true";
|
|
162
37
|
console.log("Verbose mode: ENABLED (detailed diagnostics)");
|
|
163
38
|
});
|
|
164
|
-
const runCommand = async (options, commandOptions, commandName) => {
|
|
165
|
-
try {
|
|
166
|
-
const validCommands = ["translate", "fix", "analyze", "advanced"];
|
|
167
|
-
if (!validCommands.includes(commandName)) {
|
|
168
|
-
throw new Error(`Invalid command: ${commandName}`);
|
|
169
|
-
}
|
|
170
|
-
const globalOpts = program.opts();
|
|
171
|
-
if (globalOpts.debug) {
|
|
172
|
-
const safeGlobalOpts = { ...globalOpts };
|
|
173
|
-
const safeCommandOptions = { ...commandOptions };
|
|
174
|
-
if (safeGlobalOpts.targets) {
|
|
175
|
-
safeGlobalOpts.targets = `[${safeGlobalOpts.targets.length} languages]`;
|
|
176
|
-
}
|
|
177
|
-
if (safeGlobalOpts.localesDir) {
|
|
178
|
-
safeGlobalOpts.localesDir = "[directory_path]";
|
|
179
|
-
}
|
|
180
|
-
if (safeCommandOptions.provider) {
|
|
181
|
-
safeCommandOptions.provider = "[provider_name]";
|
|
182
|
-
}
|
|
183
|
-
console.log("CLI Command:", commandName);
|
|
184
|
-
console.log("Global Options:", JSON.stringify(safeGlobalOpts, null, 2));
|
|
185
|
-
console.log("Command Options:", JSON.stringify(safeCommandOptions, null, 2));
|
|
186
|
-
}
|
|
187
|
-
const sanitizedGlobalOpts = { ...globalOpts };
|
|
188
|
-
const sanitizedCommandOptions = { ...commandOptions };
|
|
189
|
-
if (sanitizedGlobalOpts.source) {
|
|
190
|
-
sanitizedGlobalOpts.source = InputValidator.validateLanguageCode(sanitizedGlobalOpts.source, "source language");
|
|
191
|
-
}
|
|
192
|
-
if (sanitizedGlobalOpts.targets && Array.isArray(sanitizedGlobalOpts.targets)) {
|
|
193
|
-
sanitizedGlobalOpts.targets = InputValidator.validateLanguageCodes(sanitizedGlobalOpts.targets, "target languages");
|
|
194
|
-
}
|
|
195
|
-
if (sanitizedGlobalOpts.localesDir) {
|
|
196
|
-
sanitizedGlobalOpts.localesDir = InputValidator.validateDirectoryPath(sanitizedGlobalOpts.localesDir, "locales directory");
|
|
197
|
-
}
|
|
198
|
-
if (sanitizedCommandOptions.provider) {
|
|
199
|
-
sanitizedCommandOptions.provider = InputValidator.validateProvider(sanitizedCommandOptions.provider, "API provider");
|
|
200
|
-
}
|
|
201
|
-
if (sanitizedCommandOptions.concurrency !== undefined) {
|
|
202
|
-
const concurrency = parseInt(sanitizedCommandOptions.concurrency);
|
|
203
|
-
if (isNaN(concurrency) || concurrency < 1 || concurrency > 20) {
|
|
204
|
-
throw new Error("Concurrency must be a number between 1 and 20");
|
|
205
|
-
}
|
|
206
|
-
sanitizedCommandOptions.concurrency = concurrency;
|
|
207
|
-
}
|
|
208
|
-
if (sanitizedCommandOptions.contextThreshold !== undefined) {
|
|
209
|
-
const threshold = parseInt(sanitizedCommandOptions.contextThreshold);
|
|
210
|
-
if (isNaN(threshold) || threshold < 1 || threshold > 10) {
|
|
211
|
-
throw new Error("Context threshold must be a number between 1 and 10");
|
|
212
|
-
}
|
|
213
|
-
sanitizedCommandOptions.contextThreshold = threshold;
|
|
214
|
-
}
|
|
215
|
-
if (sanitizedCommandOptions.contextConfidence !== undefined) {
|
|
216
|
-
const confidence = parseFloat(sanitizedCommandOptions.contextConfidence);
|
|
217
|
-
if (isNaN(confidence) || confidence < 0 || confidence > 1) {
|
|
218
|
-
throw new Error("Context confidence must be a number between 0 and 1");
|
|
219
|
-
}
|
|
220
|
-
sanitizedCommandOptions.contextConfidence = confidence;
|
|
221
|
-
}
|
|
222
|
-
if (sanitizedCommandOptions.length) {
|
|
223
|
-
const validLengthModes = ["strict", "flexible", "exact", "relaxed", "smart"];
|
|
224
|
-
if (!validLengthModes.includes(sanitizedCommandOptions.length)) {
|
|
225
|
-
throw new Error(`Invalid length mode: ${sanitizedCommandOptions.length}. Valid modes: ${validLengthModes.join(", ")}`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
const mergedOpts = {
|
|
229
|
-
source: defaultConfig.source,
|
|
230
|
-
targets: defaultConfig.targets,
|
|
231
|
-
localesDir: defaultConfig.localesDir,
|
|
232
|
-
apiProvider: defaultConfig.apiProvider,
|
|
233
|
-
...(sanitizedGlobalOpts.source && { source: sanitizedGlobalOpts.source }),
|
|
234
|
-
...(sanitizedGlobalOpts.targets && { targets: sanitizedGlobalOpts.targets }),
|
|
235
|
-
...(sanitizedGlobalOpts.localesDir && {
|
|
236
|
-
localesDir: sanitizedGlobalOpts.localesDir,
|
|
237
|
-
}),
|
|
238
|
-
...sanitizedCommandOptions,
|
|
239
|
-
};
|
|
240
|
-
let concurrencyLimit = parseInt(mergedOpts.concurrency) || defaultConfig.concurrencyLimit || 5;
|
|
241
|
-
if (mergedOpts.autoOptimize) {
|
|
242
|
-
const cpuCount = os.cpus().length;
|
|
243
|
-
const memoryGB = Math.floor(os.totalmem() / (1024 * 1024 * 1024));
|
|
244
|
-
if (memoryGB < 4) {
|
|
245
|
-
concurrencyLimit = Math.min(3, cpuCount);
|
|
246
|
-
}
|
|
247
|
-
else if (memoryGB < 8) {
|
|
248
|
-
concurrencyLimit = Math.min(5, Math.ceil(cpuCount * 0.5));
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
concurrencyLimit = Math.min(10, Math.ceil(cpuCount * 0.75));
|
|
252
|
-
}
|
|
253
|
-
console.log(`Auto-optimized settings for your system (${cpuCount} CPUs, ${memoryGB}GB RAM):`);
|
|
254
|
-
console.log(` - Concurrency: ${concurrencyLimit}`);
|
|
255
|
-
}
|
|
256
|
-
if (concurrencyLimit < 1 || concurrencyLimit > 20) {
|
|
257
|
-
throw new Error("Invalid concurrency limit after optimization");
|
|
258
|
-
}
|
|
259
|
-
const finalConfig = {
|
|
260
|
-
...defaultConfig,
|
|
261
|
-
command: commandName,
|
|
262
|
-
source: mergedOpts.source,
|
|
263
|
-
targets: mergedOpts.targets,
|
|
264
|
-
localesDir: mergedOpts.localesDir,
|
|
265
|
-
apiProvider: mergedOpts.provider || defaultConfig.apiProvider,
|
|
266
|
-
concurrencyLimit: concurrencyLimit,
|
|
267
|
-
cacheEnabled: mergedOpts.noCache === undefined
|
|
268
|
-
? defaultConfig.cacheEnabled
|
|
269
|
-
: !mergedOpts.noCache,
|
|
270
|
-
debug: mergedOpts.debug,
|
|
271
|
-
verbose: mergedOpts.verbose || defaultConfig.logging?.verbose || false,
|
|
272
|
-
forceUpdate: mergedOpts.force || false,
|
|
273
|
-
showDetailedStats: mergedOpts.stats || false,
|
|
274
|
-
autoOptimize: mergedOpts.autoOptimize || defaultConfig.advanced?.autoOptimize || false,
|
|
275
|
-
fixLength: commandName === "fix",
|
|
276
|
-
apiConfig: defaultConfig.apiConfig || {},
|
|
277
|
-
styleGuide: defaultConfig.styleGuide,
|
|
278
|
-
qualityChecks: defaultConfig.qualityChecks,
|
|
279
|
-
lengthControl: {
|
|
280
|
-
...defaultConfig.lengthControl,
|
|
281
|
-
mode: mergedOpts.length || defaultConfig.lengthControl?.mode || "smart",
|
|
282
|
-
},
|
|
283
|
-
retryOptions: {
|
|
284
|
-
...defaultConfig.retryOptions,
|
|
285
|
-
maxRetries: mergedOpts.maxRetries || defaultConfig.retryOptions?.maxRetries || 2,
|
|
286
|
-
initialDelay: mergedOpts.initialDelay || defaultConfig.retryOptions?.initialDelay || 1000,
|
|
287
|
-
maxDelay: mergedOpts.maxDelay || defaultConfig.retryOptions?.maxDelay || 10000,
|
|
288
|
-
jitter: defaultConfig.retryOptions?.jitter !== false,
|
|
289
|
-
},
|
|
290
|
-
context: {
|
|
291
|
-
...defaultConfig.context,
|
|
292
|
-
enabled: true,
|
|
293
|
-
debug: mergedOpts.contextDebug || defaultConfig.context.debug || false,
|
|
294
|
-
useAI: mergedOpts.useAi ||
|
|
295
|
-
mergedOpts.contextProvider !== undefined ||
|
|
296
|
-
defaultConfig.context.useAI ||
|
|
297
|
-
false,
|
|
298
|
-
aiProvider: mergedOpts.contextProvider || defaultConfig.context.aiProvider,
|
|
299
|
-
minTextLength: mergedOpts.minTextLength || defaultConfig.context.minTextLength,
|
|
300
|
-
allowNewCategories: mergedOpts.allowNewCategories !== undefined
|
|
301
|
-
? mergedOpts.allowNewCategories
|
|
302
|
-
: defaultConfig.context.allowNewCategories,
|
|
303
|
-
detection: {
|
|
304
|
-
threshold: mergedOpts.contextThreshold ||
|
|
305
|
-
defaultConfig.context.detection?.threshold ||
|
|
306
|
-
2,
|
|
307
|
-
minConfidence: mergedOpts.contextConfidence ||
|
|
308
|
-
defaultConfig.context.detection?.minConfidence ||
|
|
309
|
-
0.6,
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
advanced: {
|
|
313
|
-
...defaultConfig.advanced,
|
|
314
|
-
timeoutMs: mergedOpts.timeout || defaultConfig.advanced?.timeoutMs || 60000,
|
|
315
|
-
maxKeyLength: defaultConfig.advanced?.maxKeyLength || 10000,
|
|
316
|
-
maxBatchSize: defaultConfig.advanced?.maxBatchSize || 50,
|
|
317
|
-
autoOptimize: mergedOpts.autoOptimize || defaultConfig.advanced?.autoOptimize || false,
|
|
318
|
-
debug: mergedOpts.debug || defaultConfig.advanced?.debug || false,
|
|
319
|
-
},
|
|
320
|
-
rateLimiter: {
|
|
321
|
-
...defaultConfig.rateLimiter,
|
|
322
|
-
enabled: defaultConfig.rateLimiter?.enabled !== false,
|
|
323
|
-
},
|
|
324
|
-
fileOperations: defaultConfig.fileOperations || {},
|
|
325
|
-
logging: {
|
|
326
|
-
...defaultConfig.logging,
|
|
327
|
-
verbose: mergedOpts.verbose || defaultConfig.logging?.verbose || false,
|
|
328
|
-
},
|
|
329
|
-
};
|
|
330
|
-
try {
|
|
331
|
-
InputValidator.validateConfig(finalConfig);
|
|
332
|
-
if (finalConfig.debug || finalConfig.verbose) {
|
|
333
|
-
console.log("Configuration validated successfully");
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
catch (configError) {
|
|
337
|
-
const error = ErrorHelper.configValidationError(configError.message.includes("validation failed")
|
|
338
|
-
? configError.message.split("\n").slice(1)
|
|
339
|
-
: [configError.message]);
|
|
340
|
-
console.error(ErrorHelper.formatError(error, { showDebug: globalOpts.debug }));
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
configureComponents(finalConfig);
|
|
344
|
-
validateEnvironment();
|
|
345
|
-
if (finalConfig.debug) {
|
|
346
|
-
console.log("\nConfiguration details:");
|
|
347
|
-
const safeConfig = {
|
|
348
|
-
...finalConfig,
|
|
349
|
-
apiConfig: Object.keys(finalConfig.apiConfig || {}).reduce((acc, provider) => {
|
|
350
|
-
acc[provider] = {
|
|
351
|
-
model: finalConfig.apiConfig[provider]?.model || "configured",
|
|
352
|
-
temperature: finalConfig.apiConfig[provider]?.temperature,
|
|
353
|
-
maxTokens: finalConfig.apiConfig[provider]?.maxTokens,
|
|
354
|
-
};
|
|
355
|
-
return acc;
|
|
356
|
-
}, {}),
|
|
357
|
-
advanced: {
|
|
358
|
-
...finalConfig.advanced,
|
|
359
|
-
timeoutMs: finalConfig.advanced?.timeoutMs,
|
|
360
|
-
maxKeyLength: finalConfig.advanced?.maxKeyLength,
|
|
361
|
-
maxBatchSize: finalConfig.advanced?.maxBatchSize,
|
|
362
|
-
autoOptimize: finalConfig.advanced?.autoOptimize,
|
|
363
|
-
debug: finalConfig.advanced?.debug,
|
|
364
|
-
},
|
|
365
|
-
};
|
|
366
|
-
delete safeConfig.apiProvider; // Could leak preferred provider info
|
|
367
|
-
delete safeConfig.localesDir; // Could leak file system structure
|
|
368
|
-
console.log(JSON.stringify(safeConfig, null, 2));
|
|
369
|
-
}
|
|
370
|
-
await displayPerformanceTips(finalConfig);
|
|
371
|
-
const localesDir = path.resolve(finalConfig.localesDir);
|
|
372
|
-
console.log(`\nLooking for source files in: ${localesDir}`);
|
|
373
|
-
const files = await findLocaleFiles(localesDir, finalConfig.source);
|
|
374
|
-
if (!files || !files.length) {
|
|
375
|
-
const error = ErrorHelper.fileNotFoundError(`${localesDir}/${finalConfig.source}.json`, `${finalConfig.source}.json`);
|
|
376
|
-
console.error(ErrorHelper.formatError(error, {
|
|
377
|
-
showDebug: finalConfig.debug,
|
|
378
|
-
showContext: true,
|
|
379
|
-
}));
|
|
380
|
-
process.exit(1);
|
|
381
|
-
}
|
|
382
|
-
switch (commandName) {
|
|
383
|
-
case "fix":
|
|
384
|
-
console.log("\nRunning in FIX mode");
|
|
385
|
-
await Promise.all(files.map((file) => validateAndFixExistingTranslations(file, finalConfig)));
|
|
386
|
-
break;
|
|
387
|
-
case "analyze":
|
|
388
|
-
console.log("\nRunning in ANALYZE mode");
|
|
389
|
-
console.log("Context analysis mode is not fully implemented yet.");
|
|
390
|
-
break;
|
|
391
|
-
case "advanced":
|
|
392
|
-
console.log("\nRunning ADVANCED configuration");
|
|
393
|
-
// Fall through to translate
|
|
394
|
-
case "translate":
|
|
395
|
-
default:
|
|
396
|
-
console.log("\nRunning in TRANSLATION mode");
|
|
397
|
-
if (finalConfig.context.useAI) {
|
|
398
|
-
console.log(`AI Context Analysis: ENABLED (Provider: ${finalConfig.context.aiProvider || finalConfig.apiProvider})`);
|
|
399
|
-
if (finalConfig.context.allowNewCategories) {
|
|
400
|
-
console.log("New category suggestions: ENABLED");
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
console.log(`Performance settings:`);
|
|
404
|
-
console.log(` - Concurrency: ${finalConfig.concurrencyLimit} parallel operations`);
|
|
405
|
-
console.log(` - Caching: ${finalConfig.cacheEnabled ? "Enabled" : "Disabled"}`);
|
|
406
|
-
console.log(` - Retries: ${finalConfig.retryOptions.maxRetries} max retries`);
|
|
407
|
-
if (finalConfig.rateLimiter?.adaptiveThrottling) {
|
|
408
|
-
console.log(` - Adaptive Rate Limiting: Enabled`);
|
|
409
|
-
}
|
|
410
|
-
if (finalConfig.forceUpdate) {
|
|
411
|
-
console.log(`Warning: Force update mode ENABLED (will update existing translations)`);
|
|
412
|
-
}
|
|
413
|
-
for (const file of files) {
|
|
414
|
-
await translateFile(file, finalConfig);
|
|
415
|
-
}
|
|
416
|
-
// Exit after successful translation in CLI mode
|
|
417
|
-
// This allows translateFile to be used as library without forcing exit
|
|
418
|
-
process.exit(0);
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
// Other commands (fix, analyze) handle their own completion
|
|
422
|
-
}
|
|
423
|
-
catch (validationError) {
|
|
424
|
-
if (validationError.message.includes("Configuration validation")) {
|
|
425
|
-
// Already handled above with better formatting
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
console.error(`\nInput validation error: ${validationError.message}`);
|
|
429
|
-
if (validationError.stack && process.env.DEBUG) {
|
|
430
|
-
console.error("\nStack trace:");
|
|
431
|
-
console.error(validationError.stack);
|
|
432
|
-
}
|
|
433
|
-
process.exit(1);
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
39
|
program
|
|
437
40
|
.command("translate")
|
|
438
41
|
.description("Translate missing strings (default command)")
|
|
@@ -447,10 +50,9 @@ const configureCLI = async (defaultConfig) => {
|
|
|
447
50
|
.option("--save-review-queue", "Save low-confidence items for manual review", false)
|
|
448
51
|
.action(async (options) => {
|
|
449
52
|
try {
|
|
450
|
-
await runCommand(
|
|
53
|
+
await runCommand(defaultConfig, options, "translate");
|
|
451
54
|
}
|
|
452
55
|
catch (error) {
|
|
453
|
-
// Handle errors with better formatting
|
|
454
56
|
if (error.code && error.code.startsWith("ERR_")) {
|
|
455
57
|
console.error(ErrorHelper.formatError(error, {
|
|
456
58
|
showDebug: process.env.DEBUG === "true",
|
|
@@ -473,7 +75,7 @@ const configureCLI = async (defaultConfig) => {
|
|
|
473
75
|
.option("--length", "Fix length issues in existing translations", true)
|
|
474
76
|
.action(async (options) => {
|
|
475
77
|
try {
|
|
476
|
-
await runCommand(
|
|
78
|
+
await runCommand(defaultConfig, options, "fix");
|
|
477
79
|
}
|
|
478
80
|
catch (error) {
|
|
479
81
|
if (error.code && error.code.startsWith("ERR_")) {
|
|
@@ -491,12 +93,12 @@ const configureCLI = async (defaultConfig) => {
|
|
|
491
93
|
program
|
|
492
94
|
.command("analyze")
|
|
493
95
|
.description("Analyze context patterns")
|
|
494
|
-
.option("--use-ai", "Enable AI-based context analysis", defaultConfig.context
|
|
495
|
-
.option("--context-provider <provider>", "AI provider for analysis", defaultConfig.context
|
|
496
|
-
.option("--context-threshold <number>", "Minimum match count", Number, defaultConfig.context
|
|
96
|
+
.option("--use-ai", "Enable AI-based context analysis", defaultConfig.context?.useAI)
|
|
97
|
+
.option("--context-provider <provider>", "AI provider for analysis", defaultConfig.context?.aiProvider)
|
|
98
|
+
.option("--context-threshold <number>", "Minimum match count", Number, defaultConfig.context?.detection?.threshold)
|
|
497
99
|
.action(async (options) => {
|
|
498
100
|
try {
|
|
499
|
-
await runCommand(
|
|
101
|
+
await runCommand(defaultConfig, options, "analyze");
|
|
500
102
|
}
|
|
501
103
|
catch (error) {
|
|
502
104
|
if (error.code && error.code.startsWith("ERR_")) {
|
|
@@ -514,14 +116,14 @@ const configureCLI = async (defaultConfig) => {
|
|
|
514
116
|
program
|
|
515
117
|
.command("advanced")
|
|
516
118
|
.description("Access advanced configuration options")
|
|
517
|
-
.option("--context-confidence <number>", "Minimum confidence score", Number, defaultConfig.context
|
|
518
|
-
.option("--context-debug", "Show context details", defaultConfig.context
|
|
519
|
-
.option("--min-text-length <number>", "Minimum text length for AI analysis", Number, defaultConfig.context
|
|
520
|
-
.option("--allow-new-categories", "Allow AI to suggest new categories", defaultConfig.context
|
|
119
|
+
.option("--context-confidence <number>", "Minimum confidence score", Number, defaultConfig.context?.detection?.minConfidence)
|
|
120
|
+
.option("--context-debug", "Show context details", defaultConfig.context?.debug)
|
|
121
|
+
.option("--min-text-length <number>", "Minimum text length for AI analysis", Number, defaultConfig.context?.minTextLength)
|
|
122
|
+
.option("--allow-new-categories", "Allow AI to suggest new categories", defaultConfig.context?.allowNewCategories)
|
|
521
123
|
.option("--max-retries <number>", "Maximum number of retries for API calls", Number, defaultConfig.retryOptions?.maxRetries || 2)
|
|
522
124
|
.action(async (options) => {
|
|
523
125
|
try {
|
|
524
|
-
await runCommand(
|
|
126
|
+
await runCommand(defaultConfig, options, "advanced");
|
|
525
127
|
}
|
|
526
128
|
catch (error) {
|
|
527
129
|
if (error.code && error.code.startsWith("ERR_")) {
|
|
@@ -559,7 +161,7 @@ const configureCLI = async (defaultConfig) => {
|
|
|
559
161
|
console.log(` Version: ${defaultConfig.version}`);
|
|
560
162
|
}
|
|
561
163
|
console.log(` Source: ${defaultConfig.source}`);
|
|
562
|
-
console.log(` Targets: ${defaultConfig.targets.length} languages (${defaultConfig.targets.slice(0, 5).join(", ")}${defaultConfig.targets.length > 5 ? "..." : ""})`);
|
|
164
|
+
console.log(` Targets: ${(defaultConfig.targets || []).length} languages (${(defaultConfig.targets || []).slice(0, 5).join(", ")}${(defaultConfig.targets || []).length > 5 ? "..." : ""})`);
|
|
563
165
|
console.log(` API Provider: ${defaultConfig.apiProvider || "auto-detect"}`);
|
|
564
166
|
console.log(` Concurrency: ${defaultConfig.concurrencyLimit || 5} parallel operations`);
|
|
565
167
|
console.log(` Cache: ${defaultConfig.cacheEnabled !== false ? "Enabled" : "Disabled"}`);
|
|
@@ -593,8 +195,16 @@ const configureCLI = async (defaultConfig) => {
|
|
|
593
195
|
.option("--export <format>", "Export review queue (json|csv)")
|
|
594
196
|
.action(async (options) => {
|
|
595
197
|
try {
|
|
596
|
-
const ReviewCommand = (await import("
|
|
597
|
-
|
|
198
|
+
const ReviewCommand = (await import("./commands/review.js")).default;
|
|
199
|
+
// Ensure config matches expectations of ReviewCommand
|
|
200
|
+
// ReviewCommand expects ConfigLayer/LocalizeConfig, need to ensure mandatory fields or cast
|
|
201
|
+
// ReviewCommandConfig requires localesDir and source to be strings (not undefined)
|
|
202
|
+
const reviewConfig = {
|
|
203
|
+
...defaultConfig,
|
|
204
|
+
localesDir: defaultConfig.localesDir || "./locales",
|
|
205
|
+
source: defaultConfig.source || "en",
|
|
206
|
+
};
|
|
207
|
+
const review = new ReviewCommand(reviewConfig);
|
|
598
208
|
if (options.export) {
|
|
599
209
|
review.exportReviewQueue(options.export);
|
|
600
210
|
}
|
|
@@ -612,107 +222,16 @@ const configureCLI = async (defaultConfig) => {
|
|
|
612
222
|
});
|
|
613
223
|
program.action(async () => {
|
|
614
224
|
try {
|
|
615
|
-
await runCommand(
|
|
225
|
+
await runCommand(defaultConfig, {}, "translate");
|
|
616
226
|
}
|
|
617
227
|
catch (error) {
|
|
618
228
|
console.error(`\nError: ${error.message}`);
|
|
619
|
-
if (error.stack && process.env.DEBUG) {
|
|
620
|
-
console.error(error.stack);
|
|
621
|
-
}
|
|
622
229
|
process.exit(1);
|
|
623
230
|
}
|
|
624
231
|
});
|
|
625
|
-
program.
|
|
626
|
-
return defaultConfig;
|
|
627
|
-
};
|
|
628
|
-
/**
|
|
629
|
-
* Validate that necessary environment variables are set.
|
|
630
|
-
* @returns {Array<string>} - List of available providers.
|
|
631
|
-
*/
|
|
632
|
-
const validateEnvironment = () => {
|
|
633
|
-
try {
|
|
634
|
-
// Check that ProviderFactory can validate providers
|
|
635
|
-
const availableProviders = ProviderFactory.validateProviders();
|
|
636
|
-
console.log(`\nAvailable API providers: ${availableProviders.join(", ")}`);
|
|
637
|
-
return availableProviders;
|
|
638
|
-
}
|
|
639
|
-
catch (error) {
|
|
640
|
-
console.error("\nError: " + error.message);
|
|
641
|
-
console.error("Please set at least one of the following environment variables:");
|
|
642
|
-
const possibleProviders = [
|
|
643
|
-
"DASHSCOPE_API_KEY",
|
|
644
|
-
"OPENAI_API_KEY",
|
|
645
|
-
"DEEPSEEK_API_KEY",
|
|
646
|
-
"GEMINI_API_KEY",
|
|
647
|
-
"XAI_API_KEY",
|
|
648
|
-
];
|
|
649
|
-
possibleProviders.forEach((key) => console.error(` - ${key}`));
|
|
650
|
-
process.exit(1);
|
|
651
|
-
}
|
|
232
|
+
await program.parseAsync(process.argv);
|
|
652
233
|
};
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
* @param {any} options - Configuration options.
|
|
656
|
-
*/
|
|
657
|
-
const displayPerformanceTips = async (options) => {
|
|
658
|
-
if (!options.debug)
|
|
659
|
-
return;
|
|
660
|
-
try {
|
|
661
|
-
const cpuCount = os.cpus().length;
|
|
662
|
-
const cpuModel = os.cpus()[0]?.model || "Unknown CPU";
|
|
663
|
-
const memoryGB = Math.floor(os.totalmem() / (1024 * 1024 * 1024));
|
|
664
|
-
const freememGB = Math.floor(os.freemem() / (1024 * 1024 * 1024));
|
|
665
|
-
const orchestrator = new Orchestrator(options);
|
|
666
|
-
const cacheStats = orchestrator.getCacheStats();
|
|
667
|
-
console.log("\nPerformance Information:");
|
|
668
|
-
console.log(` - CPU: ${cpuModel} (${cpuCount} cores)`);
|
|
669
|
-
console.log(` - Memory: ${freememGB}GB free of ${memoryGB}GB total`);
|
|
670
|
-
console.log(` - Concurrency: ${options.concurrencyLimit} parallel operations`);
|
|
671
|
-
console.log(` - Cache: ${options.cacheEnabled ? "Enabled" : "Disabled"} (${cacheStats.size} items cached)`);
|
|
672
|
-
// Provide tips
|
|
673
|
-
console.log("\nPerformance Tips:");
|
|
674
|
-
if (cpuCount > options.concurrencyLimit * 2) {
|
|
675
|
-
console.log(` - Increase concurrency with --concurrency ${Math.min(cpuCount, 10)} to better utilize your CPU`);
|
|
676
|
-
}
|
|
677
|
-
if (!options.cacheEnabled) {
|
|
678
|
-
console.log(` - Enable caching to improve repeated runs speed`);
|
|
679
|
-
}
|
|
680
|
-
if (memoryGB < 4) {
|
|
681
|
-
console.log(` - Your system has limited memory (${memoryGB}GB). Consider reducing concurrency if you experience issues.`);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
catch (error) {
|
|
685
|
-
console.log("Warning: Could not calculate performance tips", error);
|
|
686
|
-
}
|
|
687
|
-
};
|
|
688
|
-
try {
|
|
689
|
-
await loadEnvironmentVariables();
|
|
690
|
-
let defaultConfig;
|
|
691
|
-
try {
|
|
692
|
-
const { config, configFile } = await loadConfig();
|
|
693
|
-
defaultConfig = config;
|
|
694
|
-
if (configFile) {
|
|
695
|
-
console.log(`Loaded config from: ${path.relative(process.cwd(), configFile)}`);
|
|
696
|
-
}
|
|
697
|
-
else {
|
|
698
|
-
console.log("No config file found, using defaults");
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
catch (err) {
|
|
702
|
-
console.warn(`Warning: Could not load config: ${err.message}`);
|
|
703
|
-
// Fallback defaults if logic fails totally (shouldn't with c12 defaults)
|
|
704
|
-
defaultConfig = {
|
|
705
|
-
source: "en",
|
|
706
|
-
targets: [],
|
|
707
|
-
concurrencyLimit: 5,
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
await configureCLI(defaultConfig);
|
|
711
|
-
}
|
|
712
|
-
catch (error) {
|
|
713
|
-
console.error(`\nError: ${error.message}`);
|
|
714
|
-
if (error.stack && process.env.DEBUG) {
|
|
715
|
-
console.error(error.stack);
|
|
716
|
-
}
|
|
234
|
+
main().catch((err) => {
|
|
235
|
+
console.error(err);
|
|
717
236
|
process.exit(1);
|
|
718
|
-
}
|
|
237
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlobalStats, TranslationOptions } from "../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Main translator function to process source file and create translations.
|
|
4
4
|
* @param {string} file - Source file path.
|
package/dist/config/index.d.ts
CHANGED