lingo.dev 0.116.2 → 0.116.3

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
@@ -2036,7 +2036,7 @@ function createTextFileLoader(pathPattern) {
2036
2036
  const trimmedResult = result.trim();
2037
2037
  return trimmedResult;
2038
2038
  },
2039
- async push(locale, data, _36, originalLocale) {
2039
+ async push(locale, data, _37, originalLocale) {
2040
2040
  const draftPath = pathPattern.replaceAll("[locale]", locale);
2041
2041
  const finalPath = path10.resolve(draftPath);
2042
2042
  const dirPath = path10.dirname(finalPath);
@@ -3666,7 +3666,7 @@ function createPropertiesLoader() {
3666
3666
  return result;
3667
3667
  },
3668
3668
  async push(locale, payload) {
3669
- const result = Object.entries(payload).filter(([_36, value]) => value != null).map(([key, value]) => `${key}=${value}`).join("\n");
3669
+ const result = Object.entries(payload).filter(([_37, value]) => value != null).map(([key, value]) => `${key}=${value}`).join("\n");
3670
3670
  return result;
3671
3671
  }
3672
3672
  });
@@ -3988,7 +3988,7 @@ function createXcodeStringsLoader() {
3988
3988
  return result;
3989
3989
  },
3990
3990
  async push(locale, payload) {
3991
- const lines = Object.entries(payload).filter(([_36, value]) => value != null).map(([key, value]) => {
3991
+ const lines = Object.entries(payload).filter(([_37, value]) => value != null).map(([key, value]) => {
3992
3992
  const escapedValue = escapeString(value);
3993
3993
  return `"${key}" = "${escapedValue}";`;
3994
3994
  });
@@ -4186,218 +4186,21 @@ function _removeLocale(input2, locale) {
4186
4186
  return { ...input2, strings: newStrings };
4187
4187
  }
4188
4188
 
4189
- // src/cli/loaders/xcode-xcstrings-icu.ts
4190
- var ICU_TYPE_MARKER = Symbol.for("@lingo.dev/icu-plural-object");
4191
- var CLDR_PLURAL_CATEGORIES = /* @__PURE__ */ new Set([
4192
- "zero",
4193
- "one",
4194
- "two",
4195
- "few",
4196
- "many",
4197
- "other"
4198
- ]);
4199
- function isICUPluralObject(value) {
4200
- if (!value || typeof value !== "object" || Array.isArray(value)) {
4201
- return false;
4202
- }
4203
- if (ICU_TYPE_MARKER in value) {
4204
- return true;
4205
- }
4206
- if (!("icu" in value) || typeof value.icu !== "string") {
4207
- return false;
4208
- }
4209
- const icuPluralPattern = /^\{[\w]+,\s*plural,\s*.+\}$/;
4210
- if (!icuPluralPattern.test(value.icu)) {
4211
- return false;
4212
- }
4213
- if (value._meta !== void 0) {
4214
- if (typeof value._meta !== "object" || !value._meta.variables || typeof value._meta.variables !== "object") {
4215
- return false;
4216
- }
4217
- for (const [varName, varMeta] of Object.entries(value._meta.variables)) {
4218
- if (!varMeta || typeof varMeta !== "object" || typeof varMeta.format !== "string" || varMeta.role !== "plural" && varMeta.role !== "other") {
4219
- return false;
4220
- }
4221
- }
4222
- }
4223
- return true;
4224
- }
4225
- function isPluralFormsObject(value) {
4226
- if (!value || typeof value !== "object" || Array.isArray(value)) {
4227
- return false;
4228
- }
4229
- const keys = Object.keys(value);
4230
- if (keys.length === 0) {
4231
- return false;
4232
- }
4233
- const allKeysAreCldr = keys.every((key) => CLDR_PLURAL_CATEGORIES.has(key));
4234
- if (!allKeysAreCldr) {
4235
- return false;
4236
- }
4237
- const allValuesAreStrings = keys.every(
4238
- (key) => typeof value[key] === "string"
4189
+ // src/cli/loaders/xcode-xcstrings-v2.ts
4190
+ import _10 from "lodash";
4191
+ function buildIcuPluralString(forms) {
4192
+ const parts = Object.entries(forms).map(
4193
+ ([form, text]) => `${form} {${text}}`
4239
4194
  );
4240
- if (!allValuesAreStrings) {
4241
- return false;
4242
- }
4243
- if (!("other" in value)) {
4244
- return false;
4245
- }
4246
- return true;
4247
- }
4248
- function getRequiredPluralCategories(locale) {
4249
- try {
4250
- const pluralRules = new Intl.PluralRules(locale);
4251
- const categories = pluralRules.resolvedOptions().pluralCategories;
4252
- if (!categories || categories.length === 0) {
4253
- throw new Error(`No plural categories found for locale: ${locale}`);
4254
- }
4255
- return categories;
4256
- } catch (error) {
4257
- console.warn(
4258
- `[xcode-xcstrings-icu] Failed to resolve plural categories for locale "${locale}". Using fallback ["one", "other"]. Error: ${error instanceof Error ? error.message : String(error)}`
4259
- );
4260
- return ["one", "other"];
4261
- }
4262
- }
4263
- var CLDR_CATEGORY_TO_NUMBER = {
4264
- zero: 0,
4265
- one: 1,
4266
- two: 2
4267
- };
4268
- var NUMBER_TO_CLDR_CATEGORY = {
4269
- 0: "zero",
4270
- 1: "one",
4271
- 2: "two"
4272
- };
4273
- function xcstringsToPluralWithMeta(pluralForms, sourceLocale = "en") {
4274
- if (!pluralForms || Object.keys(pluralForms).length === 0) {
4275
- throw new Error("pluralForms cannot be empty");
4276
- }
4277
- const requiredCategories = getRequiredPluralCategories(sourceLocale);
4278
- const variables = {};
4279
- const formatRegex = /(%(?:(\d+)\$)?(?:[+-])?(?:\d+)?(?:\.(\d+))?([lhqLzjt]*)([diuoxXfFeEgGaAcspn@]))/g;
4280
- let maxMatches = [];
4281
- let maxMatchText = "";
4282
- for (const [form, text] of Object.entries(pluralForms)) {
4283
- if (typeof text !== "string") {
4284
- console.warn(
4285
- `Warning: Plural form "${form}" has non-string value:`,
4286
- text
4287
- );
4288
- continue;
4289
- }
4290
- const matches = [...text.matchAll(formatRegex)];
4291
- if (matches.length > maxMatches.length) {
4292
- maxMatches = matches;
4293
- maxMatchText = text;
4294
- }
4295
- }
4296
- let lastNumericIndex = -1;
4297
- maxMatches.forEach((match2, idx) => {
4298
- const specifier = match2[5];
4299
- if (/[diuoxXfFeE]/.test(specifier)) {
4300
- lastNumericIndex = idx;
4301
- }
4302
- });
4303
- let nonPluralCounter = 0;
4304
- maxMatches.forEach((match2, idx) => {
4305
- const fullFormat = match2[1];
4306
- const position = match2[2];
4307
- const precision = match2[3];
4308
- const lengthMod = match2[4];
4309
- const specifier = match2[5];
4310
- const isPluralVar = idx === lastNumericIndex;
4311
- const varName = isPluralVar ? "count" : `var${nonPluralCounter++}`;
4312
- variables[varName] = {
4313
- format: fullFormat,
4314
- role: isPluralVar ? "plural" : "other"
4315
- };
4316
- });
4317
- const variableKeys = Object.keys(variables);
4318
- const icuForms = Object.entries(pluralForms).filter(([form, text]) => {
4319
- if (typeof text !== "string") {
4320
- return false;
4321
- }
4322
- return true;
4323
- }).map(([form, text]) => {
4324
- let processed = text;
4325
- let vIdx = 0;
4326
- processed = processed.replace(formatRegex, () => {
4327
- if (vIdx >= variableKeys.length) {
4328
- vIdx++;
4329
- return "#";
4330
- }
4331
- const varName = variableKeys[vIdx];
4332
- const varMeta = variables[varName];
4333
- vIdx++;
4334
- if (varMeta.role === "plural") {
4335
- return "#";
4336
- } else {
4337
- return `{${varName}}`;
4338
- }
4339
- });
4340
- const isRequired = requiredCategories.includes(form);
4341
- const formKey = !isRequired && form in CLDR_CATEGORY_TO_NUMBER ? `=${CLDR_CATEGORY_TO_NUMBER[form]}` : form;
4342
- return `${formKey} {${processed}}`;
4343
- }).join(" ");
4344
- const pluralVarName = Object.keys(variables).find((name) => variables[name].role === "plural") || "count";
4345
- const icu = `{${pluralVarName}, plural, ${icuForms}}`;
4346
- const result = {
4347
- icu,
4348
- _meta: Object.keys(variables).length > 0 ? { variables } : void 0,
4349
- [ICU_TYPE_MARKER]: true
4350
- // Add type marker for robust detection
4351
- };
4352
- return result;
4195
+ return `{count, plural, ${parts.join(" ")}}`;
4353
4196
  }
4354
- function pluralWithMetaToXcstrings(data) {
4355
- if (!data.icu) {
4356
- throw new Error("ICU string is required");
4357
- }
4358
- const ast = parseICU(data.icu);
4359
- if (!ast || ast.length === 0) {
4360
- throw new Error("Invalid ICU format");
4361
- }
4362
- const pluralNode = ast.find((node) => node.type === "plural");
4363
- if (!pluralNode) {
4364
- throw new Error("No plural found in ICU format");
4197
+ function parseIcuPluralString(icuString) {
4198
+ const pluralMatch = icuString.match(/\{[\w]+,\s*plural,\s*(.+)\}$/);
4199
+ if (!pluralMatch) {
4200
+ throw new Error(`Invalid ICU plural format: ${icuString}`);
4365
4201
  }
4202
+ const formsText = pluralMatch[1];
4366
4203
  const forms = {};
4367
- for (const [form, option] of Object.entries(pluralNode.options)) {
4368
- let text = "";
4369
- const optionValue = option.value;
4370
- for (const element of optionValue) {
4371
- if (element.type === "literal") {
4372
- text += element.value;
4373
- } else if (element.type === "pound") {
4374
- const pluralVar = Object.entries(data._meta?.variables || {}).find(
4375
- ([_36, meta]) => meta.role === "plural"
4376
- );
4377
- text += pluralVar?.[1].format || "%lld";
4378
- } else if (element.type === "argument") {
4379
- const varName = element.value;
4380
- const varMeta = data._meta?.variables?.[varName];
4381
- text += varMeta?.format || "%@";
4382
- }
4383
- }
4384
- let xcstringsFormName = form;
4385
- if (form.startsWith("=")) {
4386
- const numValue = parseInt(form.substring(1), 10);
4387
- xcstringsFormName = NUMBER_TO_CLDR_CATEGORY[numValue] || form;
4388
- }
4389
- forms[xcstringsFormName] = text;
4390
- }
4391
- return forms;
4392
- }
4393
- function parseICU(icu) {
4394
- const match2 = icu.match(/\{(\w+),\s*plural,\s*(.+)\}$/);
4395
- if (!match2) {
4396
- throw new Error("Invalid ICU plural format");
4397
- }
4398
- const varName = match2[1];
4399
- const formsText = match2[2];
4400
- const options = {};
4401
4204
  let i = 0;
4402
4205
  while (i < formsText.length) {
4403
4206
  while (i < formsText.length && /\s/.test(formsText[i])) {
@@ -4443,149 +4246,228 @@ function parseICU(icu) {
4443
4246
  i++;
4444
4247
  }
4445
4248
  if (braceCount !== 0) {
4446
- const preview = formsText.substring(
4447
- Math.max(0, i - 50),
4448
- Math.min(formsText.length, i + 50)
4449
- );
4450
4249
  throw new Error(
4451
- `Unclosed brace for form '${formName}' in ICU MessageFormat.
4452
- Expected ${braceCount} more closing brace(s).
4453
- Context: ...${preview}...
4454
- Full ICU: {${varName}, plural, ${formsText}}`
4250
+ `Unclosed brace for form '${formName}' in ICU: ${icuString}`
4455
4251
  );
4456
4252
  }
4457
- const elements = parseFormText(formText);
4458
- options[formName] = {
4459
- value: elements
4460
- };
4253
+ forms[formName] = formText;
4461
4254
  }
4462
- return [
4463
- {
4464
- type: "plural",
4465
- value: varName,
4466
- options
4467
- }
4468
- ];
4255
+ return forms;
4469
4256
  }
4470
- function parseFormText(text) {
4471
- const elements = [];
4472
- let currentText = "";
4473
- let i = 0;
4474
- while (i < text.length) {
4475
- if (text[i] === "#") {
4476
- if (currentText) {
4477
- elements.push({ type: "literal", value: currentText });
4478
- currentText = "";
4479
- }
4480
- elements.push({ type: "pound" });
4481
- i++;
4482
- } else if (text[i] === "{") {
4483
- if (currentText) {
4484
- elements.push({ type: "literal", value: currentText });
4485
- currentText = "";
4486
- }
4487
- let braceCount = 1;
4488
- let j = i + 1;
4489
- while (j < text.length && braceCount > 0) {
4490
- if (text[j] === "{") {
4491
- braceCount++;
4492
- } else if (text[j] === "}") {
4493
- braceCount--;
4494
- }
4495
- j++;
4496
- }
4497
- if (braceCount !== 0) {
4498
- throw new Error("Unclosed variable reference");
4499
- }
4500
- const varName = text.slice(i + 1, j - 1);
4501
- elements.push({ type: "argument", value: varName });
4502
- i = j;
4503
- } else {
4504
- currentText += text[i];
4505
- i++;
4506
- }
4507
- }
4508
- if (currentText) {
4509
- elements.push({ type: "literal", value: currentText });
4510
- }
4511
- return elements;
4257
+ function isIcuPluralString(value) {
4258
+ return typeof value === "string" && /^\{[\w]+,\s*plural,\s*.+\}$/.test(value);
4512
4259
  }
4513
-
4514
- // src/cli/loaders/xcode-xcstrings-v2-loader.ts
4515
- function createXcodeXcstringsV2Loader(defaultLocale = "en") {
4260
+ function createXcodeXcstringsV2Loader(defaultLocale) {
4516
4261
  return createLoader({
4517
- async pull(locale, input2) {
4518
- const result = {};
4519
- for (const [key, value] of Object.entries(input2)) {
4520
- if (isPluralFormsObject(value)) {
4521
- try {
4522
- result[key] = xcstringsToPluralWithMeta(value, locale);
4523
- } catch (error) {
4524
- console.error(
4525
- `
4526
- [xcode-xcstrings-icu] Failed to convert plural forms for key "${key}":`,
4527
- `
4528
- Error: ${error instanceof Error ? error.message : String(error)}`,
4529
- `
4530
- Locale: ${locale}
4531
- `
4532
- );
4533
- result[key] = value;
4262
+ async pull(locale, input2, initCtx) {
4263
+ const resultData = {};
4264
+ const isSourceLanguage = locale === defaultLocale;
4265
+ for (const [translationKey, _translationEntity] of Object.entries(
4266
+ input2.strings
4267
+ )) {
4268
+ const rootTranslationEntity = _translationEntity;
4269
+ if (rootTranslationEntity.shouldTranslate === false) {
4270
+ continue;
4271
+ }
4272
+ const langTranslationEntity = rootTranslationEntity?.localizations?.[locale];
4273
+ if (langTranslationEntity) {
4274
+ if (!resultData[translationKey]) {
4275
+ resultData[translationKey] = {};
4534
4276
  }
4535
- } else {
4536
- result[key] = value;
4277
+ if ("stringUnit" in langTranslationEntity) {
4278
+ resultData[translationKey].stringUnit = langTranslationEntity.stringUnit.value;
4279
+ if ("substitutions" in langTranslationEntity) {
4280
+ resultData[translationKey].substitutions = {};
4281
+ for (const [subName, subData] of Object.entries(
4282
+ langTranslationEntity.substitutions
4283
+ )) {
4284
+ const pluralForms = subData.variations?.plural;
4285
+ if (pluralForms) {
4286
+ const forms = {};
4287
+ for (const [form, formData] of Object.entries(pluralForms)) {
4288
+ forms[form] = formData.stringUnit.value;
4289
+ }
4290
+ const icuString = buildIcuPluralString(forms);
4291
+ resultData[translationKey].substitutions[subName] = {
4292
+ variations: {
4293
+ plural: icuString
4294
+ }
4295
+ };
4296
+ }
4297
+ }
4298
+ }
4299
+ } else if ("stringSet" in langTranslationEntity) {
4300
+ const values = langTranslationEntity.stringSet.values;
4301
+ if (Array.isArray(values) && values.length > 0) {
4302
+ resultData[translationKey].stringSet = values;
4303
+ }
4304
+ } else if ("variations" in langTranslationEntity) {
4305
+ if ("plural" in langTranslationEntity.variations) {
4306
+ const pluralForms = langTranslationEntity.variations.plural;
4307
+ const forms = {};
4308
+ for (const [form, formData] of Object.entries(pluralForms)) {
4309
+ if (formData?.stringUnit?.value) {
4310
+ forms[form] = formData.stringUnit.value;
4311
+ }
4312
+ }
4313
+ const icuString = buildIcuPluralString(forms);
4314
+ resultData[translationKey].variations = {
4315
+ plural: icuString
4316
+ };
4317
+ }
4318
+ }
4319
+ } else if (isSourceLanguage) {
4320
+ if (!resultData[translationKey]) {
4321
+ resultData[translationKey] = {};
4322
+ }
4323
+ resultData[translationKey].stringUnit = translationKey;
4537
4324
  }
4538
4325
  }
4539
- return result;
4326
+ return resultData;
4540
4327
  },
4541
- async push(locale, payload) {
4542
- const result = {};
4543
- for (const [key, value] of Object.entries(payload)) {
4544
- if (isICUPluralObject(value)) {
4545
- try {
4546
- const pluralForms = pluralWithMetaToXcstrings(value);
4547
- result[key] = pluralForms;
4548
- } catch (error) {
4549
- throw new Error(
4550
- `Failed to write plural translation for key "${key}" (locale: ${locale}).
4328
+ async push(locale, payload, originalInput) {
4329
+ const langDataToMerge = {};
4330
+ langDataToMerge.strings = {};
4331
+ const input2 = _10.cloneDeep(originalInput) || {
4332
+ sourceLanguage: locale,
4333
+ strings: {}
4334
+ };
4335
+ for (const [baseKey, keyData] of Object.entries(payload)) {
4336
+ if (!keyData || typeof keyData !== "object") {
4337
+ continue;
4338
+ }
4339
+ const hasDoNotTranslateFlag = originalInput && originalInput.strings && originalInput.strings[baseKey] && originalInput.strings[baseKey].shouldTranslate === false;
4340
+ const localizationData = {};
4341
+ if ("stringUnit" in keyData) {
4342
+ localizationData.stringUnit = {
4343
+ state: "translated",
4344
+ value: keyData.stringUnit
4345
+ };
4346
+ }
4347
+ if ("substitutions" in keyData && keyData.substitutions) {
4348
+ const subs = {};
4349
+ for (const [subName, subData] of Object.entries(
4350
+ keyData.substitutions
4351
+ )) {
4352
+ const pluralValue = subData?.variations?.plural;
4353
+ if (pluralValue && isIcuPluralString(pluralValue)) {
4354
+ try {
4355
+ const pluralForms = parseIcuPluralString(pluralValue);
4356
+ const pluralOut = {};
4357
+ for (const [form, text] of Object.entries(pluralForms)) {
4358
+ pluralOut[form] = {
4359
+ stringUnit: {
4360
+ state: "translated",
4361
+ value: text
4362
+ }
4363
+ };
4364
+ }
4365
+ const sourceLocale = originalInput?.sourceLanguage || "en";
4366
+ const origFormatSpec = originalInput?.strings?.[baseKey]?.localizations?.[sourceLocale]?.substitutions?.[subName]?.formatSpecifier || subName;
4367
+ subs[subName] = {
4368
+ formatSpecifier: origFormatSpec,
4369
+ variations: {
4370
+ plural: pluralOut
4371
+ }
4372
+ };
4373
+ } catch (error) {
4374
+ throw new Error(
4375
+ `Failed to write substitution plural translation for key "${baseKey}/substitutions/${subName}" (locale: ${locale}).
4551
4376
  ${error instanceof Error ? error.message : String(error)}`
4552
- );
4377
+ );
4378
+ }
4379
+ }
4380
+ }
4381
+ if (Object.keys(subs).length > 0) {
4382
+ localizationData.substitutions = subs;
4383
+ }
4384
+ }
4385
+ if ("stringSet" in keyData && Array.isArray(keyData.stringSet)) {
4386
+ localizationData.stringSet = {
4387
+ state: "translated",
4388
+ values: keyData.stringSet
4389
+ };
4390
+ }
4391
+ if ("variations" in keyData && keyData.variations?.plural) {
4392
+ const pluralValue = keyData.variations.plural;
4393
+ if (isIcuPluralString(pluralValue)) {
4394
+ try {
4395
+ const pluralForms = parseIcuPluralString(pluralValue);
4396
+ const pluralOut = {};
4397
+ for (const [form, text] of Object.entries(pluralForms)) {
4398
+ pluralOut[form] = {
4399
+ stringUnit: {
4400
+ state: "translated",
4401
+ value: text
4402
+ }
4403
+ };
4404
+ }
4405
+ localizationData.variations = {
4406
+ plural: pluralOut
4407
+ };
4408
+ } catch (error) {
4409
+ throw new Error(
4410
+ `Failed to write plural translation for key "${baseKey}" (locale: ${locale}).
4411
+ ${error instanceof Error ? error.message : String(error)}`
4412
+ );
4413
+ }
4414
+ }
4415
+ }
4416
+ if (Object.keys(localizationData).length > 0) {
4417
+ langDataToMerge.strings[baseKey] = {
4418
+ extractionState: originalInput?.strings?.[baseKey]?.extractionState,
4419
+ localizations: {
4420
+ [locale]: localizationData
4421
+ }
4422
+ };
4423
+ if (hasDoNotTranslateFlag) {
4424
+ langDataToMerge.strings[baseKey].shouldTranslate = false;
4553
4425
  }
4554
- } else {
4555
- result[key] = value;
4556
4426
  }
4557
4427
  }
4558
- return result;
4428
+ return _10.merge(input2, langDataToMerge);
4429
+ },
4430
+ async pullHints(input2) {
4431
+ const hints = {};
4432
+ for (const [translationKey, _translationEntity] of Object.entries(
4433
+ input2.strings || {}
4434
+ )) {
4435
+ const rootTranslationEntity = _translationEntity;
4436
+ if (rootTranslationEntity.comment && typeof rootTranslationEntity.comment === "string") {
4437
+ hints[translationKey] = { hint: rootTranslationEntity.comment };
4438
+ }
4439
+ }
4440
+ return hints;
4559
4441
  }
4560
4442
  });
4561
4443
  }
4562
4444
 
4563
4445
  // src/cli/loaders/unlocalizable.ts
4564
- import _10 from "lodash";
4446
+ import _11 from "lodash";
4565
4447
  import _isUrl from "is-url";
4566
4448
  import { isValid, parseISO } from "date-fns";
4567
4449
  function createUnlocalizableLoader(returnUnlocalizedKeys = false) {
4568
4450
  return createLoader({
4569
4451
  async pull(locale, input2) {
4570
4452
  const unlocalizableKeys = _getUnlocalizableKeys(input2);
4571
- const result = _10.omitBy(
4453
+ const result = _11.omitBy(
4572
4454
  input2,
4573
- (_36, key) => unlocalizableKeys.includes(key)
4455
+ (_37, key) => unlocalizableKeys.includes(key)
4574
4456
  );
4575
4457
  if (returnUnlocalizedKeys) {
4576
- result.unlocalizable = _10.omitBy(
4458
+ result.unlocalizable = _11.omitBy(
4577
4459
  input2,
4578
- (_36, key) => !unlocalizableKeys.includes(key)
4460
+ (_37, key) => !unlocalizableKeys.includes(key)
4579
4461
  );
4580
4462
  }
4581
4463
  return result;
4582
4464
  },
4583
4465
  async push(locale, data, originalInput) {
4584
4466
  const unlocalizableKeys = _getUnlocalizableKeys(originalInput);
4585
- const result = _10.merge(
4467
+ const result = _11.merge(
4586
4468
  {},
4587
4469
  data,
4588
- _10.omitBy(originalInput, (_36, key) => !unlocalizableKeys.includes(key))
4470
+ _11.omitBy(originalInput, (_37, key) => !unlocalizableKeys.includes(key))
4589
4471
  );
4590
4472
  return result;
4591
4473
  }
@@ -4599,12 +4481,12 @@ function _isIsoDate(v) {
4599
4481
  }
4600
4482
  function _getUnlocalizableKeys(input2) {
4601
4483
  const rules = {
4602
- isEmpty: (v) => _10.isEmpty(v),
4484
+ isEmpty: (v) => _11.isEmpty(v),
4603
4485
  isNumber: (v) => typeof v === "number" || /^[0-9]+$/.test(v),
4604
- isBoolean: (v) => _10.isBoolean(v),
4605
- isIsoDate: (v) => _10.isString(v) && _isIsoDate(v),
4606
- isSystemId: (v) => _10.isString(v) && _isSystemId(v),
4607
- isUrl: (v) => _10.isString(v) && _isUrl(v)
4486
+ isBoolean: (v) => _11.isBoolean(v),
4487
+ isIsoDate: (v) => _11.isString(v) && _isIsoDate(v),
4488
+ isSystemId: (v) => _11.isString(v) && _isSystemId(v),
4489
+ isUrl: (v) => _11.isString(v) && _isUrl(v)
4608
4490
  };
4609
4491
  if (!input2) {
4610
4492
  return [];
@@ -4616,7 +4498,7 @@ function _getUnlocalizableKeys(input2) {
4616
4498
  }
4617
4499
  }
4618
4500
  return false;
4619
- }).map(([key, _36]) => key);
4501
+ }).map(([key, _37]) => key);
4620
4502
  }
4621
4503
 
4622
4504
  // src/cli/loaders/formatters/prettier.ts
@@ -4783,7 +4665,7 @@ function createFormatterLoader(formatterType, parser, bucketPathPattern) {
4783
4665
  }
4784
4666
 
4785
4667
  // src/cli/loaders/po/index.ts
4786
- import _11 from "lodash";
4668
+ import _12 from "lodash";
4787
4669
  import gettextParser from "gettext-parser";
4788
4670
  function createPoLoader(params = { multiline: false }) {
4789
4671
  return composeLoaders(createPoDataLoader(params), createPoContentLoader());
@@ -4799,7 +4681,7 @@ function createPoDataLoader(params) {
4799
4681
  if (Object.keys(sectionPo.translations).length === 0) {
4800
4682
  continue;
4801
4683
  }
4802
- const contextKey = _11.keys(sectionPo.translations)[0];
4684
+ const contextKey = _12.keys(sectionPo.translations)[0];
4803
4685
  const entries = sectionPo.translations[contextKey];
4804
4686
  Object.entries(entries).forEach(([msgid, entry]) => {
4805
4687
  if (msgid && entry.msgid) {
@@ -4821,13 +4703,13 @@ function createPoDataLoader(params) {
4821
4703
  if (Object.keys(sectionPo.translations).length === 0) {
4822
4704
  return null;
4823
4705
  }
4824
- const contextKey = _11.keys(sectionPo.translations)[0];
4706
+ const contextKey = _12.keys(sectionPo.translations)[0];
4825
4707
  const entries = sectionPo.translations[contextKey];
4826
4708
  const msgid = Object.keys(entries).find((key) => entries[key].msgid);
4827
4709
  if (!msgid) {
4828
4710
  const currentSection = currentSections.find((cs) => {
4829
4711
  const csPo = gettextParser.po.parse(cs);
4830
- const csContextKey = _11.keys(csPo.translations)[0];
4712
+ const csContextKey = _12.keys(csPo.translations)[0];
4831
4713
  const csEntries = csPo.translations[csContextKey];
4832
4714
  const csMsgid = Object.keys(csEntries).find(
4833
4715
  (key) => csEntries[key].msgid
@@ -4840,7 +4722,7 @@ function createPoDataLoader(params) {
4840
4722
  return section;
4841
4723
  }
4842
4724
  if (data[msgid]) {
4843
- const updatedPo = _11.merge({}, sectionPo, {
4725
+ const updatedPo = _12.merge({}, sectionPo, {
4844
4726
  translations: {
4845
4727
  [contextKey]: {
4846
4728
  [msgid]: {
@@ -4864,7 +4746,7 @@ function createPoDataLoader(params) {
4864
4746
  function createPoContentLoader() {
4865
4747
  return createLoader({
4866
4748
  async pull(locale, input2, initCtx, originalLocale) {
4867
- const result = _11.chain(input2).entries().filter(([, entry]) => !!entry.msgid).map(([, entry]) => {
4749
+ const result = _12.chain(input2).entries().filter(([, entry]) => !!entry.msgid).map(([, entry]) => {
4868
4750
  const singularFallback = locale === originalLocale ? entry.msgid : null;
4869
4751
  const pluralFallback = locale === originalLocale ? entry.msgid_plural || entry.msgid : null;
4870
4752
  const hasPlural = entry.msgstr.length > 1;
@@ -4879,7 +4761,7 @@ function createPoContentLoader() {
4879
4761
  return result;
4880
4762
  },
4881
4763
  async push(locale, data, originalInput) {
4882
- const result = _11.chain(originalInput).entries().map(([, entry]) => [
4764
+ const result = _12.chain(originalInput).entries().map(([, entry]) => [
4883
4765
  entry.msgid,
4884
4766
  {
4885
4767
  ...entry,
@@ -5422,41 +5304,41 @@ var datoSettingsSchema = Z2.object({
5422
5304
  });
5423
5305
 
5424
5306
  // src/cli/loaders/dato/filter.ts
5425
- import _12 from "lodash";
5307
+ import _13 from "lodash";
5426
5308
  function createDatoFilterLoader() {
5427
5309
  return createLoader({
5428
5310
  async pull(locale, input2) {
5429
5311
  const result = {};
5430
- for (const [modelId, modelInfo] of _12.entries(input2)) {
5312
+ for (const [modelId, modelInfo] of _13.entries(input2)) {
5431
5313
  result[modelId] = {};
5432
5314
  for (const record of modelInfo.records) {
5433
- result[modelId][record.id] = _12.chain(modelInfo.fields).mapKeys((field) => field.api_key).mapValues((field) => _12.get(record, [field.api_key, locale])).value();
5315
+ result[modelId][record.id] = _13.chain(modelInfo.fields).mapKeys((field) => field.api_key).mapValues((field) => _13.get(record, [field.api_key, locale])).value();
5434
5316
  }
5435
5317
  }
5436
5318
  return result;
5437
5319
  },
5438
5320
  async push(locale, data, originalInput, originalLocale) {
5439
- const result = _12.cloneDeep(originalInput || {});
5440
- for (const [modelId, modelInfo] of _12.entries(result)) {
5321
+ const result = _13.cloneDeep(originalInput || {});
5322
+ for (const [modelId, modelInfo] of _13.entries(result)) {
5441
5323
  for (const record of modelInfo.records) {
5442
- for (const [fieldId, fieldValue] of _12.entries(record)) {
5324
+ for (const [fieldId, fieldValue] of _13.entries(record)) {
5443
5325
  const fieldInfo = modelInfo.fields.find(
5444
5326
  (field) => field.api_key === fieldId
5445
5327
  );
5446
5328
  if (fieldInfo) {
5447
- const sourceFieldValue = _12.get(fieldValue, [originalLocale]);
5448
- const targetFieldValue = _12.get(data, [
5329
+ const sourceFieldValue = _13.get(fieldValue, [originalLocale]);
5330
+ const targetFieldValue = _13.get(data, [
5449
5331
  modelId,
5450
5332
  record.id,
5451
5333
  fieldId
5452
5334
  ]);
5453
5335
  if (targetFieldValue) {
5454
- _12.set(record, [fieldId, locale], targetFieldValue);
5336
+ _13.set(record, [fieldId, locale], targetFieldValue);
5455
5337
  } else {
5456
- _12.set(record, [fieldId, locale], sourceFieldValue);
5338
+ _13.set(record, [fieldId, locale], sourceFieldValue);
5457
5339
  }
5458
- _12.chain(fieldValue).keys().reject((loc) => loc === locale || loc === originalLocale).filter((loc) => _12.isEmpty(_12.get(fieldValue, [loc]))).forEach(
5459
- (loc) => _12.set(record, [fieldId, loc], sourceFieldValue)
5340
+ _13.chain(fieldValue).keys().reject((loc) => loc === locale || loc === originalLocale).filter((loc) => _13.isEmpty(_13.get(fieldValue, [loc]))).forEach(
5341
+ (loc) => _13.set(record, [fieldId, loc], sourceFieldValue)
5460
5342
  ).value();
5461
5343
  }
5462
5344
  }
@@ -5468,10 +5350,10 @@ function createDatoFilterLoader() {
5468
5350
  }
5469
5351
 
5470
5352
  // src/cli/loaders/dato/api.ts
5471
- import _14 from "lodash";
5353
+ import _15 from "lodash";
5472
5354
 
5473
5355
  // src/cli/loaders/dato/_utils.ts
5474
- import _13 from "lodash";
5356
+ import _14 from "lodash";
5475
5357
  import { buildClient } from "@datocms/cma-client-node";
5476
5358
  function createDatoClient(params) {
5477
5359
  if (!params.apiKey) {
@@ -5664,7 +5546,7 @@ function createDatoApiLoader(config, onConfigUpdate) {
5664
5546
  const result = {
5665
5547
  models: {}
5666
5548
  };
5667
- const updatedConfig = _14.cloneDeep(config);
5549
+ const updatedConfig = _15.cloneDeep(config);
5668
5550
  console.log(`Initializing DatoCMS loader...`);
5669
5551
  const project = await dato.findProject();
5670
5552
  const modelChoices = await getModelChoices(dato, config);
@@ -5682,7 +5564,7 @@ function createDatoApiLoader(config, onConfigUpdate) {
5682
5564
  delete updatedConfig.models[modelId];
5683
5565
  }
5684
5566
  }
5685
- for (const modelId of _14.keys(updatedConfig.models)) {
5567
+ for (const modelId of _15.keys(updatedConfig.models)) {
5686
5568
  const { modelName, fields } = await getModelFields(dato, modelId);
5687
5569
  if (fields.length > 0) {
5688
5570
  result.models[modelId] = { fields: [], records: [] };
@@ -5700,7 +5582,7 @@ function createDatoApiLoader(config, onConfigUpdate) {
5700
5582
  );
5701
5583
  if (isLocalized) {
5702
5584
  result.models[modelId].fields.push(fieldInfo);
5703
- updatedConfig.models[modelId].fields = _14.uniq([
5585
+ updatedConfig.models[modelId].fields = _15.uniq([
5704
5586
  ...updatedConfig.models[modelId].fields || [],
5705
5587
  fieldInfo.api_key
5706
5588
  ]);
@@ -5728,7 +5610,7 @@ function createDatoApiLoader(config, onConfigUpdate) {
5728
5610
  },
5729
5611
  async pull(locale, input2, initCtx) {
5730
5612
  const result = {};
5731
- for (const modelId of _14.keys(initCtx?.models || {})) {
5613
+ for (const modelId of _15.keys(initCtx?.models || {})) {
5732
5614
  let records = initCtx?.models[modelId].records || [];
5733
5615
  const recordIds = records.map((record) => record.id);
5734
5616
  records = await dato.findRecords(recordIds);
@@ -5743,7 +5625,7 @@ function createDatoApiLoader(config, onConfigUpdate) {
5743
5625
  return result;
5744
5626
  },
5745
5627
  async push(locale, data, originalInput) {
5746
- for (const modelId of _14.keys(data)) {
5628
+ for (const modelId of _15.keys(data)) {
5747
5629
  for (let i = 0; i < data[modelId].records.length; i++) {
5748
5630
  const record = data[modelId].records[i];
5749
5631
  console.log(
@@ -5759,7 +5641,7 @@ async function getModelFields(dato, modelId) {
5759
5641
  const modelInfo = await dato.findModel(modelId);
5760
5642
  return {
5761
5643
  modelName: modelInfo.name,
5762
- fields: _14.filter(modelInfo.fields, (field) => field.type === "field")
5644
+ fields: _15.filter(modelInfo.fields, (field) => field.type === "field")
5763
5645
  };
5764
5646
  }
5765
5647
  async function getFieldDetails(dato, fields) {
@@ -5839,17 +5721,17 @@ async function promptModelSelection(choices) {
5839
5721
  }
5840
5722
 
5841
5723
  // src/cli/loaders/dato/extract.ts
5842
- import _15 from "lodash";
5724
+ import _16 from "lodash";
5843
5725
  function createDatoExtractLoader() {
5844
5726
  return createLoader({
5845
5727
  async pull(locale, input2) {
5846
5728
  const result = {};
5847
- for (const [modelId, modelInfo] of _15.entries(input2)) {
5848
- for (const [recordId, record] of _15.entries(modelInfo)) {
5849
- for (const [fieldName, fieldValue] of _15.entries(record)) {
5729
+ for (const [modelId, modelInfo] of _16.entries(input2)) {
5730
+ for (const [recordId, record] of _16.entries(modelInfo)) {
5731
+ for (const [fieldName, fieldValue] of _16.entries(record)) {
5850
5732
  const parsedValue = createParsedDatoValue(fieldValue);
5851
5733
  if (parsedValue) {
5852
- _15.set(result, [modelId, `_${recordId}`, fieldName], parsedValue);
5734
+ _16.set(result, [modelId, `_${recordId}`, fieldName], parsedValue);
5853
5735
  }
5854
5736
  }
5855
5737
  }
@@ -5857,12 +5739,12 @@ function createDatoExtractLoader() {
5857
5739
  return result;
5858
5740
  },
5859
5741
  async push(locale, data, originalInput) {
5860
- const result = _15.cloneDeep(originalInput || {});
5861
- for (const [modelId, modelInfo] of _15.entries(data)) {
5862
- for (const [virtualRecordId, record] of _15.entries(modelInfo)) {
5863
- for (const [fieldName, fieldValue] of _15.entries(record)) {
5742
+ const result = _16.cloneDeep(originalInput || {});
5743
+ for (const [modelId, modelInfo] of _16.entries(data)) {
5744
+ for (const [virtualRecordId, record] of _16.entries(modelInfo)) {
5745
+ for (const [fieldName, fieldValue] of _16.entries(record)) {
5864
5746
  const [, recordId] = virtualRecordId.split("_");
5865
- const originalFieldValue = _15.get(originalInput, [
5747
+ const originalFieldValue = _16.get(originalInput, [
5866
5748
  modelId,
5867
5749
  recordId,
5868
5750
  fieldName
@@ -5872,7 +5754,7 @@ function createDatoExtractLoader() {
5872
5754
  originalFieldValue,
5873
5755
  true
5874
5756
  );
5875
- _15.set(
5757
+ _16.set(
5876
5758
  result,
5877
5759
  [modelId, recordId, fieldName],
5878
5760
  rawValue || originalFieldValue
@@ -5885,25 +5767,25 @@ function createDatoExtractLoader() {
5885
5767
  });
5886
5768
  }
5887
5769
  function detectDatoFieldType(rawDatoValue) {
5888
- if (_15.has(rawDatoValue, "document") && _15.get(rawDatoValue, "schema") === "dast") {
5770
+ if (_16.has(rawDatoValue, "document") && _16.get(rawDatoValue, "schema") === "dast") {
5889
5771
  return "structured_text";
5890
- } else if (_15.has(rawDatoValue, "no_index") || _15.has(rawDatoValue, "twitter_card")) {
5772
+ } else if (_16.has(rawDatoValue, "no_index") || _16.has(rawDatoValue, "twitter_card")) {
5891
5773
  return "seo";
5892
- } else if (_15.get(rawDatoValue, "type") === "item") {
5774
+ } else if (_16.get(rawDatoValue, "type") === "item") {
5893
5775
  return "single_block";
5894
- } else if (_15.isArray(rawDatoValue) && _15.every(rawDatoValue, (item) => _15.get(item, "type") === "item")) {
5776
+ } else if (_16.isArray(rawDatoValue) && _16.every(rawDatoValue, (item) => _16.get(item, "type") === "item")) {
5895
5777
  return "rich_text";
5896
5778
  } else if (_isFile(rawDatoValue)) {
5897
5779
  return "file";
5898
- } else if (_15.isArray(rawDatoValue) && _15.every(rawDatoValue, (item) => _isFile(item))) {
5780
+ } else if (_16.isArray(rawDatoValue) && _16.every(rawDatoValue, (item) => _isFile(item))) {
5899
5781
  return "gallery";
5900
5782
  } else if (_isJson(rawDatoValue)) {
5901
5783
  return "json";
5902
- } else if (_15.isString(rawDatoValue)) {
5784
+ } else if (_16.isString(rawDatoValue)) {
5903
5785
  return "string";
5904
5786
  } else if (_isVideo(rawDatoValue)) {
5905
5787
  return "video";
5906
- } else if (_15.isArray(rawDatoValue) && _15.every(rawDatoValue, (item) => _15.isString(item))) {
5788
+ } else if (_16.isArray(rawDatoValue) && _16.every(rawDatoValue, (item) => _16.isString(item))) {
5907
5789
  return "ref_list";
5908
5790
  } else {
5909
5791
  return null;
@@ -5973,9 +5855,9 @@ function serializeStructuredText(rawStructuredText) {
5973
5855
  acc
5974
5856
  );
5975
5857
  }
5976
- if (!_15.isNil(node.value)) {
5858
+ if (!_16.isNil(node.value)) {
5977
5859
  acc[[...path19, "value"].join(".")] = node.value;
5978
- } else if (_15.get(node, "type") === "block") {
5860
+ } else if (_16.get(node, "type") === "block") {
5979
5861
  acc[[...path19, "item"].join(".")] = serializeBlock(node.item);
5980
5862
  }
5981
5863
  if (node.children) {
@@ -5991,48 +5873,48 @@ function serializeStructuredText(rawStructuredText) {
5991
5873
  }
5992
5874
  }
5993
5875
  function serializeSeo(rawSeo) {
5994
- return _15.chain(rawSeo).pick(["title", "description"]).value();
5876
+ return _16.chain(rawSeo).pick(["title", "description"]).value();
5995
5877
  }
5996
5878
  function serializeBlock(rawBlock) {
5997
- if (_15.get(rawBlock, "type") === "item" && _15.has(rawBlock, "id")) {
5879
+ if (_16.get(rawBlock, "type") === "item" && _16.has(rawBlock, "id")) {
5998
5880
  return serializeBlock(rawBlock.attributes);
5999
5881
  }
6000
5882
  const result = {};
6001
- for (const [attributeName, attributeValue] of _15.entries(rawBlock)) {
5883
+ for (const [attributeName, attributeValue] of _16.entries(rawBlock)) {
6002
5884
  result[attributeName] = createParsedDatoValue(attributeValue);
6003
5885
  }
6004
5886
  return result;
6005
5887
  }
6006
5888
  function serializeBlockList(rawBlockList) {
6007
- return _15.chain(rawBlockList).map((block) => serializeBlock(block)).value();
5889
+ return _16.chain(rawBlockList).map((block) => serializeBlock(block)).value();
6008
5890
  }
6009
5891
  function serializeVideo(rawVideo) {
6010
- return _15.chain(rawVideo).pick(["title"]).value();
5892
+ return _16.chain(rawVideo).pick(["title"]).value();
6011
5893
  }
6012
5894
  function serializeFile(rawFile) {
6013
- return _15.chain(rawFile).pick(["alt", "title"]).value();
5895
+ return _16.chain(rawFile).pick(["alt", "title"]).value();
6014
5896
  }
6015
5897
  function serializeGallery(rawGallery) {
6016
- return _15.chain(rawGallery).map((item) => serializeFile(item)).value();
5898
+ return _16.chain(rawGallery).map((item) => serializeFile(item)).value();
6017
5899
  }
6018
5900
  function deserializeFile(parsedFile, originalRawFile) {
6019
- return _15.chain(parsedFile).defaults(originalRawFile).value();
5901
+ return _16.chain(parsedFile).defaults(originalRawFile).value();
6020
5902
  }
6021
5903
  function deserializeGallery(parsedGallery, originalRawGallery) {
6022
- return _15.chain(parsedGallery).map((item, i) => deserializeFile(item, originalRawGallery[i])).value();
5904
+ return _16.chain(parsedGallery).map((item, i) => deserializeFile(item, originalRawGallery[i])).value();
6023
5905
  }
6024
5906
  function deserializeVideo(parsedVideo, originalRawVideo) {
6025
- return _15.chain(parsedVideo).defaults(originalRawVideo).value();
5907
+ return _16.chain(parsedVideo).defaults(originalRawVideo).value();
6026
5908
  }
6027
5909
  function deserializeBlock(payload, rawNode, isClean = false) {
6028
- const result = _15.cloneDeep(rawNode);
6029
- for (const [attributeName, attributeValue] of _15.entries(rawNode.attributes)) {
5910
+ const result = _16.cloneDeep(rawNode);
5911
+ for (const [attributeName, attributeValue] of _16.entries(rawNode.attributes)) {
6030
5912
  const rawValue = createRawDatoValue(
6031
5913
  payload[attributeName],
6032
5914
  attributeValue,
6033
5915
  isClean
6034
5916
  );
6035
- _15.set(result, ["attributes", attributeName], rawValue);
5917
+ _16.set(result, ["attributes", attributeName], rawValue);
6036
5918
  }
6037
5919
  if (isClean) {
6038
5920
  delete result["id"];
@@ -6040,40 +5922,40 @@ function deserializeBlock(payload, rawNode, isClean = false) {
6040
5922
  return result;
6041
5923
  }
6042
5924
  function deserializeSeo(parsedSeo, originalRawSeo) {
6043
- return _15.chain(parsedSeo).pick(["title", "description"]).defaults(originalRawSeo).value();
5925
+ return _16.chain(parsedSeo).pick(["title", "description"]).defaults(originalRawSeo).value();
6044
5926
  }
6045
5927
  function deserializeBlockList(parsedBlockList, originalRawBlockList, isClean = false) {
6046
- return _15.chain(parsedBlockList).map(
5928
+ return _16.chain(parsedBlockList).map(
6047
5929
  (block, i) => deserializeBlock(block, originalRawBlockList[i], isClean)
6048
5930
  ).value();
6049
5931
  }
6050
5932
  function deserializeStructuredText(parsedStructuredText, originalRawStructuredText) {
6051
- const result = _15.cloneDeep(originalRawStructuredText);
6052
- for (const [path19, value] of _15.entries(parsedStructuredText)) {
6053
- const realPath = _15.chain(path19.split(".")).flatMap((s) => !_15.isNaN(_15.toNumber(s)) ? ["children", s] : s).value();
5933
+ const result = _16.cloneDeep(originalRawStructuredText);
5934
+ for (const [path19, value] of _16.entries(parsedStructuredText)) {
5935
+ const realPath = _16.chain(path19.split(".")).flatMap((s) => !_16.isNaN(_16.toNumber(s)) ? ["children", s] : s).value();
6054
5936
  const deserializedValue = createRawDatoValue(
6055
5937
  value,
6056
- _15.get(originalRawStructuredText, realPath),
5938
+ _16.get(originalRawStructuredText, realPath),
6057
5939
  true
6058
5940
  );
6059
- _15.set(result, realPath, deserializedValue);
5941
+ _16.set(result, realPath, deserializedValue);
6060
5942
  }
6061
5943
  return result;
6062
5944
  }
6063
5945
  function _isJson(rawDatoValue) {
6064
5946
  try {
6065
- return _15.isString(rawDatoValue) && rawDatoValue.startsWith("{") && rawDatoValue.endsWith("}") && !!JSON.parse(rawDatoValue);
5947
+ return _16.isString(rawDatoValue) && rawDatoValue.startsWith("{") && rawDatoValue.endsWith("}") && !!JSON.parse(rawDatoValue);
6066
5948
  } catch (e) {
6067
5949
  return false;
6068
5950
  }
6069
5951
  }
6070
5952
  function _isFile(rawDatoValue) {
6071
- return _15.isObject(rawDatoValue) && ["alt", "title", "custom_data", "focal_point", "upload_id"].every(
6072
- (key) => _15.has(rawDatoValue, key)
5953
+ return _16.isObject(rawDatoValue) && ["alt", "title", "custom_data", "focal_point", "upload_id"].every(
5954
+ (key) => _16.has(rawDatoValue, key)
6073
5955
  );
6074
5956
  }
6075
5957
  function _isVideo(rawDatoValue) {
6076
- return _15.isObject(rawDatoValue) && [
5958
+ return _16.isObject(rawDatoValue) && [
6077
5959
  "url",
6078
5960
  "title",
6079
5961
  "width",
@@ -6081,7 +5963,7 @@ function _isVideo(rawDatoValue) {
6081
5963
  "provider",
6082
5964
  "provider_uid",
6083
5965
  "thumbnail_url"
6084
- ].every((key) => _15.has(rawDatoValue, key));
5966
+ ].every((key) => _16.has(rawDatoValue, key));
6085
5967
  }
6086
5968
 
6087
5969
  // src/cli/loaders/dato/index.ts
@@ -6151,7 +6033,7 @@ function createVttLoader() {
6151
6033
  }
6152
6034
 
6153
6035
  // src/cli/loaders/variable/index.ts
6154
- import _16 from "lodash";
6036
+ import _17 from "lodash";
6155
6037
  function createVariableLoader(params) {
6156
6038
  return composeLoaders(variableExtractLoader(params), variableContentLoader());
6157
6039
  }
@@ -6160,18 +6042,9 @@ function variableExtractLoader(params) {
6160
6042
  return createLoader({
6161
6043
  pull: async (locale, input2, initXtx, originalLocale, originalInput) => {
6162
6044
  const result = {};
6163
- const inputValues = _16.omitBy(input2, _16.isEmpty);
6045
+ const inputValues = _17.omitBy(input2, _17.isEmpty);
6164
6046
  for (const [key, value] of Object.entries(inputValues)) {
6165
6047
  const originalValue = originalInput[key];
6166
- if (isICUPluralObject(originalValue)) {
6167
- const icuValue = isICUPluralObject(value) ? { icu: value.icu } : value;
6168
- result[key] = {
6169
- value: icuValue,
6170
- variables: []
6171
- // Metadata stored separately, not in variables
6172
- };
6173
- continue;
6174
- }
6175
6048
  const matches = originalValue.match(specifierPattern) || [];
6176
6049
  result[key] = result[key] || {
6177
6050
  value,
@@ -6191,19 +6064,14 @@ function variableExtractLoader(params) {
6191
6064
  const result = {};
6192
6065
  for (const [key, valueObj] of Object.entries(data)) {
6193
6066
  result[key] = valueObj.value;
6194
- const resultValue = result[key];
6195
- if (isICUPluralObject(resultValue)) {
6196
- const originalValue = originalInput?.[key];
6197
- if (isICUPluralObject(originalValue) && originalValue._meta) {
6198
- resultValue._meta = originalValue._meta;
6199
- resultValue[Symbol.for("@lingo.dev/icu-plural-object")] = true;
6200
- }
6201
- }
6202
6067
  for (let i = 0; i < valueObj.variables.length; i++) {
6203
6068
  const variable = valueObj.variables[i];
6204
6069
  const currentValue = result[key];
6205
6070
  if (typeof currentValue === "string") {
6206
- const newValue = currentValue?.replace(`{variable:${i}}`, variable);
6071
+ const newValue = currentValue?.replaceAll(
6072
+ `{variable:${i}}`,
6073
+ variable
6074
+ );
6207
6075
  result[key] = newValue;
6208
6076
  }
6209
6077
  }
@@ -6215,11 +6083,11 @@ function variableExtractLoader(params) {
6215
6083
  function variableContentLoader() {
6216
6084
  return createLoader({
6217
6085
  pull: async (locale, input2) => {
6218
- const result = _16.mapValues(input2, (payload) => payload.value);
6086
+ const result = _17.mapValues(input2, (payload) => payload.value);
6219
6087
  return result;
6220
6088
  },
6221
6089
  push: async (locale, data, originalInput, defaultLocale, pullInput) => {
6222
- const result = _16.cloneDeep(
6090
+ const result = _17.cloneDeep(
6223
6091
  originalInput || {}
6224
6092
  );
6225
6093
  for (const [key, originalValueObj] of Object.entries(result)) {
@@ -6244,20 +6112,20 @@ function getFormatSpecifierPattern(type) {
6244
6112
  }
6245
6113
 
6246
6114
  // src/cli/loaders/sync.ts
6247
- import _17 from "lodash";
6115
+ import _18 from "lodash";
6248
6116
  function createSyncLoader() {
6249
6117
  return createLoader({
6250
6118
  async pull(locale, input2, initCtx, originalLocale, originalInput) {
6251
6119
  if (!originalInput) {
6252
6120
  return input2;
6253
6121
  }
6254
- return _17.chain(originalInput).mapValues((value, key) => input2[key]).value();
6122
+ return _18.chain(originalInput).mapValues((value, key) => input2[key]).value();
6255
6123
  },
6256
6124
  async push(locale, data, originalInput) {
6257
6125
  if (!originalInput) {
6258
6126
  return data;
6259
6127
  }
6260
- return _17.chain(originalInput || {}).mapValues((value, key) => data[key]).value();
6128
+ return _18.chain(originalInput || {}).mapValues((value, key) => data[key]).value();
6261
6129
  }
6262
6130
  });
6263
6131
  }
@@ -6439,7 +6307,7 @@ function parseVueFile(input2) {
6439
6307
 
6440
6308
  // src/cli/loaders/typescript/index.ts
6441
6309
  import { parse as parse3 } from "@babel/parser";
6442
- import _18 from "lodash";
6310
+ import _19 from "lodash";
6443
6311
  import babelTraverseModule from "@babel/traverse";
6444
6312
  import * as t from "@babel/types";
6445
6313
  import babelGenerateModule from "@babel/generator";
@@ -6475,7 +6343,7 @@ function createTypescriptLoader() {
6475
6343
  },
6476
6344
  push: async (locale, data, originalInput, defaultLocale, pullInput, pullOutput) => {
6477
6345
  const ast = parseTypeScript(originalInput || "");
6478
- const finalData = _18.merge({}, pullOutput, data);
6346
+ const finalData = _19.merge({}, pullOutput, data);
6479
6347
  updateStringsInDefaultExport(ast, finalData);
6480
6348
  const { code } = generate(ast, {
6481
6349
  jsescOption: {
@@ -6676,7 +6544,7 @@ function getPropertyKey(prop) {
6676
6544
  }
6677
6545
 
6678
6546
  // src/cli/loaders/inject-locale.ts
6679
- import _19 from "lodash";
6547
+ import _20 from "lodash";
6680
6548
 
6681
6549
  // ../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
6682
6550
  var balanced = (a, b, str) => {
@@ -7367,7 +7235,7 @@ var AST = class _AST {
7367
7235
  if (!this.type) {
7368
7236
  const noEmpty = this.isStart() && this.isEnd();
7369
7237
  const src = this.#parts.map((p) => {
7370
- const [re, _36, hasMagic, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
7238
+ const [re, _37, hasMagic, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
7371
7239
  this.#hasMagic = this.#hasMagic || hasMagic;
7372
7240
  this.#uflag = this.#uflag || uflag;
7373
7241
  return re;
@@ -7440,7 +7308,7 @@ var AST = class _AST {
7440
7308
  if (typeof p === "string") {
7441
7309
  throw new Error("string type in extglob ast??");
7442
7310
  }
7443
- const [re, _36, _hasMagic, uflag] = p.toRegExpSource(dot);
7311
+ const [re, _37, _hasMagic, uflag] = p.toRegExpSource(dot);
7444
7312
  this.#uflag = this.#uflag || uflag;
7445
7313
  return re;
7446
7314
  }).filter((p) => !(this.isStart() && this.isEnd()) || !!p).join("|");
@@ -7685,7 +7553,7 @@ var Minimatch = class {
7685
7553
  }
7686
7554
  return false;
7687
7555
  }
7688
- debug(..._36) {
7556
+ debug(..._37) {
7689
7557
  }
7690
7558
  make() {
7691
7559
  const pattern = this.pattern;
@@ -7707,7 +7575,7 @@ var Minimatch = class {
7707
7575
  const rawGlobParts = this.globSet.map((s) => this.slashSplit(s));
7708
7576
  this.globParts = this.preprocess(rawGlobParts);
7709
7577
  this.debug(this.pattern, this.globParts);
7710
- let set = this.globParts.map((s, _36, __) => {
7578
+ let set = this.globParts.map((s, _37, __) => {
7711
7579
  if (this.isWindows && this.windowsNoMagicRoot) {
7712
7580
  const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]);
7713
7581
  const isDrive = /^[a-z]:/i.test(s[0]);
@@ -8224,7 +8092,7 @@ function createInjectLocaleLoader(injectLocaleKeys) {
8224
8092
  return data;
8225
8093
  }
8226
8094
  const omitKeys = _getKeysWithLocales(data, injectLocaleKeys, locale);
8227
- const result = _19.omit(data, omitKeys);
8095
+ const result = _20.omit(data, omitKeys);
8228
8096
  return result;
8229
8097
  },
8230
8098
  async push(locale, data, originalInput, originalLocale) {
@@ -8237,7 +8105,7 @@ function createInjectLocaleLoader(injectLocaleKeys) {
8237
8105
  originalLocale
8238
8106
  );
8239
8107
  localeKeys.forEach((key) => {
8240
- _19.set(data, key, locale);
8108
+ _20.set(data, key, locale);
8241
8109
  });
8242
8110
  return data;
8243
8111
  }
@@ -8246,7 +8114,7 @@ function createInjectLocaleLoader(injectLocaleKeys) {
8246
8114
  function _getKeysWithLocales(data, injectLocaleKeys, locale) {
8247
8115
  const allKeys = _getAllKeys(data);
8248
8116
  return allKeys.filter((key) => {
8249
- return injectLocaleKeys.some((pattern) => minimatch(key, pattern)) && _19.get(data, key) === locale;
8117
+ return injectLocaleKeys.some((pattern) => minimatch(key, pattern)) && _20.get(data, key) === locale;
8250
8118
  });
8251
8119
  }
8252
8120
  function _getAllKeys(obj, prefix = "") {
@@ -8264,7 +8132,7 @@ function _getAllKeys(obj, prefix = "") {
8264
8132
  }
8265
8133
 
8266
8134
  // src/cli/loaders/locked-keys.ts
8267
- import _20 from "lodash";
8135
+ import _21 from "lodash";
8268
8136
 
8269
8137
  // src/cli/utils/key-matching.ts
8270
8138
  function matchesKeyPattern(key, patterns) {
@@ -8283,14 +8151,14 @@ function formatDisplayValue(value, maxLength = 50) {
8283
8151
  function createLockedKeysLoader(lockedKeys) {
8284
8152
  return createLoader({
8285
8153
  pull: async (locale, data) => {
8286
- return _20.pickBy(
8154
+ return _21.pickBy(
8287
8155
  data,
8288
8156
  (value, key) => !matchesKeyPattern(key, lockedKeys)
8289
8157
  );
8290
8158
  },
8291
8159
  push: async (locale, data, originalInput) => {
8292
- const lockedSubObject = _20.chain(originalInput).pickBy((value, key) => matchesKeyPattern(key, lockedKeys)).value();
8293
- return _20.merge({}, data, lockedSubObject);
8160
+ const lockedSubObject = _21.chain(originalInput).pickBy((value, key) => matchesKeyPattern(key, lockedKeys)).value();
8161
+ return _21.merge({}, data, lockedSubObject);
8294
8162
  }
8295
8163
  });
8296
8164
  }
@@ -8342,7 +8210,7 @@ function md5(input2) {
8342
8210
  }
8343
8211
 
8344
8212
  // src/cli/loaders/mdx2/code-placeholder.ts
8345
- import _21 from "lodash";
8213
+ import _22 from "lodash";
8346
8214
  var fenceRegex = /([ \t]*)(^>\s*)?```([\s\S]*?)```/gm;
8347
8215
  var inlineCodeRegex = /(?<!`)`([^`\r\n]+?)`(?!`)/g;
8348
8216
  var imageRegex = /([ \t]*)(^>\s*)?!\[[^\]]*?\]\(([^()]*(\([^()]*\)[^()]*)*)\)/gm;
@@ -8365,7 +8233,7 @@ ${match2}
8365
8233
  found = true;
8366
8234
  }
8367
8235
  } while (found);
8368
- content = _21.chain(content).split("\n\n").map((section) => _21.trim(section, "\n")).filter(Boolean).join("\n\n").value();
8236
+ content = _22.chain(content).split("\n\n").map((section) => _22.trim(section, "\n")).filter(Boolean).join("\n\n").value();
8369
8237
  return content;
8370
8238
  }
8371
8239
  function ensureTrailingFenceNewline(_content) {
@@ -8387,7 +8255,7 @@ ${match2}
8387
8255
  found = true;
8388
8256
  }
8389
8257
  } while (found);
8390
- content = _21.chain(content).split("\n\n").map((section) => _21.trim(section, "\n")).filter(Boolean).join("\n\n").value();
8258
+ content = _22.chain(content).split("\n\n").map((section) => _22.trim(section, "\n")).filter(Boolean).join("\n\n").value();
8391
8259
  return content;
8392
8260
  }
8393
8261
  function extractCodePlaceholders(content) {
@@ -8429,7 +8297,7 @@ function createMdxCodePlaceholderLoader() {
8429
8297
  async push(locale, data, originalInput, originalLocale, pullInput) {
8430
8298
  const sourceInfo = extractCodePlaceholders(originalInput ?? "");
8431
8299
  const currentInfo = extractCodePlaceholders(pullInput ?? "");
8432
- const codePlaceholders = _21.merge(
8300
+ const codePlaceholders = _22.merge(
8433
8301
  sourceInfo.codePlaceholders,
8434
8302
  currentInfo.codePlaceholders,
8435
8303
  globalPlaceholderRegistry
@@ -8437,7 +8305,7 @@ function createMdxCodePlaceholderLoader() {
8437
8305
  );
8438
8306
  let result = data;
8439
8307
  for (const [placeholder, original] of Object.entries(codePlaceholders)) {
8440
- const replacement = original.startsWith(">") ? _21.trimStart(original, "> ") : original;
8308
+ const replacement = original.startsWith(">") ? _22.trimStart(original, "> ") : original;
8441
8309
  result = result.replaceAll(placeholder, () => replacement);
8442
8310
  }
8443
8311
  return result;
@@ -8465,11 +8333,11 @@ function createLocalizableMdxDocumentLoader() {
8465
8333
  }
8466
8334
 
8467
8335
  // src/cli/loaders/mdx2/sections-split-2.ts
8468
- import _22 from "lodash";
8336
+ import _23 from "lodash";
8469
8337
  function createMdxSectionsSplit2Loader() {
8470
8338
  return createLoader({
8471
8339
  async pull(locale, input2) {
8472
- const sections = _22.chain(input2.content).split("\n\n").filter(Boolean).map((section, index) => [index, section]).fromPairs().value();
8340
+ const sections = _23.chain(input2.content).split("\n\n").filter(Boolean).map((section, index) => [index, section]).fromPairs().value();
8473
8341
  const result = {
8474
8342
  frontmatter: input2.frontmatter,
8475
8343
  sections
@@ -8477,7 +8345,7 @@ function createMdxSectionsSplit2Loader() {
8477
8345
  return result;
8478
8346
  },
8479
8347
  async push(locale, data, originalInput, _originalLocale, pullInput) {
8480
- const content = _22.chain(data.sections).values().join("\n\n").value();
8348
+ const content = _23.chain(data.sections).values().join("\n\n").value();
8481
8349
  const result = {
8482
8350
  frontmatter: data.frontmatter,
8483
8351
  codePlaceholders: pullInput?.codePlaceholders || {},
@@ -8543,18 +8411,18 @@ function createLockedPatternsLoader(defaultPatterns) {
8543
8411
  }
8544
8412
 
8545
8413
  // src/cli/loaders/ignored-keys.ts
8546
- import _23 from "lodash";
8414
+ import _24 from "lodash";
8547
8415
  function createIgnoredKeysLoader(ignoredKeys) {
8548
8416
  return createLoader({
8549
8417
  pull: async (locale, data) => {
8550
- const result = _23.omitBy(
8418
+ const result = _24.omitBy(
8551
8419
  data,
8552
8420
  (value, key) => matchesKeyPattern(key, ignoredKeys)
8553
8421
  );
8554
8422
  return result;
8555
8423
  },
8556
8424
  push: async (locale, data, originalInput, originalLocale, pullInput) => {
8557
- const result = _23.omitBy(
8425
+ const result = _24.omitBy(
8558
8426
  data,
8559
8427
  (value, key) => matchesKeyPattern(key, ignoredKeys)
8560
8428
  );
@@ -8695,7 +8563,7 @@ function createEjsLoader() {
8695
8563
  }
8696
8564
 
8697
8565
  // src/cli/loaders/ensure-key-order.ts
8698
- import _24 from "lodash";
8566
+ import _25 from "lodash";
8699
8567
  function createEnsureKeyOrderLoader() {
8700
8568
  return createLoader({
8701
8569
  pull: async (_locale, input2) => {
@@ -8710,10 +8578,10 @@ function createEnsureKeyOrderLoader() {
8710
8578
  });
8711
8579
  }
8712
8580
  function reorderKeys(data, originalInput) {
8713
- if (_24.isArray(originalInput) && _24.isArray(data)) {
8581
+ if (_25.isArray(originalInput) && _25.isArray(data)) {
8714
8582
  return data.map((item, idx) => reorderKeys(item, originalInput[idx] ?? {}));
8715
8583
  }
8716
- if (!_24.isObject(data) || _24.isArray(data) || _24.isDate(data)) {
8584
+ if (!_25.isObject(data) || _25.isArray(data) || _25.isDate(data)) {
8717
8585
  return data;
8718
8586
  }
8719
8587
  const orderedData = {};
@@ -8745,13 +8613,13 @@ function createTxtLoader() {
8745
8613
  const sortedEntries = Object.entries(payload).sort(
8746
8614
  ([a], [b]) => parseInt(a) - parseInt(b)
8747
8615
  );
8748
- return sortedEntries.map(([_36, value]) => value).join("\n");
8616
+ return sortedEntries.map(([_37, value]) => value).join("\n");
8749
8617
  }
8750
8618
  });
8751
8619
  }
8752
8620
 
8753
8621
  // src/cli/loaders/json-dictionary.ts
8754
- import _25 from "lodash";
8622
+ import _26 from "lodash";
8755
8623
  var TOP_LEVEL_KEY = "--content--";
8756
8624
  function createJsonDictionaryLoader() {
8757
8625
  return createLoader({
@@ -8766,7 +8634,7 @@ function createJsonDictionaryLoader() {
8766
8634
  if (!originalInput) {
8767
8635
  throw new Error("Error while parsing json-dictionary bucket");
8768
8636
  }
8769
- const input2 = _25.cloneDeep(originalInput);
8637
+ const input2 = _26.cloneDeep(originalInput);
8770
8638
  if (Object.keys(data).length === 1 && Object.keys(data)[0] === TOP_LEVEL_KEY) {
8771
8639
  setNestedLocale(
8772
8640
  { [TOP_LEVEL_KEY]: input2 },
@@ -9036,9 +8904,8 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
9036
8904
  createPlutilJsonTextLoader(),
9037
8905
  createLockedPatternsLoader(lockedPatterns),
9038
8906
  createJsonLoader(),
9039
- createXcodeXcstringsLoader(options.defaultLocale),
9040
8907
  createXcodeXcstringsV2Loader(options.defaultLocale),
9041
- createFlatLoader({ shouldPreserveObject: isICUPluralObject }),
8908
+ createFlatLoader(),
9042
8909
  createEnsureKeyOrderLoader(),
9043
8910
  createLockedKeysLoader(lockedKeys || []),
9044
8911
  createIgnoredKeysLoader(ignoredKeys || []),
@@ -9347,7 +9214,7 @@ import { Command as Command13 } from "interactive-commander";
9347
9214
  import { Command as Command10 } from "interactive-commander";
9348
9215
  import chalk2 from "chalk";
9349
9216
  import dedent from "dedent";
9350
- import _26 from "lodash";
9217
+ import _27 from "lodash";
9351
9218
  var set_default = new Command10().name("set").description("Set or update a CLI setting in ~/.lingodotdevrc").addHelpText("afterAll", `
9352
9219
  Available keys:
9353
9220
  ${SETTINGS_KEYS.join("\n ")}`).argument(
@@ -9365,8 +9232,8 @@ Available keys:
9365
9232
  return;
9366
9233
  }
9367
9234
  const current = loadSystemSettings();
9368
- const updated = _26.cloneDeep(current);
9369
- _26.set(updated, key, value);
9235
+ const updated = _27.cloneDeep(current);
9236
+ _27.set(updated, key, value);
9370
9237
  try {
9371
9238
  saveSettings(updated);
9372
9239
  console.log(`${chalk2.green("\u2714")} Set ${chalk2.bold(key)}`);
@@ -9386,7 +9253,7 @@ Available keys:
9386
9253
  import { Command as Command11 } from "interactive-commander";
9387
9254
  import chalk3 from "chalk";
9388
9255
  import dedent2 from "dedent";
9389
- import _27 from "lodash";
9256
+ import _28 from "lodash";
9390
9257
  var unset_default = new Command11().name("unset").description("Remove a CLI setting from ~/.lingodotdevrc").addHelpText("afterAll", `
9391
9258
  Available keys:
9392
9259
  ${SETTINGS_KEYS.join("\n ")}`).argument(
@@ -9406,13 +9273,13 @@ Available keys:
9406
9273
  return;
9407
9274
  }
9408
9275
  const settings = loadSystemSettings();
9409
- const currentValue = _27.get(settings, key);
9410
- if (!_27.trim(String(currentValue || ""))) {
9276
+ const currentValue = _28.get(settings, key);
9277
+ if (!_28.trim(String(currentValue || ""))) {
9411
9278
  console.log(`${chalk3.cyan("\u2139")} ${chalk3.bold(key)} is not set.`);
9412
9279
  return;
9413
9280
  } else {
9414
- const updated = _27.cloneDeep(settings);
9415
- _27.unset(updated, key);
9281
+ const updated = _28.cloneDeep(settings);
9282
+ _28.unset(updated, key);
9416
9283
  try {
9417
9284
  saveSettings(updated);
9418
9285
  console.log(
@@ -9434,7 +9301,7 @@ Available keys:
9434
9301
  // src/cli/cmd/config/get.ts
9435
9302
  import { Command as Command12 } from "interactive-commander";
9436
9303
  import chalk4 from "chalk";
9437
- import _28 from "lodash";
9304
+ import _29 from "lodash";
9438
9305
  import dedent3 from "dedent";
9439
9306
  var get_default = new Command12().name("get").description("Display the value of a CLI setting from ~/.lingodotdevrc").addHelpText("afterAll", `
9440
9307
  Available keys:
@@ -9453,7 +9320,7 @@ Available keys:
9453
9320
  return;
9454
9321
  }
9455
9322
  const settings = loadSystemSettings();
9456
- const value = _28.get(settings, key);
9323
+ const value = _29.get(settings, key);
9457
9324
  if (!value) {
9458
9325
  console.log(`${chalk4.cyan("\u2139")} ${chalk4.bold(key)} is not set.`);
9459
9326
  return;
@@ -9478,7 +9345,7 @@ import {
9478
9345
  } from "@lingo.dev/_spec";
9479
9346
  import { Command as Command14 } from "interactive-commander";
9480
9347
  import Z3 from "zod";
9481
- import _31 from "lodash";
9348
+ import _32 from "lodash";
9482
9349
  import Ora9 from "ora";
9483
9350
  import chalk6 from "chalk";
9484
9351
  import { createTwoFilesPatch } from "diff";
@@ -9518,7 +9385,7 @@ function createLingoLocalizer(params) {
9518
9385
 
9519
9386
  // src/cli/processor/basic.ts
9520
9387
  import { generateText } from "ai";
9521
- import _29 from "lodash";
9388
+ import _30 from "lodash";
9522
9389
  function createBasicTranslator(model, systemPrompt, settings = {}) {
9523
9390
  return async (input2, onProgress) => {
9524
9391
  const chunks = extractPayloadChunks(input2.processableData);
@@ -9532,7 +9399,7 @@ function createBasicTranslator(model, systemPrompt, settings = {}) {
9532
9399
  subResults.push(result2);
9533
9400
  onProgress(i / chunks.length * 100, chunk, result2);
9534
9401
  }
9535
- const result = _29.merge({}, ...subResults);
9402
+ const result = _30.merge({}, ...subResults);
9536
9403
  return result;
9537
9404
  };
9538
9405
  async function doJob(input2) {
@@ -9809,7 +9676,7 @@ function trackEvent(distinctId, event, properties) {
9809
9676
  }
9810
9677
 
9811
9678
  // src/cli/utils/delta.ts
9812
- import _30 from "lodash";
9679
+ import _31 from "lodash";
9813
9680
  import z from "zod";
9814
9681
 
9815
9682
  // src/cli/utils/fs.ts
@@ -9858,11 +9725,11 @@ function createDeltaProcessor(fileKey) {
9858
9725
  return checkIfFileExists(lockfilePath);
9859
9726
  },
9860
9727
  async calculateDelta(params) {
9861
- let added = _30.difference(
9728
+ let added = _31.difference(
9862
9729
  Object.keys(params.sourceData),
9863
9730
  Object.keys(params.targetData)
9864
9731
  );
9865
- let removed = _30.difference(
9732
+ let removed = _31.difference(
9866
9733
  Object.keys(params.targetData),
9867
9734
  Object.keys(params.sourceData)
9868
9735
  );
@@ -9924,12 +9791,41 @@ function createDeltaProcessor(fileKey) {
9924
9791
  await this.saveLock(lockfileData);
9925
9792
  },
9926
9793
  async createChecksums(sourceData) {
9927
- const checksums = _30.mapValues(sourceData, (value) => md5(value));
9794
+ const checksums = _31.mapValues(sourceData, (value) => md5(value));
9928
9795
  return checksums;
9929
9796
  }
9930
9797
  };
9931
9798
  }
9932
9799
 
9800
+ // src/cli/loaders/xcode-xcstrings-icu.ts
9801
+ var ICU_TYPE_MARKER = Symbol.for("@lingo.dev/icu-plural-object");
9802
+ function isICUPluralObject(value) {
9803
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
9804
+ return false;
9805
+ }
9806
+ if (ICU_TYPE_MARKER in value) {
9807
+ return true;
9808
+ }
9809
+ if (!("icu" in value) || typeof value.icu !== "string") {
9810
+ return false;
9811
+ }
9812
+ const icuPluralPattern = /^\{[\w]+,\s*plural,\s*.+\}$/;
9813
+ if (!icuPluralPattern.test(value.icu)) {
9814
+ return false;
9815
+ }
9816
+ if (value._meta !== void 0) {
9817
+ if (typeof value._meta !== "object" || !value._meta.variables || typeof value._meta.variables !== "object") {
9818
+ return false;
9819
+ }
9820
+ for (const [varName, varMeta] of Object.entries(value._meta.variables)) {
9821
+ if (!varMeta || typeof varMeta !== "object" || typeof varMeta.format !== "string" || varMeta.role !== "plural" && varMeta.role !== "other") {
9822
+ return false;
9823
+ }
9824
+ }
9825
+ }
9826
+ return true;
9827
+ }
9828
+
9933
9829
  // src/cli/cmd/i18n.ts
9934
9830
  var i18n_default = new Command14().command("i18n").description(
9935
9831
  "DEPRECATED: Run localization pipeline (prefer `run` command instead)"
@@ -10117,7 +10013,7 @@ var i18n_default = new Command14().command("i18n").description(
10117
10013
  const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
10118
10014
  const sourceChecksums = await deltaProcessor.createChecksums(sourceData);
10119
10015
  const savedChecksums = await deltaProcessor.loadChecksums();
10120
- const updatedSourceData = _31.pickBy(
10016
+ const updatedSourceData = _32.pickBy(
10121
10017
  sourceData,
10122
10018
  (value, key) => sourceChecksums[key] !== savedChecksums[key]
10123
10019
  );
@@ -10131,15 +10027,15 @@ var i18n_default = new Command14().command("i18n").description(
10131
10027
  bucketPath.delimiter
10132
10028
  );
10133
10029
  const { unlocalizable: targetUnlocalizable, ...targetData } = await bucketLoader.pull(targetLocale);
10134
- const missingKeys = _31.difference(
10030
+ const missingKeys = _32.difference(
10135
10031
  Object.keys(sourceData),
10136
10032
  Object.keys(targetData)
10137
10033
  );
10138
- const extraKeys = _31.difference(
10034
+ const extraKeys = _32.difference(
10139
10035
  Object.keys(targetData),
10140
10036
  Object.keys(sourceData)
10141
10037
  );
10142
- const unlocalizableDataDiff = !_31.isEqual(
10038
+ const unlocalizableDataDiff = !_32.isEqual(
10143
10039
  sourceUnlocalizable,
10144
10040
  targetUnlocalizable
10145
10041
  );
@@ -10223,13 +10119,13 @@ var i18n_default = new Command14().command("i18n").description(
10223
10119
  targetData,
10224
10120
  checksums: checksums2
10225
10121
  });
10226
- let processableData = _31.chain(sourceData).entries().filter(
10122
+ let processableData = _32.chain(sourceData).entries().filter(
10227
10123
  ([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!flags.force
10228
10124
  ).fromPairs().value();
10229
10125
  if (flags.key) {
10230
- processableData = _31.pickBy(
10126
+ processableData = _32.pickBy(
10231
10127
  processableData,
10232
- (_36, key) => key === flags.key
10128
+ (_37, key) => key === flags.key
10233
10129
  );
10234
10130
  }
10235
10131
  if (flags.verbose) {
@@ -10262,13 +10158,13 @@ var i18n_default = new Command14().command("i18n").description(
10262
10158
  if (flags.verbose) {
10263
10159
  bucketOra.info(JSON.stringify(processedTargetData, null, 2));
10264
10160
  }
10265
- let finalTargetData = _31.merge(
10161
+ let finalTargetData = _32.merge(
10266
10162
  {},
10267
10163
  sourceData,
10268
10164
  targetData,
10269
10165
  processedTargetData
10270
10166
  );
10271
- finalTargetData = _31.chain(finalTargetData).entries().map(([key, value]) => {
10167
+ finalTargetData = _32.chain(finalTargetData).entries().map(([key, value]) => {
10272
10168
  const renaming = delta.renamed.find(
10273
10169
  ([oldKey, newKey]) => oldKey === key
10274
10170
  );
@@ -10292,10 +10188,10 @@ var i18n_default = new Command14().command("i18n").description(
10292
10188
  `Applying changes to ${bucketPath} (${targetLocale})`
10293
10189
  );
10294
10190
  }
10295
- const finalDiffSize = _31.chain(finalTargetData).omitBy((value, key) => {
10191
+ const finalDiffSize = _32.chain(finalTargetData).omitBy((value, key) => {
10296
10192
  const targetValue = targetData[key];
10297
10193
  if (isICUPluralObject(value) && isICUPluralObject(targetValue)) {
10298
- return _31.isEqual(
10194
+ return _32.isEqual(
10299
10195
  { icu: value.icu, _meta: value._meta },
10300
10196
  { icu: targetValue.icu, _meta: targetValue._meta }
10301
10197
  );
@@ -10532,7 +10428,7 @@ Reviewing changes for ${chalk6.blue(args.pathPattern)} (${chalk6.yellow(
10532
10428
  return args.currentData;
10533
10429
  }
10534
10430
  const customData = { ...args.currentData };
10535
- const changes = _31.reduce(
10431
+ const changes = _32.reduce(
10536
10432
  args.proposedData,
10537
10433
  (result, value, key) => {
10538
10434
  if (args.currentData[key] !== value) {
@@ -10605,7 +10501,7 @@ import path16 from "path";
10605
10501
  import Z4 from "zod";
10606
10502
  import YAML6 from "yaml";
10607
10503
  import { MD5 as MD52 } from "object-hash";
10608
- import _32 from "lodash";
10504
+ import _33 from "lodash";
10609
10505
  function createLockfileHelper() {
10610
10506
  return {
10611
10507
  isLockfileExists: () => {
@@ -10615,18 +10511,18 @@ function createLockfileHelper() {
10615
10511
  registerSourceData: (pathPattern, sourceData) => {
10616
10512
  const lockfile = _loadLockfile();
10617
10513
  const sectionKey = MD52(pathPattern);
10618
- const sectionChecksums = _32.mapValues(sourceData, (value) => MD52(value));
10514
+ const sectionChecksums = _33.mapValues(sourceData, (value) => MD52(value));
10619
10515
  lockfile.checksums[sectionKey] = sectionChecksums;
10620
10516
  _saveLockfile(lockfile);
10621
10517
  },
10622
10518
  registerPartialSourceData: (pathPattern, partialSourceData) => {
10623
10519
  const lockfile = _loadLockfile();
10624
10520
  const sectionKey = MD52(pathPattern);
10625
- const sectionChecksums = _32.mapValues(
10521
+ const sectionChecksums = _33.mapValues(
10626
10522
  partialSourceData,
10627
10523
  (value) => MD52(value)
10628
10524
  );
10629
- lockfile.checksums[sectionKey] = _32.merge(
10525
+ lockfile.checksums[sectionKey] = _33.merge(
10630
10526
  {},
10631
10527
  lockfile.checksums[sectionKey] ?? {},
10632
10528
  sectionChecksums
@@ -10636,9 +10532,9 @@ function createLockfileHelper() {
10636
10532
  extractUpdatedData: (pathPattern, sourceData) => {
10637
10533
  const lockfile = _loadLockfile();
10638
10534
  const sectionKey = MD52(pathPattern);
10639
- const currentChecksums = _32.mapValues(sourceData, (value) => MD52(value));
10535
+ const currentChecksums = _33.mapValues(sourceData, (value) => MD52(value));
10640
10536
  const savedChecksums = lockfile.checksums[sectionKey] || {};
10641
- const updatedData = _32.pickBy(
10537
+ const updatedData = _33.pickBy(
10642
10538
  sourceData,
10643
10539
  (value, key) => savedChecksums[key] !== currentChecksums[key]
10644
10540
  );
@@ -10731,7 +10627,7 @@ var flagsSchema = Z5.object({
10731
10627
  // src/cli/cmd/cleanup.ts
10732
10628
  import { resolveOverriddenLocale as resolveOverriddenLocale6 } from "@lingo.dev/_spec";
10733
10629
  import { Command as Command16 } from "interactive-commander";
10734
- import _33 from "lodash";
10630
+ import _34 from "lodash";
10735
10631
  import Ora11 from "ora";
10736
10632
  var cleanup_default = new Command16().command("cleanup").description(
10737
10633
  "Remove translation keys from target locales that no longer exist in the source locale"
@@ -10795,7 +10691,7 @@ var cleanup_default = new Command16().command("cleanup").description(
10795
10691
  try {
10796
10692
  const targetData = await bucketLoader.pull(targetLocale);
10797
10693
  const targetKeys = Object.keys(targetData);
10798
- const keysToRemove = _33.difference(targetKeys, sourceKeys);
10694
+ const keysToRemove = _34.difference(targetKeys, sourceKeys);
10799
10695
  if (keysToRemove.length === 0) {
10800
10696
  bucketOra.succeed(`[${targetLocale}] No keys to remove`);
10801
10697
  continue;
@@ -10810,7 +10706,7 @@ var cleanup_default = new Command16().command("cleanup").description(
10810
10706
  );
10811
10707
  }
10812
10708
  if (!options.dryRun) {
10813
- const cleanedData = _33.pick(targetData, sourceKeys);
10709
+ const cleanedData = _34.pick(targetData, sourceKeys);
10814
10710
  await bucketLoader.push(targetLocale, cleanedData);
10815
10711
  bucketOra.succeed(
10816
10712
  `[${targetLocale}] Removed ${keysToRemove.length} keys`
@@ -10873,7 +10769,7 @@ import Z6 from "zod";
10873
10769
  import { ReplexicaEngine } from "@lingo.dev/_sdk";
10874
10770
  var mcp_default = new Command17().command("mcp").description(
10875
10771
  "Start a Model Context Protocol (MCP) server for AI assistant integration"
10876
- ).helpOption("-h, --help", "Show help").action(async (_36, program) => {
10772
+ ).helpOption("-h, --help", "Show help").action(async (_37, program) => {
10877
10773
  const apiKey = program.args[0];
10878
10774
  const settings = getSettings(apiKey);
10879
10775
  if (!settings.auth.apiKey) {
@@ -11459,7 +11355,7 @@ async function plan(input2) {
11459
11355
  import chalk12 from "chalk";
11460
11356
  import { Listr as Listr3 } from "listr2";
11461
11357
  import pLimit from "p-limit";
11462
- import _34 from "lodash";
11358
+ import _35 from "lodash";
11463
11359
  var MAX_WORKER_COUNT = 10;
11464
11360
  async function execute(input2) {
11465
11361
  const effectiveConcurrency = Math.min(
@@ -11489,7 +11385,7 @@ async function execute(input2) {
11489
11385
  return;
11490
11386
  }
11491
11387
  const initialChecksumsMap = /* @__PURE__ */ new Map();
11492
- const uniqueBucketPatterns = _34.uniq(
11388
+ const uniqueBucketPatterns = _35.uniq(
11493
11389
  ctx.tasks.map((t2) => t2.bucketPathPattern)
11494
11390
  );
11495
11391
  for (const bucketPathPattern of uniqueBucketPatterns) {
@@ -11511,7 +11407,7 @@ async function execute(input2) {
11511
11407
  const workerTasks = [];
11512
11408
  for (let i = 0; i < workersCount; i++) {
11513
11409
  const assignedTasks = ctx.tasks.filter(
11514
- (_36, idx) => idx % workersCount === i
11410
+ (_37, idx) => idx % workersCount === i
11515
11411
  );
11516
11412
  workerTasks.push(
11517
11413
  createWorkerTask({
@@ -11621,7 +11517,7 @@ function createWorkerTask(args) {
11621
11517
  targetData,
11622
11518
  checksums: initialChecksums
11623
11519
  });
11624
- const processableData = _34.chain(sourceData).entries().filter(
11520
+ const processableData = _35.chain(sourceData).entries().filter(
11625
11521
  ([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!args.ctx.flags.force
11626
11522
  ).filter(
11627
11523
  ([key]) => !assignedTask.onlyKeys.length || assignedTask.onlyKeys?.some(
@@ -11639,7 +11535,7 @@ function createWorkerTask(args) {
11639
11535
  targetLocale: assignedTask.targetLocale
11640
11536
  };
11641
11537
  }
11642
- const relevantHints = _34.pick(hints, Object.keys(processableData));
11538
+ const relevantHints = _35.pick(hints, Object.keys(processableData));
11643
11539
  const processedTargetData = await args.ctx.localizer.localize(
11644
11540
  {
11645
11541
  sourceLocale: assignedTask.sourceLocale,
@@ -11655,7 +11551,7 @@ function createWorkerTask(args) {
11655
11551
  const latestTargetData = await bucketLoader.pull(
11656
11552
  assignedTask.targetLocale
11657
11553
  );
11658
- const _partialData = _34.merge(
11554
+ const _partialData = _35.merge(
11659
11555
  {},
11660
11556
  latestTargetData,
11661
11557
  processedChunk
@@ -11675,7 +11571,7 @@ function createWorkerTask(args) {
11675
11571
  });
11676
11572
  }
11677
11573
  );
11678
- const finalTargetData = _34.merge(
11574
+ const finalTargetData = _35.merge(
11679
11575
  {},
11680
11576
  sourceData,
11681
11577
  targetData,
@@ -11724,7 +11620,7 @@ function countTasks(ctx, predicate) {
11724
11620
  ).length;
11725
11621
  }
11726
11622
  function processRenamedKeys(delta, targetData) {
11727
- return _34.chain(targetData).entries().map(([key, value]) => {
11623
+ return _35.chain(targetData).entries().map(([key, value]) => {
11728
11624
  const renaming = delta.renamed.find(([oldKey]) => oldKey === key);
11729
11625
  if (!renaming) {
11730
11626
  return [key, value];
@@ -11892,7 +11788,7 @@ var flagsSchema2 = z2.object({
11892
11788
  // src/cli/cmd/run/frozen.ts
11893
11789
  import chalk14 from "chalk";
11894
11790
  import { Listr as Listr4 } from "listr2";
11895
- import _35 from "lodash";
11791
+ import _36 from "lodash";
11896
11792
  import { resolveOverriddenLocale as resolveOverriddenLocale8 } from "@lingo.dev/_spec";
11897
11793
  async function frozen(input2) {
11898
11794
  console.log(chalk14.hex(colors.orange)("[Frozen]"));
@@ -11980,7 +11876,7 @@ async function frozen(input2) {
11980
11876
  const delta = createDeltaProcessor(bucketPath.pathPattern);
11981
11877
  const sourceChecksums = await delta.createChecksums(src);
11982
11878
  const savedChecksums = await delta.loadChecksums();
11983
- const updatedSourceData = _35.pickBy(
11879
+ const updatedSourceData = _36.pickBy(
11984
11880
  src,
11985
11881
  (value, key) => sourceChecksums[key] !== savedChecksums[key]
11986
11882
  );
@@ -11995,7 +11891,7 @@ async function frozen(input2) {
11995
11891
  bucketPath.delimiter
11996
11892
  );
11997
11893
  const { unlocalizable: tgtUnlocalizable, ...tgt } = await loader.pull(resolvedTargetLocale);
11998
- const missingKeys = _35.difference(
11894
+ const missingKeys = _36.difference(
11999
11895
  Object.keys(src),
12000
11896
  Object.keys(tgt)
12001
11897
  );
@@ -12004,7 +11900,7 @@ async function frozen(input2) {
12004
11900
  `Localization data has changed; please update i18n.lock or run without --frozen. Details: Target file is missing translations.`
12005
11901
  );
12006
11902
  }
12007
- const extraKeys = _35.difference(
11903
+ const extraKeys = _36.difference(
12008
11904
  Object.keys(tgt),
12009
11905
  Object.keys(src)
12010
11906
  );
@@ -12013,7 +11909,7 @@ async function frozen(input2) {
12013
11909
  `Localization data has changed; please update i18n.lock or run without --frozen. Details: Target file has extra translations not present in the source file.`
12014
11910
  );
12015
11911
  }
12016
- const unlocalizableDataDiff = !_35.isEqual(
11912
+ const unlocalizableDataDiff = !_36.isEqual(
12017
11913
  srcUnlocalizable,
12018
11914
  tgtUnlocalizable
12019
11915
  );
@@ -13556,7 +13452,7 @@ async function renderHero2() {
13556
13452
  // package.json
13557
13453
  var package_default = {
13558
13454
  name: "lingo.dev",
13559
- version: "0.116.2",
13455
+ version: "0.116.3",
13560
13456
  description: "Lingo.dev CLI",
13561
13457
  private: false,
13562
13458
  publishConfig: {