locai 2.3.0 → 2.5.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.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 { getLogger } from "./utils/logger.js";
16
- import { FrameworkDetector } from "./core/detection/framework-detector.js";
17
- import { StructureDetector, LocaleStructure } from "./core/detection/structure-detector.js";
18
- import { loadConfig } from "./config/index.js";
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
- * Load environment variables from .env files.
24
- * Prioritizes .env.local over .env.
25
- */
26
- const loadEnvironmentVariables = async () => {
27
- // Load environment files in priority order
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
- // Use config version if available, otherwise use package.json version
19
+ validateEnvironment(defaultConfig);
145
20
  const toolVersion = defaultConfig.version || version;
146
21
  program
147
22
  .name("locai")
@@ -151,6 +26,7 @@ const configureCLI = async (defaultConfig) => {
151
26
  .option("-s, --source <lang>", "Source language", defaultConfig.source)
152
27
  .option("-t, --targets <langs>", "Target languages (comma separated)", (val) => val.split(","), defaultConfig.targets)
153
28
  .option("--localesDir <dir>", "Localization files directory", defaultConfig.localesDir)
29
+ .option("--no-deep-context", "Disable deep context analysis (source code scanning)")
154
30
  .option("--debug", "Enable debug mode with verbose logging", false)
155
31
  .option("--verbose", "Enable detailed diagnostic output", false);
156
32
  program.on("option:debug", function () {
@@ -161,278 +37,6 @@ const configureCLI = async (defaultConfig) => {
161
37
  process.env.VERBOSE = "true";
162
38
  console.log("Verbose mode: ENABLED (detailed diagnostics)");
163
39
  });
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
40
  program
437
41
  .command("translate")
438
42
  .description("Translate missing strings (default command)")
@@ -447,10 +51,9 @@ const configureCLI = async (defaultConfig) => {
447
51
  .option("--save-review-queue", "Save low-confidence items for manual review", false)
448
52
  .action(async (options) => {
449
53
  try {
450
- await runCommand(program.opts(), options, "translate");
54
+ await runCommand(defaultConfig, options, "translate");
451
55
  }
452
56
  catch (error) {
453
- // Handle errors with better formatting
454
57
  if (error.code && error.code.startsWith("ERR_")) {
455
58
  console.error(ErrorHelper.formatError(error, {
456
59
  showDebug: process.env.DEBUG === "true",
@@ -473,7 +76,7 @@ const configureCLI = async (defaultConfig) => {
473
76
  .option("--length", "Fix length issues in existing translations", true)
474
77
  .action(async (options) => {
475
78
  try {
476
- await runCommand(program.opts(), options, "fix");
79
+ await runCommand(defaultConfig, options, "fix");
477
80
  }
478
81
  catch (error) {
479
82
  if (error.code && error.code.startsWith("ERR_")) {
@@ -491,12 +94,12 @@ const configureCLI = async (defaultConfig) => {
491
94
  program
492
95
  .command("analyze")
493
96
  .description("Analyze context patterns")
494
- .option("--use-ai", "Enable AI-based context analysis", defaultConfig.context.useAI)
495
- .option("--context-provider <provider>", "AI provider for analysis", defaultConfig.context.aiProvider)
496
- .option("--context-threshold <number>", "Minimum match count", Number, defaultConfig.context.detection.threshold)
97
+ .option("--use-ai", "Enable AI-based context analysis", defaultConfig.context?.useAI)
98
+ .option("--context-provider <provider>", "AI provider for analysis", defaultConfig.context?.aiProvider)
99
+ .option("--context-threshold <number>", "Minimum match count", Number, defaultConfig.context?.detection?.threshold)
497
100
  .action(async (options) => {
498
101
  try {
499
- await runCommand(program.opts(), options, "analyze");
102
+ await runCommand(defaultConfig, options, "analyze");
500
103
  }
501
104
  catch (error) {
502
105
  if (error.code && error.code.startsWith("ERR_")) {
@@ -514,14 +117,14 @@ const configureCLI = async (defaultConfig) => {
514
117
  program
515
118
  .command("advanced")
516
119
  .description("Access advanced configuration options")
517
- .option("--context-confidence <number>", "Minimum confidence score", Number, defaultConfig.context.detection.minConfidence)
518
- .option("--context-debug", "Show context details", defaultConfig.context.debug)
519
- .option("--min-text-length <number>", "Minimum text length for AI analysis", Number, defaultConfig.context.minTextLength)
520
- .option("--allow-new-categories", "Allow AI to suggest new categories", defaultConfig.context.allowNewCategories)
120
+ .option("--context-confidence <number>", "Minimum confidence score", Number, defaultConfig.context?.detection?.minConfidence)
121
+ .option("--context-debug", "Show context details", defaultConfig.context?.debug)
122
+ .option("--min-text-length <number>", "Minimum text length for AI analysis", Number, defaultConfig.context?.minTextLength)
123
+ .option("--allow-new-categories", "Allow AI to suggest new categories", defaultConfig.context?.allowNewCategories)
521
124
  .option("--max-retries <number>", "Maximum number of retries for API calls", Number, defaultConfig.retryOptions?.maxRetries || 2)
522
125
  .action(async (options) => {
523
126
  try {
524
- await runCommand(program.opts(), options, "advanced");
127
+ await runCommand(defaultConfig, options, "advanced");
525
128
  }
526
129
  catch (error) {
527
130
  if (error.code && error.code.startsWith("ERR_")) {
@@ -559,7 +162,7 @@ const configureCLI = async (defaultConfig) => {
559
162
  console.log(` Version: ${defaultConfig.version}`);
560
163
  }
561
164
  console.log(` Source: ${defaultConfig.source}`);
562
- console.log(` Targets: ${defaultConfig.targets.length} languages (${defaultConfig.targets.slice(0, 5).join(", ")}${defaultConfig.targets.length > 5 ? "..." : ""})`);
165
+ console.log(` Targets: ${(defaultConfig.targets || []).length} languages (${(defaultConfig.targets || []).slice(0, 5).join(", ")}${(defaultConfig.targets || []).length > 5 ? "..." : ""})`);
563
166
  console.log(` API Provider: ${defaultConfig.apiProvider || "auto-detect"}`);
564
167
  console.log(` Concurrency: ${defaultConfig.concurrencyLimit || 5} parallel operations`);
565
168
  console.log(` Cache: ${defaultConfig.cacheEnabled !== false ? "Enabled" : "Disabled"}`);
@@ -593,8 +196,16 @@ const configureCLI = async (defaultConfig) => {
593
196
  .option("--export <format>", "Export review queue (json|csv)")
594
197
  .action(async (options) => {
595
198
  try {
596
- const ReviewCommand = (await import("../src/commands/review.js")).default;
597
- const review = new ReviewCommand(defaultConfig);
199
+ const ReviewCommand = (await import("./commands/review.js")).default;
200
+ // Ensure config matches expectations of ReviewCommand
201
+ // ReviewCommand expects ConfigLayer/LocalizeConfig, need to ensure mandatory fields or cast
202
+ // ReviewCommandConfig requires localesDir and source to be strings (not undefined)
203
+ const reviewConfig = {
204
+ ...defaultConfig,
205
+ localesDir: defaultConfig.localesDir || "./locales",
206
+ source: defaultConfig.source || "en",
207
+ };
208
+ const review = new ReviewCommand(reviewConfig);
598
209
  if (options.export) {
599
210
  review.exportReviewQueue(options.export);
600
211
  }
@@ -612,107 +223,16 @@ const configureCLI = async (defaultConfig) => {
612
223
  });
613
224
  program.action(async () => {
614
225
  try {
615
- await runCommand(program.opts(), {}, "translate");
226
+ await runCommand(defaultConfig, {}, "translate");
616
227
  }
617
228
  catch (error) {
618
229
  console.error(`\nError: ${error.message}`);
619
- if (error.stack && process.env.DEBUG) {
620
- console.error(error.stack);
621
- }
622
230
  process.exit(1);
623
231
  }
624
232
  });
625
- program.parse(process.argv);
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
- }
233
+ await program.parseAsync(process.argv);
652
234
  };
653
- /**
654
- * Display performance optimizations and tips.
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
- }
235
+ main().catch((err) => {
236
+ console.error(err);
717
237
  process.exit(1);
718
- }
238
+ });
@@ -1,4 +1,4 @@
1
- import { TranslationOptions, GlobalStats } from "../services/translation-service.js";
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.
@@ -9,6 +9,12 @@ export interface LocalizeConfig extends Omit<Partial<OrchestratorOptions>, "cont
9
9
  * Target language codes (e.g. ['tr', 'es'])
10
10
  */
11
11
  targets?: string[];
12
+ /**
13
+ * Deep Context (Source Code Analysis)
14
+ * Scans source code for context (component name, comments, props)
15
+ * @default true
16
+ */
17
+ deepContext?: boolean;
12
18
  /**
13
19
  * Filter by file extensions (e.g. ['.json', '.arb'])
14
20
  */
@@ -143,3 +149,4 @@ export declare function loadConfig(cwd?: string): Promise<{
143
149
  configFile: string | undefined;
144
150
  layers: ConfigLayer<LocalizeConfig, import("c12").ConfigLayerMeta>[] | undefined;
145
151
  }>;
152
+ export declare const validateEnvironment: (config?: any) => void;