lingo.dev 0.112.1 → 0.113.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/build/cli.mjs CHANGED
@@ -2016,7 +2016,7 @@ function createTextFileLoader(pathPattern) {
2016
2016
  const trimmedResult = result.trim();
2017
2017
  return trimmedResult;
2018
2018
  },
2019
- async push(locale, data, _35, originalLocale) {
2019
+ async push(locale, data, _36, originalLocale) {
2020
2020
  const draftPath = pathPattern.replaceAll("[locale]", locale);
2021
2021
  const finalPath = path10.resolve(draftPath);
2022
2022
  const dirPath = path10.dirname(finalPath);
@@ -2722,7 +2722,7 @@ function createPropertiesLoader() {
2722
2722
  return result;
2723
2723
  },
2724
2724
  async push(locale, payload) {
2725
- const result = Object.entries(payload).filter(([_35, value]) => value != null).map(([key, value]) => `${key}=${value}`).join("\n");
2725
+ const result = Object.entries(payload).filter(([_36, value]) => value != null).map(([key, value]) => `${key}=${value}`).join("\n");
2726
2726
  return result;
2727
2727
  }
2728
2728
  });
@@ -2951,12 +2951,12 @@ function createUnlocalizableLoader(returnUnlocalizedKeys = false) {
2951
2951
  const unlocalizableKeys = _getUnlocalizableKeys(input2);
2952
2952
  const result = _10.omitBy(
2953
2953
  input2,
2954
- (_35, key) => unlocalizableKeys.includes(key)
2954
+ (_36, key) => unlocalizableKeys.includes(key)
2955
2955
  );
2956
2956
  if (returnUnlocalizedKeys) {
2957
2957
  result.unlocalizable = _10.omitBy(
2958
2958
  input2,
2959
- (_35, key) => !unlocalizableKeys.includes(key)
2959
+ (_36, key) => !unlocalizableKeys.includes(key)
2960
2960
  );
2961
2961
  }
2962
2962
  return result;
@@ -2966,7 +2966,7 @@ function createUnlocalizableLoader(returnUnlocalizedKeys = false) {
2966
2966
  const result = _10.merge(
2967
2967
  {},
2968
2968
  data,
2969
- _10.omitBy(originalInput, (_35, key) => !unlocalizableKeys.includes(key))
2969
+ _10.omitBy(originalInput, (_36, key) => !unlocalizableKeys.includes(key))
2970
2970
  );
2971
2971
  return result;
2972
2972
  }
@@ -2997,7 +2997,7 @@ function _getUnlocalizableKeys(input2) {
2997
2997
  }
2998
2998
  }
2999
2999
  return false;
3000
- }).map(([key, _35]) => key);
3000
+ }).map(([key, _36]) => key);
3001
3001
  }
3002
3002
 
3003
3003
  // src/cli/loaders/formatters/prettier.ts
