lingo.dev 0.117.7 → 0.117.9

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
@@ -2321,9 +2321,119 @@ function _isMetadataKey(key) {
2321
2321
  return key.startsWith("@");
2322
2322
  }
2323
2323
 
2324
+ // src/cli/loaders/ail.ts
2325
+ import { parseStringPromise, Builder } from "xml2js";
2326
+ function createAilLoader() {
2327
+ return createLoader({
2328
+ async pull(locale, input2) {
2329
+ const result = {};
2330
+ if (!input2 || !input2.trim()) {
2331
+ return result;
2332
+ }
2333
+ try {
2334
+ const parsed = await parseStringPromise(input2, {
2335
+ explicitArray: true,
2336
+ // Always use arrays for consistency
2337
+ mergeAttrs: false,
2338
+ // Keep attributes separate in $
2339
+ trim: true,
2340
+ explicitRoot: true
2341
+ });
2342
+ const dictionary = parsed.DICTIONARY;
2343
+ if (!dictionary) {
2344
+ return result;
2345
+ }
2346
+ const entries = dictionary.ENTRY || [];
2347
+ for (const entry of entries) {
2348
+ const id = entry.$?.id;
2349
+ if (!id) {
2350
+ continue;
2351
+ }
2352
+ const strings = entry.STRING || [];
2353
+ const sourceString = strings.find(
2354
+ (s) => s.$?.lang === locale
2355
+ );
2356
+ if (sourceString?.$.value) {
2357
+ result[id] = sourceString.$.value;
2358
+ }
2359
+ }
2360
+ return result;
2361
+ } catch (error) {
2362
+ console.error("Failed to parse AIL file:", error);
2363
+ return result;
2364
+ }
2365
+ },
2366
+ async push(locale, data, originalInput) {
2367
+ if (!originalInput || !originalInput.trim()) {
2368
+ const dictionary = {
2369
+ $: { type: "multilanguage" },
2370
+ ENTRY: Object.entries(data).map(([id, value]) => ({
2371
+ $: { id },
2372
+ STRING: [
2373
+ {
2374
+ $: { lang: locale, value }
2375
+ }
2376
+ ]
2377
+ }))
2378
+ };
2379
+ const builder = new Builder({
2380
+ xmldec: { version: "1.0", encoding: "UTF-8" },
2381
+ headless: false
2382
+ });
2383
+ return builder.buildObject({ DICTIONARY: dictionary });
2384
+ }
2385
+ try {
2386
+ const parsed = await parseStringPromise(originalInput, {
2387
+ explicitArray: true,
2388
+ mergeAttrs: false,
2389
+ trim: true,
2390
+ explicitRoot: true
2391
+ });
2392
+ const dictionary = parsed.DICTIONARY;
2393
+ if (!dictionary) {
2394
+ throw new Error("No DICTIONARY root element found");
2395
+ }
2396
+ const entries = dictionary.ENTRY || [];
2397
+ for (const [id, value] of Object.entries(data)) {
2398
+ let entry = entries.find((e) => e.$?.id === id);
2399
+ if (!entry) {
2400
+ entry = {
2401
+ $: { id },
2402
+ STRING: []
2403
+ };
2404
+ entries.push(entry);
2405
+ }
2406
+ if (!entry.STRING) {
2407
+ entry.STRING = [];
2408
+ }
2409
+ let targetString = entry.STRING.find(
2410
+ (s) => s.$?.lang === locale
2411
+ );
2412
+ if (targetString) {
2413
+ targetString.$.value = value;
2414
+ } else {
2415
+ entry.STRING.push({
2416
+ $: { lang: locale, value }
2417
+ });
2418
+ }
2419
+ }
2420
+ dictionary.ENTRY = entries;
2421
+ const builder = new Builder({
2422
+ xmldec: { version: "1.0", encoding: "UTF-8" },
2423
+ headless: false
2424
+ });
2425
+ return builder.buildObject({ DICTIONARY: dictionary });
2426
+ } catch (error) {
2427
+ console.error("Failed to build AIL file:", error);
2428
+ throw error;
2429
+ }
2430
+ }
2431
+ });
2432
+ }
2433
+
2324
2434
  // src/cli/loaders/android.ts
2325
2435
  import { createRequire } from "module";
