lingo.dev 0.100.1 → 0.101.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
@@ -31,7 +31,8 @@ function getSettings(explicitApiKey) {
31
31
  anthropicApiKey: env.ANTHROPIC_API_KEY || systemFile.llm?.anthropicApiKey,
32
32
  groqApiKey: env.GROQ_API_KEY || systemFile.llm?.groqApiKey,
33
33
  googleApiKey: env.GOOGLE_API_KEY || systemFile.llm?.googleApiKey,
34
- openrouterApiKey: env.OPENROUTER_API_KEY || systemFile.llm?.openrouterApiKey
34
+ openrouterApiKey: env.OPENROUTER_API_KEY || systemFile.llm?.openrouterApiKey,
35
+ mistralApiKey: env.MISTRAL_API_KEY || systemFile.llm?.mistralApiKey
35
36
  }
36
37
  };
37
38
  }
@@ -61,7 +62,8 @@ var SettingsSchema = Z.object({
61
62
  anthropicApiKey: Z.string().optional(),
62
63
  groqApiKey: Z.string().optional(),
63
64
  googleApiKey: Z.string().optional(),
64
- openrouterApiKey: Z.string().optional()
65
+ openrouterApiKey: Z.string().optional(),
66
+ mistralApiKey: Z.string().optional()
65
67
  })
66
68
  });
67
69
  var SETTINGS_KEYS = flattenZodObject(
@@ -86,7 +88,8 @@ function _loadEnv() {
86
88
  ANTHROPIC_API_KEY: Z.string().optional(),
87
89
  GROQ_API_KEY: Z.string().optional(),
88
90
  GOOGLE_API_KEY: Z.string().optional(),
89
- OPENROUTER_API_KEY: Z.string().optional()
91
+ OPENROUTER_API_KEY: Z.string().optional(),
92
+ MISTRAL_API_KEY: Z.string().optional()
90
93
  }).passthrough().parse(process.env);
91
94
  }
92
95
  function _loadSystemFile() {
@@ -104,7 +107,8 @@ function _loadSystemFile() {
104
107
  anthropicApiKey: Z.string().optional(),
105
108
  groqApiKey: Z.string().optional(),
106
109
  googleApiKey: Z.string().optional(),
107
- openrouterApiKey: Z.string().optional()
110
+ openrouterApiKey: Z.string().optional(),
111
+ mistralApiKey: Z.string().optional()
108
112
  }).optional()
109
113
  }).passthrough().parse(data);
110
114
  }
@@ -173,6 +177,12 @@ function _envVarsInfo() {
173
177
  `\u2139\uFE0F Using OPENROUTER_API_KEY env var instead of key from user config`
174
178
  );
175
179
  }