@@ -5729,7 +5729,7 @@ var AST = class _AST {
5729
5729
  if (!this.type) {
5730
5730
  const noEmpty = this.isStart() && this.isEnd();
5731
5731
  const src = this.#parts.map((p) => {
5732
- const [re, _35, hasMagic, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
5732
+ const [re, _36, hasMagic, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
5733
5733
  this.#hasMagic = this.#hasMagic || hasMagic;
5734
5734
  this.#uflag = this.#uflag || uflag;
5735
5735
  return re;
@@ -5802,7 +5802,7 @@ var AST = class _AST {
5802
5802
  if (typeof p === "string") {
5803
5803
  throw new Error("string type in extglob ast??");
5804
5804
  }
5805
- const [re, _35, _hasMagic, uflag] = p.toRegExpSource(dot);
5805
+ const [re, _36, _hasMagic, uflag] = p.toRegExpSource(dot);
5806
5806
  this.#uflag = this.#uflag || uflag;
5807
5807
  return re;
5808
5808
  }).filter((p) => !(this.isStart() && this.isEnd()) || !!p).join("|");
@@ -6047,7 +6047,7 @@ var Minimatch = class {
6047
6047
  }
6048
6048
  return false;
6049
6049
  }
6050
- debug(..._35) {
6050
+ debug(..._36) {
6051
6051
  }
6052
6052
  make() {
6053
6053
  const pattern = this.pattern;
@@ -6069,7 +6069,7 @@ var Minimatch = class {
6069
6069
  const rawGlobParts = this.globSet.map((s) => this.slashSplit(s));
6070
6070
  this.globParts = this.preprocess(rawGlobParts);
6071
6071
  this.debug(this.pattern, this.globParts);
6072
- let set = this.globParts.map((s, _35, __) => {
6072
+ let set = this.globParts.map((s, _36, __) => {
6073
6073
  if (this.isWindows && this.windowsNoMagicRoot) {
6074
6074
  const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]);
6075
6075
  const isDrive = /^[a-z]:/i.test(s[0]);
@@ -7108,7 +7108,7 @@ function createTxtLoader() {
7108
7108
  const sortedEntries = Object.entries(payload).sort(
7109
7109
  ([a], [b]) => parseInt(a) - parseInt(b)
7110
7110
  );
7111
- return sortedEntries.map(([_35, value]) => value).join("\n");
7111
+ return sortedEntries.map(([_36, value]) => value).join("\n");
7112
7112
  }
7113
7113
  });
7114
7114
  }
@@ -8500,7 +8500,7 @@ var i18n_default = new Command14().command("i18n").description(
8500
8500
  if (flags.key) {
8501
8501
  processableData = _31.pickBy(
8502
8502
  processableData,
8503
- (_35, key) => key === flags.key
8503
+ (_36, key) => key === flags.key
8504
8504
  );
8505
8505
  }
8506
8506
  if (flags.verbose) {
@@ -9129,7 +9129,7 @@ import Z6 from "zod";
9129
9129
  import { ReplexicaEngine } from "@lingo.dev/_sdk";
9130
9130
  var mcp_default = new Command17().command("mcp").description(
9131
9131
  "Start a Model Context Protocol (MCP) server for AI assistant integration"
9132
- ).helpOption("-h, --help", "Show help").action(async (_35, program) => {
9132
+ ).helpOption("-h, --help", "Show help").action(async (_36, program) => {
9133
9133
  const apiKey = program.args[0];
9134
9134
  const settings = getSettings(apiKey);
9135
9135
  if (!settings.auth.apiKey) {
@@ -9756,7 +9756,7 @@ async function execute(input2) {
9756
9756
  const workerTasks = [];
9757
9757
  for (let i = 0; i < workersCount; i++) {
9758
9758
  const assignedTasks = ctx.tasks.filter(
9759
- (_35, idx) => idx % workersCount === i
9759
+ (_36, idx) => idx % workersCount === i
9760
9760
  );
9761
9761
  workerTasks.push(
9762
9762
  createWorkerTask({
@@ -10127,6 +10127,150 @@ var flagsSchema2 = z2.object({
10127
10127
  sound: z2.boolean().optional()
10128
10128
  });
10129
10129
 
10130
+ // src/cli/cmd/run/frozen.ts
10131
+ import chalk14 from "chalk";
10132
+ import { Listr as Listr4 } from "listr2";
10133
+ import _35 from "lodash";
10134
+ import { resolveOverriddenLocale as resolveOverriddenLocale8 } from "@lingo.dev/_spec";
10135
+ async function frozen(input2) {
10136
+ console.log(chalk14.hex(colors.orange)("[Frozen]"));
10137
+ let buckets = getBuckets(input2.config);
10138
+ if (input2.flags.bucket?.length) {
10139
+ buckets = buckets.filter((b) => input2.flags.bucket.includes(b.type));
10140
+ }
10141
+ if (input2.flags.file?.length) {
10142
+ buckets = buckets.map((bucket) => {
10143
+ const paths = bucket.paths.filter(
10144
+ (p) => input2.flags.file.some(
10145
+ (f) => p.pathPattern.includes(f) || minimatch(p.pathPattern, f)
10146
+ )
10147
+ );
10148
+ return { ...bucket, paths };
10149
+ }).filter((bucket) => bucket.paths.length > 0);
10150
+ }
10151
+ const _sourceLocale = input2.flags.sourceLocale || input2.config.locale.source;
10152
+ const _targetLocales = input2.flags.targetLocale || input2.config.locale.targets;
10153
+ return new Listr4(
10154
+ [
10155
+ {
10156
+ title: "Setting up localization cache",
10157
+ task: async (_ctx, task) => {
10158
+ const checkLockfileProcessor = createDeltaProcessor("");
10159
+ const lockfileExists = await checkLockfileProcessor.checkIfLockExists();
10160
+ if (!lockfileExists) {
10161
+ for (const bucket of buckets) {
10162
+ for (const bucketPath of bucket.paths) {
10163
+ const resolvedSourceLocale = resolveOverriddenLocale8(
10164
+ _sourceLocale,
10165
+ bucketPath.delimiter
10166
+ );
10167
+ const loader = createBucketLoader(
10168
+ bucket.type,
10169
+ bucketPath.pathPattern,
10170
+ {
10171
+ defaultLocale: resolvedSourceLocale,
10172
+ injectLocale: bucket.injectLocale,
10173
+ formatter: input2.config.formatter
10174
+ },
10175
+ bucket.lockedKeys,
10176
+ bucket.lockedPatterns,
10177
+ bucket.ignoredKeys
10178
+ );
10179
+ loader.setDefaultLocale(resolvedSourceLocale);
10180
+ await loader.init();
10181
+ const sourceData = await loader.pull(_sourceLocale);
10182
+ const delta = createDeltaProcessor(bucketPath.pathPattern);
10183
+ const checksums = await delta.createChecksums(sourceData);
10184
+ await delta.saveChecksums(checksums);
10185
+ }
10186
+ }
10187
+ task.title = "Localization cache initialized";
10188
+ } else {
10189
+ task.title = "Localization cache loaded";
10190
+ }
10191
+ }
10192
+ },
10193
+ {
10194
+ title: "Validating frozen state",
10195
+ enabled: () => !!input2.flags.frozen,
10196
+ task: async (_ctx, task) => {
10197
+ for (const bucket of buckets) {
10198
+ for (const bucketPath of bucket.paths) {
10199
+ const resolvedSourceLocale = resolveOverriddenLocale8(
10200
+ _sourceLocale,
10201
+ bucketPath.delimiter
10202
+ );
10203
+ const loader = createBucketLoader(
10204
+ bucket.type,
10205
+ bucketPath.pathPattern,
10206
+ {
10207
+ defaultLocale: resolvedSourceLocale,
10208
+ returnUnlocalizedKeys: true,
10209
+ injectLocale: bucket.injectLocale
10210
+ },
10211
+ bucket.lockedKeys
10212
+ );
10213
+ loader.setDefaultLocale(resolvedSourceLocale);
10214
+ await loader.init();
10215
+ const { unlocalizable: srcUnlocalizable, ...src } = await loader.pull(_sourceLocale);
10216
+ const delta = createDeltaProcessor(bucketPath.pathPattern);
10217
+ const sourceChecksums = await delta.createChecksums(src);
10218
+ const savedChecksums = await delta.loadChecksums();
10219
+ const updatedSourceData = _35.pickBy(
10220
+ src,
10221
+ (value, key) => sourceChecksums[key] !== savedChecksums[key]
10222
+ );
10223
+ if (Object.keys(updatedSourceData).length > 0) {
10224
+ throw new Error(
10225
+ `Localization data has changed; please update i18n.lock or run without --frozen. Details: Source file has been updated.`
10226
+ );
10227
+ }
10228
+ for (const _tgt of _targetLocales) {
10229
+ const resolvedTargetLocale = resolveOverriddenLocale8(
10230
+ _tgt,
10231
+ bucketPath.delimiter
10232
+ );
10233
+ const { unlocalizable: tgtUnlocalizable, ...tgt } = await loader.pull(resolvedTargetLocale);
10234
+ const missingKeys = _35.difference(
10235
+ Object.keys(src),
10236
+ Object.keys(tgt)
10237
+ );
10238
+ if (missingKeys.length > 0) {
10239
+ throw new Error(
10240
+ `Localization data has changed; please update i18n.lock or run without --frozen. Details: Target file is missing translations.`
10241
+ );
10242
+ }
10243
+ const extraKeys = _35.difference(
10244
+ Object.keys(tgt),
10245
+ Object.keys(src)
10246
+ );
10247
+ if (extraKeys.length > 0) {
10248
+ throw new Error(
10249
+ `Localization data has changed; please update i18n.lock or run without --frozen. Details: Target file has extra translations not present in the source file.`
10250
+ );
10251
+ }
10252
+ const unlocalizableDataDiff = !_35.isEqual(
10253
+ srcUnlocalizable,
10254
+ tgtUnlocalizable
10255
+ );
10256
+ if (unlocalizableDataDiff) {
10257
+ throw new Error(
10258
+ `Localization data has changed; please update i18n.lock or run without --frozen. Details: Unlocalizable data (such as booleans, dates, URLs, etc.) do not match.`
10259
+ );
10260
+ }
10261
+ }
10262
+ }
10263
+ }
10264
+ task.title = "No lockfile updates required";
10265
+ }
10266
+ }
10267
+ ],
10268
+ {
10269
+ rendererOptions: commonTaskRendererOptions
10270
+ }
10271
+ ).run(input2);
10272
+ }
10273
+
10130
10274
  // src/cli/cmd/run/_utils.ts
10131
10275
  async function determineAuthId(ctx) {
10132
10276
  const isByokMode = !!ctx.config?.provider;
@@ -10191,6 +10335,9 @@ var run_default = new Command18().command("run").description("Run localization p
10191
10335
  ).option(
10192
10336
  "--force",
10193
10337
  "Force re-translation of all keys, bypassing change detection. Useful when you want to regenerate translations with updated AI models or translation settings"
10338
+ ).option(
10339
+ "--frozen",
10340
+ "Validate translations are up-to-date without making changes - fails if source files, target files, or lockfile are out of sync. Ideal for CI/CD to ensure translation consistency before deployment"
10194
10341
  ).option(
10195
10342
  "--api-key <api-key>",
10196
10343
  "Override API key from settings or environment variables"
@@ -10233,6 +10380,8 @@ var run_default = new Command18().command("run").description("Run localization p
10233
10380
  await renderSpacer();
10234
10381
  await plan(ctx);
10235
10382
  await renderSpacer();
10383
+ await frozen(ctx);
10384
+ await renderSpacer();
10236
10385
  await execute(ctx);
10237
10386
  await renderSpacer();
10238
10387
  await renderSummary(ctx.results);
@@ -10998,12 +11147,12 @@ function parseBooleanArg(val) {
10998
11147
  import {
10999
11148
  bucketTypeSchema as bucketTypeSchema4,
11000
11149
  localeCodeSchema as localeCodeSchema3,
11001
- resolveOverriddenLocale as resolveOverriddenLocale8
11150
+ resolveOverriddenLocale as resolveOverriddenLocale9
11002
11151
  } from "@lingo.dev/_spec";
11003
11152
  import { Command as Command20 } from "interactive-commander";
11004
11153
  import Z11 from "zod";
11005
11154
  import Ora12 from "ora";
11006
- import chalk14 from "chalk";
11155
+ import chalk15 from "chalk";
11007
11156
  import Table from "cli-table3";
11008
11157
 
11009
11158
  // src/cli/utils/exit-gracefully.ts
@@ -11145,7 +11294,7 @@ var status_default = new Command20().command("status").description("Show the sta
11145
11294
  const bucketOra = Ora12({ indent: 2 }).info(
11146
11295
  `Analyzing path: ${bucketPath.pathPattern}`
11147
11296
  );
11148
- const sourceLocale = resolveOverriddenLocale8(
11297
+ const sourceLocale = resolveOverriddenLocale9(
11149
11298
  i18nConfig.locale.source,
11150
11299
  bucketPath.delimiter
11151
11300
  );
@@ -11192,7 +11341,7 @@ var status_default = new Command20().command("status").description("Show the sta
11192
11341
  }
11193
11342
  fileStats[filePath].wordCount = sourceWordCount;
11194
11343
  for (const _targetLocale of targetLocales) {
11195
- const targetLocale = resolveOverriddenLocale8(
11344
+ const targetLocale = resolveOverriddenLocale9(
11196
11345
  _targetLocale,
11197
11346
  bucketPath.delimiter
11198
11347
  );
@@ -11219,7 +11368,7 @@ var status_default = new Command20().command("status").description("Show the sta
11219
11368
  (totalWordCount.get(_targetLocale) || 0) + sourceWordCount
11220
11369
  );
11221
11370
  bucketOra.succeed(
11222
- `[${sourceLocale} -> ${targetLocale}] ${chalk14.red(
11371
+ `[${sourceLocale} -> ${targetLocale}] ${chalk15.red(
11223
11372
  `0% complete`
11224
11373
  )} (0/${sourceKeys.length} keys) - file not found`
11225
11374
  );
@@ -11264,30 +11413,30 @@ var status_default = new Command20().command("status").description("Show the sta
11264
11413
  const completionPercent = (completeKeys.length / totalKeysInFile * 100).toFixed(1);
11265
11414
  if (missingKeys.length === 0 && updatedKeys.length === 0) {
11266
11415
  bucketOra.succeed(
11267
- `[${sourceLocale} -> ${targetLocale}] ${chalk14.green(
11416
+ `[${sourceLocale} -> ${targetLocale}] ${chalk15.green(
11268
11417
  `100% complete`
11269
11418
  )} (${completeKeys.length}/${totalKeysInFile} keys)`
11270
11419
  );
11271
11420
  } else {
11272
- const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? chalk14.yellow(`${completionPercent}% complete`) : chalk14.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
11421
+ const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? chalk15.yellow(`${completionPercent}% complete`) : chalk15.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
11273
11422
  bucketOra.succeed(message);
11274
11423
  if (flags.verbose) {
11275
11424
  if (missingKeys.length > 0) {
11276
11425
  console.log(
11277
- ` ${chalk14.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`
11426
+ ` ${chalk15.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`
11278
11427
  );
11279
11428
  console.log(
11280
- ` ${chalk14.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`
11429
+ ` ${chalk15.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`
11281
11430
  );
11282
11431
  console.log(
11283
- ` ${chalk14.dim(
11432
+ ` ${chalk15.dim(
11284
11433
  `Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`
11285
11434
  )}`
11286
11435
  );
11287
11436
  }
11288
11437
  if (updatedKeys.length > 0) {
11289
11438
  console.log(
11290
- ` ${chalk14.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`
11439
+ ` ${chalk15.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`
11291
11440
  );
11292
11441
  }
11293
11442
  }
@@ -11306,22 +11455,22 @@ var status_default = new Command20().command("status").description("Show the sta
11306
11455
  );
11307
11456
  const totalCompletedKeys = totalSourceKeyCount - totalKeysNeedingTranslation / targetLocales.length;
11308
11457
  console.log();
11309
- ora.succeed(chalk14.green(`Localization status completed.`));
11310
- console.log(chalk14.bold.cyan(`
11458
+ ora.succeed(chalk15.green(`Localization status completed.`));
11459
+ console.log(chalk15.bold.cyan(`
11311
11460
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
11312
- console.log(chalk14.bold.cyan(`\u2551 LOCALIZATION STATUS REPORT \u2551`));
11313
- console.log(chalk14.bold.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
11314
- console.log(chalk14.bold(`
11461
+ console.log(chalk15.bold.cyan(`\u2551 LOCALIZATION STATUS REPORT \u2551`));
11462
+ console.log(chalk15.bold.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
11463
+ console.log(chalk15.bold(`
11315
11464
  \u{1F4DD} SOURCE CONTENT:`));
11316
11465
  console.log(
11317
- `\u2022 Source language: ${chalk14.green(i18nConfig.locale.source)}`
11466
+ `\u2022 Source language: ${chalk15.green(i18nConfig.locale.source)}`
11318
11467
  );
11319
11468
  console.log(
11320
- `\u2022 Source keys: ${chalk14.yellow(
11469
+ `\u2022 Source keys: ${chalk15.yellow(
11321
11470
  totalSourceKeyCount.toString()
11322
11471
  )} keys across all files`
11323
11472
  );
11324
- console.log(chalk14.bold(`
11473
+ console.log(chalk15.bold(`
11325
11474
  \u{1F310} LANGUAGE BY LANGUAGE BREAKDOWN:`));
11326
11475
  const table = new Table({
11327
11476
  head: [
@@ -11351,19 +11500,19 @@ var status_default = new Command20().command("status").description("Show the sta
11351
11500
  let statusColor;
11352
11501
  if (stats.missing === totalSourceKeyCount) {
11353
11502
  statusText = "\u{1F534} Not started";
11354
- statusColor = chalk14.red;
11503
+ statusColor = chalk15.red;
11355
11504
  } else if (stats.missing === 0 && stats.updated === 0) {
11356
11505
  statusText = "\u2705 Complete";
11357
- statusColor = chalk14.green;
11506
+ statusColor = chalk15.green;
11358
11507
  } else if (parseFloat(percentComplete) > 80) {
11359
11508
  statusText = "\u{1F7E1} Almost done";
11360
- statusColor = chalk14.yellow;
11509
+ statusColor = chalk15.yellow;
11361
11510
  } else if (parseFloat(percentComplete) > 0) {
11362
11511
  statusText = "\u{1F7E0} In progress";
11363
- statusColor = chalk14.yellow;
11512
+ statusColor = chalk15.yellow;
11364
11513
  } else {
11365
11514
  statusText = "\u{1F534} Not started";
11366
- statusColor = chalk14.red;
11515
+ statusColor = chalk15.red;
11367
11516
  }
11368
11517
  const words = totalWordCount.get(locale) || 0;
11369
11518
  totalWordsToTranslate += words;
@@ -11371,17 +11520,17 @@ var status_default = new Command20().command("status").description("Show the sta
11371
11520
  locale,
11372
11521
  statusColor(statusText),
11373
11522
  `${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,
11374
- stats.missing > 0 ? chalk14.red(stats.missing.toString()) : "0",
11375
- stats.updated > 0 ? chalk14.yellow(stats.updated.toString()) : "0",
11376
- totalNeeded > 0 ? chalk14.magenta(totalNeeded.toString()) : "0",
11523
+ stats.missing > 0 ? chalk15.red(stats.missing.toString()) : "0",
11524
+ stats.updated > 0 ? chalk15.yellow(stats.updated.toString()) : "0",
11525
+ totalNeeded > 0 ? chalk15.magenta(totalNeeded.toString()) : "0",
11377
11526
  words > 0 ? `~${words.toLocaleString()}` : "0"
11378
11527
  ]);
11379
11528
  }
11380
11529
  console.log(table.toString());
11381
- console.log(chalk14.bold(`
11530
+ console.log(chalk15.bold(`
11382
11531
  \u{1F4CA} USAGE ESTIMATE:`));
11383
11532
  console.log(
11384
- `\u2022 WORDS TO BE CONSUMED: ~${chalk14.yellow.bold(
11533
+ `\u2022 WORDS TO BE CONSUMED: ~${chalk15.yellow.bold(
11385
11534
  totalWordsToTranslate.toLocaleString()
11386
11535
  )} words across all languages`
11387
11536
  );
@@ -11399,11 +11548,11 @@ var status_default = new Command20().command("status").description("Show the sta
11399
11548
  }
11400
11549
  }
11401
11550
  if (flags.confirm && Object.keys(fileStats).length > 0) {
11402
- console.log(chalk14.bold(`
11551
+ console.log(chalk15.bold(`
11403
11552
  \u{1F4D1} BREAKDOWN BY FILE:`));
11404
11553
  Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path19, stats]) => {
11405
11554
  if (stats.sourceKeys === 0) return;
11406
- console.log(chalk14.bold(`
11555
+ console.log(chalk15.bold(`
11407
11556
  \u2022 ${path19}:`));
11408
11557
  console.log(
11409
11558
  ` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`
@@ -11423,13 +11572,13 @@ var status_default = new Command20().command("status").description("Show the sta
11423
11572
  const total = stats.sourceKeys;
11424
11573
  const completion = (complete / total * 100).toFixed(1);
11425
11574
  let status = "\u2705 Complete";
11426
- let statusColor = chalk14.green;
11575
+ let statusColor = chalk15.green;
11427
11576
  if (langStats.missing === total) {
11428
11577
  status = "\u274C Not started";
11429
- statusColor = chalk14.red;
11578
+ statusColor = chalk15.red;
11430
11579
  } else if (langStats.missing > 0 || langStats.updated > 0) {
11431
11580
  status = `\u26A0\uFE0F ${completion}% complete`;
11432
- statusColor = chalk14.yellow;
11581
+ statusColor = chalk15.yellow;
11433
11582
  }
11434
11583
  let details = "";
11435
11584
  if (langStats.missing > 0 || langStats.updated > 0) {
@@ -11453,16 +11602,16 @@ var status_default = new Command20().command("status").description("Show the sta
11453
11602
  const missingLanguages = targetLocales.filter(
11454
11603
  (locale) => languageStats[locale].complete === 0
11455
11604
  );
11456
- console.log(chalk14.bold.green(`
11605
+ console.log(chalk15.bold.green(`
11457
11606
  \u{1F4A1} OPTIMIZATION TIPS:`));
11458
11607
  if (missingLanguages.length > 0) {
11459
11608
  console.log(
11460
- `\u2022 ${chalk14.yellow(missingLanguages.join(", "))} ${missingLanguages.length === 1 ? "has" : "have"} no translations yet`
11609
+ `\u2022 ${chalk15.yellow(missingLanguages.join(", "))} ${missingLanguages.length === 1 ? "has" : "have"} no translations yet`
11461
11610
  );
11462
11611
  }
11463
11612
  if (completeLanguages.length > 0) {
11464
11613
  console.log(
11465
- `\u2022 ${chalk14.green(completeLanguages.join(", "))} ${completeLanguages.length === 1 ? "is" : "are"} completely translated`
11614
+ `\u2022 ${chalk15.green(completeLanguages.join(", "))} ${completeLanguages.length === 1 ? "is" : "are"} completely translated`
11466
11615
  );
11467
11616
  }
11468
11617
  if (targetLocales.length > 1) {
@@ -11546,7 +11695,7 @@ function validateParams2(i18nConfig, flags) {
11546
11695
  import { Command as Command21 } from "interactive-commander";
11547
11696
  import * as cp from "node:child_process";
11548
11697
  import figlet2 from "figlet";
11549
- import chalk15 from "chalk";
11698
+ import chalk16 from "chalk";
11550
11699
  import { vice as vice2 } from "gradient-string";
11551
11700
  var colors2 = {
11552
11701
  orange: "#ff6600",
@@ -11560,7 +11709,7 @@ var may_the_fourth_default = new Command21().command("may-the-fourth").descripti
11560
11709
  await renderClear2();
11561
11710
  await renderBanner2();
11562
11711
  await renderSpacer2();
11563
- console.log(chalk15.hex(colors2.yellow)("Loading the Star Wars movie..."));
11712
+ console.log(chalk16.hex(colors2.yellow)("Loading the Star Wars movie..."));
11564
11713
  await renderSpacer2();
11565
11714
  await new Promise((resolve, reject) => {
11566
11715
  const ssh = cp.spawn("ssh", ["starwarstel.net"], {
@@ -11579,12 +11728,12 @@ var may_the_fourth_default = new Command21().command("may-the-fourth").descripti
11579
11728
  });
11580
11729
  await renderSpacer2();
11581
11730
  console.log(
11582
- `${chalk15.hex(colors2.green)("We hope you enjoyed it! :)")} ${chalk15.hex(
11731
+ `${chalk16.hex(colors2.green)("We hope you enjoyed it! :)")} ${chalk16.hex(
11583
11732
  colors2.blue
11584
11733
  )("May the Fourth be with you! \u{1F680}")}`
11585
11734
  );
11586
11735
  await renderSpacer2();
11587
- console.log(chalk15.dim(`---`));
11736
+ console.log(chalk16.dim(`---`));
11588
11737
  await renderSpacer2();
11589
11738
  await renderHero2();
11590
11739
  });
@@ -11607,24 +11756,24 @@ async function renderBanner2() {
11607
11756
  }
11608
11757
  async function renderHero2() {
11609
11758
  console.log(
11610
- `\u26A1\uFE0F ${chalk15.hex(colors2.green)(
11759
+ `\u26A1\uFE0F ${chalk16.hex(colors2.green)(
11611
11760
  "Lingo.dev"
11612
11761
  )} - open-source, AI-powered i18n CLI for web & mobile localization.`
11613
11762
  );
11614
11763
  console.log(" ");
11615
- console.log(chalk15.hex(colors2.blue)("\u{1F4DA} Docs: https://lingo.dev/go/docs"));
11764
+ console.log(chalk16.hex(colors2.blue)("\u{1F4DA} Docs: https://lingo.dev/go/docs"));
11616
11765
  console.log(
11617
- chalk15.hex(colors2.blue)("\u2B50 Star the repo: https://lingo.dev/go/gh")
11766
+ chalk16.hex(colors2.blue)("\u2B50 Star the repo: https://lingo.dev/go/gh")
11618
11767
  );
11619
11768
  console.log(
11620
- chalk15.hex(colors2.blue)("\u{1F3AE} Join Discord: https://lingo.dev/go/discord")
11769
+ chalk16.hex(colors2.blue)("\u{1F3AE} Join Discord: https://lingo.dev/go/discord")
11621
11770
  );
11622
11771
  }
11623
11772
 
11624
11773
  // package.json
11625
11774
  var package_default = {
11626
11775
  name: "lingo.dev",
11627
- version: "0.112.1",
11776
+ version: "0.113.0",
11628
11777
  description: "Lingo.dev CLI",
11629
11778
  private: false,
11630
11779
  publishConfig: {
@@ -11875,7 +12024,7 @@ var package_default = {
11875
12024
  // src/cli/cmd/purge.ts
11876
12025
  import { Command as Command22 } from "interactive-commander";
11877
12026
  import Ora13 from "ora";
11878
- import { resolveOverriddenLocale as resolveOverriddenLocale9 } from "@lingo.dev/_spec";
12027
+ import { resolveOverriddenLocale as resolveOverriddenLocale10 } from "@lingo.dev/_spec";
11879
12028
  import { confirm as confirm3 } from "@inquirer/prompts";
11880
12029
  var purge_default = new Command22().command("purge").description(
11881
12030
  "WARNING: Permanently delete translation entries from bucket path patterns defined in i18n.json. This is a destructive operation that cannot be undone. Without any filters, ALL managed keys will be removed from EVERY target locale."
@@ -11931,7 +12080,7 @@ var purge_default = new Command22().command("purge").description(
11931
12080
  ora.info(`Processing bucket: ${bucket.type}`);
11932
12081
  for (const bucketPath of bucket.paths) {
11933
12082
  for (const _targetLocale of targetLocales) {
11934
- const targetLocale = resolveOverriddenLocale9(
12083
+ const targetLocale = resolveOverriddenLocale10(
11935
12084
  _targetLocale,
11936
12085
  bucketPath.delimiter
11937
12086
  );