lingo.dev 0.117.6 → 0.117.8
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.cjs +712 -71
- package/build/cli.cjs.map +1 -1
- package/build/cli.mjs +668 -27
- package/build/cli.mjs.map +1 -1
- package/package.json +4 -4
package/build/cli.mjs
CHANGED
|
@@ -3403,8 +3403,8 @@ function serializeElement(node) {
|
|
|
3403
3403
|
const attrString = Object.entries(attributes).map(([key, value]) => ` ${key}="${escapeAttributeValue(String(value))}"`).join("");
|
|
3404
3404
|
const children = Array.isArray(node.$$) ? node.$$ : [];
|
|
3405
3405
|
if (children.length === 0) {
|
|
3406
|
-
const
|
|
3407
|
-
return `<${name}${attrString}>${
|
|
3406
|
+
const textContent3 = node._ ?? "";
|
|
3407
|
+
return `<${name}${attrString}>${textContent3}</${name}>`;
|
|
3408
3408
|
}
|
|
3409
3409
|
const childContent = children.map(serializeElement).join("");
|
|
3410
3410
|
return `<${name}${attrString}>${childContent}</${name}>`;
|
|
@@ -3598,7 +3598,7 @@ function createHtmlLoader() {
|
|
|
3598
3598
|
// <title> should be treated as a block element for translation
|
|
3599
3599
|
]);
|
|
3600
3600
|
const UNLOCALIZABLE_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
|
|
3601
|
-
const
|
|
3601
|
+
const LOCALIZABLE_ATTRIBUTES2 = {
|
|
3602
3602
|
meta: ["content"],
|
|
3603
3603
|
img: ["alt", "title"],
|
|
3604
3604
|
input: ["placeholder", "title"],
|
|
@@ -3644,12 +3644,12 @@ function createHtmlLoader() {
|
|
|
3644
3644
|
}
|
|
3645
3645
|
return hasTranslatableContent(element);
|
|
3646
3646
|
}
|
|
3647
|
-
function
|
|
3647
|
+
function getInnerHTML2(element) {
|
|
3648
3648
|
return element.children.map((child) => DomSerializer.default(child, { encodeEntities: false })).join("");
|
|
3649
3649
|
}
|
|
3650
3650
|
function extractAttributes(element, path19) {
|
|
3651
3651
|
const tagName = element.name.toLowerCase();
|
|
3652
|
-
const attrs =
|
|
3652
|
+
const attrs = LOCALIZABLE_ATTRIBUTES2[tagName];
|
|
3653
3653
|
if (!attrs) return;
|
|
3654
3654
|
for (const attr of attrs) {
|
|
3655
3655
|
const value = element.attribs?.[attr];
|
|
@@ -3666,14 +3666,14 @@ function createHtmlLoader() {
|
|
|
3666
3666
|
extractAttributes(element, path19);
|
|
3667
3667
|
const tagName = element.name.toLowerCase();
|
|
3668
3668
|
if (BLOCK_ELEMENTS.has(tagName) && isLeafBlock(element)) {
|
|
3669
|
-
const content =
|
|
3669
|
+
const content = getInnerHTML2(element).trim();
|
|
3670
3670
|
if (content) {
|
|
3671
3671
|
result[path19] = content;
|
|
3672
3672
|
}
|
|
3673
3673
|
return;
|
|
3674
3674
|
}
|
|
3675
3675
|
if (PHRASING_ELEMENTS.has(tagName) && hasTranslatableContent(element)) {
|
|
3676
|
-
const content =
|
|
3676
|
+
const content = getInnerHTML2(element).trim();
|
|
3677
3677
|
if (content) {
|
|
3678
3678
|
result[path19] = content;
|
|
3679
3679
|
}
|
|
@@ -4004,6 +4004,264 @@ function applyTranslations(node, path19, data, pathMap) {
|
|
|
4004
4004
|
}
|
|
4005
4005
|
}
|
|
4006
4006
|
|
|
4007
|
+
// src/cli/loaders/mjml.ts
|
|
4008
|
+
import { parseStringPromise as parseStringPromise2 } from "xml2js";
|
|
4009
|
+
import * as htmlparser22 from "htmlparser2";
|
|
4010
|
+
import { DomHandler as DomHandler2 } from "domhandler";
|
|
4011
|
+
var LOCALIZABLE_COMPONENTS = [
|
|
4012
|
+
"mj-text",
|
|
4013
|
+
"mj-button",
|
|
4014
|
+
"mj-title",
|
|
4015
|
+
"mj-preview",
|
|
4016
|
+
"mj-navbar-link",
|
|
4017
|
+
"mj-accordion-title",
|
|
4018
|
+
"mj-accordion-text",
|
|
4019
|
+
"p",
|
|
4020
|
+
"h1",
|
|
4021
|
+
"h2",
|
|
4022
|
+
"h3",
|
|
4023
|
+
"h4",
|
|
4024
|
+
"h5",
|
|
4025
|
+
"h6",
|
|
4026
|
+
"li"
|
|
4027
|
+
];
|
|
4028
|
+
var LOCALIZABLE_ATTRIBUTES = {
|
|
4029
|
+
"mj-image": ["alt", "title"],
|
|
4030
|
+
"mj-button": ["title", "aria-label"],
|
|
4031
|
+
"mj-social-element": ["title", "alt"],
|
|
4032
|
+
"img": ["alt", "title"],
|
|
4033
|
+
"a": ["title", "aria-label"]
|
|
4034
|
+
};
|
|
4035
|
+
function createMjmlLoader() {
|
|
4036
|
+
return createLoader({
|
|
4037
|
+
async pull(locale, input2) {
|
|
4038
|
+
const result = {};
|
|
4039
|
+
try {
|
|
4040
|
+
const parsed = await parseStringPromise2(input2, {
|
|
4041
|
+
explicitArray: true,
|
|
4042
|
+
explicitChildren: true,
|
|
4043
|
+
preserveChildrenOrder: true,
|
|
4044
|
+
charsAsChildren: true,
|
|
4045
|
+
includeWhiteChars: true,
|
|
4046
|
+
mergeAttrs: false,
|
|
4047
|
+
trim: false,
|
|
4048
|
+
attrkey: "$",
|
|
4049
|
+
charkey: "_",
|
|
4050
|
+
childkey: "$$"
|
|
4051
|
+
});
|
|
4052
|
+
if (!parsed || typeof parsed !== "object") {
|
|
4053
|
+
console.error("Failed to parse MJML: invalid parsed structure");
|
|
4054
|
+
return result;
|
|
4055
|
+
}
|
|
4056
|
+
const rootKey = Object.keys(parsed).find((key) => !key.startsWith("_") && !key.startsWith("$"));
|
|
4057
|
+
const rootNode = rootKey ? parsed[rootKey] : parsed;
|
|
4058
|
+
const rootPath = rootNode["#name"] || rootKey || "";
|
|
4059
|
+
traverse(rootNode, (node, path19, componentName) => {
|
|
4060
|
+
if (typeof node !== "object") return;
|
|
4061
|
+
const localizableAttrs = LOCALIZABLE_ATTRIBUTES[componentName];
|
|
4062
|
+
if (localizableAttrs && node.$) {
|
|
4063
|
+
localizableAttrs.forEach((attr) => {
|
|
4064
|
+
const attrValue = node.$[attr];
|
|
4065
|
+
if (attrValue) {
|
|
4066
|
+
result[`${path19}#${attr}`] = attrValue;
|
|
4067
|
+
}
|
|
4068
|
+
});
|
|
4069
|
+
}
|
|
4070
|
+
if (LOCALIZABLE_COMPONENTS.includes(componentName)) {
|
|
4071
|
+
const innerHTML = getInnerHTML(node);
|
|
4072
|
+
if (innerHTML) {
|
|
4073
|
+
result[path19] = innerHTML;
|
|
4074
|
+
return "SKIP_CHILDREN";
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
return void 0;
|
|
4078
|
+
}, rootPath);
|
|
4079
|
+
} catch (error) {
|
|
4080
|
+
console.error("Failed to parse MJML:", error);
|
|
4081
|
+
}
|
|
4082
|
+
return result;
|
|
4083
|
+
},
|
|
4084
|
+
async push(locale, data, originalInput) {
|
|
4085
|
+
try {
|
|
4086
|
+
const parsed = await parseStringPromise2(originalInput || "", {
|
|
4087
|
+
explicitArray: true,
|
|
4088
|
+
explicitChildren: true,
|
|
4089
|
+
preserveChildrenOrder: true,
|
|
4090
|
+
charsAsChildren: true,
|
|
4091
|
+
includeWhiteChars: true,
|
|
4092
|
+
mergeAttrs: false,
|
|
4093
|
+
trim: false,
|
|
4094
|
+
attrkey: "$",
|
|
4095
|
+
charkey: "_",
|
|
4096
|
+
childkey: "$$"
|
|
4097
|
+
});
|
|
4098
|
+
if (!parsed || typeof parsed !== "object") {
|
|
4099
|
+
console.error("Failed to parse MJML for push: invalid parsed structure");
|
|
4100
|
+
return originalInput || "";
|
|
4101
|
+
}
|
|
4102
|
+
const rootKey = Object.keys(parsed).find((key) => !key.startsWith("_") && !key.startsWith("$"));
|
|
4103
|
+
const rootNode = rootKey ? parsed[rootKey] : parsed;
|
|
4104
|
+
const rootPath = rootNode["#name"] || rootKey || "";
|
|
4105
|
+
traverse(rootNode, (node, path19, componentName) => {
|
|
4106
|
+
if (typeof node !== "object") return;
|
|
4107
|
+
const localizableAttrs = LOCALIZABLE_ATTRIBUTES[componentName];
|
|
4108
|
+
if (localizableAttrs && node.$) {
|
|
4109
|
+
localizableAttrs.forEach((attr) => {
|
|
4110
|
+
const attrKey = `${path19}#${attr}`;
|
|
4111
|
+
if (data[attrKey] !== void 0) {
|
|
4112
|
+
node.$[attr] = data[attrKey];
|
|
4113
|
+
}
|
|
4114
|
+
});
|
|
4115
|
+
}
|
|
4116
|
+
if (LOCALIZABLE_COMPONENTS.includes(componentName) && data[path19]) {
|
|
4117
|
+
setInnerHTML(node, data[path19]);
|
|
4118
|
+
return "SKIP_CHILDREN";
|
|
4119
|
+
}
|
|
4120
|
+
return void 0;
|
|
4121
|
+
}, rootPath);
|
|
4122
|
+
return serializeMjml(parsed);
|
|
4123
|
+
} catch (error) {
|
|
4124
|
+
console.error("Failed to build MJML:", error);
|
|
4125
|
+
return "";
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
function traverse(node, visitor, path19 = "") {
|
|
4131
|
+
if (!node || typeof node !== "object") {
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
const children = node.$$;
|
|
4135
|
+
if (!Array.isArray(children)) {
|
|
4136
|
+
return;
|
|
4137
|
+
}
|
|
4138
|
+
const elementCounts = /* @__PURE__ */ new Map();
|
|
4139
|
+
children.forEach((child) => {
|
|
4140
|
+
const elementName = child["#name"];
|
|
4141
|
+
if (!elementName || elementName.startsWith("__")) {
|
|
4142
|
+
return;
|
|
4143
|
+
}
|
|
4144
|
+
const currentIndex = elementCounts.get(elementName) || 0;
|
|
4145
|
+
elementCounts.set(elementName, currentIndex + 1);
|
|
4146
|
+
const currentPath = path19 ? `${path19}/${elementName}/${currentIndex}` : `${elementName}/${currentIndex}`;
|
|
4147
|
+
const result = visitor(child, currentPath, elementName);
|
|
4148
|
+
if (result !== "SKIP_CHILDREN") {
|
|
4149
|
+
traverse(child, visitor, currentPath);
|
|
4150
|
+
}
|
|
4151
|
+
});
|
|
4152
|
+
}
|
|
4153
|
+
function getInnerHTML(node) {
|
|
4154
|
+
if (!node.$$ || !Array.isArray(node.$$)) {
|
|
4155
|
+
return null;
|
|
4156
|
+
}
|
|
4157
|
+
let html = "";
|
|
4158
|
+
node.$$.forEach((child) => {
|
|
4159
|
+
html += serializeXmlNode(child);
|
|
4160
|
+
});
|
|
4161
|
+
return html.trim() || null;
|
|
4162
|
+
}
|
|
4163
|
+
function setInnerHTML(node, htmlContent) {
|
|
4164
|
+
const handler = new DomHandler2();
|
|
4165
|
+
const parser = new htmlparser22.Parser(handler);
|
|
4166
|
+
parser.write(htmlContent);
|
|
4167
|
+
parser.end();
|
|
4168
|
+
const newChildren = [];
|
|
4169
|
+
for (const domNode of handler.dom) {
|
|
4170
|
+
const xmlNode = convertDomToXmlNode(domNode);
|
|
4171
|
+
if (xmlNode) {
|
|
4172
|
+
newChildren.push(xmlNode);
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
node.$$ = newChildren;
|
|
4176
|
+
node._ = htmlContent;
|
|
4177
|
+
}
|
|
4178
|
+
function serializeXmlNode(node) {
|
|
4179
|
+
const name = node["#name"];
|
|
4180
|
+
if (name === "__text__") {
|
|
4181
|
+
return node._ || "";
|
|
4182
|
+
}
|
|
4183
|
+
if (name === "__cdata") {
|
|
4184
|
+
return `<![CDATA[${node._ || ""}]]>`;
|
|
4185
|
+
}
|
|
4186
|
+
if (!name || name.startsWith("__")) {
|
|
4187
|
+
return "";
|
|
4188
|
+
}
|
|
4189
|
+
const attrs = node.$ || {};
|
|
4190
|
+
const attrString = Object.entries(attrs).map(([key, value]) => ` ${key}="${escapeAttributeValue2(String(value))}"`).join("");
|
|
4191
|
+
const children = node.$$ || [];
|
|
4192
|
+
if (children.length === 0) {
|
|
4193
|
+
const textContent3 = node._ || "";
|
|
4194
|
+
if (textContent3) {
|
|
4195
|
+
return `<${name}${attrString}>${textContent3}</${name}>`;
|
|
4196
|
+
}
|
|
4197
|
+
return `<${name}${attrString} />`;
|
|
4198
|
+
}
|
|
4199
|
+
const childContent = children.map(serializeXmlNode).join("");
|
|
4200
|
+
return `<${name}${attrString}>${childContent}</${name}>`;
|
|
4201
|
+
}
|
|
4202
|
+
function convertDomToXmlNode(domNode) {
|
|
4203
|
+
if (domNode.type === "text") {
|
|
4204
|
+
return {
|
|
4205
|
+
"#name": "__text__",
|
|
4206
|
+
"_": domNode.data
|
|
4207
|
+
};
|
|
4208
|
+
}
|
|
4209
|
+
if (domNode.type === "tag") {
|
|
4210
|
+
const xmlNode = {
|
|
4211
|
+
"#name": domNode.name,
|
|
4212
|
+
"$": domNode.attribs || {},
|
|
4213
|
+
"$$": []
|
|
4214
|
+
};
|
|
4215
|
+
if (domNode.children && domNode.children.length > 0) {
|
|
4216
|
+
for (const child of domNode.children) {
|
|
4217
|
+
const xmlChild = convertDomToXmlNode(child);
|
|
4218
|
+
if (xmlChild) {
|
|
4219
|
+
xmlNode.$$.push(xmlChild);
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
return xmlNode;
|
|
4224
|
+
}
|
|
4225
|
+
return null;
|
|
4226
|
+
}
|
|
4227
|
+
function serializeMjml(parsed) {
|
|
4228
|
+
const xmlDec = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
4229
|
+
const rootKey = Object.keys(parsed).find((key) => !key.startsWith("_") && !key.startsWith("$"));
|
|
4230
|
+
const rootNode = rootKey ? parsed[rootKey] : parsed;
|
|
4231
|
+
const body = serializeElement2(rootNode);
|
|
4232
|
+
return xmlDec + body;
|
|
4233
|
+
}
|
|
4234
|
+
function serializeElement2(node, indent2 = "") {
|
|
4235
|
+
if (!node) {
|
|
4236
|
+
return "";
|
|
4237
|
+
}
|
|
4238
|
+
const name = node["#name"] ?? "mjml";
|
|
4239
|
+
if (name === "__text__") {
|
|
4240
|
+
return node._ ?? "";
|
|
4241
|
+
}
|
|
4242
|
+
if (name === "__cdata") {
|
|
4243
|
+
return `<![CDATA[${node._ ?? ""}]]>`;
|
|
4244
|
+
}
|
|
4245
|
+
if (name === "__comment__") {
|
|
4246
|
+
return `<!--${node._ ?? ""}-->`;
|
|
4247
|
+
}
|
|
4248
|
+
const attributes = node.$ ?? {};
|
|
4249
|
+
const attrString = Object.entries(attributes).map(([key, value]) => ` ${key}="${escapeAttributeValue2(String(value))}"`).join("");
|
|
4250
|
+
const children = Array.isArray(node.$$) ? node.$$ : [];
|
|
4251
|
+
if (children.length === 0) {
|
|
4252
|
+
const textContent3 = node._ ?? "";
|
|
4253
|
+
if (textContent3) {
|
|
4254
|
+
return `${indent2}<${name}${attrString}>${textContent3}</${name}>`;
|
|
4255
|
+
}
|
|
4256
|
+
return `${indent2}<${name}${attrString} />`;
|
|
4257
|
+
}
|
|
4258
|
+
const childContent = children.map((child) => serializeElement2(child, indent2)).join("");
|
|
4259
|
+
return `${indent2}<${name}${attrString}>${childContent}</${name}>`;
|
|
4260
|
+
}
|
|
4261
|
+
function escapeAttributeValue2(value) {
|
|
4262
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'");
|
|
4263
|
+
}
|
|
4264
|
+
|
|
4007
4265
|
// src/cli/loaders/properties.ts
|
|
4008
4266
|
function createPropertiesLoader() {
|
|
4009
4267
|
return createLoader({
|
|
@@ -4267,7 +4525,7 @@ function escapeString(str) {
|
|
|
4267
4525
|
}
|
|
4268
4526
|
|
|
4269
4527
|
// src/cli/loaders/xcode-strings/parser.ts
|
|
4270
|
-
var
|
|
4528
|
+
var Parser3 = class {
|
|
4271
4529
|
tokens;
|
|
4272
4530
|
pos;
|
|
4273
4531
|
constructor(tokens) {
|
|
@@ -4340,7 +4598,7 @@ function createXcodeStringsLoader() {
|
|
|
4340
4598
|
async pull(locale, input2) {
|
|
4341
4599
|
const tokenizer = new Tokenizer(input2);
|
|
4342
4600
|
const tokens = tokenizer.tokenize();
|
|
4343
|
-
const parser = new
|
|
4601
|
+
const parser = new Parser3(tokens);
|
|
4344
4602
|
const result = parser.parse();
|
|
4345
4603
|
return result;
|
|
4346
4604
|
},
|
|
@@ -5552,10 +5810,10 @@ function formatXml(xml) {
|
|
|
5552
5810
|
if (cdataNode) {
|
|
5553
5811
|
return `${indent2}${openTag}<![CDATA[${cdataNode.nodeValue}]]></${tagName}>`;
|
|
5554
5812
|
}
|
|
5555
|
-
const
|
|
5813
|
+
const textContent3 = element.textContent?.trim() || "";
|
|
5556
5814
|
const hasOnlyText = element.childNodes.length === 1 && element.childNodes[0].nodeType === 3;
|
|
5557
|
-
if (hasOnlyText &&
|
|
5558
|
-
return `${indent2}${openTag}${
|
|
5815
|
+
if (hasOnlyText && textContent3) {
|
|
5816
|
+
return `${indent2}${openTag}${textContent3}</${tagName}>`;
|
|
5559
5817
|
}
|
|
5560
5818
|
const children = Array.from(element.children);
|
|
5561
5819
|
if (children.length === 0) {
|
|
@@ -5607,7 +5865,7 @@ function pushNewFile(locale, translations, originalLocale) {
|
|
|
5607
5865
|
}
|
|
5608
5866
|
|
|
5609
5867
|
// src/cli/loaders/xml.ts
|
|
5610
|
-
import { parseStringPromise as
|
|
5868
|
+
import { parseStringPromise as parseStringPromise3, Builder as Builder2 } from "xml2js";
|
|
5611
5869
|
function normalizeXMLString(xmlString) {
|
|
5612
5870
|
return xmlString.replace(/\s+/g, " ").replace(/>\s+</g, "><").replace("\n", "").trim();
|
|
5613
5871
|
}
|
|
@@ -5616,7 +5874,7 @@ function createXmlLoader() {
|
|
|
5616
5874
|
async pull(locale, input2) {
|
|
5617
5875
|
let result = {};
|
|
5618
5876
|
try {
|
|
5619
|
-
const parsed = await
|
|
5877
|
+
const parsed = await parseStringPromise3(input2, {
|
|
5620
5878
|
explicitArray: false,
|
|
5621
5879
|
mergeAttrs: false,
|
|
5622
5880
|
normalize: true,
|
|
@@ -5634,7 +5892,7 @@ function createXmlLoader() {
|
|
|
5634
5892
|
},
|
|
5635
5893
|
async push(locale, data) {
|
|
5636
5894
|
try {
|
|
5637
|
-
const builder = new
|
|
5895
|
+
const builder = new Builder2({ headless: true });
|
|
5638
5896
|
const xmlOutput = builder.buildObject(data);
|
|
5639
5897
|
const expectedOutput = normalizeXMLString(xmlOutput);
|
|
5640
5898
|
return expectedOutput;
|
|
@@ -6727,7 +6985,7 @@ function resolveCjsExport(mod, name = "module") {
|
|
|
6727
6985
|
}
|
|
6728
6986
|
|
|
6729
6987
|
// src/cli/loaders/typescript/index.ts
|
|
6730
|
-
var
|
|
6988
|
+
var traverse2 = resolveCjsExport(babelTraverseModule, "@babel/traverse");
|
|
6731
6989
|
var generate = resolveCjsExport(babelGenerateModule, "@babel/generator");
|
|
6732
6990
|
function createTypescriptLoader() {
|
|
6733
6991
|
return createLoader({
|
|
@@ -6760,7 +7018,7 @@ function parseTypeScript(input2) {
|
|
|
6760
7018
|
}
|
|
6761
7019
|
function extractStringsFromDefaultExport(ast) {
|
|
6762
7020
|
let extracted = {};
|
|
6763
|
-
|
|
7021
|
+
traverse2(ast, {
|
|
6764
7022
|
ExportDefaultDeclaration(path19) {
|
|
6765
7023
|
const { declaration } = path19.node;
|
|
6766
7024
|
const decl = unwrapTSAsExpression(declaration);
|
|
@@ -6833,7 +7091,7 @@ function arrayExpressionToArray(arrayExpression) {
|
|
|
6833
7091
|
}
|
|
6834
7092
|
function updateStringsInDefaultExport(ast, data) {
|
|
6835
7093
|
let modified = false;
|
|
6836
|
-
|
|
7094
|
+
traverse2(ast, {
|
|
6837
7095
|
ExportDefaultDeclaration(path19) {
|
|
6838
7096
|
const { declaration } = path19.node;
|
|
6839
7097
|
const decl = unwrapTSAsExpression(declaration);
|
|
@@ -8875,14 +9133,14 @@ function parseEjsForTranslation(input2) {
|
|
|
8875
9133
|
if (part.type === "ejs") {
|
|
8876
9134
|
template += part.content;
|
|
8877
9135
|
} else {
|
|
8878
|
-
const
|
|
9136
|
+
const textContent3 = part.content;
|
|
8879
9137
|
const htmlTagRegex = /<[^>]+>/g;
|
|
8880
9138
|
const textParts = [];
|
|
8881
9139
|
let lastTextIndex = 0;
|
|
8882
9140
|
let htmlMatch;
|
|
8883
|
-
while ((htmlMatch = htmlTagRegex.exec(
|
|
9141
|
+
while ((htmlMatch = htmlTagRegex.exec(textContent3)) !== null) {
|
|
8884
9142
|
if (htmlMatch.index > lastTextIndex) {
|
|
8885
|
-
const textBefore =
|
|
9143
|
+
const textBefore = textContent3.slice(lastTextIndex, htmlMatch.index);
|
|
8886
9144
|
if (textBefore.trim()) {
|
|
8887
9145
|
textParts.push({ type: "text", content: textBefore });
|
|
8888
9146
|
} else {
|
|
@@ -8892,8 +9150,8 @@ function parseEjsForTranslation(input2) {
|
|
|
8892
9150
|
textParts.push({ type: "html", content: htmlMatch[0] });
|
|
8893
9151
|
lastTextIndex = htmlMatch.index + htmlMatch[0].length;
|
|
8894
9152
|
}
|
|
8895
|
-
if (lastTextIndex <
|
|
8896
|
-
const remainingText =
|
|
9153
|
+
if (lastTextIndex < textContent3.length) {
|
|
9154
|
+
const remainingText = textContent3.slice(lastTextIndex);
|
|
8897
9155
|
if (remainingText.trim()) {
|
|
8898
9156
|
textParts.push({ type: "text", content: remainingText });
|
|
8899
9157
|
} else {
|
|
@@ -8901,11 +9159,11 @@ function parseEjsForTranslation(input2) {
|
|
|
8901
9159
|
}
|
|
8902
9160
|
}
|
|
8903
9161
|
if (textParts.length === 0) {
|
|
8904
|
-
const trimmedContent =
|
|
9162
|
+
const trimmedContent = textContent3.trim();
|
|
8905
9163
|
if (trimmedContent) {
|
|
8906
|
-
textParts.push({ type: "text", content:
|
|
9164
|
+
textParts.push({ type: "text", content: textContent3 });
|
|
8907
9165
|
} else {
|
|
8908
|
-
textParts.push({ type: "html", content:
|
|
9166
|
+
textParts.push({ type: "html", content: textContent3 });
|
|
8909
9167
|
}
|
|
8910
9168
|
}
|
|
8911
9169
|
for (const textPart of textParts) {
|
|
@@ -8974,6 +9232,368 @@ function createEjsLoader() {
|
|
|
8974
9232
|
});
|
|
8975
9233
|
}
|
|
8976
9234
|
|
|
9235
|
+
// src/cli/loaders/twig.ts
|
|
9236
|
+
import * as htmlparser23 from "htmlparser2";
|
|
9237
|
+
import { DomHandler as DomHandler3 } from "domhandler";
|
|
9238
|
+
import * as domutils2 from "domutils";
|
|
9239
|
+
import * as DomSerializer2 from "dom-serializer";
|
|
9240
|
+
function createTwigLoader() {
|
|
9241
|
+
const PHRASING_ELEMENTS = /* @__PURE__ */ new Set([
|
|
9242
|
+
// Text-level semantics
|
|
9243
|
+
"a",
|
|
9244
|
+
"abbr",
|
|
9245
|
+
"b",
|
|
9246
|
+
"bdi",
|
|
9247
|
+
"bdo",
|
|
9248
|
+
"br",
|
|
9249
|
+
"cite",
|
|
9250
|
+
"code",
|
|
9251
|
+
"data",
|
|
9252
|
+
"dfn",
|
|
9253
|
+
"em",
|
|
9254
|
+
"i",
|
|
9255
|
+
"kbd",
|
|
9256
|
+
"mark",
|
|
9257
|
+
"q",
|
|
9258
|
+
"ruby",
|
|
9259
|
+
"s",
|
|
9260
|
+
"samp",
|
|
9261
|
+
"small",
|
|
9262
|
+
"span",
|
|
9263
|
+
"strong",
|
|
9264
|
+
"sub",
|
|
9265
|
+
"sup",
|
|
9266
|
+
"time",
|
|
9267
|
+
"u",
|
|
9268
|
+
"var",
|
|
9269
|
+
"wbr",
|
|
9270
|
+
// Media
|
|
9271
|
+
"audio",
|
|
9272
|
+
"img",
|
|
9273
|
+
"video",
|
|
9274
|
+
"picture",
|
|
9275
|
+
// Interactive
|
|
9276
|
+
"button",
|
|
9277
|
+
"input",
|
|
9278
|
+
"label",
|
|
9279
|
+
"select",
|
|
9280
|
+
"textarea",
|
|
9281
|
+
// Embedded
|
|
9282
|
+
"canvas",
|
|
9283
|
+
"iframe",
|
|
9284
|
+
"object",
|
|
9285
|
+
"svg",
|
|
9286
|
+
"math",
|
|
9287
|
+
// Other
|
|
9288
|
+
"del",
|
|
9289
|
+
"ins",
|
|
9290
|
+
"map",
|
|
9291
|
+
"area"
|
|
9292
|
+
]);
|
|
9293
|
+
const BLOCK_ELEMENTS = /* @__PURE__ */ new Set([
|
|
9294
|
+
"div",
|
|
9295
|
+
"p",
|
|
9296
|
+
"h1",
|
|
9297
|
+
"h2",
|
|
9298
|
+
"h3",
|
|
9299
|
+
"h4",
|
|
9300
|
+
"h5",
|
|
9301
|
+
"h6",
|
|
9302
|
+
"ul",
|
|
9303
|
+
"ol",
|
|
9304
|
+
"li",
|
|
9305
|
+
"dl",
|
|
9306
|
+
"dt",
|
|
9307
|
+
"dd",
|
|
9308
|
+
"blockquote",
|
|
9309
|
+
"pre",
|
|
9310
|
+
"article",
|
|
9311
|
+
"aside",
|
|
9312
|
+
"nav",
|
|
9313
|
+
"section",
|
|
9314
|
+
"header",
|
|
9315
|
+
"footer",
|
|
9316
|
+
"main",
|
|
9317
|
+
"figure",
|
|
9318
|
+
"figcaption",
|
|
9319
|
+
"table",
|
|
9320
|
+
"thead",
|
|
9321
|
+
"tbody",
|
|
9322
|
+
"tfoot",
|
|
9323
|
+
"tr",
|
|
9324
|
+
"td",
|
|
9325
|
+
"th",
|
|
9326
|
+
"caption",
|
|
9327
|
+
"form",
|
|
9328
|
+
"fieldset",
|
|
9329
|
+
"legend",
|
|
9330
|
+
"details",
|
|
9331
|
+
"summary",
|
|
9332
|
+
"address",
|
|
9333
|
+
"hr",
|
|
9334
|
+
"search",
|
|
9335
|
+
"dialog",
|
|
9336
|
+
"noscript",
|
|
9337
|
+
"title"
|
|
9338
|
+
]);
|
|
9339
|
+
const UNLOCALIZABLE_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
|
|
9340
|
+
const LOCALIZABLE_ATTRIBUTES2 = {
|
|
9341
|
+
meta: ["content"],
|
|
9342
|
+
img: ["alt", "title"],
|
|
9343
|
+
input: ["placeholder", "title", "aria-label"],
|
|
9344
|
+
textarea: ["placeholder", "title", "aria-label"],
|
|
9345
|
+
button: ["title", "aria-label"],
|
|
9346
|
+
a: ["title", "aria-label"],
|
|
9347
|
+
abbr: ["title"],
|
|
9348
|
+
link: ["title"]
|
|
9349
|
+
};
|
|
9350
|
+
function preprocessTwig(input2) {
|
|
9351
|
+
const twigBlocks = [];
|
|
9352
|
+
let counter = 0;
|
|
9353
|
+
const processed = input2.replace(/\{%[\s\S]*?%\}/g, (match2) => {
|
|
9354
|
+
twigBlocks.push(match2);
|
|
9355
|
+
return `__TWIG_BLOCK_${counter++}__`;
|
|
9356
|
+
});
|
|
9357
|
+
return {
|
|
9358
|
+
processed: processed.replace(/\{#[\s\S]*?#\}/g, (match2) => {
|
|
9359
|
+
twigBlocks.push(match2);
|
|
9360
|
+
return `__TWIG_BLOCK_${counter++}__`;
|
|
9361
|
+
}),
|
|
9362
|
+
twigBlocks
|
|
9363
|
+
};
|
|
9364
|
+
}
|
|
9365
|
+
function postprocessTwig(text, twigBlocks) {
|
|
9366
|
+
return text.replace(/__TWIG_BLOCK_(\d+)__/g, (_37, index) => {
|
|
9367
|
+
return twigBlocks[parseInt(index, 10)] || "";
|
|
9368
|
+
});
|
|
9369
|
+
}
|
|
9370
|
+
return createLoader({
|
|
9371
|
+
async pull(locale, input2) {
|
|
9372
|
+
const result = {};
|
|
9373
|
+
const { processed, twigBlocks } = preprocessTwig(input2);
|
|
9374
|
+
const handler = new DomHandler3();
|
|
9375
|
+
const parser = new htmlparser23.Parser(handler, {
|
|
9376
|
+
lowerCaseTags: false,
|
|
9377
|
+
lowerCaseAttributeNames: false
|
|
9378
|
+
});
|
|
9379
|
+
parser.write(processed);
|
|
9380
|
+
parser.end();
|
|
9381
|
+
const dom = handler.dom;
|
|
9382
|
+
function isInsideUnlocalizableTag(element) {
|
|
9383
|
+
let current = element.parent;
|
|
9384
|
+
while (current && current.type === "tag") {
|
|
9385
|
+
if (UNLOCALIZABLE_TAGS.has(current.name.toLowerCase())) {
|
|
9386
|
+
return true;
|
|
9387
|
+
}
|
|
9388
|
+
current = current.parent;
|
|
9389
|
+
}
|
|
9390
|
+
return false;
|
|
9391
|
+
}
|
|
9392
|
+
function hasTranslatableContent(element) {
|
|
9393
|
+
const text = domutils2.textContent(element);
|
|
9394
|
+
return text.trim().length > 0;
|
|
9395
|
+
}
|
|
9396
|
+
function isLeafBlock(element) {
|
|
9397
|
+
const childElements = element.children.filter(
|
|
9398
|
+
(child) => child.type === "tag"
|
|
9399
|
+
);
|
|
9400
|
+
for (const child of childElements) {
|
|
9401
|
+
if (BLOCK_ELEMENTS.has(child.name.toLowerCase())) {
|
|
9402
|
+
return false;
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
return hasTranslatableContent(element);
|
|
9406
|
+
}
|
|
9407
|
+
function getInnerHTML2(element) {
|
|
9408
|
+
const html2 = element.children.map((child) => DomSerializer2.default(child, { encodeEntities: false })).join("");
|
|
9409
|
+
return postprocessTwig(html2, twigBlocks);
|
|
9410
|
+
}
|
|
9411
|
+
function extractAttributes(element, path19) {
|
|
9412
|
+
const tagName = element.name.toLowerCase();
|
|
9413
|
+
const attrs = LOCALIZABLE_ATTRIBUTES2[tagName];
|
|
9414
|
+
if (!attrs) return;
|
|
9415
|
+
for (const attr of attrs) {
|
|
9416
|
+
const value = element.attribs?.[attr];
|
|
9417
|
+
if (value && value.trim()) {
|
|
9418
|
+
const restoredValue = postprocessTwig(value.trim(), twigBlocks);
|
|
9419
|
+
result[`${path19}#${attr}`] = restoredValue;
|
|
9420
|
+
}
|
|
9421
|
+
}
|
|
9422
|
+
}
|
|
9423
|
+
function extractFromElement(element, pathParts) {
|
|
9424
|
+
const path19 = pathParts.join("/");
|
|
9425
|
+
if (isInsideUnlocalizableTag(element)) {
|
|
9426
|
+
return;
|
|
9427
|
+
}
|
|
9428
|
+
extractAttributes(element, path19);
|
|
9429
|
+
const tagName = element.name.toLowerCase();
|
|
9430
|
+
if (BLOCK_ELEMENTS.has(tagName) && isLeafBlock(element)) {
|
|
9431
|
+
const content = getInnerHTML2(element).trim();
|
|
9432
|
+
if (content) {
|
|
9433
|
+
result[path19] = content;
|
|
9434
|
+
}
|
|
9435
|
+
return;
|
|
9436
|
+
}
|
|
9437
|
+
if (PHRASING_ELEMENTS.has(tagName) && hasTranslatableContent(element)) {
|
|
9438
|
+
const content = getInnerHTML2(element).trim();
|
|
9439
|
+
if (content) {
|
|
9440
|
+
result[path19] = content;
|
|
9441
|
+
}
|
|
9442
|
+
return;
|
|
9443
|
+
}
|
|
9444
|
+
let childIndex = 0;
|
|
9445
|
+
const childElements = element.children.filter(
|
|
9446
|
+
(child) => child.type === "tag"
|
|
9447
|
+
);
|
|
9448
|
+
for (const child of childElements) {
|
|
9449
|
+
extractFromElement(child, [...pathParts, childIndex++]);
|
|
9450
|
+
}
|
|
9451
|
+
}
|
|
9452
|
+
const html = domutils2.findOne(
|
|
9453
|
+
(elem) => elem.type === "tag" && elem.name.toLowerCase() === "html",
|
|
9454
|
+
dom,
|
|
9455
|
+
true
|
|
9456
|
+
);
|
|
9457
|
+
if (html) {
|
|
9458
|
+
const head = domutils2.findOne(
|
|
9459
|
+
(elem) => elem.type === "tag" && elem.name.toLowerCase() === "head",
|
|
9460
|
+
html.children,
|
|
9461
|
+
true
|
|
9462
|
+
);
|
|
9463
|
+
const body = domutils2.findOne(
|
|
9464
|
+
(elem) => elem.type === "tag" && elem.name.toLowerCase() === "body",
|
|
9465
|
+
html.children,
|
|
9466
|
+
true
|
|
9467
|
+
);
|
|
9468
|
+
if (head) {
|
|
9469
|
+
let headIndex = 0;
|
|
9470
|
+
const headChildren = head.children.filter(
|
|
9471
|
+
(child) => child.type === "tag"
|
|
9472
|
+
);
|
|
9473
|
+
for (const child of headChildren) {
|
|
9474
|
+
extractFromElement(child, ["head", headIndex++]);
|
|
9475
|
+
}
|
|
9476
|
+
}
|
|
9477
|
+
if (body) {
|
|
9478
|
+
let bodyIndex = 0;
|
|
9479
|
+
const bodyChildren = body.children.filter(
|
|
9480
|
+
(child) => child.type === "tag"
|
|
9481
|
+
);
|
|
9482
|
+
for (const child of bodyChildren) {
|
|
9483
|
+
extractFromElement(child, ["body", bodyIndex++]);
|
|
9484
|
+
}
|
|
9485
|
+
}
|
|
9486
|
+
} else {
|
|
9487
|
+
let rootIndex = 0;
|
|
9488
|
+
const rootElements = dom.filter(
|
|
9489
|
+
(child) => child.type === "tag"
|
|
9490
|
+
);
|
|
9491
|
+
for (const child of rootElements) {
|
|
9492
|
+
extractFromElement(child, [rootIndex++]);
|
|
9493
|
+
}
|
|
9494
|
+
}
|
|
9495
|
+
return result;
|
|
9496
|
+
},
|
|
9497
|
+
async push(locale, data, originalInput) {
|
|
9498
|
+
const { processed, twigBlocks } = preprocessTwig(originalInput || "");
|
|
9499
|
+
const handler = new DomHandler3();
|
|
9500
|
+
const parser = new htmlparser23.Parser(handler, {
|
|
9501
|
+
lowerCaseTags: false,
|
|
9502
|
+
lowerCaseAttributeNames: false
|
|
9503
|
+
});
|
|
9504
|
+
parser.write(processed || "<!DOCTYPE html><html><head></head><body></body></html>");
|
|
9505
|
+
parser.end();
|
|
9506
|
+
const dom = handler.dom;
|
|
9507
|
+
const html = domutils2.findOne(
|
|
9508
|
+
(elem) => elem.type === "tag" && elem.name.toLowerCase() === "html",
|
|
9509
|
+
dom,
|
|
9510
|
+
true
|
|
9511
|
+
);
|
|
9512
|
+
if (html) {
|
|
9513
|
+
html.attribs = html.attribs || {};
|
|
9514
|
+
html.attribs.lang = locale;
|
|
9515
|
+
}
|
|
9516
|
+
function traverseByIndices(element, indices) {
|
|
9517
|
+
let current = element;
|
|
9518
|
+
for (const indexStr of indices) {
|
|
9519
|
+
if (!current) return null;
|
|
9520
|
+
const index = parseInt(indexStr, 10);
|
|
9521
|
+
const children = current.children.filter(
|
|
9522
|
+
(child) => child.type === "tag"
|
|
9523
|
+
);
|
|
9524
|
+
if (index >= children.length) {
|
|
9525
|
+
return null;
|
|
9526
|
+
}
|
|
9527
|
+
current = children[index];
|
|
9528
|
+
}
|
|
9529
|
+
return current;
|
|
9530
|
+
}
|
|
9531
|
+
function resolvePathToElement(path19) {
|
|
9532
|
+
const parts = path19.split("/");
|
|
9533
|
+
const [rootTag, ...indices] = parts;
|
|
9534
|
+
let current = null;
|
|
9535
|
+
if (html) {
|
|
9536
|
+
if (rootTag === "head") {
|
|
9537
|
+
current = domutils2.findOne(
|
|
9538
|
+
(elem) => elem.type === "tag" && elem.name.toLowerCase() === "head",
|
|
9539
|
+
html.children,
|
|
9540
|
+
true
|
|
9541
|
+
);
|
|
9542
|
+
} else if (rootTag === "body") {
|
|
9543
|
+
current = domutils2.findOne(
|
|
9544
|
+
(elem) => elem.type === "tag" && elem.name.toLowerCase() === "body",
|
|
9545
|
+
html.children,
|
|
9546
|
+
true
|
|
9547
|
+
);
|
|
9548
|
+
}
|
|
9549
|
+
if (!current) return null;
|
|
9550
|
+
return traverseByIndices(current, indices);
|
|
9551
|
+
} else {
|
|
9552
|
+
const rootElements = dom.filter(
|
|
9553
|
+
(child) => child.type === "tag"
|
|
9554
|
+
);
|
|
9555
|
+
const rootIndex = parseInt(rootTag, 10);
|
|
9556
|
+
if (rootIndex >= rootElements.length) {
|
|
9557
|
+
return null;
|
|
9558
|
+
}
|
|
9559
|
+
current = rootElements[rootIndex];
|
|
9560
|
+
return traverseByIndices(current, indices);
|
|
9561
|
+
}
|
|
9562
|
+
}
|
|
9563
|
+
for (const [path19, value] of Object.entries(data)) {
|
|
9564
|
+
const [nodePath, attribute] = path19.split("#");
|
|
9565
|
+
const element = resolvePathToElement(nodePath);
|
|
9566
|
+
if (!element) {
|
|
9567
|
+
console.warn(`Path not found in original template: ${nodePath}`);
|
|
9568
|
+
continue;
|
|
9569
|
+
}
|
|
9570
|
+
if (attribute) {
|
|
9571
|
+
element.attribs = element.attribs || {};
|
|
9572
|
+
element.attribs[attribute] = value;
|
|
9573
|
+
} else {
|
|
9574
|
+
if (value) {
|
|
9575
|
+
const { processed: processedValue, twigBlocks: valueTwigBlocks } = preprocessTwig(value);
|
|
9576
|
+
const valueHandler = new DomHandler3();
|
|
9577
|
+
const valueParser = new htmlparser23.Parser(valueHandler);
|
|
9578
|
+
valueParser.write(processedValue);
|
|
9579
|
+
valueParser.end();
|
|
9580
|
+
element.children = valueHandler.dom;
|
|
9581
|
+
element.children.forEach((child) => {
|
|
9582
|
+
if (child.type === "text" && child.data) {
|
|
9583
|
+
child.data = postprocessTwig(child.data, valueTwigBlocks);
|
|
9584
|
+
}
|
|
9585
|
+
});
|
|
9586
|
+
} else {
|
|
9587
|
+
element.children = [];
|
|
9588
|
+
}
|
|
9589
|
+
}
|
|
9590
|
+
}
|
|
9591
|
+
const serialized = DomSerializer2.default(dom, { encodeEntities: false });
|
|
9592
|
+
return postprocessTwig(serialized, twigBlocks);
|
|
9593
|
+
}
|
|
9594
|
+
});
|
|
9595
|
+
}
|
|
9596
|
+
|
|
8977
9597
|
// src/cli/loaders/ensure-key-order.ts
|
|
8978
9598
|
import _25 from "lodash";
|
|
8979
9599
|
function createEnsureKeyOrderLoader() {
|
|
@@ -9250,6 +9870,17 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
|
|
|
9250
9870
|
createSyncLoader(),
|
|
9251
9871
|
createUnlocalizableLoader(options.returnUnlocalizedKeys)
|
|
9252
9872
|
);
|
|
9873
|
+
case "mjml":
|
|
9874
|
+
return composeLoaders(
|
|
9875
|
+
createTextFileLoader(bucketPathPattern),
|
|
9876
|
+
createFormatterLoader(options.formatter, "html", bucketPathPattern),
|
|
9877
|
+
createLockedPatternsLoader(lockedPatterns),
|
|
9878
|
+
createMjmlLoader(),
|
|
9879
|
+
createLockedKeysLoader(lockedKeys || []),
|
|
9880
|
+
createIgnoredKeysLoader(ignoredKeys || []),
|
|
9881
|
+
createSyncLoader(),
|
|
9882
|
+
createUnlocalizableLoader(options.returnUnlocalizedKeys)
|
|
9883
|
+
);
|
|
9253
9884
|
case "po":
|
|
9254
9885
|
return composeLoaders(
|
|
9255
9886
|
createTextFileLoader(bucketPathPattern),
|
|
@@ -9461,6 +10092,16 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
|
|
|
9461
10092
|
createIgnoredKeysLoader(ignoredKeys || []),
|
|
9462
10093
|
createUnlocalizableLoader(options.returnUnlocalizedKeys)
|
|
9463
10094
|
);
|
|
10095
|
+
case "twig":
|
|
10096
|
+
return composeLoaders(
|
|
10097
|
+
createTextFileLoader(bucketPathPattern),
|
|
10098
|
+
createLockedPatternsLoader(lockedPatterns),
|
|
10099
|
+
createTwigLoader(),
|
|
10100
|
+
createLockedKeysLoader(lockedKeys || []),
|
|
10101
|
+
createIgnoredKeysLoader(ignoredKeys || []),
|
|
10102
|
+
createSyncLoader(),
|
|
10103
|
+
createUnlocalizableLoader(options.returnUnlocalizedKeys)
|
|
10104
|
+
);
|
|
9464
10105
|
case "txt":
|
|
9465
10106
|
return composeLoaders(
|
|
9466
10107
|
createTextFileLoader(bucketPathPattern),
|
|
@@ -13832,7 +14473,7 @@ async function renderHero2() {
|
|
|
13832
14473
|
// package.json
|
|
13833
14474
|
var package_default = {
|
|
13834
14475
|
name: "lingo.dev",
|
|
13835
|
-
version: "0.117.
|
|
14476
|
+
version: "0.117.8",
|
|
13836
14477
|
description: "Lingo.dev CLI",
|
|
13837
14478
|
private: false,
|
|
13838
14479
|
publishConfig: {
|