2326
- import { parseStringPromise } from "xml2js";
2436
+ import { parseStringPromise as parseStringPromise2 } from "xml2js";
2327
2437
  var require2 = createRequire(import.meta.url);
2328
2438
  var sax = require2("sax");
2329
2439
  var defaultAndroidResourcesXml = `<?xml version="1.0" encoding="utf-8"?>
@@ -2401,7 +2511,7 @@ function resolveXmlDeclaration(xml) {
2401
2511
  }
2402
2512
  async function parseAndroidDocument(input2) {
2403
2513
  const xmlToParse = input2 && input2.trim().length > 0 ? input2 : defaultAndroidResourcesXml;
2404
- const parsed = await parseStringPromise(xmlToParse, {
2514
+ const parsed = await parseStringPromise2(xmlToParse, {
2405
2515
  explicitArray: true,
2406
2516
  explicitChildren: true,
2407
2517
  preserveChildrenOrder: true,
@@ -3403,8 +3513,8 @@ function serializeElement(node) {
3403
3513
  const attrString = Object.entries(attributes).map(([key, value]) => ` ${key}="${escapeAttributeValue(String(value))}"`).join("");
3404
3514
  const children = Array.isArray(node.$$) ? node.$$ : [];
3405
3515
  if (children.length === 0) {
3406
- const textContent2 = node._ ?? "";
3407
- return `<${name}${attrString}>${textContent2}</${name}>`;
3516
+ const textContent3 = node._ ?? "";
3517
+ return `<${name}${attrString}>${textContent3}</${name}>`;
3408
3518
  }
3409
3519
  const childContent = children.map(serializeElement).join("");
3410
3520
  return `<${name}${attrString}>${childContent}</${name}>`;
@@ -4005,7 +4115,7 @@ function applyTranslations(node, path19, data, pathMap) {
4005
4115
  }
4006
4116
 
4007
4117
  // src/cli/loaders/mjml.ts
4008
- import { parseStringPromise as parseStringPromise2 } from "xml2js";
4118
+ import { parseStringPromise as parseStringPromise3 } from "xml2js";
4009
4119
  import * as htmlparser22 from "htmlparser2";
4010
4120
  import { DomHandler as DomHandler2 } from "domhandler";
4011
4121
  var LOCALIZABLE_COMPONENTS = [
@@ -4037,7 +4147,7 @@ function createMjmlLoader() {
4037
4147
  async pull(locale, input2) {
4038
4148
  const result = {};
4039
4149
  try {
4040
- const parsed = await parseStringPromise2(input2, {
4150
+ const parsed = await parseStringPromise3(input2, {
4041
4151
  explicitArray: true,
4042
4152
  explicitChildren: true,
4043
4153
  preserveChildrenOrder: true,
@@ -4083,7 +4193,7 @@ function createMjmlLoader() {
4083
4193
  },
4084
4194
  async push(locale, data, originalInput) {
4085
4195
  try {
4086
- const parsed = await parseStringPromise2(originalInput || "", {
4196
+ const parsed = await parseStringPromise3(originalInput || "", {
4087
4197
  explicitArray: true,
4088
4198
  explicitChildren: true,
4089
4199
  preserveChildrenOrder: true,
@@ -4190,9 +4300,9 @@ function serializeXmlNode(node) {
4190
4300
  const attrString = Object.entries(attrs).map(([key, value]) => ` ${key}="${escapeAttributeValue2(String(value))}"`).join("");
4191
4301
  const children = node.$$ || [];
4192
4302
  if (children.length === 0) {
4193
- const textContent2 = node._ || "";
4194
- if (textContent2) {
4195
- return `<${name}${attrString}>${textContent2}</${name}>`;
4303
+ const textContent3 = node._ || "";
4304
+ if (textContent3) {
4305
+ return `<${name}${attrString}>${textContent3}</${name}>`;
4196
4306
  }
4197
4307
  return `<${name}${attrString} />`;
4198
4308
  }
@@ -4249,9 +4359,9 @@ function serializeElement2(node, indent2 = "") {
4249
4359
  const attrString = Object.entries(attributes).map(([key, value]) => ` ${key}="${escapeAttributeValue2(String(value))}"`).join("");
4250
4360
  const children = Array.isArray(node.$$) ? node.$$ : [];
4251
4361
  if (children.length === 0) {
4252
- const textContent2 = node._ ?? "";
4253
- if (textContent2) {
4254
- return `${indent2}<${name}${attrString}>${textContent2}</${name}>`;
4362
+ const textContent3 = node._ ?? "";
4363
+ if (textContent3) {
4364
+ return `${indent2}<${name}${attrString}>${textContent3}</${name}>`;
4255
4365
  }
4256
4366
  return `${indent2}<${name}${attrString} />`;
4257
4367
  }
@@ -5810,10 +5920,10 @@ function formatXml(xml) {
5810
5920
  if (cdataNode) {
5811
5921
  return `${indent2}${openTag}<![CDATA[${cdataNode.nodeValue}]]></${tagName}>`;
5812
5922
  }
5813
- const textContent2 = element.textContent?.trim() || "";
5923
+ const textContent3 = element.textContent?.trim() || "";
5814
5924
  const hasOnlyText = element.childNodes.length === 1 && element.childNodes[0].nodeType === 3;
5815
- if (hasOnlyText && textContent2) {
5816
- return `${indent2}${openTag}${textContent2}</${tagName}>`;
5925
+ if (hasOnlyText && textContent3) {
5926
+ return `${indent2}${openTag}${textContent3}</${tagName}>`;
5817
5927
  }
5818
5928
  const children = Array.from(element.children);
5819
5929
  if (children.length === 0) {
@@ -5865,7 +5975,7 @@ function pushNewFile(locale, translations, originalLocale) {
5865
5975
  }
5866
5976
 
5867
5977
  // src/cli/loaders/xml.ts
5868
- import { parseStringPromise as parseStringPromise3, Builder as Builder2 } from "xml2js";
5978
+ import { parseStringPromise as parseStringPromise4, Builder as Builder3 } from "xml2js";
5869
5979
  function normalizeXMLString(xmlString) {
5870
5980
  return xmlString.replace(/\s+/g, " ").replace(/>\s+</g, "><").replace("\n", "").trim();
5871
5981
  }
@@ -5874,7 +5984,7 @@ function createXmlLoader() {
5874
5984
  async pull(locale, input2) {
5875
5985
  let result = {};
5876
5986
  try {
5877
- const parsed = await parseStringPromise3(input2, {
5987
+ const parsed = await parseStringPromise4(input2, {
5878
5988
  explicitArray: false,
5879
5989
  mergeAttrs: false,
5880
5990
  normalize: true,
@@ -5892,7 +6002,7 @@ function createXmlLoader() {
5892
6002
  },
5893
6003
  async push(locale, data) {
5894
6004
  try {
5895
- const builder = new Builder2({ headless: true });
6005
+ const builder = new Builder3({ headless: true });
5896
6006
  const xmlOutput = builder.buildObject(data);
5897
6007
  const expectedOutput = normalizeXMLString(xmlOutput);
5898
6008
  return expectedOutput;
@@ -9133,14 +9243,14 @@ function parseEjsForTranslation(input2) {
9133
9243
  if (part.type === "ejs") {
9134
9244
  template += part.content;
9135
9245
  } else {
9136
- const textContent2 = part.content;
9246
+ const textContent3 = part.content;
9137
9247
  const htmlTagRegex = /<[^>]+>/g;
9138
9248
  const textParts = [];
9139
9249
  let lastTextIndex = 0;
9140
9250
  let htmlMatch;
9141
- while ((htmlMatch = htmlTagRegex.exec(textContent2)) !== null) {
9251
+ while ((htmlMatch = htmlTagRegex.exec(textContent3)) !== null) {
9142
9252
  if (htmlMatch.index > lastTextIndex) {
9143
- const textBefore = textContent2.slice(lastTextIndex, htmlMatch.index);
9253
+ const textBefore = textContent3.slice(lastTextIndex, htmlMatch.index);
9144
9254
  if (textBefore.trim()) {
9145
9255
  textParts.push({ type: "text", content: textBefore });
9146
9256
  } else {
@@ -9150,8 +9260,8 @@ function parseEjsForTranslation(input2) {
9150
9260
  textParts.push({ type: "html", content: htmlMatch[0] });
9151
9261
  lastTextIndex = htmlMatch.index + htmlMatch[0].length;
9152
9262
  }
9153
- if (lastTextIndex < textContent2.length) {
9154
- const remainingText = textContent2.slice(lastTextIndex);
9263
+ if (lastTextIndex < textContent3.length) {
9264
+ const remainingText = textContent3.slice(lastTextIndex);
9155
9265
  if (remainingText.trim()) {
9156
9266
  textParts.push({ type: "text", content: remainingText });
9157
9267
  } else {
@@ -9159,11 +9269,11 @@ function parseEjsForTranslation(input2) {
9159
9269
  }
9160
9270
  }
9161
9271
  if (textParts.length === 0) {
9162
- const trimmedContent = textContent2.trim();
9272
+ const trimmedContent = textContent3.trim();
9163
9273
  if (trimmedContent) {
9164
- textParts.push({ type: "text", content: textContent2 });
9274
+ textParts.push({ type: "text", content: textContent3 });
9165
9275
  } else {
9166
- textParts.push({ type: "html", content: textContent2 });
9276
+ textParts.push({ type: "html", content: textContent3 });
9167
9277
  }
9168
9278
  }
9169
9279
  for (const textPart of textParts) {
@@ -9232,6 +9342,368 @@ function createEjsLoader() {
9232
9342
  });
9233
9343
  }
9234
9344
 
9345
+ // src/cli/loaders/twig.ts
9346
+ import * as htmlparser23 from "htmlparser2";
9347
+ import { DomHandler as DomHandler3 } from "domhandler";
9348
+ import * as domutils2 from "domutils";
9349
+ import * as DomSerializer2 from "dom-serializer";
9350
+ function createTwigLoader() {
9351
+ const PHRASING_ELEMENTS = /* @__PURE__ */ new Set([
9352
+ // Text-level semantics
9353
+ "a",
9354
+ "abbr",
9355
+ "b",
9356
+ "bdi",
9357
+ "bdo",
9358
+ "br",
9359
+ "cite",
9360
+ "code",
9361
+ "data",
9362
+ "dfn",
9363
+ "em",
9364
+ "i",
9365
+ "kbd",
9366
+ "mark",
9367
+ "q",
9368
+ "ruby",
9369
+ "s",
9370
+ "samp",
9371
+ "small",
9372
+ "span",
9373
+ "strong",
9374
+ "sub",
9375
+ "sup",
9376
+ "time",
9377
+ "u",
9378
+ "var",
9379
+ "wbr",
9380
+ // Media
9381
+ "audio",
9382
+ "img",
9383
+ "video",
9384
+ "picture",
9385
+ // Interactive
9386
+ "button",
9387
+ "input",
9388
+ "label",
9389
+ "select",
9390
+ "textarea",
9391
+ // Embedded
9392
+ "canvas",
9393
+ "iframe",
9394
+ "object",
9395
+ "svg",
9396
+ "math",
9397
+ // Other
9398
+ "del",
9399
+ "ins",
9400
+ "map",
9401
+ "area"
9402
+ ]);
9403
+ const BLOCK_ELEMENTS = /* @__PURE__ */ new Set([
9404
+ "div",
9405
+ "p",
9406
+ "h1",
9407
+ "h2",
9408
+ "h3",
9409
+ "h4",
9410
+ "h5",
9411
+ "h6",
9412
+ "ul",
9413
+ "ol",
9414
+ "li",
9415
+ "dl",
9416
+ "dt",
9417
+ "dd",
9418
+ "blockquote",
9419
+ "pre",
9420
+ "article",
9421
+ "aside",
9422
+ "nav",
9423
+ "section",
9424
+ "header",
9425
+ "footer",
9426
+ "main",
9427
+ "figure",
9428
+ "figcaption",
9429
+ "table",
9430
+ "thead",
9431
+ "tbody",
9432
+ "tfoot",
9433
+ "tr",
9434
+ "td",
9435
+ "th",
9436
+ "caption",
9437
+ "form",
9438
+ "fieldset",
9439
+ "legend",
9440
+ "details",
9441
+ "summary",
9442
+ "address",
9443
+ "hr",
9444
+ "search",
9445
+ "dialog",
9446
+ "noscript",
9447
+ "title"
9448
+ ]);
9449
+ const UNLOCALIZABLE_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
9450
+ const LOCALIZABLE_ATTRIBUTES2 = {
9451
+ meta: ["content"],
9452
+ img: ["alt", "title"],
9453
+ input: ["placeholder", "title", "aria-label"],
9454
+ textarea: ["placeholder", "title", "aria-label"],
9455
+ button: ["title", "aria-label"],
9456
+ a: ["title", "aria-label"],
9457
+ abbr: ["title"],
9458
+ link: ["title"]
9459
+ };
9460
+ function preprocessTwig(input2) {
9461
+ const twigBlocks = [];
9462
+ let counter = 0;
9463
+ const processed = input2.replace(/\{%[\s\S]*?%\}/g, (match2) => {
9464
+ twigBlocks.push(match2);
9465
+ return `__TWIG_BLOCK_${counter++}__`;
9466
+ });
9467
+ return {
9468
+ processed: processed.replace(/\{#[\s\S]*?#\}/g, (match2) => {
9469
+ twigBlocks.push(match2);
9470
+ return `__TWIG_BLOCK_${counter++}__`;
9471
+ }),
9472
+ twigBlocks
9473
+ };
9474
+ }
9475
+ function postprocessTwig(text, twigBlocks) {
9476
+ return text.replace(/__TWIG_BLOCK_(\d+)__/g, (_37, index) => {
9477
+ return twigBlocks[parseInt(index, 10)] || "";
9478
+ });
9479
+ }
9480
+ return createLoader({
9481
+ async pull(locale, input2) {
9482
+ const result = {};
9483
+ const { processed, twigBlocks } = preprocessTwig(input2);
9484
+ const handler = new DomHandler3();
9485
+ const parser = new htmlparser23.Parser(handler, {
9486
+ lowerCaseTags: false,
9487
+ lowerCaseAttributeNames: false
9488
+ });
9489
+ parser.write(processed);
9490
+ parser.end();
9491
+ const dom = handler.dom;
9492
+ function isInsideUnlocalizableTag(element) {
9493
+ let current = element.parent;
9494
+ while (current && current.type === "tag") {
9495
+ if (UNLOCALIZABLE_TAGS.has(current.name.toLowerCase())) {
9496
+ return true;
9497
+ }
9498
+ current = current.parent;
9499
+ }
9500
+ return false;
9501
+ }
9502
+ function hasTranslatableContent(element) {
9503
+ const text = domutils2.textContent(element);
9504
+ return text.trim().length > 0;
9505
+ }
9506
+ function isLeafBlock(element) {
9507
+ const childElements = element.children.filter(
9508
+ (child) => child.type === "tag"
9509
+ );
9510
+ for (const child of childElements) {
9511
+ if (BLOCK_ELEMENTS.has(child.name.toLowerCase())) {
9512
+ return false;
9513
+ }
9514
+ }
9515
+ return hasTranslatableContent(element);
9516
+ }
9517
+ function getInnerHTML2(element) {
9518
+ const html2 = element.children.map((child) => DomSerializer2.default(child, { encodeEntities: false })).join("");
9519
+ return postprocessTwig(html2, twigBlocks);
9520
+ }
9521
+ function extractAttributes(element, path19) {
9522
+ const tagName = element.name.toLowerCase();
9523
+ const attrs = LOCALIZABLE_ATTRIBUTES2[tagName];
9524
+ if (!attrs) return;
9525
+ for (const attr of attrs) {
9526
+ const value = element.attribs?.[attr];
9527
+ if (value && value.trim()) {
9528
+ const restoredValue = postprocessTwig(value.trim(), twigBlocks);
9529
+ result[`${path19}#${attr}`] = restoredValue;
9530
+ }
9531
+ }
9532
+ }
9533
+ function extractFromElement(element, pathParts) {
9534
+ const path19 = pathParts.join("/");
9535
+ if (isInsideUnlocalizableTag(element)) {
9536
+ return;
9537
+ }
9538
+ extractAttributes(element, path19);
9539
+ const tagName = element.name.toLowerCase();
9540
+ if (BLOCK_ELEMENTS.has(tagName) && isLeafBlock(element)) {
9541
+ const content = getInnerHTML2(element).trim();
9542
+ if (content) {
9543
+ result[path19] = content;
9544
+ }
9545
+ return;
9546
+ }
9547
+ if (PHRASING_ELEMENTS.has(tagName) && hasTranslatableContent(element)) {
9548
+ const content = getInnerHTML2(element).trim();
9549
+ if (content) {
9550
+ result[path19] = content;
9551
+ }
9552
+ return;
9553
+ }
9554
+ let childIndex = 0;
9555
+ const childElements = element.children.filter(
9556
+ (child) => child.type === "tag"
9557
+ );
9558
+ for (const child of childElements) {
9559
+ extractFromElement(child, [...pathParts, childIndex++]);
9560
+ }
9561
+ }
9562
+ const html = domutils2.findOne(
9563
+ (elem) => elem.type === "tag" && elem.name.toLowerCase() === "html",
9564
+ dom,
9565
+ true
9566
+ );
9567
+ if (html) {
9568
+ const head = domutils2.findOne(
9569
+ (elem) => elem.type === "tag" && elem.name.toLowerCase() === "head",
9570
+ html.children,
9571
+ true
9572
+ );
9573
+ const body = domutils2.findOne(
9574
+ (elem) => elem.type === "tag" && elem.name.toLowerCase() === "body",
9575
+ html.children,
9576
+ true
9577
+ );
9578
+ if (head) {
9579
+ let headIndex = 0;
9580
+ const headChildren = head.children.filter(
9581
+ (child) => child.type === "tag"
9582
+ );
9583
+ for (const child of headChildren) {
9584
+ extractFromElement(child, ["head", headIndex++]);
9585
+ }
9586
+ }
9587
+ if (body) {
9588
+ let bodyIndex = 0;
9589
+ const bodyChildren = body.children.filter(
9590
+ (child) => child.type === "tag"
9591
+ );
9592
+ for (const child of bodyChildren) {
9593
+ extractFromElement(child, ["body", bodyIndex++]);
9594
+ }
9595
+ }
9596
+ } else {
9597
+ let rootIndex = 0;
9598
+ const rootElements = dom.filter(
9599
+ (child) => child.type === "tag"
9600
+ );
9601
+ for (const child of rootElements) {
9602
+ extractFromElement(child, [rootIndex++]);
9603
+ }
9604
+ }
9605
+ return result;
9606
+ },
9607
+ async push(locale, data, originalInput) {
9608
+ const { processed, twigBlocks } = preprocessTwig(originalInput || "");
9609
+ const handler = new DomHandler3();
9610
+ const parser = new htmlparser23.Parser(handler, {
9611
+ lowerCaseTags: false,
9612
+ lowerCaseAttributeNames: false
9613
+ });
9614
+ parser.write(processed || "<!DOCTYPE html><html><head></head><body></body></html>");
9615
+ parser.end();
9616
+ const dom = handler.dom;
9617
+ const html = domutils2.findOne(
9618
+ (elem) => elem.type === "tag" && elem.name.toLowerCase() === "html",
9619
+ dom,
9620
+ true
9621
+ );
9622
+ if (html) {
9623
+ html.attribs = html.attribs || {};
9624
+ html.attribs.lang = locale;
9625
+ }
9626
+ function traverseByIndices(element, indices) {
9627
+ let current = element;
9628
+ for (const indexStr of indices) {
9629
+ if (!current) return null;
9630
+ const index = parseInt(indexStr, 10);
9631
+ const children = current.children.filter(
9632
+ (child) => child.type === "tag"
9633
+ );
9634
+ if (index >= children.length) {
9635
+ return null;
9636
+ }
9637
+ current = children[index];
9638
+ }
9639
+ return current;
9640
+ }
9641
+ function resolvePathToElement(path19) {
9642
+ const parts = path19.split("/");
9643
+ const [rootTag, ...indices] = parts;
9644
+ let current = null;
9645
+ if (html) {
9646
+ if (rootTag === "head") {
9647
+ current = domutils2.findOne(
9648
+ (elem) => elem.type === "tag" && elem.name.toLowerCase() === "head",
9649
+ html.children,
9650
+ true
9651
+ );
9652
+ } else if (rootTag === "body") {
9653
+ current = domutils2.findOne(
9654
+ (elem) => elem.type === "tag" && elem.name.toLowerCase() === "body",
9655
+ html.children,
9656
+ true
9657
+ );
9658
+ }
9659
+ if (!current) return null;
9660
+ return traverseByIndices(current, indices);
9661
+ } else {
9662
+ const rootElements = dom.filter(
9663
+ (child) => child.type === "tag"
9664
+ );
9665
+ const rootIndex = parseInt(rootTag, 10);
9666
+ if (rootIndex >= rootElements.length) {
9667
+ return null;
9668
+ }
9669
+ current = rootElements[rootIndex];
9670
+ return traverseByIndices(current, indices);
9671
+ }
9672
+ }
9673
+ for (const [path19, value] of Object.entries(data)) {
9674
+ const [nodePath, attribute] = path19.split("#");
9675
+ const element = resolvePathToElement(nodePath);
9676
+ if (!element) {
9677
+ console.warn(`Path not found in original template: ${nodePath}`);
9678
+ continue;
9679
+ }
9680
+ if (attribute) {
9681
+ element.attribs = element.attribs || {};
9682
+ element.attribs[attribute] = value;
9683
+ } else {
9684
+ if (value) {
9685
+ const { processed: processedValue, twigBlocks: valueTwigBlocks } = preprocessTwig(value);
9686
+ const valueHandler = new DomHandler3();
9687
+ const valueParser = new htmlparser23.Parser(valueHandler);
9688
+ valueParser.write(processedValue);
9689
+ valueParser.end();
9690
+ element.children = valueHandler.dom;
9691
+ element.children.forEach((child) => {
9692
+ if (child.type === "text" && child.data) {
9693
+ child.data = postprocessTwig(child.data, valueTwigBlocks);
9694
+ }
9695
+ });
9696
+ } else {
9697
+ element.children = [];
9698
+ }
9699
+ }
9700
+ }
9701
+ const serialized = DomSerializer2.default(dom, { encodeEntities: false });
9702
+ return postprocessTwig(serialized, twigBlocks);
9703
+ }
9704
+ });
9705
+ }
9706
+
9235
9707
  // src/cli/loaders/ensure-key-order.ts