180
+ if (env.MISTRAL_API_KEY && systemFile.llm?.mistralApiKey) {
181
+ console.info(
182
+ "\x1B[36m%s\x1B[0m",
183
+ `\u2139\uFE0F Using MISTRAL_API_KEY env var instead of key from user config`
184
+ );
185
+ }
176
186
  if (env.LINGODOTDEV_API_URL) {
177
187
  console.info(
178
188
  "\x1B[36m%s\x1B[0m",
@@ -217,9 +227,12 @@ var CLIError = class extends Error {
217
227
  // src/cli/utils/cloudflare-status.ts
218
228
  async function checkCloudflareStatus() {
219
229
  try {
220
- const response = await fetch("https://www.cloudflarestatus.com/api/v2/status.json", {
221
- signal: AbortSignal.timeout(5e3)
222
- });
230
+ const response = await fetch(
231
+ "https://www.cloudflarestatus.com/api/v2/status.json",
232
+ {
233
+ signal: AbortSignal.timeout(5e3)
234
+ }
235
+ );
223
236
  if (response.ok) {
224
237
  return await response.json();
225
238
  }
@@ -378,7 +391,9 @@ async function renderBanner() {
378
391
  }
379
392
  async function renderHero() {
380
393
  console.log(
381
- `\u26A1\uFE0F ${chalk.hex(colors.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
394
+ `\u26A1\uFE0F ${chalk.hex(colors.green)(
395
+ "Lingo.dev"
396
+ )} - open-source, AI-powered i18n CLI for web & mobile localization.`
382
397
  );
383
398
  console.log("");
384
399
  const label1 = "\u2B50 GitHub Repo:";
@@ -386,13 +401,19 @@ async function renderHero() {
386
401
  const label3 = "\u{1F4AC} 24/7 Support:";
387
402
  const maxLabelWidth = 17;
388
403
  console.log(
389
- `${chalk.hex(colors.blue)(label1.padEnd(maxLabelWidth))} ${chalk.hex(colors.blue)("https://lingo.dev/go/gh")}`
404
+ `${chalk.hex(colors.blue)(label1.padEnd(maxLabelWidth))} ${chalk.hex(
405
+ colors.blue
406
+ )("https://lingo.dev/go/gh")}`
390
407
  );
391
408
  console.log(
392
- `${chalk.hex(colors.blue)(label2.padEnd(maxLabelWidth + 1))} ${chalk.hex(colors.blue)("https://lingo.dev/go/docs")}`
409
+ `${chalk.hex(colors.blue)(label2.padEnd(maxLabelWidth + 1))} ${chalk.hex(
410
+ colors.blue
411
+ )("https://lingo.dev/go/docs")}`
393
412
  );
394
413
  console.log(
395
- `${chalk.hex(colors.blue)(label3.padEnd(maxLabelWidth + 1))} ${chalk.hex(colors.blue)("hi@lingo.dev")}`
414
+ `${chalk.hex(colors.blue)(label3.padEnd(maxLabelWidth + 1))} ${chalk.hex(
415
+ colors.blue
416
+ )("hi@lingo.dev")}`
396
417
  );
397
418
  }
398
419
  async function waitForUserPrompt(message) {
@@ -541,7 +562,11 @@ function _getConfigFilePath() {
541
562
  }
542
563
 
543
564
  // src/cli/cmd/init.ts
544
- import { defaultConfig, resolveLocaleCode as resolveLocaleCode2, bucketTypes } from "@lingo.dev/_spec";
565
+ import {
566
+ defaultConfig,
567
+ resolveLocaleCode as resolveLocaleCode2,
568
+ bucketTypes
569
+ } from "@lingo.dev/_spec";
545
570
  import fs6 from "fs";
546
571
  import path7 from "path";
547
572
  import _3 from "lodash";
@@ -583,13 +608,17 @@ function findLocaleFilesWithExtension(ext2) {
583
608
  ignore: ["node_modules/**", "package*.json", "i18n.json", "lingo.json"]
584
609
  });
585
610
  const localeFilePattern = new RegExp(`/([a-z]{2}(-[A-Z]{2})?)${ext2}$`);
586
- const localeDirectoryPattern = new RegExp(`/([a-z]{2}(-[A-Z]{2})?)/[^/]+${ext2}$`);
611
+ const localeDirectoryPattern = new RegExp(
612
+ `/([a-z]{2}(-[A-Z]{2})?)/[^/]+${ext2}$`
613
+ );
587
614
  const potentialLocaleFiles = files.filter(
588
615
  (file) => localeFilePattern.test(file) || localeDirectoryPattern.test(file)
589
616
  );
590
617
  const potantialLocaleFilesAndPatterns = potentialLocaleFiles.map((file) => {
591
618
  const matchPotentialLocales = Array.from(
592
- file.matchAll(new RegExp(`/([a-z]{2}(-[A-Z]{2})?|[^/]+)(?=/|${ext2})`, "g"))
619
+ file.matchAll(
620
+ new RegExp(`/([a-z]{2}(-[A-Z]{2})?|[^/]+)(?=/|${ext2})`, "g")
621
+ )
593
622
  );
594
623
  const potantialLocales = matchPotentialLocales.map((match2) => match2[1]);
595
624
  return { file, potantialLocales };
@@ -603,10 +632,12 @@ function findLocaleFilesWithExtension(ext2) {
603
632
  }
604
633
  return { file, locale: null };
605
634
  }).filter(({ locale }) => locale !== null);
606
- const localeFilesAndPatterns = potantialLocaleFilesAndPatterns.map(({ file, locale }) => {
607
- const pattern = file.replaceAll(new RegExp(`/${locale}${ext2}`, "g"), `/[locale]${ext2}`).replaceAll(new RegExp(`/${locale}/`, "g"), `/[locale]/`).replaceAll(new RegExp(`/${locale}/`, "g"), `/[locale]/`);
608
- return { pattern, file };
609
- });
635
+ const localeFilesAndPatterns = potantialLocaleFilesAndPatterns.map(
636
+ ({ file, locale }) => {
637
+ const pattern = file.replaceAll(new RegExp(`/${locale}${ext2}`, "g"), `/[locale]${ext2}`).replaceAll(new RegExp(`/${locale}/`, "g"), `/[locale]/`).replaceAll(new RegExp(`/${locale}/`, "g"), `/[locale]/`);
638
+ return { pattern, file };
639
+ }
640
+ );
610
641
  const grouppedFilesAndPatterns = _2.groupBy(localeFilesAndPatterns, "pattern");
611
642
  const patterns = Object.keys(grouppedFilesAndPatterns);
612
643
  const defaultPatterns = [`i18n/[locale]${ext2}`];
@@ -764,7 +795,9 @@ async function initCICD(spinner) {
764
795
  message: "Would you like to use Lingo.dev in your CI/CD?"
765
796
  });
766
797
  if (!init) {
767
- spinner.warn("CI/CD not initialized. To set it up later, see docs: https://lingo.dev/ci");
798
+ spinner.warn(
799
+ "CI/CD not initialized. To set it up later, see docs: https://lingo.dev/ci"
800
+ );
768
801
  return;
769
802
  }
770
803
  const selectedPlatforms = await checkbox({
@@ -895,7 +928,9 @@ var throwHelpError = (option, value) => {
895
928
  Do you need support for ${value} ${option}? Type "help" and we will.`
896
929
  );
897
930
  };
898
- var init_default = new InteractiveCommand().command("init").description("Initialize Lingo.dev project").helpOption("-h, --help", "Show help").addOption(new InteractiveOption("-f --force", "Overwrite existing config").prompt(void 0).default(false)).addOption(
931
+ var init_default = new InteractiveCommand().command("init").description("Initialize Lingo.dev project").helpOption("-h, --help", "Show help").addOption(
932
+ new InteractiveOption("-f --force", "Overwrite existing config").prompt(void 0).default(false)
933
+ ).addOption(
899
934
  new InteractiveOption("-s --source <locale>", "Source locale").argParser((value) => {
900
935
  try {
901
936
  resolveLocaleCode2(value);
@@ -924,7 +959,10 @@ var init_default = new InteractiveCommand().command("init").description("Initial
924
959
  return value;
925
960
  }).default("json")
926
961
  ).addOption(
927
- new InteractiveOption("-p, --paths [path...]", "List of paths for the bucket").argParser((value) => {
962
+ new InteractiveOption(
963
+ "-p, --paths [path...]",
964
+ "List of paths for the bucket"
965
+ ).argParser((value) => {
928
966
  if (!value || value.length === 0) return [];
929
967
  const values = value.includes(",") ? value.split(",") : value.split(" ");
930
968
  for (const p of values) {
@@ -985,7 +1023,9 @@ var init_default = new InteractiveCommand().command("init").description("Initial
985
1023
  }
986
1024
  if (selectedPatterns.length === 0) {
987
1025
  const useDefault = await confirm2({
988
- message: `Use (and create) default path ${defaultPatterns.join(", ")}?`
1026
+ message: `Use (and create) default path ${defaultPatterns.join(
1027
+ ", "
1028
+ )}?`
989
1029
  });
990
1030
  if (useDefault) {
991
1031
  ensurePatterns(defaultPatterns, options.source);
@@ -1009,7 +1049,9 @@ var init_default = new InteractiveCommand().command("init").description("Initial
1009
1049
  spinner.succeed("Lingo.dev project initialized");
1010
1050
  if (isInteractive) {
1011
1051
  await initCICD(spinner);
1012
- const openDocs = await confirm2({ message: "Would you like to see our docs?" });
1052
+ const openDocs = await confirm2({
1053
+ message: "Would you like to see our docs?"
1054
+ });
1013
1055
  if (openDocs) {
1014
1056
  openUrl("/go/docs");
1015
1057
  }
@@ -1040,7 +1082,9 @@ var init_default = new InteractiveCommand().command("init").description("Initial
1040
1082
  }
1041
1083
  }
1042
1084
  } else {
1043
- Ora4().warn("You are not logged in. Run `npx lingo.dev@latest auth --login` to login.");
1085
+ Ora4().warn(
1086
+ "You are not logged in. Run `npx lingo.dev@latest auth --login` to login."
1087
+ );
1044
1088
  }
1045
1089
  } else {
1046
1090
  Ora4().succeed(`Authenticated as ${auth.email}`);
@@ -1229,7 +1273,13 @@ function resolveBucketItem(bucketItem) {
1229
1273
 
1230
1274
  // src/cli/cmd/show/files.ts
1231
1275
  import { resolveOverriddenLocale as resolveOverriddenLocale2 } from "@lingo.dev/_spec";
1232
- var files_default = new Command6().command("files").description("Print out the list of files managed by Lingo.dev").option("--source", "Only show source files, files containing the original translations").option("--target", "Only show target files, files containing translated content").helpOption("-h, --help", "Show help").action(async (type) => {
1276
+ var files_default = new Command6().command("files").description("Print out the list of files managed by Lingo.dev").option(
1277
+ "--source",
1278
+ "Only show source files, files containing the original translations"
1279
+ ).option(
1280
+ "--target",
1281
+ "Only show target files, files containing translated content"
1282
+ ).helpOption("-h, --help", "Show help").action(async (type) => {
1233
1283
  const ora = Ora6();
1234
1284
  try {
1235
1285
  try {
@@ -1243,12 +1293,26 @@ var files_default = new Command6().command("files").description("Print out the l
1243
1293
  const buckets = getBuckets(i18nConfig);
1244
1294
  for (const bucket of buckets) {
1245
1295
  for (const bucketConfig of bucket.paths) {
1246
- const sourceLocale = resolveOverriddenLocale2(i18nConfig.locale.source, bucketConfig.delimiter);
1247
- const sourcePath = bucketConfig.pathPattern.replace(/\[locale\]/g, sourceLocale);
1248
- const targetPaths = i18nConfig.locale.targets.map((_targetLocale) => {
1249
- const targetLocale = resolveOverriddenLocale2(_targetLocale, bucketConfig.delimiter);
1250
- return bucketConfig.pathPattern.replace(/\[locale\]/g, targetLocale);
1251
- });
1296
+ const sourceLocale = resolveOverriddenLocale2(
1297
+ i18nConfig.locale.source,
1298
+ bucketConfig.delimiter
1299
+ );
1300
+ const sourcePath = bucketConfig.pathPattern.replace(
1301
+ /\[locale\]/g,
1302
+ sourceLocale
1303
+ );
1304
+ const targetPaths = i18nConfig.locale.targets.map(
1305
+ (_targetLocale) => {
1306
+ const targetLocale = resolveOverriddenLocale2(
1307
+ _targetLocale,
1308
+ bucketConfig.delimiter
1309
+ );
1310
+ return bucketConfig.pathPattern.replace(
1311
+ /\[locale\]/g,
1312
+ targetLocale
1313
+ );
1314
+ }
1315
+ );
1252
1316
  const result = [];
1253
1317
  if (!type.source && !type.target) {
1254
1318
  result.push(sourcePath, ...targetPaths);
@@ -1328,7 +1392,9 @@ Available keys:
1328
1392
  console.error(
1329
1393
  dedent2`
1330
1394
  ${chalk3.red("\u2716")} Unknown configuration key: ${chalk3.bold(key)}
1331
- Run ${chalk3.dim("lingo.dev config unset --help")} to see available keys.
1395
+ Run ${chalk3.dim(
1396
+ "lingo.dev config unset --help"
1397
+ )} to see available keys.
1332
1398
  `
1333
1399
  );
1334
1400
  process.exitCode = 1;
@@ -1402,7 +1468,7 @@ import {
1402
1468
  } from "@lingo.dev/_spec";
1403
1469
  import { Command as Command12 } from "interactive-commander";
1404
1470
  import Z3 from "zod";
1405
- import _29 from "lodash";
1471
+ import _30 from "lodash";
1406
1472
  import Ora7 from "ora";
1407
1473
 
1408
1474
  // src/cli/loaders/_utils.ts
@@ -1627,13 +1693,17 @@ function createTextFileLoader(pathPattern) {
1627
1693
  const trimmedResult = result.trim();
1628
1694
  return trimmedResult;
1629
1695
  },
1630
- async push(locale, data, _33, originalLocale) {
1696
+ async push(locale, data, _34, originalLocale) {
1631
1697
  const draftPath = pathPattern.replaceAll("[locale]", locale);
1632
1698
  const finalPath = path10.resolve(draftPath);
1633
1699
  const dirPath = path10.dirname(finalPath);
1634
1700
  await fs8.mkdir(dirPath, { recursive: true });
1635
1701
  const trimmedPayload = data.trim();
1636
- const trailingNewLine = await getTrailingNewLine(pathPattern, locale, originalLocale);
1702
+ const trailingNewLine = await getTrailingNewLine(
1703
+ pathPattern,
1704
+ locale,
1705
+ originalLocale
1706
+ );
1637
1707
  let finalPayload = trimmedPayload + trailingNewLine;
1638
1708
  await fs8.writeFile(finalPath, finalPayload, {
1639
1709
  encoding: "utf-8",
@@ -1727,15 +1797,22 @@ import _10 from "lodash";
1727
1797
  function createFlutterLoader() {
1728
1798
  return createLoader({
1729
1799
  async pull(locale, input2) {
1730
- const result = _10.pickBy(input2, (value, key) => !key.startsWith("@"));
1800
+ const result = _10.pickBy(input2, (value, key) => !_isMetadataKey(key));
1731
1801
  return result;
1732
1802
  },
1733
1803
  async push(locale, data, originalInput) {
1734
- const result = _10.merge({}, originalInput, { "@@locale": locale }, data);
1804
+ const metadata = _10.pickBy(
1805
+ originalInput,
1806
+ (value, key) => _isMetadataKey(key)
1807
+ );
1808
+ const result = _10.merge({}, metadata, { "@@locale": locale }, data);
1735
1809
  return result;
1736
1810
  }
1737
1811
  });
1738
1812
  }
1813
+ function _isMetadataKey(key) {
1814
+ return key.startsWith("@");
1815
+ }
1739
1816
 
1740
1817
  // src/cli/loaders/android.ts
1741
1818
  import { parseStringPromise, Builder } from "xml2js";
@@ -1930,7 +2007,9 @@ function createCsvLoader() {
1930
2007
  function _createCsvLoader() {
1931
2008
  return createLoader({
1932
2009
  async pull(locale, input2) {
1933
- const keyColumnName = detectKeyColumnName(input2.split("\n").find((l) => l.length));
2010
+ const keyColumnName = detectKeyColumnName(
2011
+ input2.split("\n").find((l) => l.length)
2012
+ );
1934
2013
  const inputParsed = parse(input2, {
1935
2014
  columns: true,
1936
2015
  skip_empty_lines: true,
@@ -1958,7 +2037,9 @@ function _createCsvLoader() {
1958
2037
  ...row,
1959
2038
  [locale]: items[row[keyColumnName]] || row[locale] || ""
1960
2039
  }));
1961
- const existingKeys = new Set(inputParsed.map((row) => row[keyColumnName]));
2040
+ const existingKeys = new Set(
2041
+ inputParsed.map((row) => row[keyColumnName])
2042
+ );
1962
2043
  Object.entries(items).forEach(([key, value]) => {
1963
2044
  if (!existingKeys.has(key)) {
1964
2045
  const newRow = {
@@ -2054,15 +2135,23 @@ function createHtmlLoader() {
2054
2135
  result[getPath(element, attr)] = value;
2055
2136
  }
2056
2137
  });
2057
- Array.from(element.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()).forEach(processNode);
2138
+ Array.from(element.childNodes).filter(
2139
+ (n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()
2140
+ ).forEach(processNode);
2058
2141
  }
2059
2142
  };
2060
- Array.from(document.head.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()).forEach(processNode);
2061
- Array.from(document.body.childNodes).filter((n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()).forEach(processNode);
2143
+ Array.from(document.head.childNodes).filter(
2144
+ (n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()
2145
+ ).forEach(processNode);
2146
+ Array.from(document.body.childNodes).filter(
2147
+ (n) => n.nodeType === 1 || n.nodeType === 3 && n.textContent?.trim()
2148
+ ).forEach(processNode);
2062
2149
  return result;
2063
2150
  },
2064
2151
  async push(locale, data, originalInput) {
2065
- const dom = new JSDOM(originalInput ?? "<!DOCTYPE html><html><head></head><body></body></html>");
2152
+ const dom = new JSDOM(
2153
+ originalInput ?? "<!DOCTYPE html><html><head></head><body></body></html>"
2154
+ );
2066
2155
  const document = dom.window.document;
2067
2156
  const htmlElement = document.documentElement;
2068
2157
  htmlElement.setAttribute("lang", locale);
@@ -2136,14 +2225,21 @@ function createMarkdownLoader() {
2136
2225
  ...Object.fromEntries(
2137
2226
  sections.map((section, index) => [`${MD_SECTION_PREFIX}${index}`, section]).filter(([, section]) => Boolean(section))
2138
2227
  ),
2139
- ...Object.fromEntries(Object.entries(frontmatter).map(([key, value]) => [`${FM_ATTR_PREFIX}${key}`, value]))
2228
+ ...Object.fromEntries(
2229
+ Object.entries(frontmatter).map(([key, value]) => [
2230
+ `${FM_ATTR_PREFIX}${key}`,
2231
+ value
2232
+ ])
2233
+ )
2140
2234
  };
2141
2235
  },
2142
2236
  async push(locale, data) {
2143
2237
  const frontmatter = Object.fromEntries(
2144
2238
  Object.entries(data).filter(([key]) => key.startsWith(FM_ATTR_PREFIX)).map(([key, value]) => [key.replace(FM_ATTR_PREFIX, ""), value])
2145
2239
  );
2146
- let content = Object.entries(data).filter(([key]) => key.startsWith(MD_SECTION_PREFIX)).sort(([a], [b]) => Number(a.split("-").pop()) - Number(b.split("-").pop())).map(([, value]) => value?.trim() ?? "").filter(Boolean).join("\n\n");
2240
+ let content = Object.entries(data).filter(([key]) => key.startsWith(MD_SECTION_PREFIX)).sort(
2241
+ ([a], [b]) => Number(a.split("-").pop()) - Number(b.split("-").pop())
2242
+ ).map(([, value]) => value?.trim() ?? "").filter(Boolean).join("\n\n");
2147
2243
  if (Object.keys(frontmatter).length > 0) {
2148
2244
  content = `
2149
2245
  ${content}`;
@@ -2176,7 +2272,7 @@ function createPropertiesLoader() {
2176
2272
  return result;
2177
2273
  },
2178
2274
  async push(locale, payload) {
2179
- const result = Object.entries(payload).filter(([_33, value]) => value != null).map(([key, value]) => `${key}=${value}`).join("\n");
2275
+ const result = Object.entries(payload).filter(([_34, value]) => value != null).map(([key, value]) => `${key}=${value}`).join("\n");
2180
2276
  return result;
2181
2277
  }
2182
2278
  });
@@ -2268,7 +2364,9 @@ function createXcodeXcstringsLoader(defaultLocale) {
2268
2364
  async pull(locale, input2, initCtx) {
2269
2365
  const resultData = {};
2270
2366
  const isSourceLanguage = locale === defaultLocale;
2271
- for (const [translationKey, _translationEntity] of Object.entries(input2.strings)) {
2367
+ for (const [translationKey, _translationEntity] of Object.entries(
2368
+ input2.strings
2369
+ )) {
2272
2370
  const rootTranslationEntity = _translationEntity;
2273
2371
  if (rootTranslationEntity.shouldTranslate === false) {
2274
2372
  continue;
@@ -2297,7 +2395,10 @@ function createXcodeXcstringsLoader(defaultLocale) {
2297
2395
  async push(locale, payload, originalInput) {
2298
2396
  const langDataToMerge = {};
2299
2397
  langDataToMerge.strings = {};
2300
- const input2 = _12.cloneDeep(originalInput) || { sourceLanguage: locale, strings: {} };
2398
+ const input2 = _12.cloneDeep(originalInput) || {
2399
+ sourceLanguage: locale,
2400
+ strings: {}
2401
+ };
2301
2402
  for (const [key, value] of Object.entries(payload)) {
2302
2403
  if (value === null || value === void 0) {
2303
2404
  continue;
@@ -2343,11 +2444,22 @@ function createXcodeXcstringsLoader(defaultLocale) {
2343
2444
  }
2344
2445
  }
2345
2446
  }
2346
- const result = _12.merge({}, originalInput, langDataToMerge);
2447
+ const originalInputWithoutLocale = originalInput ? _removeLocale(originalInput, locale) : {};
2448
+ const result = _12.merge({}, originalInputWithoutLocale, langDataToMerge);
2347
2449
  return result;
2348
2450
  }
2349
2451
  });
2350
2452
  }
2453
+ function _removeLocale(input2, locale) {
2454
+ const { sourceLanguage, strings } = input2;
2455
+ const newStrings = _12.cloneDeep(strings);
2456
+ for (const [key, value] of Object.entries(newStrings)) {
2457
+ if (value.localizations?.[locale]) {
2458
+ delete value.localizations[locale];
2459
+ }
2460
+ }
2461
+ return { sourceLanguage, strings: newStrings };
2462
+ }
2351
2463
 
2352
2464
  // src/cli/loaders/prettier.ts
2353
2465
  import path11 from "path";
@@ -2425,36 +2537,29 @@ async function formatDataWithPrettier(data, filePath, options) {
2425
2537
  import _13 from "lodash";
2426
2538
  import _isUrl from "is-url";
2427
2539
  import { isValid, parseISO } from "date-fns";
2428
- function createUnlocalizableLoader(isCacheRestore = false, returnUnlocalizedKeys = false) {
2429
- const rules = {
2430
- isEmpty: (v) => _13.isEmpty(v),
2431
- isNumber: (v) => typeof v === "number" || /^[0-9]+$/.test(v),
2432
- isBoolean: (v) => _13.isBoolean(v),
2433
- isIsoDate: (v) => _13.isString(v) && _isIsoDate(v),
2434
- isSystemId: (v) => _13.isString(v) && _isSystemId(v),
2435
- isUrl: (v) => _13.isString(v) && _isUrl(v)
2436
- };
2540
+ function createUnlocalizableLoader(returnUnlocalizedKeys = false) {
2437
2541
  return createLoader({
2438
2542
  async pull(locale, input2) {
2439
- const passthroughKeys = Object.entries(input2).filter(([key, value]) => {
2440
- for (const [ruleName, rule] of Object.entries(rules)) {
2441
- if (rule(value)) {
2442
- return true;
2443
- }
2444
- }
2445
- return false;
2446
- }).map(([key, _33]) => key);
2447
- const result = _13.omitBy(input2, (_33, key) => passthroughKeys.includes(key));
2543
+ const unlocalizableKeys = _getUnlocalizableKeys(input2);
2544
+ const result = _13.omitBy(
2545
+ input2,
2546
+ (_34, key) => unlocalizableKeys.includes(key)
2547
+ );
2448
2548
  if (returnUnlocalizedKeys) {
2449
- result.unlocalizable = _13.omitBy(input2, (_33, key) => !passthroughKeys.includes(key));
2549
+ result.unlocalizable = _13.omitBy(
2550
+ input2,
2551
+ (_34, key) => !unlocalizableKeys.includes(key)
2552
+ );
2450
2553
  }
2451
2554
  return result;
2452
2555
  },
2453
2556
  async push(locale, data, originalInput) {
2454
- if (isCacheRestore) {
2455
- return _13.merge({}, data);
2456
- }
2457
- const result = _13.merge({}, originalInput, data);
2557
+ const unlocalizableKeys = _getUnlocalizableKeys(originalInput);
2558
+ const result = _13.merge(
2559
+ {},
2560
+ data,
2561
+ _13.omitBy(originalInput, (_34, key) => !unlocalizableKeys.includes(key))
2562
+ );
2458
2563
  return result;
2459
2564
  }
2460
2565
  });
@@ -2465,6 +2570,27 @@ function _isSystemId(v) {
2465
2570
  function _isIsoDate(v) {
2466
2571
  return isValid(parseISO(v));
2467
2572
  }
2573
+ function _getUnlocalizableKeys(input2) {
2574
+ const rules = {
2575
+ isEmpty: (v) => _13.isEmpty(v),
2576
+ isNumber: (v) => typeof v === "number" || /^[0-9]+$/.test(v),
2577
+ isBoolean: (v) => _13.isBoolean(v),
2578
+ isIsoDate: (v) => _13.isString(v) && _isIsoDate(v),
2579
+ isSystemId: (v) => _13.isString(v) && _isSystemId(v),
2580
+ isUrl: (v) => _13.isString(v) && _isUrl(v)
2581
+ };
2582
+ if (!input2) {
2583
+ return [];
2584
+ }
2585
+ return Object.entries(input2).filter(([key, value]) => {
2586
+ for (const [ruleName, rule] of Object.entries(rules)) {
2587
+ if (rule(value)) {
2588
+ return true;
2589
+ }
2590
+ }
2591
+ return false;
2592
+ }).map(([key, _34]) => key);
2593
+ }
2468
2594
 
2469
2595
  // src/cli/loaders/po/index.ts
2470
2596
  import _14 from "lodash";
@@ -2736,16 +2862,24 @@ function createDatoFilterLoader() {
2736
2862
  for (const [modelId, modelInfo] of _15.entries(result)) {
2737
2863
  for (const record of modelInfo.records) {
2738
2864
  for (const [fieldId, fieldValue] of _15.entries(record)) {
2739
- const fieldInfo = modelInfo.fields.find((field) => field.api_key === fieldId);
2865
+ const fieldInfo = modelInfo.fields.find(
2866
+ (field) => field.api_key === fieldId
2867
+ );
2740
2868
  if (fieldInfo) {
2741
2869
  const sourceFieldValue = _15.get(fieldValue, [originalLocale]);
2742
- const targetFieldValue = _15.get(data, [modelId, record.id, fieldId]);
2870
+ const targetFieldValue = _15.get(data, [
2871
+ modelId,
2872
+ record.id,
2873
+ fieldId
2874
+ ]);
2743
2875
  if (targetFieldValue) {
2744
2876
  _15.set(record, [fieldId, locale], targetFieldValue);
2745
2877
  } else {
2746
2878
  _15.set(record, [fieldId, locale], sourceFieldValue);
2747
2879
  }
2748
- _15.chain(fieldValue).keys().reject((loc) => loc === locale || loc === originalLocale).filter((loc) => _15.isEmpty(_15.get(fieldValue, [loc]))).forEach((loc) => _15.set(record, [fieldId, loc], sourceFieldValue)).value();
2880
+ _15.chain(fieldValue).keys().reject((loc) => loc === locale || loc === originalLocale).filter((loc) => _15.isEmpty(_15.get(fieldValue, [loc]))).forEach(
2881
+ (loc) => _15.set(record, [fieldId, loc], sourceFieldValue)
2882
+ ).value();
2749
2883
  }
2750
2884
  }
2751
2885
  }
@@ -2763,7 +2897,9 @@ import _16 from "lodash";
2763
2897
  import { buildClient } from "@datocms/cma-client-node";
2764
2898
  function createDatoClient(params) {
2765
2899
  if (!params.apiKey) {
2766
- throw new Error("Missing required environment variable: DATO_API_TOKEN. Please set this variable and try again.");
2900
+ throw new Error(
2901
+ "Missing required environment variable: DATO_API_TOKEN. Please set this variable and try again."
2902
+ );
2767
2903
  }
2768
2904
  const dato = buildClient({
2769
2905
  apiToken: params.apiKey,
@@ -2810,11 +2946,16 @@ function createDatoClient(params) {
2810
2946
  findModels: async () => {
2811
2947
  try {
2812
2948
  const models = await dato.itemTypes.list();
2813
- const modelsWithoutBlocks = models.filter((model) => !model.modular_block);
2949
+ const modelsWithoutBlocks = models.filter(
2950
+ (model) => !model.modular_block
2951
+ );
2814
2952
  return modelsWithoutBlocks;
2815
2953
  } catch (_error) {
2816
2954
  throw new Error(
2817
- [`Failed to find models in DatoCMS.`, `Error: ${JSON.stringify(_error, null, 2)}`].join("\n\n")
2955
+ [
2956
+ `Failed to find models in DatoCMS.`,
2957
+ `Error: ${JSON.stringify(_error, null, 2)}`
2958
+ ].join("\n\n")
2818
2959
  );
2819
2960
  }
2820
2961
  },
@@ -2845,7 +2986,9 @@ function createDatoClient(params) {
2845
2986
  only_valid: "true",
2846
2987
  ids: !records.length ? void 0 : records.join(",")
2847
2988
  }
2848
- }).catch((error) => Promise.reject(error?.response?.body?.data?.[0] || error));
2989
+ }).catch(
2990
+ (error) => Promise.reject(error?.response?.body?.data?.[0] || error)
2991
+ );
2849
2992
  },
2850
2993
  findRecordsForModel: async (modelId, records) => {
2851
2994
  try {
@@ -2857,7 +3000,9 @@ function createDatoClient(params) {
2857
3000
  only_valid: "true",
2858
3001
  ids: !records?.length ? void 0 : records.join(",")
2859
3002
  }
2860
- }).catch((error) => Promise.reject(error?.response?.body?.data?.[0] || error));
3003
+ }).catch(
3004
+ (error) => Promise.reject(error?.response?.body?.data?.[0] || error)
3005
+ );
2861
3006
  return result;
2862
3007
  } catch (_error) {
2863
3008
  throw new Error(
@@ -2871,7 +3016,9 @@ function createDatoClient(params) {
2871
3016
  },
2872
3017
  updateRecord: async (id, payload) => {
2873
3018
  try {
2874
- await dato.items.update(id, payload).catch((error) => Promise.reject(error?.response?.body?.data?.[0] || error));
3019
+ await dato.items.update(id, payload).catch(
3020
+ (error) => Promise.reject(error?.response?.body?.data?.[0] || error)
3021
+ );
2875
3022
  } catch (_error) {
2876
3023
  if (_error?.attributes?.details?.message) {
2877
3024
  throw new Error(
@@ -2894,7 +3041,9 @@ function createDatoClient(params) {
2894
3041
  },
2895
3042
  enableFieldLocalization: async (args) => {
2896
3043
  try {
2897
- await dato.fields.update(`${args.modelId}::${args.fieldId}`, { localized: true }).catch((error) => Promise.reject(error?.response?.body?.data?.[0] || error));
3044
+ await dato.fields.update(`${args.modelId}::${args.fieldId}`, { localized: true }).catch(
3045
+ (error) => Promise.reject(error?.response?.body?.data?.[0] || error)
3046
+ );
2898
3047
  } catch (_error) {
2899
3048
  if (_error?.attributes?.code === "NOT_FOUND") {
2900
3049
  throw new Error(
@@ -2906,7 +3055,10 @@ function createDatoClient(params) {
2906
3055
  }
2907
3056
  if (_error?.attributes?.details?.message) {
2908
3057
  throw new Error(
2909
- [`${_error.attributes.details.message}`, `Error: ${JSON.stringify(_error, null, 2)}`].join("\n\n")
3058
+ [
3059
+ `${_error.attributes.details.message}`,
3060
+ `Error: ${JSON.stringify(_error, null, 2)}`
3061
+ ].join("\n\n")
2910
3062
  );
2911
3063
  }
2912
3064
  throw new Error(
@@ -2958,9 +3110,16 @@ function createDatoApiLoader(config, onConfigUpdate) {
2958
3110
  result.models[modelId] = { fields: [], records: [] };
2959
3111
  const fieldInfos = await getFieldDetails(dato, fields);
2960
3112
  const fieldChoices = createFieldChoices(fieldInfos);
2961
- const selectedFields = await promptFieldSelection(modelName, fieldChoices);
3113
+ const selectedFields = await promptFieldSelection(
3114
+ modelName,
3115
+ fieldChoices
3116
+ );
2962
3117
  for (const fieldInfo of fieldInfos) {
2963
- const isLocalized = await updateFieldLocalization(dato, fieldInfo, selectedFields.includes(fieldInfo.id));
3118
+ const isLocalized = await updateFieldLocalization(
3119
+ dato,
3120
+ fieldInfo,
3121
+ selectedFields.includes(fieldInfo.id)
3122
+ );
2964
3123
  if (isLocalized) {
2965
3124
  result.models[modelId].fields.push(fieldInfo);
2966
3125
  updatedConfig.models[modelId].fields = _17.uniq([
@@ -2970,9 +3129,18 @@ function createDatoApiLoader(config, onConfigUpdate) {
2970
3129
  }
2971
3130
  }
2972
3131
  const records = await dato.findRecordsForModel(modelId);
2973
- const recordChoices = createRecordChoices(records, config.models[modelId]?.records || [], project);
2974
- const selectedRecords = await promptRecordSelection(modelName, recordChoices);
2975
- result.models[modelId].records = records.filter((record) => selectedRecords.includes(record.id));
3132
+ const recordChoices = createRecordChoices(
3133
+ records,
3134
+ config.models[modelId]?.records || [],
3135
+ project
3136
+ );
3137
+ const selectedRecords = await promptRecordSelection(
3138
+ modelName,
3139
+ recordChoices
3140
+ );
3141
+ result.models[modelId].records = records.filter(
3142
+ (record) => selectedRecords.includes(record.id)
3143
+ );
2976
3144
  updatedConfig.models[modelId].records = selectedRecords;
2977
3145
  }
2978
3146
  }
@@ -3000,7 +3168,9 @@ function createDatoApiLoader(config, onConfigUpdate) {
3000
3168
  for (const modelId of _17.keys(data)) {
3001
3169
  for (let i = 0; i < data[modelId].records.length; i++) {
3002
3170
  const record = data[modelId].records[i];
3003
- console.log(`Updating record ${i + 1}/${data[modelId].records.length} for model ${modelId}...`);
3171
+ console.log(
3172
+ `Updating record ${i + 1}/${data[modelId].records.length} for model ${modelId}...`
3173
+ );
3004
3174
  await dato.updateRecord(record.id, record);
3005
3175
  }
3006
3176
  }
@@ -3039,7 +3209,9 @@ async function promptFieldSelection(modelName, choices) {
3039
3209
  }
3040
3210
  async function updateFieldLocalization(dato, fieldInfo, shouldBeLocalized) {
3041
3211
  if (shouldBeLocalized !== fieldInfo.localized) {
3042
- console.log(`${shouldBeLocalized ? "Enabling" : "Disabling"} localization for ${fieldInfo.label}...`);
3212
+ console.log(
3213
+ `${shouldBeLocalized ? "Enabling" : "Disabling"} localization for ${fieldInfo.label}...`
3214
+ );
3043
3215
  await dato.updateField(fieldInfo.id, { localized: shouldBeLocalized });
3044
3216
  }
3045
3217
  return shouldBeLocalized;
@@ -3112,9 +3284,21 @@ function createDatoExtractLoader() {
3112
3284
  for (const [virtualRecordId, record] of _18.entries(modelInfo)) {
3113
3285
  for (const [fieldName, fieldValue] of _18.entries(record)) {
3114
3286
  const [, recordId] = virtualRecordId.split("_");
3115
- const originalFieldValue = _18.get(originalInput, [modelId, recordId, fieldName]);
3116
- const rawValue = createRawDatoValue(fieldValue, originalFieldValue, true);
3117
- _18.set(result, [modelId, recordId, fieldName], rawValue || originalFieldValue);
3287
+ const originalFieldValue = _18.get(originalInput, [
3288
+ modelId,
3289
+ recordId,
3290
+ fieldName
3291
+ ]);
3292
+ const rawValue = createRawDatoValue(
3293
+ fieldValue,
3294
+ originalFieldValue,
3295
+ true
3296
+ );
3297
+ _18.set(
3298
+ result,
3299
+ [modelId, recordId, fieldName],
3300
+ rawValue || originalFieldValue
3301
+ );
3118
3302
  }
3119
3303
  }
3120
3304
  }
@@ -3184,7 +3368,11 @@ function createRawDatoValue(parsedDatoValue, originalRawDatoValue, isClean = fal
3184
3368
  case "single_block":
3185
3369
  return deserializeBlock(parsedDatoValue, originalRawDatoValue, isClean);
3186
3370
  case "rich_text":
3187
- return deserializeBlockList(parsedDatoValue, originalRawDatoValue, isClean);
3371
+ return deserializeBlockList(
3372
+ parsedDatoValue,
3373
+ originalRawDatoValue,
3374
+ isClean
3375
+ );
3188
3376
  case "json":
3189
3377
  return JSON.stringify(parsedDatoValue, null, 2);
3190
3378
  case "video":
@@ -3201,7 +3389,11 @@ function serializeStructuredText(rawStructuredText) {
3201
3389
  return serializeStructuredTextNode(rawStructuredText);
3202
3390
  function serializeStructuredTextNode(node, path17 = [], acc = {}) {
3203
3391
  if ("document" in node) {
3204
- return serializeStructuredTextNode(node.document, [...path17, "document"], acc);
3392
+ return serializeStructuredTextNode(
3393
+ node.document,
3394
+ [...path17, "document"],
3395
+ acc
3396
+ );
3205
3397
  }
3206
3398
  if (!_18.isNil(node.value)) {
3207
3399
  acc[[...path17, "value"].join(".")] = node.value;
@@ -3210,7 +3402,11 @@ function serializeStructuredText(rawStructuredText) {
3210
3402
  }
3211
3403
  if (node.children) {
3212
3404
  for (let i = 0; i < node.children.length; i++) {
3213
- serializeStructuredTextNode(node.children[i], [...path17, i.toString()], acc);
3405
+ serializeStructuredTextNode(
3406
+ node.children[i],
3407
+ [...path17, i.toString()],
3408
+ acc
3409
+ );
3214
3410
  }
3215
3411
  }
3216
3412
  return acc;
@@ -3253,7 +3449,11 @@ function deserializeVideo(parsedVideo, originalRawVideo) {
3253
3449
  function deserializeBlock(payload, rawNode, isClean = false) {
3254
3450
  const result = _18.cloneDeep(rawNode);
3255
3451
  for (const [attributeName, attributeValue] of _18.entries(rawNode.attributes)) {
3256
- const rawValue = createRawDatoValue(payload[attributeName], attributeValue, isClean);
3452
+ const rawValue = createRawDatoValue(
3453
+ payload[attributeName],
3454
+ attributeValue,
3455
+ isClean
3456
+ );
3257
3457
  _18.set(result, ["attributes", attributeName], rawValue);
3258
3458
  }
3259
3459
  if (isClean) {
@@ -3265,13 +3465,19 @@ function deserializeSeo(parsedSeo, originalRawSeo) {
3265
3465
  return _18.chain(parsedSeo).pick(["title", "description"]).defaults(originalRawSeo).value();
3266
3466
  }
3267
3467
  function deserializeBlockList(parsedBlockList, originalRawBlockList, isClean = false) {
3268
- return _18.chain(parsedBlockList).map((block, i) => deserializeBlock(block, originalRawBlockList[i], isClean)).value();
3468
+ return _18.chain(parsedBlockList).map(
3469
+ (block, i) => deserializeBlock(block, originalRawBlockList[i], isClean)
3470
+ ).value();
3269
3471
  }
3270
3472
  function deserializeStructuredText(parsedStructuredText, originalRawStructuredText) {
3271
3473
  const result = _18.cloneDeep(originalRawStructuredText);
3272
3474
  for (const [path17, value] of _18.entries(parsedStructuredText)) {
3273
3475
  const realPath = _18.chain(path17.split(".")).flatMap((s) => !_18.isNaN(_18.toNumber(s)) ? ["children", s] : s).value();
3274
- const deserializedValue = createRawDatoValue(value, _18.get(originalRawStructuredText, realPath), true);
3476
+ const deserializedValue = createRawDatoValue(
3477
+ value,
3478
+ _18.get(originalRawStructuredText, realPath),
3479
+ true
3480
+ );
3275
3481
  _18.set(result, realPath, deserializedValue);
3276
3482
  }
3277
3483
  return result;
@@ -3284,13 +3490,21 @@ function _isJson(rawDatoValue) {
3284
3490
  }
3285
3491
  }
3286
3492
  function _isFile(rawDatoValue) {
3287
- return _18.isObject(rawDatoValue) && ["alt", "title", "custom_data", "focal_point", "upload_id"].every((key) => _18.has(rawDatoValue, key));
3288
- }
3289
- function _isVideo(rawDatoValue) {
3290
- return _18.isObject(rawDatoValue) && ["url", "title", "width", "height", "provider", "provider_uid", "thumbnail_url"].every(
3493
+ return _18.isObject(rawDatoValue) && ["alt", "title", "custom_data", "focal_point", "upload_id"].every(
3291
3494
  (key) => _18.has(rawDatoValue, key)
3292
3495
  );
3293
3496
  }
3497
+ function _isVideo(rawDatoValue) {
3498
+ return _18.isObject(rawDatoValue) && [
3499
+ "url",
3500
+ "title",
3501
+ "width",
3502
+ "height",
3503
+ "provider",
3504
+ "provider_uid",
3505
+ "thumbnail_url"
3506
+ ].every((key) => _18.has(rawDatoValue, key));
3507
+ }
3294
3508
 
3295
3509
  // src/cli/loaders/dato/index.ts
3296
3510
  function createDatoLoader(configFilePath) {
@@ -3300,13 +3514,20 @@ function createDatoLoader(configFilePath) {
3300
3514
  return composeLoaders(
3301
3515
  createDatoApiLoader(
3302
3516
  datoConfig,
3303
- (updatedConfig) => fs9.writeFileSync(configFilePath, JSON5.stringify(updatedConfig, null, 2))
3517
+ (updatedConfig) => fs9.writeFileSync(
3518
+ configFilePath,
3519
+ JSON5.stringify(updatedConfig, null, 2)
3520
+ )
3304
3521
  ),
3305
3522
  createDatoFilterLoader(),
3306
3523
  createDatoExtractLoader()
3307
3524
  );
3308
3525
  } catch (error) {
3309
- throw new Error([`Failed to parse DatoCMS config file.`, `Error: ${error.message}`].join("\n\n"));
3526
+ throw new Error(
3527
+ [`Failed to parse DatoCMS config file.`, `Error: ${error.message}`].join(
3528
+ "\n\n"
3529
+ )
3530
+ );
3310
3531
  }
3311
3532
  }
3312
3533
 
@@ -3455,7 +3676,9 @@ function formatPlutilStyle(jsonData, existingJson) {
3455
3676
  }
3456
3677
  if (Array.isArray(data)) {
3457
3678
  if (data.length === 0) return "[]";
3458
- const items2 = data.map((item) => `${nextIndent}${format(item, level + 1)}`);
3679
+ const items2 = data.map(
3680
+ (item) => `${nextIndent}${format(item, level + 1)}`
3681
+ );
3459
3682
  return `[
3460
3683
  ${items2.join(",\n")}
3461
3684
  ${currentIndent}]`;
@@ -3475,7 +3698,10 @@ ${currentIndent}}`;
3475
3698
  });
3476
3699
  const items = sortedKeys.map((key) => {
3477
3700
  const value = data[key];
3478
- return `${nextIndent}${JSON.stringify(key)} : ${format(value, level + 1)}`;
3701
+ return `${nextIndent}${JSON.stringify(key)} : ${format(
3702
+ value,
3703
+ level + 1
3704
+ )}`;
3479
3705
  });
3480
3706
  return `{
3481
3707
  ${items.join(",\n")}
@@ -3548,11 +3774,23 @@ function toPhpArray(data, shortSyntax = true, indentLevel = 1) {
3548
3774
  const arrayEnd = shortSyntax ? "]" : ")";
3549
3775
  if (Array.isArray(data)) {
3550
3776
  return `${arrayStart}
3551
- ${data.map((value) => `${indent(indentLevel)}${toPhpArray(value, shortSyntax, indentLevel + 1)}`).join(",\n")}
3777
+ ${data.map(
3778
+ (value) => `${indent(indentLevel)}${toPhpArray(
3779
+ value,
3780
+ shortSyntax,
3781
+ indentLevel + 1
3782
+ )}`
3783
+ ).join(",\n")}
3552
3784
  ${indent(indentLevel - 1)}${arrayEnd}`;
3553
3785
  }
3554
3786
  const output = `${arrayStart}
3555
- ${Object.entries(data).map(([key, value]) => `${indent(indentLevel)}'${key}' => ${toPhpArray(value, shortSyntax, indentLevel + 1)}`).join(",\n")}
3787
+ ${Object.entries(data).map(
3788
+ ([key, value]) => `${indent(indentLevel)}'${key}' => ${toPhpArray(
3789
+ value,
3790
+ shortSyntax,
3791
+ indentLevel + 1
3792
+ )}`
3793
+ ).join(",\n")}
3556
3794
  ${indent(indentLevel - 1)}${arrayEnd}`;
3557
3795
  return output;
3558
3796
  }
@@ -3578,7 +3816,11 @@ function createVueJsonLoader() {
3578
3816
  }
3579
3817
  parsed.i18n[locale] = data;
3580
3818
  return `${parsed.before}<i18n>
3581
- ${JSON.stringify(parsed.i18n, null, 2)}
3819
+ ${JSON.stringify(
3820
+ parsed.i18n,
3821
+ null,
3822
+ 2
3823
+ )}
3582
3824
  </i18n>${parsed.after}`;
3583
3825
  }
3584
3826
  });
@@ -3844,42 +4086,48 @@ function createInjectLocaleLoader(injectLocaleKeys) {
3844
4086
  if (!injectLocaleKeys) {
3845
4087
  return data;
3846
4088
  }
3847
- const omitKeys = injectLocaleKeys.filter((key) => {
3848
- return _22.get(data, key) === locale;
3849
- });
4089
+ const omitKeys = _getKeysWithLocales(data, injectLocaleKeys, locale);
3850
4090
  const result = _22.omit(data, omitKeys);
3851
4091
  return result;
3852
4092
  },
3853
4093
  async push(locale, data, originalInput, originalLocale) {
3854
- if (!injectLocaleKeys) {
4094
+ if (!injectLocaleKeys || !originalInput) {
3855
4095
  return data;
3856
4096
  }
3857
- const mergedData = _22.merge({}, originalInput, data);
3858
- injectLocaleKeys.forEach((key) => {
3859
- if (_22.get(mergedData, key) === originalLocale) {
3860
- _22.set(mergedData, key, locale);
3861
- }
4097
+ const localeKeys = _getKeysWithLocales(
4098
+ originalInput,
4099
+ injectLocaleKeys,
4100
+ originalLocale
4101
+ );
4102
+ localeKeys.forEach((key) => {
4103
+ _22.set(data, key, locale);
3862
4104
  });
3863
- return mergedData;
4105
+ return data;
3864
4106
  }
3865
4107
  });
3866
4108
  }
4109
+ function _getKeysWithLocales(data, injectLocaleKeys, locale) {
4110
+ return injectLocaleKeys.filter((key) => {
4111
+ return _22.get(data, key) === locale;
4112
+ });
4113
+ }
3867
4114
 
3868
4115
  // src/cli/loaders/locked-keys.ts
3869
4116
  import _23 from "lodash";
3870
- function createLockedKeysLoader(lockedKeys, isCacheRestore = false) {
4117
+ function createLockedKeysLoader(lockedKeys) {
3871
4118
  return createLoader({
3872
- pull: async (locale, data) => _23.chain(data).pickBy((value, key) => !lockedKeys.some((lockedKey) => key.startsWith(lockedKey))).value(),
4119
+ pull: async (locale, data) => {
4120
+ return _23.pickBy(data, (value, key) => !_isLockedKey(key, lockedKeys));
4121
+ },
3873
4122
  push: async (locale, data, originalInput) => {
3874
- const lockedSubObject = _23.chain(originalInput).pickBy((value, key) => lockedKeys.some((lockedKey) => key.startsWith(lockedKey))).value();
3875
- if (isCacheRestore) {
3876
- return _23.merge({}, data, lockedSubObject);
3877
- } else {
3878
- return _23.merge({}, originalInput, data, lockedSubObject);
3879
- }
4123
+ const lockedSubObject = _23.chain(originalInput).pickBy((value, key) => _isLockedKey(key, lockedKeys)).value();
4124
+ return _23.merge({}, data, lockedSubObject);
3880
4125
  }
3881
4126
  });
3882
4127
  }
4128
+ function _isLockedKey(key, lockedKeys) {
4129
+ return lockedKeys.some((lockedKey) => key.startsWith(lockedKey));
4130
+ }
3883
4131
 
3884
4132
  // src/cli/loaders/mdx2/frontmatter-split.ts
3885
4133
  import matter2 from "gray-matter";
@@ -4109,9 +4357,14 @@ function createMdxLockedPatternsLoader(defaultPatterns) {
4109
4357
  if (!pullInput) {
4110
4358
  return data;
4111
4359
  }
4112
- const { lockedPlaceholders } = extractLockedPatterns(pullInput, patterns);
4360
+ const { lockedPlaceholders } = extractLockedPatterns(
4361
+ pullInput,
4362
+ patterns
4363
+ );
4113
4364
  let result = data;
4114
- for (const [placeholder, original] of Object.entries(lockedPlaceholders)) {
4365
+ for (const [placeholder, original] of Object.entries(
4366
+ lockedPlaceholders
4367
+ )) {
4115
4368
  result = result.replaceAll(placeholder, original);
4116
4369
  }
4117
4370
  return result;
@@ -4205,7 +4458,10 @@ function parseEjsForTranslation(input2) {
4205
4458
  if (trimmedContent) {
4206
4459
  const key = `text_${counter++}`;
4207
4460
  translatable[key] = trimmedContent;
4208
- template += textPart.content.replace(trimmedContent, `__LINGO_PLACEHOLDER_${key}__`);
4461
+ template += textPart.content.replace(
4462
+ trimmedContent,
4463
+ `__LINGO_PLACEHOLDER_${key}__`
4464
+ );
4209
4465
  } else {
4210
4466
  template += textPart.content;
4211
4467
  }
@@ -4235,7 +4491,9 @@ function createEjsLoader() {
4235
4491
  const parseResult = parseEjsForTranslation(input2);
4236
4492
  return parseResult.translatable;
4237
4493
  } catch (error) {
4238
- console.warn("Warning: Could not parse EJS template, treating as plain text");
4494
+ console.warn(
4495
+ "Warning: Could not parse EJS template, treating as plain text"
4496
+ );
4239
4497
  return { content: input2.trim() };
4240
4498
  }
4241
4499
  },
@@ -4246,15 +4504,53 @@ function createEjsLoader() {
4246
4504
  try {
4247
4505
  const parseResult = parseEjsForTranslation(originalInput);
4248
4506
  const mergedTranslatable = { ...parseResult.translatable, ...data };
4249
- return reconstructEjsWithTranslation(parseResult.content, mergedTranslatable);
4507
+ return reconstructEjsWithTranslation(
4508
+ parseResult.content,
4509
+ mergedTranslatable
4510
+ );
4250
4511
  } catch (error) {
4251
- console.warn("Warning: Could not reconstruct EJS template, returning translated data");
4512
+ console.warn(
4513
+ "Warning: Could not reconstruct EJS template, returning translated data"
4514
+ );
4252
4515
  return Object.values(data).join("\n");
4253
4516
  }
4254
4517
  }
4255
4518
  });
4256
4519
  }
4257
4520
 
4521
+ // src/cli/loaders/ensure-key-order.ts
4522
+ import _27 from "lodash";
4523
+ function createEnsureKeyOrderLoader() {
4524
+ return createLoader({
4525
+ pull: async (_locale, input2) => {
4526
+ return input2;
4527
+ },
4528
+ push: async (_locale, data, originalInput) => {
4529
+ if (!originalInput || !data) {
4530
+ return data;
4531
+ }
4532
+ return reorderKeys(data, originalInput);
4533
+ }
4534
+ });
4535
+ }
4536
+ function reorderKeys(data, originalInput) {
4537
+ if (!_27.isObject(data) || _27.isArray(data) || _27.isDate(data)) {
4538
+ return data;
4539
+ }
4540
+ const orderedData = {};
4541
+ const originalKeys = Object.keys(originalInput);
4542
+ const dataKeys = new Set(Object.keys(data));
4543
+ for (const key of originalKeys) {
4544
+ if (dataKeys.has(key)) {
4545
+ if (data[key]) {
4546
+ orderedData[key] = reorderKeys(data[key], originalInput[key]);
4547
+ }
4548
+ dataKeys.delete(key);
4549
+ }
4550
+ }
4551
+ return orderedData;
4552
+ }
4553
+
4258
4554
  // src/cli/loaders/index.ts
4259
4555
  function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys, lockedPatterns, ignoredKeys) {
4260
4556
  switch (bucketType) {
@@ -4264,23 +4560,19 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4264
4560
  return composeLoaders(
4265
4561
  createTextFileLoader(bucketPathPattern),
4266
4562
  createAndroidLoader(),
4563
+ createEnsureKeyOrderLoader(),
4267
4564
  createFlatLoader(),
4268
4565
  createSyncLoader(),
4269
- createUnlocalizableLoader(
4270
- options.isCacheRestore,
4271
- options.returnUnlocalizedKeys
4272
- )
4566
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4273
4567
  );
4274
4568
  case "csv":
4275
4569
  return composeLoaders(
4276
4570
  createTextFileLoader(bucketPathPattern),
4277
4571
  createCsvLoader(),
4572
+ createEnsureKeyOrderLoader(),
4278
4573
  createFlatLoader(),
4279
4574
  createSyncLoader(),
4280
- createUnlocalizableLoader(
4281
- options.isCacheRestore,
4282
- options.returnUnlocalizedKeys
4283
- )
4575
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4284
4576
  );
4285
4577
  case "html":
4286
4578
  return composeLoaders(
@@ -4288,34 +4580,26 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4288
4580
  createPrettierLoader({ parser: "html", bucketPathPattern }),
4289
4581
  createHtmlLoader(),
4290
4582
  createSyncLoader(),
4291
- createUnlocalizableLoader(
4292
- options.isCacheRestore,
4293
- options.returnUnlocalizedKeys
4294
- )
4583
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4295
4584
  );
4296
4585
  case "ejs":
4297
4586
  return composeLoaders(
4298
4587
  createTextFileLoader(bucketPathPattern),
4299
4588
  createEjsLoader(),
4300
4589
  createSyncLoader(),
4301
- createUnlocalizableLoader(
4302
- options.isCacheRestore,
4303
- options.returnUnlocalizedKeys
4304
- )
4590
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4305
4591
  );
4306
4592
  case "json":
4307
4593
  return composeLoaders(
4308
4594
  createTextFileLoader(bucketPathPattern),
4309
4595
  createPrettierLoader({ parser: "json", bucketPathPattern }),
4310
4596
  createJsonLoader(),
4597
+ createEnsureKeyOrderLoader(),
4311
4598
  createInjectLocaleLoader(options.injectLocale),
4312
4599
  createFlatLoader(),
4313
- createLockedKeysLoader(lockedKeys || [], options.isCacheRestore),
4600
+ createLockedKeysLoader(lockedKeys || []),
4314
4601
  createSyncLoader(),
4315
- createUnlocalizableLoader(
4316
- options.isCacheRestore,
4317
- options.returnUnlocalizedKeys
4318
- )
4602
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4319
4603
  );
4320
4604
  case "markdown":
4321
4605
  return composeLoaders(
@@ -4323,10 +4607,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4323
4607
  createPrettierLoader({ parser: "markdown", bucketPathPattern }),
4324
4608
  createMarkdownLoader(),
4325
4609
  createSyncLoader(),
4326
- createUnlocalizableLoader(
4327
- options.isCacheRestore,
4328
- options.returnUnlocalizedKeys
4329
- )
4610
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4330
4611
  );
4331
4612
  case "mdx":
4332
4613
  return composeLoaders(
@@ -4341,55 +4622,43 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4341
4622
  createMdxSectionsSplit2Loader(),
4342
4623
  createLocalizableMdxDocumentLoader(),
4343
4624
  createFlatLoader(),
4344
- createLockedKeysLoader(lockedKeys || [], options.isCacheRestore),
4625
+ createEnsureKeyOrderLoader(),
4626
+ createLockedKeysLoader(lockedKeys || []),
4345
4627
  createSyncLoader(),
4346
- createUnlocalizableLoader(
4347
- options.isCacheRestore,
4348
- options.returnUnlocalizedKeys
4349
- )
4628
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4350
4629
  );
4351
4630
  case "po":
4352
4631
  return composeLoaders(
4353
4632
  createTextFileLoader(bucketPathPattern),
4354
4633
  createPoLoader(),
4355
4634
  createFlatLoader(),
4635
+ createEnsureKeyOrderLoader(),
4356
4636
  createSyncLoader(),
4357
4637
  createVariableLoader({ type: "python" }),
4358
- createUnlocalizableLoader(
4359
- options.isCacheRestore,
4360
- options.returnUnlocalizedKeys
4361
- )
4638
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4362
4639
  );
4363
4640
  case "properties":
4364
4641
  return composeLoaders(
4365
4642
  createTextFileLoader(bucketPathPattern),
4366
4643
  createPropertiesLoader(),
4367
4644
  createSyncLoader(),
4368
- createUnlocalizableLoader(
4369
- options.isCacheRestore,
4370
- options.returnUnlocalizedKeys
4371
- )
4645
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4372
4646
  );
4373
4647
  case "xcode-strings":
4374
4648
  return composeLoaders(
4375
4649
  createTextFileLoader(bucketPathPattern),
4376
4650
  createXcodeStringsLoader(),
4377
4651
  createSyncLoader(),
4378
- createUnlocalizableLoader(
4379
- options.isCacheRestore,
4380
- options.returnUnlocalizedKeys
4381
- )
4652
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4382
4653
  );
4383
4654
  case "xcode-stringsdict":
4384
4655
  return composeLoaders(
4385
4656
  createTextFileLoader(bucketPathPattern),
4386
4657
  createXcodeStringsdictLoader(),
4387
4658
  createFlatLoader(),
4659
+ createEnsureKeyOrderLoader(),
4388
4660
  createSyncLoader(),
4389
- createUnlocalizableLoader(
4390
- options.isCacheRestore,
4391
- options.returnUnlocalizedKeys
4392
- )
4661
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4393
4662
  );
4394
4663
  case "xcode-xcstrings":
4395
4664
  return composeLoaders(
@@ -4398,12 +4667,10 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4398
4667
  createJsonLoader(),
4399
4668
  createXcodeXcstringsLoader(options.defaultLocale),
4400
4669
  createFlatLoader(),
4670
+ createEnsureKeyOrderLoader(),
4401
4671
  createSyncLoader(),
4402
4672
  createVariableLoader({ type: "ieee" }),
4403
- createUnlocalizableLoader(
4404
- options.isCacheRestore,
4405
- options.returnUnlocalizedKeys
4406
- )
4673
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4407
4674
  );
4408
4675
  case "yaml":
4409
4676
  return composeLoaders(
@@ -4411,12 +4678,10 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4411
4678
  createPrettierLoader({ parser: "yaml", bucketPathPattern }),
4412
4679
  createYamlLoader(),
4413
4680
  createFlatLoader(),
4414
- createLockedKeysLoader(lockedKeys || [], options.isCacheRestore),
4681
+ createEnsureKeyOrderLoader(),
4682
+ createLockedKeysLoader(lockedKeys || []),
4415
4683
  createSyncLoader(),
4416
- createUnlocalizableLoader(
4417
- options.isCacheRestore,
4418
- options.returnUnlocalizedKeys
4419
- )
4684
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4420
4685
  );
4421
4686
  case "yaml-root-key":
4422
4687
  return composeLoaders(
@@ -4425,76 +4690,60 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4425
4690
  createYamlLoader(),
4426
4691
  createRootKeyLoader(true),
4427
4692
  createFlatLoader(),
4693
+ createEnsureKeyOrderLoader(),
4428
4694
  createSyncLoader(),
4429
- createUnlocalizableLoader(
4430
- options.isCacheRestore,
4431
- options.returnUnlocalizedKeys
4432
- )
4695
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4433
4696
  );
4434
4697
  case "flutter":
4435
4698
  return composeLoaders(
4436
4699
  createTextFileLoader(bucketPathPattern),
4437
4700
  createPrettierLoader({ parser: "json", bucketPathPattern }),
4438
4701
  createJsonLoader(),
4702
+ createEnsureKeyOrderLoader(),
4439
4703
  createFlutterLoader(),
4440
4704
  createFlatLoader(),
4441
4705
  createSyncLoader(),
4442
- createUnlocalizableLoader(
4443
- options.isCacheRestore,
4444
- options.returnUnlocalizedKeys
4445
- )
4706
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4446
4707
  );
4447
4708
  case "xliff":
4448
4709
  return composeLoaders(
4449
4710
  createTextFileLoader(bucketPathPattern),
4450
4711
  createXliffLoader(),
4451
4712
  createFlatLoader(),
4713
+ createEnsureKeyOrderLoader(),
4452
4714
  createSyncLoader(),
4453
- createUnlocalizableLoader(
4454
- options.isCacheRestore,
4455
- options.returnUnlocalizedKeys
4456
- )
4715
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4457
4716
  );
4458
4717
  case "xml":
4459
4718
  return composeLoaders(
4460
4719
  createTextFileLoader(bucketPathPattern),
4461
4720
  createXmlLoader(),
4462
4721
  createFlatLoader(),
4722
+ createEnsureKeyOrderLoader(),
4463
4723
  createSyncLoader(),
4464
- createUnlocalizableLoader(
4465
- options.isCacheRestore,
4466
- options.returnUnlocalizedKeys
4467
- )
4724
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4468
4725
  );
4469
4726
  case "srt":
4470
4727
  return composeLoaders(
4471
4728
  createTextFileLoader(bucketPathPattern),
4472
4729
  createSrtLoader(),
4473
4730
  createSyncLoader(),
4474
- createUnlocalizableLoader(
4475
- options.isCacheRestore,
4476
- options.returnUnlocalizedKeys
4477
- )
4731
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4478
4732
  );
4479
4733
  case "dato":
4480
4734
  return composeLoaders(
4481
4735
  createDatoLoader(bucketPathPattern),
4482
4736
  createSyncLoader(),
4483
4737
  createFlatLoader(),
4484
- createUnlocalizableLoader(
4485
- options.isCacheRestore,
4486
- options.returnUnlocalizedKeys
4487
- )
4738
+ createEnsureKeyOrderLoader(),
4739
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4488
4740
  );
4489
4741
  case "vtt":
4490
4742
  return composeLoaders(
4491
4743
  createTextFileLoader(bucketPathPattern),
4492
4744
  createVttLoader(),
4493
4745
  createSyncLoader(),
4494
- createUnlocalizableLoader(
4495
- options.isCacheRestore,
4496
- options.returnUnlocalizedKeys
4497
- )
4746
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4498
4747
  );
4499
4748
  case "php":
4500
4749
  return composeLoaders(
@@ -4502,10 +4751,8 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4502
4751
  createPhpLoader(),
4503
4752
  createSyncLoader(),
4504
4753
  createFlatLoader(),
4505
- createUnlocalizableLoader(
4506
- options.isCacheRestore,
4507
- options.returnUnlocalizedKeys
4508
- )
4754
+ createEnsureKeyOrderLoader(),
4755
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4509
4756
  );
4510
4757
  case "vue-json":
4511
4758
  return composeLoaders(
@@ -4513,10 +4760,8 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4513
4760
  createVueJsonLoader(),
4514
4761
  createSyncLoader(),
4515
4762
  createFlatLoader(),
4516
- createUnlocalizableLoader(
4517
- options.isCacheRestore,
4518
- options.returnUnlocalizedKeys
4519
- )
4763
+ createEnsureKeyOrderLoader(),
4764
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4520
4765
  );
4521
4766
  case "typescript":
4522
4767
  return composeLoaders(
@@ -4524,13 +4769,11 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
4524
4769
  createPrettierLoader({ parser: "typescript", bucketPathPattern }),
4525
4770
  createTypescriptLoader(),
4526
4771
  createFlatLoader(),
4772
+ createEnsureKeyOrderLoader(),
4527
4773
  createSyncLoader(),
4528
- createLockedKeysLoader(lockedKeys || [], options.isCacheRestore),
4774
+ createLockedKeysLoader(lockedKeys || []),
4529
4775
  createIgnoredKeysLoader(ignoredKeys || []),
4530
- createUnlocalizableLoader(
4531
- options.isCacheRestore,
4532
- options.returnUnlocalizedKeys
4533
- )
4776
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
4534
4777
  );
4535
4778
  }
4536
4779
  }
@@ -4574,7 +4817,7 @@ function createLingoLocalizer(params) {
4574
4817
 
4575
4818
  // src/cli/processor/basic.ts
4576
4819
  import { generateText } from "ai";
4577
- import _27 from "lodash";
4820
+ import _28 from "lodash";
4578
4821
  function createBasicTranslator(model, systemPrompt) {
4579
4822
  return async (input2, onProgress) => {
4580
4823
  const chunks = extractPayloadChunks(input2.processableData);
@@ -4588,7 +4831,7 @@ function createBasicTranslator(model, systemPrompt) {
4588
4831
  subResults.push(result2);
4589
4832
  onProgress(i / chunks.length * 100, chunk, result2);
4590
4833
  }
4591
- const result = _27.merge({}, ...subResults);
4834
+ const result = _28.merge({}, ...subResults);
4592
4835
  return result;
4593
4836
  };
4594
4837
  async function doJob(input2) {
@@ -4679,6 +4922,7 @@ import { createOpenAI } from "@ai-sdk/openai";
4679
4922
  import { createAnthropic } from "@ai-sdk/anthropic";
4680
4923
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
4681
4924
  import { createOpenRouter } from "@openrouter/ai-sdk-provider";
4925
+ import { createMistral } from "@ai-sdk/mistral";
4682
4926
  import { createOllama } from "ollama-ai-provider";
4683
4927
  function createProcessor(provider, params) {
4684
4928
  if (!provider) {
@@ -4696,7 +4940,11 @@ function getPureModelProvider(provider) {
4696
4940
 
4697
4941
  To fix this issue:
4698
4942
  1. ${envVar ? `Set ${chalk5.dim(envVar)} in your environment variables` : "Set the environment variable for your provider (if required)"}, or
4699
- 2. Remove the ${chalk5.italic("provider")} node from your i18n.json configuration to switch to ${chalk5.hex(colors.green)("Lingo.dev")}
4943
+ 2. Remove the ${chalk5.italic(
4944
+ "provider"
4945
+ )} node from your i18n.json configuration to switch to ${chalk5.hex(
4946
+ colors.green
4947
+ )("Lingo.dev")}
4700
4948
 
4701
4949
  ${chalk5.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
4702
4950
  `;
@@ -4705,7 +4953,11 @@ function getPureModelProvider(provider) {
4705
4953
 
4706
4954
  To fix this issue:
4707
4955
  1. Switch to one of the supported providers, or
4708
- 2. Remove the ${chalk5.italic("provider")} node from your i18n.json configuration to switch to ${chalk5.hex(colors.green)("Lingo.dev")}
4956
+ 2. Remove the ${chalk5.italic(
4957
+ "provider"
4958
+ )} node from your i18n.json configuration to switch to ${chalk5.hex(
4959
+ colors.green
4960
+ )("Lingo.dev")}
4709
4961
 
4710
4962
  ${chalk5.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
4711
4963
  `;
@@ -4755,6 +5007,17 @@ function getPureModelProvider(provider) {
4755
5007
  case "ollama": {
4756
5008
  return createOllama()(provider.model);
4757
5009
  }
5010
+ case "mistral": {
5011
+ if (!process.env.MISTRAL_API_KEY) {
5012
+ throw new Error(
5013
+ createMissingKeyErrorMessage("Mistral", "MISTRAL_API_KEY")
5014
+ );
5015
+ }
5016
+ return createMistral({
5017
+ apiKey: process.env.MISTRAL_API_KEY,
5018
+ baseURL: provider.baseUrl
5019
+ })(provider.model);
5020
+ }
4758
5021
  default: {
4759
5022
  throw new Error(createUnsupportedProviderErrorMessage(provider?.id));
4760
5023
  }
@@ -4815,7 +5078,7 @@ async function trackEvent(distinctId, event, properties) {
4815
5078
  }
4816
5079
 
4817
5080
  // src/cli/utils/delta.ts
4818
- import _28 from "lodash";
5081
+ import _29 from "lodash";
4819
5082
  import z from "zod";
4820
5083
 
4821
5084
  // src/cli/utils/fs.ts
@@ -4864,11 +5127,17 @@ function createDeltaProcessor(fileKey) {
4864
5127
  return checkIfFileExists(lockfilePath);
4865
5128
  },
4866
5129
  async calculateDelta(params) {
4867
- let added = _28.difference(Object.keys(params.sourceData), Object.keys(params.targetData));
4868
- let removed = _28.difference(Object.keys(params.targetData), Object.keys(params.sourceData));
4869
- const updated = _28.filter(Object.keys(params.sourceData), (key) => {
4870
- return md5(params.sourceData[key]) !== params.checksums[key] && params.checksums[key];
4871
- });
5130
+ let added = _29.difference(
5131
+ Object.keys(params.sourceData),
5132
+ Object.keys(params.targetData)
5133
+ );
5134
+ let removed = _29.difference(
5135
+ Object.keys(params.targetData),
5136
+ Object.keys(params.sourceData)
5137
+ );
5138
+ const updated = Object.keys(params.sourceData).filter(
5139
+ (key) => md5(params.sourceData[key]) !== params.checksums[key] && params.checksums[key]
5140
+ );
4872
5141
  const renamed = [];
4873
5142
  for (const addedKey of added) {
4874
5143
  const addedHash = md5(params.sourceData[addedKey]);
@@ -4879,9 +5148,18 @@ function createDeltaProcessor(fileKey) {
4879
5148
  }
4880
5149
  }
4881
5150
  }
4882
- added = added.filter((key) => !renamed.some(([oldKey, newKey]) => newKey === key));
4883
- removed = removed.filter((key) => !renamed.some(([oldKey, newKey]) => oldKey === key));
4884
- const hasChanges = [added.length > 0, removed.length > 0, updated.length > 0, renamed.length > 0].some((v) => v);
5151
+ added = added.filter(
5152
+ (key) => !renamed.some(([oldKey, newKey]) => newKey === key)
5153
+ );
5154
+ removed = removed.filter(
5155
+ (key) => !renamed.some(([oldKey, newKey]) => oldKey === key)
5156
+ );
5157
+ const hasChanges = [
5158
+ added.length > 0,
5159
+ removed.length > 0,
5160
+ updated.length > 0,
5161
+ renamed.length > 0
5162
+ ].some((v) => v);
4885
5163
  return {
4886
5164
  added,
4887
5165
  removed,
@@ -4915,7 +5193,7 @@ function createDeltaProcessor(fileKey) {
4915
5193
  await this.saveLock(lockfileData);
4916
5194
  },
4917
5195
  async createChecksums(sourceData) {
4918
- const checksums = _28.mapValues(sourceData, (value) => md5(value));
5196
+ const checksums = _29.mapValues(sourceData, (value) => md5(value));
4919
5197
  return checksums;
4920
5198
  }
4921
5199
  };
@@ -5039,7 +5317,6 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5039
5317
  bucket.type,
5040
5318
  bucketPath.pathPattern,
5041
5319
  {
5042
- isCacheRestore: false,
5043
5320
  defaultLocale: sourceLocale,
5044
5321
  injectLocale: bucket.injectLocale
5045
5322
  },
@@ -5074,7 +5351,6 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5074
5351
  bucket.type,
5075
5352
  bucketPath.pathPattern,
5076
5353
  {
5077
- isCacheRestore: false,
5078
5354
  defaultLocale: sourceLocale,
5079
5355
  returnUnlocalizedKeys: true,
5080
5356
  injectLocale: bucket.injectLocale
@@ -5087,7 +5363,7 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5087
5363
  const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
5088
5364
  const sourceChecksums = await deltaProcessor.createChecksums(sourceData);
5089
5365
  const savedChecksums = await deltaProcessor.loadChecksums();
5090
- const updatedSourceData = _29.pickBy(
5366
+ const updatedSourceData = _30.pickBy(
5091
5367
  sourceData,
5092
5368
  (value, key) => sourceChecksums[key] !== savedChecksums[key]
5093
5369
  );
@@ -5101,15 +5377,15 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5101
5377
  bucketPath.delimiter
5102
5378
  );
5103
5379
  const { unlocalizable: targetUnlocalizable, ...targetData } = await bucketLoader.pull(targetLocale);
5104
- const missingKeys = _29.difference(
5380
+ const missingKeys = _30.difference(
5105
5381
  Object.keys(sourceData),
5106
5382
  Object.keys(targetData)
5107
5383
  );
5108
- const extraKeys = _29.difference(
5384
+ const extraKeys = _30.difference(
5109
5385
  Object.keys(targetData),
5110
5386
  Object.keys(sourceData)
5111
5387
  );
5112
- const unlocalizableDataDiff = !_29.isEqual(
5388
+ const unlocalizableDataDiff = !_30.isEqual(
5113
5389
  sourceUnlocalizable,
5114
5390
  targetUnlocalizable
5115
5391
  );
@@ -5160,7 +5436,6 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5160
5436
  bucket.type,
5161
5437
  bucketPath.pathPattern,
5162
5438
  {
5163
- isCacheRestore: false,
5164
5439
  defaultLocale: sourceLocale,
5165
5440
  injectLocale: bucket.injectLocale
5166
5441
  },
@@ -5191,13 +5466,13 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5191
5466
  targetData,
5192
5467
  checksums: checksums2
5193
5468
  });
5194
- let processableData = _29.chain(sourceData).entries().filter(
5469
+ let processableData = _30.chain(sourceData).entries().filter(
5195
5470
  ([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!flags.force
5196
5471
  ).fromPairs().value();
5197
5472
  if (flags.key) {
5198
- processableData = _29.pickBy(
5473
+ processableData = _30.pickBy(
5199
5474
  processableData,
5200
- (_33, key) => key === flags.key
5475
+ (_34, key) => key === flags.key
5201
5476
  );
5202
5477
  }
5203
5478
  if (flags.verbose) {
@@ -5230,13 +5505,13 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5230
5505
  if (flags.verbose) {
5231
5506
  bucketOra.info(JSON.stringify(processedTargetData, null, 2));
5232
5507
  }
5233
- let finalTargetData = _29.merge(
5508
+ let finalTargetData = _30.merge(
5234
5509
  {},
5235
5510
  sourceData,
5236
5511
  targetData,
5237
5512
  processedTargetData
5238
5513
  );
5239
- finalTargetData = _29.chain(finalTargetData).entries().map(([key, value]) => {
5514
+ finalTargetData = _30.chain(finalTargetData).entries().map(([key, value]) => {
5240
5515
  const renaming = delta.renamed.find(
5241
5516
  ([oldKey, newKey]) => oldKey === key
5242
5517
  );
@@ -5260,7 +5535,7 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
5260
5535
  `Applying changes to ${bucketPath} (${targetLocale})`
5261
5536
  );
5262
5537
  }
5263
- const finalDiffSize = _29.chain(finalTargetData).omitBy((value, key) => value === targetData[key]).size().value();
5538
+ const finalDiffSize = _30.chain(finalTargetData).omitBy((value, key) => value === targetData[key]).size().value();
5264
5539
  await bucketLoader.push(targetLocale, finalTargetData);
5265
5540
  if (finalDiffSize > 0 || flags.force) {
5266
5541
  bucketOra.succeed(
@@ -5387,7 +5662,9 @@ async function reviewChanges(args) {
5387
5662
  if (currentStr === proposedStr && !args.force) {
5388
5663
  console.log(
5389
5664
  `
5390
- ${chalk6.blue(args.pathPattern)} (${chalk6.yellow(args.targetLocale)}): ${chalk6.gray("No changes to review")}`
5665
+ ${chalk6.blue(args.pathPattern)} (${chalk6.yellow(
5666
+ args.targetLocale
5667
+ )}): ${chalk6.gray("No changes to review")}`
5391
5668
  );
5392
5669
  return args.proposedData;
5393
5670
  }
@@ -5408,7 +5685,9 @@ ${chalk6.blue(args.pathPattern)} (${chalk6.yellow(args.targetLocale)}): ${chalk6
5408
5685
  }).join("\n");
5409
5686
  console.log(
5410
5687
  `
5411
- Reviewing changes for ${chalk6.blue(args.pathPattern)} (${chalk6.yellow(args.targetLocale)}):`
5688
+ Reviewing changes for ${chalk6.blue(args.pathPattern)} (${chalk6.yellow(
5689
+ args.targetLocale
5690
+ )}):`
5412
5691
  );
5413
5692
  console.log(coloredDiff);
5414
5693
  const { action } = await inquirer2.prompt([
@@ -5431,7 +5710,7 @@ Reviewing changes for ${chalk6.blue(args.pathPattern)} (${chalk6.yellow(args.tar
5431
5710
  return args.currentData;
5432
5711
  }
5433
5712
  const customData = { ...args.currentData };
5434
- const changes = _29.reduce(
5713
+ const changes = _30.reduce(
5435
5714
  args.proposedData,
5436
5715
  (result, value, key) => {
5437
5716
  if (args.currentData[key] !== value) {
@@ -5504,7 +5783,7 @@ import path14 from "path";
5504
5783
  import Z4 from "zod";
5505
5784
  import YAML5 from "yaml";
5506
5785
  import { MD5 as MD52 } from "object-hash";
5507
- import _30 from "lodash";
5786
+ import _31 from "lodash";
5508
5787
  function createLockfileHelper() {
5509
5788
  return {
5510
5789
  isLockfileExists: () => {
@@ -5514,23 +5793,33 @@ function createLockfileHelper() {
5514
5793
  registerSourceData: (pathPattern, sourceData) => {
5515
5794
  const lockfile = _loadLockfile();
5516
5795
  const sectionKey = MD52(pathPattern);
5517
- const sectionChecksums = _30.mapValues(sourceData, (value) => MD52(value));
5796
+ const sectionChecksums = _31.mapValues(sourceData, (value) => MD52(value));
5518
5797
  lockfile.checksums[sectionKey] = sectionChecksums;
5519
5798
  _saveLockfile(lockfile);
5520
5799
  },
5521
5800
  registerPartialSourceData: (pathPattern, partialSourceData) => {
5522
5801
  const lockfile = _loadLockfile();
5523
5802
  const sectionKey = MD52(pathPattern);
5524
- const sectionChecksums = _30.mapValues(partialSourceData, (value) => MD52(value));
5525
- lockfile.checksums[sectionKey] = _30.merge({}, lockfile.checksums[sectionKey] ?? {}, sectionChecksums);
5803
+ const sectionChecksums = _31.mapValues(
5804
+ partialSourceData,
5805
+ (value) => MD52(value)
5806
+ );
5807
+ lockfile.checksums[sectionKey] = _31.merge(
5808
+ {},
5809
+ lockfile.checksums[sectionKey] ?? {},
5810
+ sectionChecksums
5811
+ );
5526
5812
  _saveLockfile(lockfile);
5527
5813
  },
5528
5814
  extractUpdatedData: (pathPattern, sourceData) => {
5529
5815
  const lockfile = _loadLockfile();
5530
5816
  const sectionKey = MD52(pathPattern);
5531
- const currentChecksums = _30.mapValues(sourceData, (value) => MD52(value));
5817
+ const currentChecksums = _31.mapValues(sourceData, (value) => MD52(value));
5532
5818
  const savedChecksums = lockfile.checksums[sectionKey] || {};
5533
- const updatedData = _30.pickBy(sourceData, (value, key) => savedChecksums[key] !== currentChecksums[key]);
5819
+ const updatedData = _31.pickBy(
5820
+ sourceData,
5821
+ (value, key) => savedChecksums[key] !== currentChecksums[key]
5822
+ );
5534
5823
  return updatedData;
5535
5824
  }
5536
5825
  };
@@ -5574,20 +5863,31 @@ var lockfile_default = new Command13().command("lockfile").description("Create a
5574
5863
  const ora = Ora8();
5575
5864
  const lockfileHelper = createLockfileHelper();
5576
5865
  if (lockfileHelper.isLockfileExists() && !flags.force) {
5577
- ora.warn(`Lockfile won't be created because it already exists. Use --force to overwrite.`);
5866
+ ora.warn(
5867
+ `Lockfile won't be created because it already exists. Use --force to overwrite.`
5868
+ );
5578
5869
  } else {
5579
5870
  const i18nConfig = getConfig();
5580
5871
  const buckets = getBuckets(i18nConfig);
5581
5872
  for (const bucket of buckets) {
5582
5873
  for (const bucketConfig of bucket.paths) {
5583
- const sourceLocale = resolveOverriddenLocale4(i18nConfig.locale.source, bucketConfig.delimiter);
5584
- const bucketLoader = createBucketLoader(bucket.type, bucketConfig.pathPattern, {
5585
- isCacheRestore: false,
5586
- defaultLocale: sourceLocale
5587
- });
5874
+ const sourceLocale = resolveOverriddenLocale4(
5875
+ i18nConfig.locale.source,
5876
+ bucketConfig.delimiter
5877
+ );
5878
+ const bucketLoader = createBucketLoader(
5879
+ bucket.type,
5880
+ bucketConfig.pathPattern,
5881
+ {
5882
+ defaultLocale: sourceLocale
5883
+ }
5884
+ );
5588
5885
  bucketLoader.setDefaultLocale(sourceLocale);
5589
5886
  const sourceData = await bucketLoader.pull(sourceLocale);
5590
- lockfileHelper.registerSourceData(bucketConfig.pathPattern, sourceData);
5887
+ lockfileHelper.registerSourceData(
5888
+ bucketConfig.pathPattern,
5889
+ sourceData
5890
+ );
5591
5891
  }
5592
5892
  }
5593
5893
  ora.succeed("Lockfile created");
@@ -5600,9 +5900,11 @@ var flagsSchema = Z5.object({
5600
5900
  // src/cli/cmd/cleanup.ts
5601
5901
  import { resolveOverriddenLocale as resolveOverriddenLocale5 } from "@lingo.dev/_spec";
5602
5902
  import { Command as Command14 } from "interactive-commander";
5603
- import _31 from "lodash";
5903
+ import _32 from "lodash";
5604
5904
  import Ora9 from "ora";
5605
- var cleanup_default = new Command14().command("cleanup").description("Remove keys from target files that do not exist in the source file").helpOption("-h, --help", "Show help").option("--locale <locale>", "Specific locale to cleanup").option("--bucket <bucket>", "Specific bucket to cleanup").option("--dry-run", "Show what would be removed without making changes").option(
5905
+ var cleanup_default = new Command14().command("cleanup").description(
5906
+ "Remove keys from target files that do not exist in the source file"
5907
+ ).helpOption("-h, --help", "Show help").option("--locale <locale>", "Specific locale to cleanup").option("--bucket <bucket>", "Specific bucket to cleanup").option("--dry-run", "Show what would be removed without making changes").option(
5606
5908
  "--verbose",
5607
5909
  "Show detailed output including:\n - List of keys that would be removed.\n - Processing steps."
5608
5910
  ).action(async function(options) {
@@ -5615,44 +5917,69 @@ var cleanup_default = new Command14().command("cleanup").description("Remove key
5615
5917
  ora.succeed("Configuration loaded");
5616
5918
  let buckets = getBuckets(i18nConfig);
5617
5919
  if (options.bucket) {
5618
- buckets = buckets.filter((bucket) => bucket.type === options.bucket);
5920
+ buckets = buckets.filter(
5921
+ (bucket) => bucket.type === options.bucket
5922
+ );
5619
5923
  }
5620
5924
  const targetLocales = options.locale ? [options.locale] : i18nConfig.locale.targets;
5621
5925
  for (const bucket of buckets) {
5622
5926
  console.log();
5623
5927
  ora.info(`Processing bucket: ${bucket.type}`);
5624
5928
  for (const bucketConfig of bucket.paths) {
5625
- const sourceLocale = resolveOverriddenLocale5(i18nConfig.locale.source, bucketConfig.delimiter);
5626
- const bucketOra = Ora9({ indent: 2 }).info(`Processing path: ${bucketConfig.pathPattern}`);
5627
- const bucketLoader = createBucketLoader(bucket.type, bucketConfig.pathPattern, {
5628
- isCacheRestore: false,
5629
- defaultLocale: sourceLocale
5630
- });
5929
+ const sourceLocale = resolveOverriddenLocale5(
5930
+ i18nConfig.locale.source,
5931
+ bucketConfig.delimiter
5932
+ );
5933
+ const bucketOra = Ora9({ indent: 2 }).info(
5934
+ `Processing path: ${bucketConfig.pathPattern}`
5935
+ );
5936
+ const bucketLoader = createBucketLoader(
5937
+ bucket.type,
5938
+ bucketConfig.pathPattern,
5939
+ {
5940
+ defaultLocale: sourceLocale
5941
+ }
5942
+ );
5631
5943
  bucketLoader.setDefaultLocale(sourceLocale);
5632
5944
  const sourceData = await bucketLoader.pull(sourceLocale);
5633
5945
  const sourceKeys = Object.keys(sourceData);
5634
5946
  for (const _targetLocale of targetLocales) {
5635
- const targetLocale = resolveOverriddenLocale5(_targetLocale, bucketConfig.delimiter);
5947
+ const targetLocale = resolveOverriddenLocale5(
5948
+ _targetLocale,
5949
+ bucketConfig.delimiter
5950
+ );
5636
5951
  try {
5637
5952
  const targetData = await bucketLoader.pull(targetLocale);
5638
5953
  const targetKeys = Object.keys(targetData);
5639
- const keysToRemove = _31.difference(targetKeys, sourceKeys);
5954
+ const keysToRemove = _32.difference(targetKeys, sourceKeys);
5640
5955
  if (keysToRemove.length === 0) {
5641
5956
  bucketOra.succeed(`[${targetLocale}] No keys to remove`);
5642
5957
  continue;
5643
5958
  }
5644
5959
  if (options.verbose) {
5645
- bucketOra.info(`[${targetLocale}] Keys to remove: ${JSON.stringify(keysToRemove, null, 2)}`);
5960
+ bucketOra.info(
5961
+ `[${targetLocale}] Keys to remove: ${JSON.stringify(
5962
+ keysToRemove,
5963
+ null,
5964
+ 2
5965
+ )}`
5966
+ );
5646
5967
  }
5647
5968
  if (!options.dryRun) {
5648
- const cleanedData = _31.pick(targetData, sourceKeys);
5969
+ const cleanedData = _32.pick(targetData, sourceKeys);
5649
5970
  await bucketLoader.push(targetLocale, cleanedData);
5650
- bucketOra.succeed(`[${targetLocale}] Removed ${keysToRemove.length} keys`);
5971
+ bucketOra.succeed(
5972
+ `[${targetLocale}] Removed ${keysToRemove.length} keys`
5973
+ );
5651
5974
  } else {
5652
- bucketOra.succeed(`[${targetLocale}] Would remove ${keysToRemove.length} keys (dry run)`);
5975
+ bucketOra.succeed(
5976
+ `[${targetLocale}] Would remove ${keysToRemove.length} keys (dry run)`
5977
+ );
5653
5978
  }
5654
5979
  } catch (error) {
5655
- bucketOra.fail(`[${targetLocale}] Failed to cleanup: ${error.message}`);
5980
+ bucketOra.fail(
5981
+ `[${targetLocale}] Failed to cleanup: ${error.message}`
5982
+ );
5656
5983
  results.push({
5657
5984
  step: `Cleanup ${bucket.type}/${bucketConfig} for ${targetLocale}`,
5658
5985
  status: "Failed",
@@ -5700,7 +6027,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
5700
6027
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5701
6028
  import Z6 from "zod";
5702
6029
  import { ReplexicaEngine } from "@lingo.dev/_sdk";
5703
- var mcp_default = new Command15().command("mcp").description("Use Lingo.dev model context provider with your AI agent").helpOption("-h, --help", "Show help").action(async (_33, program) => {
6030
+ var mcp_default = new Command15().command("mcp").description("Use Lingo.dev model context provider with your AI agent").helpOption("-h, --help", "Show help").action(async (_34, program) => {
5704
6031
  const apiKey = program.args[0];
5705
6032
  const settings = getSettings(apiKey);
5706
6033
  if (!settings.auth.apiKey) {
@@ -5802,7 +6129,9 @@ function createLingoDotDevLocalizer(explicitApiKey) {
5802
6129
  if (!auth) {
5803
6130
  throw new Error(
5804
6131
  dedent5`
5805
- You're trying to use ${chalk8.hex(colors.green)("Lingo.dev")} provider, however, you are not authenticated.
6132
+ You're trying to use ${chalk8.hex(colors.green)(
6133
+ "Lingo.dev"
6134
+ )} provider, however, you are not authenticated.
5806
6135
 
5807
6136
  To fix this issue:
5808
6137
  1. Run ${chalk8.dim("lingo.dev login")} to authenticate, or
@@ -5854,6 +6183,7 @@ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
5854
6183
  import { createGoogleGenerativeAI as createGoogleGenerativeAI2 } from "@ai-sdk/google";
5855
6184
  import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
5856
6185
  import { createOpenRouter as createOpenRouter2 } from "@openrouter/ai-sdk-provider";
6186
+ import { createMistral as createMistral2 } from "@ai-sdk/mistral";
5857
6187
  import chalk9 from "chalk";
5858
6188
  import dedent6 from "dedent";
5859
6189
  import { generateText as generateText2 } from "ai";
@@ -5868,7 +6198,11 @@ function createExplicitLocalizer(provider) {
5868
6198
 
5869
6199
  To fix this issue:
5870
6200
  1. Switch to one of the supported providers, or
5871
- 2. Remove the ${chalk9.italic("provider")} node from your i18n.json configuration to switch to ${chalk9.hex(colors.green)("Lingo.dev")}
6201
+ 2. Remove the ${chalk9.italic(
6202
+ "provider"
6203
+ )} node from your i18n.json configuration to switch to ${chalk9.hex(
6204
+ colors.green
6205
+ )("Lingo.dev")}
5872
6206
 
5873
6207
  ${chalk9.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
5874
6208
  `
@@ -5912,6 +6246,14 @@ function createExplicitLocalizer(provider) {
5912
6246
  prompt: provider.prompt,
5913
6247
  skipAuth: true
5914
6248
  });
6249
+ case "mistral":
6250
+ return createAiSdkLocalizer({
6251
+ factory: (params) => createMistral2(params).languageModel(provider.model),
6252
+ id: provider.id,
6253
+ prompt: provider.prompt,
6254
+ apiKeyName: "MISTRAL_API_KEY",
6255
+ baseUrl: provider.baseUrl
6256
+ });
5915
6257
  }
5916
6258
  }
5917
6259
  function createAiSdkLocalizer(params) {
@@ -5920,11 +6262,19 @@ function createAiSdkLocalizer(params) {
5920
6262
  if (!skipAuth && !apiKey || !params.apiKeyName) {
5921
6263
  throw new Error(
5922
6264
  dedent6`
5923
- You're trying to use raw ${chalk9.dim(params.id)} API for translation. ${params.apiKeyName ? `However, ${chalk9.dim(params.apiKeyName)} environment variable is not set.` : "However, that provider is unavailable."}
6265
+ You're trying to use raw ${chalk9.dim(params.id)} API for translation. ${params.apiKeyName ? `However, ${chalk9.dim(
6266
+ params.apiKeyName
6267
+ )} environment variable is not set.` : "However, that provider is unavailable."}
5924
6268
 
5925
6269
  To fix this issue:
5926
- 1. ${params.apiKeyName ? `Set ${chalk9.dim(params.apiKeyName)} in your environment variables` : "Set the environment variable for your provider (if required)"}, or
5927
- 2. Remove the ${chalk9.italic("provider")} node from your i18n.json configuration to switch to ${chalk9.hex(colors.green)("Lingo.dev")}
6270
+ 1. ${params.apiKeyName ? `Set ${chalk9.dim(
6271
+ params.apiKeyName
6272
+ )} in your environment variables` : "Set the environment variable for your provider (if required)"}, or
6273
+ 2. Remove the ${chalk9.italic(
6274
+ "provider"
6275
+ )} node from your i18n.json configuration to switch to ${chalk9.hex(
6276
+ colors.green
6277
+ )("Lingo.dev")}
5928
6278
 
5929
6279
  ${chalk9.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
5930
6280
  `
@@ -6064,10 +6414,14 @@ async function setup(input2) {
6064
6414
  const authStatus = await ctx.localizer.checkAuth();
6065
6415
  if (!authStatus.authenticated) {
6066
6416
  throw new Error(
6067
- `Failed to authenticate with ${chalk10.hex(colors.yellow)(ctx.localizer.id)} provider. Please check your API key and try again.`
6417
+ `Failed to authenticate with ${chalk10.hex(colors.yellow)(
6418
+ ctx.localizer.id
6419
+ )} provider. Please check your API key and try again.`
6068
6420
  );
6069
6421
  }
6070
- task.title = `Authenticated as ${chalk10.hex(colors.yellow)(authStatus.username)}`;
6422
+ task.title = `Authenticated as ${chalk10.hex(colors.yellow)(
6423
+ authStatus.username
6424
+ )}`;
6071
6425
  }
6072
6426
  },
6073
6427
  {
@@ -6128,14 +6482,22 @@ async function plan(input2) {
6128
6482
  title: "Locating content buckets",
6129
6483
  task: async (ctx, task) => {
6130
6484
  const bucketCount = buckets.length;
6131
- const bucketFilter = input2.flags.bucket ? ` ${chalk11.dim(`(filtered by: ${chalk11.hex(colors.yellow)(input2.flags.bucket.join(", "))})`)}` : "";
6132
- task.title = `Found ${chalk11.hex(colors.yellow)(bucketCount.toString())} bucket(s)${bucketFilter}`;
6485
+ const bucketFilter = input2.flags.bucket ? ` ${chalk11.dim(
6486
+ `(filtered by: ${chalk11.hex(colors.yellow)(
6487
+ input2.flags.bucket.join(", ")
6488
+ )})`
6489
+ )}` : "";
6490
+ task.title = `Found ${chalk11.hex(colors.yellow)(
6491
+ bucketCount.toString()
6492
+ )} bucket(s)${bucketFilter}`;
6133
6493
  }
6134
6494
  },
6135
6495
  {
6136
6496
  title: "Detecting locales",
6137
6497
  task: async (ctx, task) => {
6138
- task.title = `Found ${chalk11.hex(colors.yellow)(_targetLocales.length.toString())} target locale(s)`;
6498
+ task.title = `Found ${chalk11.hex(colors.yellow)(
6499
+ _targetLocales.length.toString()
6500
+ )} target locale(s)`;
6139
6501
  }
6140
6502
  },
6141
6503
  {
@@ -6154,8 +6516,14 @@ async function plan(input2) {
6154
6516
  patterns.push(bucketPath.pathPattern);
6155
6517
  }
6156
6518
  }
6157
- const fileFilter = input2.flags.file ? ` ${chalk11.dim(`(filtered by: ${chalk11.hex(colors.yellow)(input2.flags.file.join(", "))})`)}` : "";
6158
- task.title = `Found ${chalk11.hex(colors.yellow)(patterns.length.toString())} path pattern(s)${fileFilter}`;
6519
+ const fileFilter = input2.flags.file ? ` ${chalk11.dim(
6520
+ `(filtered by: ${chalk11.hex(colors.yellow)(
6521
+ input2.flags.file.join(", ")
6522
+ )})`
6523
+ )}` : "";
6524
+ task.title = `Found ${chalk11.hex(colors.yellow)(
6525
+ patterns.length.toString()
6526
+ )} path pattern(s)${fileFilter}`;
6159
6527
  }
6160
6528
  },
6161
6529
  {
@@ -6193,7 +6561,9 @@ async function plan(input2) {
6193
6561
  }
6194
6562
  }
6195
6563
  }
6196
- task.title = `Prepared ${chalk11.hex(colors.green)(ctx.tasks.length.toString())} translation task(s)`;
6564
+ task.title = `Prepared ${chalk11.hex(colors.green)(
6565
+ ctx.tasks.length.toString()
6566
+ )} translation task(s)`;
6197
6567
  }
6198
6568
  }
6199
6569
  ],
@@ -6207,7 +6577,7 @@ async function plan(input2) {
6207
6577
  import chalk12 from "chalk";
6208
6578
  import { Listr as Listr3 } from "listr2";
6209
6579
  import pLimit from "p-limit";
6210
- import _32 from "lodash";
6580
+ import _33 from "lodash";
6211
6581
 
6212
6582
  // ../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
6213
6583
  var balanced = (a, b, str) => {
@@ -6898,7 +7268,7 @@ var AST = class _AST {
6898
7268
  if (!this.type) {
6899
7269
  const noEmpty = this.isStart() && this.isEnd();
6900
7270
  const src = this.#parts.map((p) => {
6901
- const [re, _33, hasMagic, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
7271
+ const [re, _34, hasMagic, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
6902
7272
  this.#hasMagic = this.#hasMagic || hasMagic;
6903
7273
  this.#uflag = this.#uflag || uflag;
6904
7274
  return re;
@@ -6971,7 +7341,7 @@ var AST = class _AST {
6971
7341
  if (typeof p === "string") {
6972
7342
  throw new Error("string type in extglob ast??");
6973
7343
  }
6974
- const [re, _33, _hasMagic, uflag] = p.toRegExpSource(dot);
7344
+ const [re, _34, _hasMagic, uflag] = p.toRegExpSource(dot);
6975
7345
  this.#uflag = this.#uflag || uflag;
6976
7346
  return re;
6977
7347
  }).filter((p) => !(this.isStart() && this.isEnd()) || !!p).join("|");
@@ -7216,7 +7586,7 @@ var Minimatch = class {
7216
7586
  }
7217
7587
  return false;
7218
7588
  }
7219
- debug(..._33) {
7589
+ debug(..._34) {
7220
7590
  }
7221
7591
  make() {
7222
7592
  const pattern = this.pattern;
@@ -7238,7 +7608,7 @@ var Minimatch = class {
7238
7608
  const rawGlobParts = this.globSet.map((s) => this.slashSplit(s));
7239
7609
  this.globParts = this.preprocess(rawGlobParts);
7240
7610
  this.debug(this.pattern, this.globParts);
7241
- let set = this.globParts.map((s, _33, __) => {
7611
+ let set = this.globParts.map((s, _34, __) => {
7242
7612
  if (this.isWindows && this.windowsNoMagicRoot) {
7243
7613
  const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]);
7244
7614
  const isDrive = /^[a-z]:/i.test(s[0]);
@@ -7761,11 +8131,15 @@ async function execute(input2) {
7761
8131
  {
7762
8132
  title: "Initializing localization engine",
7763
8133
  task: async (ctx, task) => {
7764
- task.title = `Localization engine ${chalk12.hex(colors.green)("ready")} (${ctx.localizer.id})`;
8134
+ task.title = `Localization engine ${chalk12.hex(colors.green)(
8135
+ "ready"
8136
+ )} (${ctx.localizer.id})`;
7765
8137
  }
7766
8138
  },
7767
8139
  {
7768
- title: `Processing localization tasks ${chalk12.dim(`(tasks: ${input2.tasks.length}, concurrency: ${effectiveConcurrency})`)}`,
8140
+ title: `Processing localization tasks ${chalk12.dim(
8141
+ `(tasks: ${input2.tasks.length}, concurrency: ${effectiveConcurrency})`
8142
+ )}`,
7769
8143
  task: (ctx, task) => {
7770
8144
  if (input2.tasks.length < 1) {
7771
8145
  task.title = `Skipping, nothing to localize.`;
@@ -7778,7 +8152,7 @@ async function execute(input2) {
7778
8152
  const workerTasks = [];
7779
8153
  for (let i = 0; i < workersCount; i++) {
7780
8154
  const assignedTasks = ctx.tasks.filter(
7781
- (_33, idx) => idx % workersCount === i
8155
+ (_34, idx) => idx % workersCount === i
7782
8156
  );
7783
8157
  workerTasks.push(
7784
8158
  createWorkerTask({
@@ -7814,11 +8188,11 @@ function createWorkerStatusMessage(args) {
7814
8188
  "[locale]",
7815
8189
  args.assignedTask.targetLocale
7816
8190
  );
7817
- return `[${chalk12.hex(colors.yellow)(`${args.percentage}%`)}] Processing: ${chalk12.dim(
7818
- displayPath
7819
- )} (${chalk12.hex(colors.yellow)(args.assignedTask.sourceLocale)} -> ${chalk12.hex(
7820
- colors.yellow
7821
- )(args.assignedTask.targetLocale)})`;
8191
+ return `[${chalk12.hex(colors.yellow)(
8192
+ `${args.percentage}%`
8193
+ )}] Processing: ${chalk12.dim(displayPath)} (${chalk12.hex(colors.yellow)(
8194
+ args.assignedTask.sourceLocale
8195
+ )} -> ${chalk12.hex(colors.yellow)(args.assignedTask.targetLocale)})`;
7822
8196
  }
7823
8197
  function createExecutionProgressMessage(ctx) {
7824
8198
  const succeededTasksCount = countTasks(
@@ -7833,7 +8207,9 @@ function createExecutionProgressMessage(ctx) {
7833
8207
  ctx,
7834
8208
  (_t, result) => result.status === "skipped"
7835
8209
  );
7836
- return `Processed ${chalk12.green(succeededTasksCount)}/${ctx.tasks.length}, Failed ${chalk12.red(failedTasksCount)}, Skipped ${chalk12.dim(skippedTasksCount)}`;
8210
+ return `Processed ${chalk12.green(succeededTasksCount)}/${ctx.tasks.length}, Failed ${chalk12.red(failedTasksCount)}, Skipped ${chalk12.dim(
8211
+ skippedTasksCount
8212
+ )}`;
7837
8213
  }
7838
8214
  function createLoaderForTask(assignedTask) {
7839
8215
  const bucketLoader = createBucketLoader(
@@ -7841,7 +8217,6 @@ function createLoaderForTask(assignedTask) {
7841
8217
  assignedTask.bucketPathPattern,
7842
8218
  {
7843
8219
  defaultLocale: assignedTask.sourceLocale,
7844
- isCacheRestore: false,
7845
8220
  injectLocale: assignedTask.injectLocale
7846
8221
  },
7847
8222
  assignedTask.lockedKeys,
@@ -7877,7 +8252,7 @@ function createWorkerTask(args) {
7877
8252
  targetData,
7878
8253
  checksums
7879
8254
  });
7880
- const processableData = _32.chain(sourceData).entries().filter(
8255
+ const processableData = _33.chain(sourceData).entries().filter(
7881
8256
  ([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!args.ctx.flags.force
7882
8257
  ).filter(
7883
8258
  ([key]) => !assignedTask.onlyKeys.length || assignedTask.onlyKeys?.some(
@@ -7895,33 +8270,47 @@ function createWorkerTask(args) {
7895
8270
  targetData,
7896
8271
  processableData
7897
8272
  },
7898
- (progress) => {
8273
+ async (progress, _sourceChunk, processedChunk) => {
8274
+ await args.ioLimiter(async () => {
8275
+ await bucketLoader.pull(assignedTask.sourceLocale);
8276
+ const latestTargetData = await bucketLoader.pull(
8277
+ assignedTask.targetLocale
8278
+ );
8279
+ const _partialData = _33.merge(
8280
+ {},
8281
+ latestTargetData,
8282
+ processedChunk
8283
+ );
8284
+ const finalChunkTargetData = processRenamedKeys(
8285
+ delta,
8286
+ _partialData
8287
+ );
8288
+ await bucketLoader.push(
8289
+ assignedTask.targetLocale,
8290
+ finalChunkTargetData
8291
+ );
8292
+ });
7899
8293
  subTask.title = createWorkerStatusMessage({
7900
8294
  assignedTask,
7901
8295
  percentage: progress
7902
8296
  });
7903
8297
  }
7904
8298
  );
7905
- let finalTargetData = _32.merge(
8299
+ const finalTargetData = _33.merge(
7906
8300
  {},
7907
8301
  sourceData,
7908
8302
  targetData,
7909
8303
  processedTargetData
7910
8304
  );
7911
- finalTargetData = _32.chain(finalTargetData).entries().map(([key, value]) => {
7912
- const renaming = delta.renamed.find(
7913
- ([oldKey]) => oldKey === key
7914
- );
7915
- if (!renaming) {
7916
- return [key, value];
7917
- }
7918
- return [renaming[1], value];
7919
- }).fromPairs().value();
8305
+ const finalRenamedTargetData = processRenamedKeys(
8306
+ delta,
8307
+ finalTargetData
8308
+ );
7920
8309
  await args.ioLimiter(async () => {
7921
8310
  await bucketLoader.pull(assignedTask.sourceLocale);
7922
8311
  await bucketLoader.push(
7923
8312
  assignedTask.targetLocale,
7924
- finalTargetData
8313
+ finalRenamedTargetData
7925
8314
  );
7926
8315
  const checksums2 = await deltaProcessor.createChecksums(sourceData);
7927
8316
  await deltaProcessor.saveChecksums(checksums2);
@@ -7945,6 +8334,15 @@ function countTasks(ctx, predicate) {
7945
8334
  ([task, result]) => predicate(task, result)
7946
8335
  ).length;
7947
8336
  }
8337
+ function processRenamedKeys(delta, targetData) {
8338
+ return _33.chain(targetData).entries().map(([key, value]) => {
8339
+ const renaming = delta.renamed.find(([oldKey]) => oldKey === key);
8340
+ if (!renaming) {
8341
+ return [key, value];
8342
+ }
8343
+ return [renaming[1], value];
8344
+ }).fromPairs().value();
8345
+ }
7948
8346
 
7949
8347
  // src/cli/cmd/run/_types.ts
7950
8348
  import {
@@ -8075,7 +8473,9 @@ var InBranchFlow = class extends IntegrationFlow {
8075
8473
  execSync(`git add .`, { stdio: "inherit" });
8076
8474
  execSync(`git status --porcelain`, { stdio: "inherit" });
8077
8475
  execSync(
8078
- `git commit -m ${escapeShellArg(this.platformKit.config.commitMessage)} --no-verify`,
8476
+ `git commit -m ${escapeShellArg(
8477
+ this.platformKit.config.commitMessage
8478
+ )} --no-verify`,
8079
8479
  {
8080
8480
  stdio: "inherit"
8081
8481
  }
@@ -8198,7 +8598,9 @@ var PullRequestFlow = class extends InBranchFlow {
8198
8598
  this.ora.start("Checking if PR already exists");
8199
8599
  const pullRequestNumber = await this.ensureFreshPr(this.i18nBranchName);
8200
8600
  this.ora.succeed(
8201
- `Pull request ready: ${this.platformKit.buildPullRequestUrl(pullRequestNumber)}`
8601
+ `Pull request ready: ${this.platformKit.buildPullRequestUrl(
8602
+ pullRequestNumber
8603
+ )}`
8202
8604
  );
8203
8605
  }
8204
8606
  calculatePrBranchName() {
@@ -8368,11 +8770,17 @@ var PlatformKit = class {
8368
8770
  get config() {
8369
8771
  const env = Z7.object({
8370
8772
  LINGODOTDEV_API_KEY: Z7.string(),
8371
- LINGODOTDEV_PULL_REQUEST: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()),
8773
+ LINGODOTDEV_PULL_REQUEST: Z7.preprocess(
8774
+ (val) => val === "true" || val === true,
8775
+ Z7.boolean()
8776
+ ),
8372
8777
  LINGODOTDEV_COMMIT_MESSAGE: Z7.string().optional(),
8373
8778
  LINGODOTDEV_PULL_REQUEST_TITLE: Z7.string().optional(),
8374
8779
  LINGODOTDEV_WORKING_DIRECTORY: Z7.string().optional(),
8375
- LINGODOTDEV_PROCESS_OWN_COMMITS: Z7.preprocess((val) => val === "true" || val === true, Z7.boolean()).optional()
8780
+ LINGODOTDEV_PROCESS_OWN_COMMITS: Z7.preprocess(
8781
+ (val) => val === "true" || val === true,
8782
+ Z7.boolean()
8783
+ ).optional()
8376
8784
  }).parse(process.env);
8377
8785
  return {
8378
8786
  replexicaApiKey: env.LINGODOTDEV_API_KEY,
@@ -8767,16 +9175,34 @@ function parseBooleanArg(val) {
8767
9175
  }
8768
9176
 
8769
9177
  // src/cli/cmd/status.ts
8770
- import { bucketTypeSchema as bucketTypeSchema4, localeCodeSchema as localeCodeSchema3, resolveOverriddenLocale as resolveOverriddenLocale7 } from "@lingo.dev/_spec";
9178
+ import {
9179
+ bucketTypeSchema as bucketTypeSchema4,
9180
+ localeCodeSchema as localeCodeSchema3,
9181
+ resolveOverriddenLocale as resolveOverriddenLocale7
9182
+ } from "@lingo.dev/_spec";
8771
9183
  import { Command as Command18 } from "interactive-commander";
8772
9184
  import Z11 from "zod";
8773
9185
  import Ora10 from "ora";
8774
9186
  import chalk13 from "chalk";
8775
9187
  import Table from "cli-table3";
8776
- var status_default = new Command18().command("status").description("Show the status of the localization process").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
9188
+ var status_default = new Command18().command("status").description("Show the status of the localization process").helpOption("-h, --help", "Show help").option(
9189
+ "--locale <locale>",
9190
+ "Locale to process",
9191
+ (val, prev) => prev ? [...prev, val] : [val]
9192
+ ).option(
9193
+ "--bucket <bucket>",
9194
+ "Bucket to process",
9195
+ (val, prev) => prev ? [...prev, val] : [val]
9196
+ ).option(
8777
9197
  "--file [files...]",
8778
9198
  "File to process. Process only a specific path, may contain asterisk * to match multiple files."
8779
- ).option("--force", "Ignore lockfile and process all keys, useful for estimating full re-translation").option("--verbose", "Show detailed output including key-level word counts").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").action(async function(options) {
9199
+ ).option(
9200
+ "--force",
9201
+ "Ignore lockfile and process all keys, useful for estimating full re-translation"
9202
+ ).option("--verbose", "Show detailed output including key-level word counts").option(
9203
+ "--api-key <api-key>",
9204
+ "Explicitly set the API key to use, override the default API key from settings"
9205
+ ).action(async function(options) {
8780
9206
  const ora = Ora10();
8781
9207
  const flags = parseFlags2(options);
8782
9208
  let authId = null;
@@ -8808,16 +9234,22 @@ var status_default = new Command18().command("status").description("Show the sta
8808
9234
  });
8809
9235
  let buckets = getBuckets(i18nConfig);
8810
9236
  if (flags.bucket?.length) {
8811
- buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
9237
+ buckets = buckets.filter(
9238
+ (bucket) => flags.bucket.includes(bucket.type)
9239
+ );
8812
9240
  }
8813
9241
  ora.succeed("Buckets retrieved");
8814
9242
  if (flags.file?.length) {
8815
9243
  buckets = buckets.map((bucket) => {
8816
- const paths = bucket.paths.filter((path17) => flags.file.find((file) => path17.pathPattern?.match(file)));
9244
+ const paths = bucket.paths.filter(
9245
+ (path17) => flags.file.find((file) => path17.pathPattern?.match(file))
9246
+ );
8817
9247
  return { ...bucket, paths };
8818
9248
  }).filter((bucket) => bucket.paths.length > 0);
8819
9249
  if (buckets.length === 0) {
8820
- ora.fail("No buckets found. All buckets were filtered out by --file option.");
9250
+ ora.fail(
9251
+ "No buckets found. All buckets were filtered out by --file option."
9252
+ );
8821
9253
  process.exit(1);
8822
9254
  } else {
8823
9255
  ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
@@ -8850,13 +9282,17 @@ var status_default = new Command18().command("status").description("Show the sta
8850
9282
  console.log();
8851
9283
  ora.info(`Analyzing bucket: ${bucket.type}`);
8852
9284
  for (const bucketPath of bucket.paths) {
8853
- const bucketOra = Ora10({ indent: 2 }).info(`Analyzing path: ${bucketPath.pathPattern}`);
8854
- const sourceLocale = resolveOverriddenLocale7(i18nConfig.locale.source, bucketPath.delimiter);
9285
+ const bucketOra = Ora10({ indent: 2 }).info(
9286
+ `Analyzing path: ${bucketPath.pathPattern}`
9287
+ );
9288
+ const sourceLocale = resolveOverriddenLocale7(
9289
+ i18nConfig.locale.source,
9290
+ bucketPath.delimiter
9291
+ );
8855
9292
  const bucketLoader = createBucketLoader(
8856
9293
  bucket.type,
8857
9294
  bucketPath.pathPattern,
8858
9295
  {
8859
- isCacheRestore: false,
8860
9296
  defaultLocale: sourceLocale,
8861
9297
  injectLocale: bucket.injectLocale
8862
9298
  },
@@ -8895,8 +9331,13 @@ var status_default = new Command18().command("status").description("Show the sta
8895
9331
  }
8896
9332
  fileStats[filePath].wordCount = sourceWordCount;
8897
9333
  for (const _targetLocale of targetLocales) {
8898
- const targetLocale = resolveOverriddenLocale7(_targetLocale, bucketPath.delimiter);
8899
- bucketOra.start(`[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`);
9334
+ const targetLocale = resolveOverriddenLocale7(
9335
+ _targetLocale,
9336
+ bucketPath.delimiter
9337
+ );
9338
+ bucketOra.start(
9339
+ `[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`
9340
+ );
8900
9341
  let targetData = {};
8901
9342
  let fileExists = true;
8902
9343
  try {
@@ -8912,13 +9353,20 @@ var status_default = new Command18().command("status").description("Show the sta
8912
9353
  fileStats[filePath].languageStats[_targetLocale].words = sourceWordCount;
8913
9354
  languageStats[_targetLocale].missing += sourceKeys.length;
8914
9355
  languageStats[_targetLocale].words += sourceWordCount;
8915
- totalWordCount.set(_targetLocale, (totalWordCount.get(_targetLocale) || 0) + sourceWordCount);
9356
+ totalWordCount.set(
9357
+ _targetLocale,
9358
+ (totalWordCount.get(_targetLocale) || 0) + sourceWordCount
9359
+ );
8916
9360
  bucketOra.succeed(
8917
- `[${sourceLocale} -> ${targetLocale}] ${chalk13.red(`0% complete`)} (0/${sourceKeys.length} keys) - file not found`
9361
+ `[${sourceLocale} -> ${targetLocale}] ${chalk13.red(
9362
+ `0% complete`
9363
+ )} (0/${sourceKeys.length} keys) - file not found`
8918
9364
  );
8919
9365
  continue;
8920
9366
  }
8921
- const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
9367
+ const deltaProcessor = createDeltaProcessor(
9368
+ bucketPath.pathPattern
9369
+ );
8922
9370
  const checksums = await deltaProcessor.loadChecksums();
8923
9371
  const delta = await deltaProcessor.calculateDelta({
8924
9372
  sourceData,
@@ -8927,7 +9375,9 @@ var status_default = new Command18().command("status").description("Show the sta
8927
9375
  });
8928
9376
  const missingKeys = delta.added;
8929
9377
  const updatedKeys = delta.updated;
8930
- const completeKeys = sourceKeys.filter((key) => !missingKeys.includes(key) && !updatedKeys.includes(key));
9378
+ const completeKeys = sourceKeys.filter(
9379
+ (key) => !missingKeys.includes(key) && !updatedKeys.includes(key)
9380
+ );
8931
9381
  let wordsToTranslate = 0;
8932
9382
  const keysToProcess = flags.force ? sourceKeys : [...missingKeys, ...updatedKeys];
8933
9383
  for (const key of keysToProcess) {
@@ -8945,25 +9395,39 @@ var status_default = new Command18().command("status").description("Show the sta
8945
9395
  languageStats[_targetLocale].updated += updatedKeys.length;
8946
9396
  languageStats[_targetLocale].complete += completeKeys.length;
8947
9397
  languageStats[_targetLocale].words += wordsToTranslate;
8948
- totalWordCount.set(_targetLocale, (totalWordCount.get(_targetLocale) || 0) + wordsToTranslate);
9398
+ totalWordCount.set(
9399
+ _targetLocale,
9400
+ (totalWordCount.get(_targetLocale) || 0) + wordsToTranslate
9401
+ );
8949
9402
  const totalKeysInFile = sourceKeys.length;
8950
9403
  const completionPercent = (completeKeys.length / totalKeysInFile * 100).toFixed(1);
8951
9404
  if (missingKeys.length === 0 && updatedKeys.length === 0) {
8952
9405
  bucketOra.succeed(
8953
- `[${sourceLocale} -> ${targetLocale}] ${chalk13.green(`100% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`
9406
+ `[${sourceLocale} -> ${targetLocale}] ${chalk13.green(
9407
+ `100% complete`
9408
+ )} (${completeKeys.length}/${totalKeysInFile} keys)`
8954
9409
  );
8955
9410
  } else {
8956
9411
  const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? chalk13.yellow(`${completionPercent}% complete`) : chalk13.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
8957
9412
  bucketOra.succeed(message);
8958
9413
  if (flags.verbose) {
8959
9414
  if (missingKeys.length > 0) {
8960
- console.log(` ${chalk13.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`);
8961
9415
  console.log(
8962
- ` ${chalk13.dim(`Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`)}`
9416
+ ` ${chalk13.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`
9417
+ );
9418
+ console.log(
9419
+ ` ${chalk13.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`
9420
+ );
9421
+ console.log(
9422
+ ` ${chalk13.dim(
9423
+ `Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`
9424
+ )}`
8963
9425
  );
8964
9426
  }
8965
9427
  if (updatedKeys.length > 0) {
8966
- console.log(` ${chalk13.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`);
9428
+ console.log(
9429
+ ` ${chalk13.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`
9430
+ );
8967
9431
  }
8968
9432
  }
8969
9433
  }
@@ -8973,9 +9437,12 @@ var status_default = new Command18().command("status").description("Show the sta
8973
9437
  ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);
8974
9438
  }
8975
9439
  }
8976
- const totalKeysNeedingTranslation = Object.values(languageStats).reduce((sum, stats) => {
8977
- return sum + stats.missing + stats.updated;
8978
- }, 0);
9440
+ const totalKeysNeedingTranslation = Object.values(languageStats).reduce(
9441
+ (sum, stats) => {
9442
+ return sum + stats.missing + stats.updated;
9443
+ },
9444
+ 0
9445
+ );
8979
9446
  const totalCompletedKeys = totalSourceKeyCount - totalKeysNeedingTranslation / targetLocales.length;
8980
9447
  console.log();
8981
9448
  ora.succeed(chalk13.green(`Localization status completed.`));
@@ -8985,12 +9452,26 @@ var status_default = new Command18().command("status").description("Show the sta
8985
9452
  console.log(chalk13.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`));
8986
9453
  console.log(chalk13.bold(`
8987
9454
  \u{1F4DD} SOURCE CONTENT:`));
8988
- console.log(`\u2022 Source language: ${chalk13.green(i18nConfig.locale.source)}`);
8989
- console.log(`\u2022 Source keys: ${chalk13.yellow(totalSourceKeyCount.toString())} keys across all files`);
9455
+ console.log(
9456
+ `\u2022 Source language: ${chalk13.green(i18nConfig.locale.source)}`
9457
+ );
9458
+ console.log(
9459
+ `\u2022 Source keys: ${chalk13.yellow(
9460
+ totalSourceKeyCount.toString()
9461
+ )} keys across all files`
9462
+ );
8990
9463
  console.log(chalk13.bold(`
8991
9464
  \u{1F310} LANGUAGE BY LANGUAGE BREAKDOWN:`));
8992
9465
  const table = new Table({
8993
- head: ["Language", "Status", "Complete", "Missing", "Updated", "Total Keys", "Words to Translate"],
9466
+ head: [
9467
+ "Language",
9468
+ "Status",
9469
+ "Complete",
9470
+ "Missing",
9471
+ "Updated",
9472
+ "Total Keys",
9473
+ "Words to Translate"
9474
+ ],
8994
9475
  style: {
8995
9476
  head: ["white"],
8996
9477
  // White color for headers
@@ -9039,15 +9520,21 @@ var status_default = new Command18().command("status").description("Show the sta
9039
9520
  console.log(chalk13.bold(`
9040
9521
  \u{1F4CA} USAGE ESTIMATE:`));
9041
9522
  console.log(
9042
- `\u2022 WORDS TO BE CONSUMED: ~${chalk13.yellow.bold(totalWordsToTranslate.toLocaleString())} words across all languages`
9523
+ `\u2022 WORDS TO BE CONSUMED: ~${chalk13.yellow.bold(
9524
+ totalWordsToTranslate.toLocaleString()
9525
+ )} words across all languages`
9526
+ );
9527
+ console.log(
9528
+ ` (Words are counted from source language for keys that need translation in target languages)`
9043
9529
  );
9044
- console.log(` (Words are counted from source language for keys that need translation in target languages)`);
9045
9530
  if (targetLocales.length > 1) {
9046
9531
  console.log(`\u2022 Per-language breakdown:`);
9047
9532
  for (const locale of targetLocales) {
9048
9533
  const words = totalWordCount.get(locale) || 0;
9049
9534
  const percent = (words / totalWordsToTranslate * 100).toFixed(1);
9050
- console.log(` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`);
9535
+ console.log(
9536
+ ` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`
9537
+ );
9051
9538
  }
9052
9539
  }
9053
9540
  if (flags.confirm && Object.keys(fileStats).length > 0) {
@@ -9057,7 +9544,9 @@ var status_default = new Command18().command("status").description("Show the sta
9057
9544
  if (stats.sourceKeys === 0) return;
9058
9545
  console.log(chalk13.bold(`
9059
9546
  \u2022 ${path17}:`));
9060
- console.log(` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`);
9547
+ console.log(
9548
+ ` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`
9549
+ );
9061
9550
  const fileTable = new Table({
9062
9551
  head: ["Language", "Status", "Details"],
9063
9552
  style: {
@@ -9084,8 +9573,10 @@ var status_default = new Command18().command("status").description("Show the sta
9084
9573
  let details = "";
9085
9574
  if (langStats.missing > 0 || langStats.updated > 0) {
9086
9575
  const parts = [];
9087
- if (langStats.missing > 0) parts.push(`${langStats.missing} missing`);
9088
- if (langStats.updated > 0) parts.push(`${langStats.updated} changed`);
9576
+ if (langStats.missing > 0)
9577
+ parts.push(`${langStats.missing} missing`);
9578
+ if (langStats.updated > 0)
9579
+ parts.push(`${langStats.updated} changed`);
9089
9580
  details = `${parts.join(", ")}, ~${langStats.words} words`;
9090
9581
  } else {
9091
9582
  details = "All keys translated";
@@ -9098,7 +9589,9 @@ var status_default = new Command18().command("status").description("Show the sta
9098
9589
  const completeLanguages = targetLocales.filter(
9099
9590
  (locale) => languageStats[locale].missing === 0 && languageStats[locale].updated === 0
9100
9591
  );
9101
- const missingLanguages = targetLocales.filter((locale) => languageStats[locale].complete === 0);
9592
+ const missingLanguages = targetLocales.filter(
9593
+ (locale) => languageStats[locale].complete === 0
9594
+ );
9102
9595
  console.log(chalk13.bold.green(`
9103
9596
  \u{1F4A1} OPTIMIZATION TIPS:`));
9104
9597
  if (missingLanguages.length > 0) {
@@ -9113,7 +9606,9 @@ var status_default = new Command18().command("status").description("Show the sta
9113
9606
  }
9114
9607
  if (targetLocales.length > 1) {
9115
9608
  console.log(`\u2022 Translating one language at a time reduces complexity`);
9116
- console.log(`\u2022 Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`);
9609
+ console.log(
9610
+ `\u2022 Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`
9611
+ );
9117
9612
  }
9118
9613
  trackEvent(authId || "status", "cmd.status.success", {
9119
9614
  i18nConfig,
@@ -9175,7 +9670,9 @@ function validateParams2(i18nConfig, flags) {
9175
9670
  message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
9176
9671
  docUrl: "localeTargetNotFound"
9177
9672
  });
9178
- } else if (flags.bucket?.some((bucket) => !i18nConfig.buckets[bucket])) {
9673
+ } else if (flags.bucket?.some(
9674
+ (bucket) => !i18nConfig.buckets[bucket]
9675
+ )) {
9179
9676
  throw new CLIError({
9180
9677
  message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
9181
9678
  docUrl: "bucketNotFound"
@@ -9220,7 +9717,9 @@ var may_the_fourth_default = new Command19().command("may-the-fourth").descripti
9220
9717
  });
9221
9718
  await renderSpacer2();
9222
9719
  console.log(
9223
- `${chalk14.hex(colors2.green)("We hope you enjoyed it! :)")} ${chalk14.hex(colors2.blue)("May the Fourth be with you! \u{1F680}")}`
9720
+ `${chalk14.hex(colors2.green)("We hope you enjoyed it! :)")} ${chalk14.hex(
9721
+ colors2.blue
9722
+ )("May the Fourth be with you! \u{1F680}")}`
9224
9723
  );
9225
9724
  await renderSpacer2();
9226
9725
  console.log(chalk14.dim(`---`));
@@ -9246,7 +9745,9 @@ async function renderBanner2() {
9246
9745
  }
9247
9746
  async function renderHero2() {
9248
9747
  console.log(
9249
- `\u26A1\uFE0F ${chalk14.hex(colors2.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
9748
+ `\u26A1\uFE0F ${chalk14.hex(colors2.green)(
9749
+ "Lingo.dev"
9750
+ )} - open-source, AI-powered i18n CLI for web & mobile localization.`
9250
9751
  );
9251
9752
  console.log(" ");
9252
9753
  console.log(
@@ -9258,7 +9759,7 @@ async function renderHero2() {
9258
9759
  // package.json
9259
9760
  var package_default = {
9260
9761
  name: "lingo.dev",
9261
- version: "0.100.1",
9762
+ version: "0.101.0",
9262
9763
  description: "Lingo.dev CLI",
9263
9764
  private: false,
9264
9765
  publishConfig: {
@@ -9372,6 +9873,7 @@ var package_default = {
9372
9873
  dependencies: {
9373
9874
  "@ai-sdk/anthropic": "^1.2.11",
9374
9875
  "@ai-sdk/google": "^1.2.19",
9876
+ "@ai-sdk/mistral": "^1.2.8",
9375
9877
  "@ai-sdk/openai": "^1.3.22",
9376
9878
  "@babel/generator": "^7.27.1",
9377
9879
  "@babel/parser": "^7.27.1",
@@ -9488,6 +9990,167 @@ var package_default = {
9488
9990
  packageManager: "pnpm@9.12.3"
9489
9991
  };
9490
9992
 
9993
+ // src/cli/cmd/purge.ts
9994
+ import { Command as Command20 } from "interactive-commander";
9995
+ import Ora11 from "ora";
9996
+ import { resolveOverriddenLocale as resolveOverriddenLocale8 } from "@lingo.dev/_spec";
9997
+ import { confirm as confirm3 } from "@inquirer/prompts";
9998
+ var purge_default = new Command20().command("purge").description(
9999
+ "Remove translations for given --bucket, --file, --key, --locale"
10000
+ ).helpOption("-h, --help", "Show help").option(
10001
+ "--bucket <bucket>",
10002
+ "Bucket to process",
10003
+ (val, prev) => prev ? [...prev, val] : [val]
10004
+ ).option(
10005
+ "--file [files...]",
10006
+ "File(s) to process. Only process files that match the given glob pattern(s)."
10007
+ ).option(
10008
+ "--key <key>",
10009
+ "Key to remove. Remove all translation keys matching the given glob pattern."
10010
+ ).option(
10011
+ "--locale <locale>",
10012
+ "Locale to process",
10013
+ (val, prev) => prev ? [...prev, val] : [val]
10014
+ ).option(
10015
+ "--yes-really",
10016
+ "Skip interactive confirmation and delete without asking."
10017
+ ).action(async function(options) {
10018
+ const ora = Ora11();
10019
+ try {
10020
+ ora.start("Loading configuration...");
10021
+ const i18nConfig = getConfig();
10022
+ if (!i18nConfig) {
10023
+ throw new Error("i18n.json not found. Please run `lingo.dev init`.");
10024
+ }
10025
+ ora.succeed("Configuration loaded");
10026
+ let buckets = getBuckets(i18nConfig);
10027
+ if (options.bucket && options.bucket.length) {
10028
+ buckets = buckets.filter(
10029
+ (bucket) => options.bucket.includes(bucket.type)
10030
+ );
10031
+ }
10032
+ if (options.file && options.file.length) {
10033
+ buckets = buckets.map((bucket) => {
10034
+ const paths = bucket.paths.filter(
10035
+ (bucketPath) => options.file?.some((f) => bucketPath.pathPattern.includes(f))
10036
+ );
10037
+ return { ...bucket, paths };
10038
+ }).filter((bucket) => bucket.paths.length > 0);
10039
+ if (buckets.length === 0) {
10040
+ ora.fail("All files were filtered out by --file option.");
10041
+ process.exit(1);
10042
+ }
10043
+ }
10044
+ const sourceLocale = i18nConfig.locale.source;
10045
+ const targetLocales = options.locale && options.locale.length ? options.locale : i18nConfig.locale.targets;
10046
+ let removedAny = false;
10047
+ for (const bucket of buckets) {
10048
+ console.log();
10049
+ ora.info(`Processing bucket: ${bucket.type}`);
10050
+ for (const bucketPath of bucket.paths) {
10051
+ for (const _targetLocale of targetLocales) {
10052
+ const targetLocale = resolveOverriddenLocale8(
10053
+ _targetLocale,
10054
+ bucketPath.delimiter
10055
+ );
10056
+ const bucketOra = Ora11({ indent: 2 }).start(
10057
+ `Processing path: ${bucketPath.pathPattern} [${targetLocale}]`
10058
+ );
10059
+ try {
10060
+ const bucketLoader = createBucketLoader(
10061
+ bucket.type,
10062
+ bucketPath.pathPattern,
10063
+ {
10064
+ defaultLocale: sourceLocale,
10065
+ injectLocale: bucket.injectLocale
10066
+ },
10067
+ bucket.lockedKeys,
10068
+ bucket.lockedPatterns,
10069
+ bucket.ignoredKeys
10070
+ );
10071
+ await bucketLoader.init();
10072
+ bucketLoader.setDefaultLocale(sourceLocale);
10073
+ await bucketLoader.pull(sourceLocale);
10074
+ let targetData = await bucketLoader.pull(targetLocale);
10075
+ if (!targetData || Object.keys(targetData).length === 0) {
10076
+ bucketOra.info(
10077
+ `No translations found for ${bucketPath.pathPattern} [${targetLocale}]`
10078
+ );
10079
+ continue;
10080
+ }
10081
+ let newData = { ...targetData };
10082
+ let keysToRemove = [];
10083
+ if (options.key) {
10084
+ keysToRemove = Object.keys(newData).filter(
10085
+ (k) => minimatch(k, options.key)
10086
+ );
10087
+ } else {
10088
+ keysToRemove = Object.keys(newData);
10089
+ }
10090
+ if (keysToRemove.length > 0) {
10091
+ if (options.key) {
10092
+ bucketOra.info(
10093
+ `About to delete ${keysToRemove.length} key(s) matching '${options.key}' from ${bucketPath.pathPattern} [${targetLocale}]:
10094
+ ${keysToRemove.slice(0, 10).join(", ")}${keysToRemove.length > 10 ? ", ..." : ""}`
10095
+ );
10096
+ } else {
10097
+ bucketOra.info(
10098
+ `About to delete all (${keysToRemove.length}) keys from ${bucketPath.pathPattern} [${targetLocale}]`
10099
+ );
10100
+ }
10101
+ if (!options.yesReally) {
10102
+ bucketOra.warn(
10103
+ "This is a destructive operation. If you are sure, type 'y' to continue. (Use --yes-really to skip this check.)"
10104
+ );
10105
+ const confirmed = await confirm3({
10106
+ message: `Delete these keys from ${bucketPath.pathPattern} [${targetLocale}]?`,
10107
+ default: false
10108
+ });
10109
+ if (!confirmed) {
10110
+ bucketOra.info("Skipped by user.");
10111
+ continue;
10112
+ }
10113
+ }
10114
+ for (const key of keysToRemove) {
10115
+ delete newData[key];
10116
+ }
10117
+ removedAny = true;
10118
+ await bucketLoader.push(targetLocale, newData);
10119
+ if (options.key) {
10120
+ bucketOra.succeed(
10121
+ `Removed ${keysToRemove.length} key(s) matching '${options.key}' from ${bucketPath.pathPattern} [${targetLocale}]`
10122
+ );
10123
+ } else {
10124
+ bucketOra.succeed(
10125
+ `Removed all keys (${keysToRemove.length}) from ${bucketPath.pathPattern} [${targetLocale}]`
10126
+ );
10127
+ }
10128
+ } else if (options.key) {
10129
+ bucketOra.info(
10130
+ `No keys matching '${options.key}' found in ${bucketPath.pathPattern} [${targetLocale}]`
10131
+ );
10132
+ } else {
10133
+ bucketOra.info("No keys to remove.");
10134
+ }
10135
+ } catch (error) {
10136
+ const err = error;
10137
+ bucketOra.fail(`Failed: ${err.message}`);
10138
+ }
10139
+ }
10140
+ }
10141
+ }
10142
+ if (!removedAny) {
10143
+ ora.info("No keys were removed.");
10144
+ } else {
10145
+ ora.succeed("Purge completed.");
10146
+ }
10147
+ } catch (error) {
10148
+ const err = error;
10149
+ ora.fail(err.message);
10150
+ process.exit(1);
10151
+ }
10152
+ });
10153
+
9491
10154
  // src/cli/index.ts
9492
10155
  dotenv.config();
9493
10156
  var cli_default = new InteractiveCommand2().name("lingo.dev").description("Lingo.dev CLI").helpOption("-h, --help", "Show help").addHelpText(
@@ -9505,7 +10168,7 @@ ${vice3(
9505
10168
 
9506
10169
  Star the the repo :) https://github.com/LingoDotDev/lingo.dev
9507
10170
  `
9508
- ).version(`v${package_default.version}`, "-v, --version", "Show version").addCommand(init_default).interactive("-y, --no-interactive", "Disable interactive mode").addCommand(i18n_default).addCommand(auth_default).addCommand(login_default).addCommand(logout_default).addCommand(show_default).addCommand(config_default2).addCommand(lockfile_default).addCommand(cleanup_default).addCommand(mcp_default).addCommand(ci_default).addCommand(status_default).addCommand(may_the_fourth_default, { hidden: true }).addCommand(run_default, { hidden: true }).exitOverride((err) => {
10171
+ ).version(`v${package_default.version}`, "-v, --version", "Show version").addCommand(init_default).interactive("-y, --no-interactive", "Disable interactive mode").addCommand(i18n_default).addCommand(auth_default).addCommand(login_default).addCommand(logout_default).addCommand(show_default).addCommand(config_default2).addCommand(lockfile_default).addCommand(cleanup_default).addCommand(mcp_default).addCommand(ci_default).addCommand(status_default).addCommand(may_the_fourth_default, { hidden: true }).addCommand(run_default).addCommand(purge_default).exitOverride((err) => {
9509
10172
  if (err.code === "commander.helpDisplayed" || err.code === "commander.version" || err.code === "commander.help") {
9510
10173
  process.exit(0);
9511
10174
  }