9236
9708
  import _25 from "lodash";
9237
9709
  function createEnsureKeyOrderLoader() {
@@ -9384,6 +9856,18 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
9384
9856
  switch (bucketType) {
9385
9857
  default:
9386
9858
  throw new Error(`Unsupported bucket type: ${bucketType}`);
9859
+ case "ail":
9860
+ return composeLoaders(
9861
+ createTextFileLoader(bucketPathPattern),
9862
+ createLockedPatternsLoader(lockedPatterns),
9863
+ createAilLoader(),
9864
+ createEnsureKeyOrderLoader(),
9865
+ createFlatLoader(),
9866
+ createLockedKeysLoader(lockedKeys || []),
9867
+ createIgnoredKeysLoader(ignoredKeys || []),
9868
+ createSyncLoader(),
9869
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
9870
+ );
9387
9871
  case "android":
9388
9872
  return composeLoaders(
9389
9873
  createTextFileLoader(bucketPathPattern),
@@ -9730,6 +10214,16 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
9730
10214
  createIgnoredKeysLoader(ignoredKeys || []),
9731
10215
  createUnlocalizableLoader(options.returnUnlocalizedKeys)
9732
10216
  );
10217
+ case "twig":
10218
+ return composeLoaders(
10219
+ createTextFileLoader(bucketPathPattern),
10220
+ createLockedPatternsLoader(lockedPatterns),
10221
+ createTwigLoader(),
10222
+ createLockedKeysLoader(lockedKeys || []),
10223
+ createIgnoredKeysLoader(ignoredKeys || []),
10224
+ createSyncLoader(),
10225
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
10226
+ );
9733
10227
  case "txt":
9734
10228
  return composeLoaders(
9735
10229
  createTextFileLoader(bucketPathPattern),
@@ -14101,7 +14595,7 @@ async function renderHero2() {
14101
14595
  // package.json
14102
14596
  var package_default = {
14103
14597
  name: "lingo.dev",
14104
- version: "0.117.7",
14598
+ version: "0.117.9",
14105
14599
  description: "Lingo.dev CLI",
14106
14600
  private: false,
14107
14601
  publishConfig: {