lingo.dev 0.105.1 → 0.105.2

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
@@ -2749,18 +2749,406 @@ function preserveCommentOrder(section, originalSection) {
2749
2749
  }
2750
2750
 
2751
2751
  // src/cli/loaders/xliff.ts
2752
- import xliff from "xliff";
2752
+ import { JSDOM as JSDOM2 } from "jsdom";
2753
2753
  function createXliffLoader() {
2754
2754
  return createLoader({
2755
- async pull(locale, input2) {
2756
- const js = await xliff.xliff2js(input2);
2757
- return js || {};
2755
+ async pull(locale, input2, _ctx, originalLocale) {
2756
+ const trimmedInput = (input2 ?? "").trim();
2757
+ if (!trimmedInput) {
2758
+ return createEmptyResult(originalLocale, locale);
2759
+ }
2760
+ try {
2761
+ const dom = new JSDOM2(trimmedInput, { contentType: "text/xml" });
2762
+ const document = dom.window.document;
2763
+ const parserError = document.querySelector("parsererror");
2764
+ if (parserError) {
2765
+ throw new Error(`XML parsing failed: ${parserError.textContent}`);
2766
+ }
2767
+ const xliffElement = document.documentElement;
2768
+ if (!xliffElement || xliffElement.tagName !== "xliff") {
2769
+ throw new Error("Invalid XLIFF: missing root <xliff> element");
2770
+ }
2771
+ const version = xliffElement.getAttribute("version") || "1.2";
2772
+ const isV2 = version === "2.0";
2773
+ if (isV2) {
2774
+ return pullV2(xliffElement, locale, originalLocale);
2775
+ } else {
2776
+ return pullV1(xliffElement, locale, originalLocale);
2777
+ }
2778
+ } catch (error) {
2779
+ throw new Error(`Failed to parse XLIFF file: ${error.message}`);
2780
+ }
2758
2781
  },
2759
- async push(locale, payload) {
2760
- const res = await xliff.js2xliff(payload);
2761
- return res.trim();
2782
+ async push(locale, translations, originalInput, originalLocale, pullInput) {
2783
+ if (!originalInput) {
2784
+ return pushNewFile(locale, translations, originalLocale);
2785
+ }
2786
+ try {
2787
+ const dom = new JSDOM2(originalInput, { contentType: "text/xml" });
2788
+ const document = dom.window.document;
2789
+ const xliffElement = document.documentElement;
2790
+ const version = xliffElement.getAttribute("version") || "1.2";
2791
+ const isV2 = version === "2.0";
2792
+ if (isV2) {
2793
+ return pushV2(
2794
+ dom,
2795
+ xliffElement,
2796
+ locale,
2797
+ translations,
2798
+ originalLocale,
2799
+ originalInput
2800
+ );
2801
+ } else {
2802
+ return pushV1(
2803
+ dom,
2804
+ xliffElement,
2805
+ locale,
2806
+ translations,
2807
+ originalLocale,
2808
+ originalInput
2809
+ );
2810
+ }
2811
+ } catch (error) {
2812
+ throw new Error(`Failed to update XLIFF file: ${error.message}`);
2813
+ }
2814
+ }
2815
+ });
2816
+ }
2817
+ function pullV1(xliffElement, locale, originalLocale) {
2818
+ const result = {};
2819
+ const fileElement = xliffElement.querySelector("file");
2820
+ if (!fileElement) {
2821
+ return result;
2822
+ }
2823
+ const sourceLanguage = fileElement.getAttribute("source-language") || originalLocale;
2824
+ const isSourceLocale = sourceLanguage === locale;
2825
+ const bodyElement = fileElement.querySelector("body");
2826
+ if (!bodyElement) {
2827
+ return result;
2828
+ }
2829
+ const transUnits = bodyElement.querySelectorAll("trans-unit");
2830
+ const seenKeys = /* @__PURE__ */ new Set();
2831
+ transUnits.forEach((unit) => {
2832
+ let key = getTransUnitKey(unit);
2833
+ if (!key) return;
2834
+ if (seenKeys.has(key)) {
2835
+ const id = unit.getAttribute("id")?.trim();
2836
+ if (id) {
2837
+ key = `${key}#${id}`;
2838
+ } else {
2839
+ let counter = 1;
2840
+ let newKey = `${key}__${counter}`;
2841
+ while (seenKeys.has(newKey)) {
2842
+ counter++;
2843
+ newKey = `${key}__${counter}`;
2844
+ }
2845
+ key = newKey;
2846
+ }
2847
+ }
2848
+ seenKeys.add(key);
2849
+ const elementName = isSourceLocale ? "source" : "target";
2850
+ const textElement = unit.querySelector(elementName);
2851
+ if (textElement) {
2852
+ result[key] = extractTextContent(textElement);
2853
+ } else if (isSourceLocale) {
2854
+ result[key] = key;
2855
+ } else {
2856
+ result[key] = "";
2857
+ }
2858
+ });
2859
+ return result;
2860
+ }
2861
+ function pushV1(dom, xliffElement, locale, translations, originalLocale, originalInput) {
2862
+ const document = dom.window.document;
2863
+ const fileElement = xliffElement.querySelector("file");
2864
+ if (!fileElement) {
2865
+ throw new Error("Invalid XLIFF 1.2: missing <file> element");
2866
+ }
2867
+ const sourceLanguage = fileElement.getAttribute("source-language") || originalLocale;
2868
+ const isSourceLocale = sourceLanguage === locale;
2869
+ if (!isSourceLocale) {
2870
+ fileElement.setAttribute("target-language", locale);
2871
+ }
2872
+ let bodyElement = fileElement.querySelector("body");
2873
+ if (!bodyElement) {
2874
+ bodyElement = document.createElement("body");
2875
+ fileElement.appendChild(bodyElement);
2876
+ }
2877
+ const existingUnits = /* @__PURE__ */ new Map();
2878
+ const seenKeys = /* @__PURE__ */ new Set();
2879
+ bodyElement.querySelectorAll("trans-unit").forEach((unit) => {
2880
+ let key = getTransUnitKey(unit);
2881
+ if (!key) return;
2882
+ if (seenKeys.has(key)) {
2883
+ const id = unit.getAttribute("id")?.trim();
2884
+ if (id) {
2885
+ key = `${key}#${id}`;
2886
+ } else {
2887
+ let counter = 1;
2888
+ let newKey = `${key}__${counter}`;
2889
+ while (seenKeys.has(newKey)) {
2890
+ counter++;
2891
+ newKey = `${key}__${counter}`;
2892
+ }
2893
+ key = newKey;
2894
+ }
2895
+ }
2896
+ seenKeys.add(key);
2897
+ existingUnits.set(key, unit);
2898
+ });
2899
+ Object.entries(translations).forEach(([key, value]) => {
2900
+ let unit = existingUnits.get(key);
2901
+ if (!unit) {
2902
+ unit = document.createElement("trans-unit");
2903
+ unit.setAttribute("id", key);
2904
+ unit.setAttribute("resname", key);
2905
+ unit.setAttribute("restype", "string");
2906
+ unit.setAttribute("datatype", "plaintext");
2907
+ const sourceElement = document.createElement("source");
2908
+ setTextContent(sourceElement, isSourceLocale ? value : key);
2909
+ unit.appendChild(sourceElement);
2910
+ if (!isSourceLocale) {
2911
+ const targetElement = document.createElement("target");
2912
+ targetElement.setAttribute("state", value ? "translated" : "new");
2913
+ setTextContent(targetElement, value);
2914
+ unit.appendChild(targetElement);
2915
+ }
2916
+ bodyElement.appendChild(unit);
2917
+ existingUnits.set(key, unit);
2918
+ } else {
2919
+ updateTransUnitV1(unit, key, value, isSourceLocale);
2920
+ }
2921
+ });
2922
+ const translationKeys = new Set(Object.keys(translations));
2923
+ existingUnits.forEach((unit, key) => {
2924
+ if (!translationKeys.has(key)) {
2925
+ unit.parentNode?.removeChild(unit);
2926
+ }
2927
+ });
2928
+ return serializeWithDeclaration(
2929
+ dom,
2930
+ extractXmlDeclaration(originalInput || "")
2931
+ );
2932
+ }
2933
+ function updateTransUnitV1(unit, key, value, isSourceLocale) {
2934
+ const document = unit.ownerDocument;
2935
+ if (isSourceLocale) {
2936
+ let sourceElement = unit.querySelector("source");
2937
+ if (!sourceElement) {
2938
+ sourceElement = document.createElement("source");
2939
+ unit.appendChild(sourceElement);
2940
+ }
2941
+ setTextContent(sourceElement, value);
2942
+ } else {
2943
+ let targetElement = unit.querySelector("target");
2944
+ if (!targetElement) {
2945
+ targetElement = document.createElement("target");
2946
+ unit.appendChild(targetElement);
2762
2947
  }
2948
+ setTextContent(targetElement, value);
2949
+ targetElement.setAttribute("state", value.trim() ? "translated" : "new");
2950
+ }
2951
+ }
2952
+ function pullV2(xliffElement, locale, originalLocale) {
2953
+ const result = {};
2954
+ const srcLang = xliffElement.getAttribute("srcLang") || originalLocale;
2955
+ result.sourceLanguage = srcLang;
2956
+ const fileElements = xliffElement.querySelectorAll("file");
2957
+ fileElements.forEach((fileElement) => {
2958
+ const fileId = fileElement.getAttribute("id");
2959
+ if (!fileId) return;
2960
+ traverseUnitsV2(fileElement, fileId, "", result);
2763
2961
  });
2962
+ return result;
2963
+ }
2964
+ function traverseUnitsV2(container, fileId, currentPath, result) {
2965
+ Array.from(container.children).forEach((child) => {
2966
+ const tagName = child.tagName;
2967
+ if (tagName === "unit") {
2968
+ const unitId = child.getAttribute("id")?.trim();
2969
+ if (!unitId) return;
2970
+ const key = `resources/${fileId}/${currentPath}${unitId}/source`;
2971
+ const segment = child.querySelector("segment");
2972
+ const source = segment?.querySelector("source");
2973
+ if (source) {
2974
+ result[key] = extractTextContent(source);
2975
+ } else {
2976
+ result[key] = unitId;
2977
+ }
2978
+ } else if (tagName === "group") {
2979
+ const groupId = child.getAttribute("id")?.trim();
2980
+ const newPath = groupId ? `${currentPath}${groupId}/groupUnits/` : currentPath;
2981
+ traverseUnitsV2(child, fileId, newPath, result);
2982
+ }
2983
+ });
2984
+ }
2985
+ function pushV2(dom, xliffElement, locale, translations, originalLocale, originalInput) {
2986
+ const document = dom.window.document;
2987
+ if (translations.sourceLanguage) {
2988
+ xliffElement.setAttribute("srcLang", translations.sourceLanguage);
2989
+ delete translations.sourceLanguage;
2990
+ }
2991
+ const existingUnits = /* @__PURE__ */ new Map();
2992
+ const fileElements = xliffElement.querySelectorAll("file");
2993
+ fileElements.forEach((fileElement) => {
2994
+ const fileId = fileElement.getAttribute("id");
2995
+ if (!fileId) return;
2996
+ indexUnitsV2(fileElement, fileId, "", existingUnits);
2997
+ });
2998
+ Object.entries(translations).forEach(([key, value]) => {
2999
+ const unit = existingUnits.get(key);
3000
+ if (unit) {
3001
+ updateUnitV2(unit, value);
3002
+ } else {
3003
+ console.warn(`Cannot create new unit for key: ${key} in XLIFF 2.0`);
3004
+ }
3005
+ });
3006
+ return serializeWithDeclaration(
3007
+ dom,
3008
+ extractXmlDeclaration(originalInput || "")
3009
+ );
3010
+ }
3011
+ function indexUnitsV2(container, fileId, currentPath, index) {
3012
+ Array.from(container.children).forEach((child) => {
3013
+ const tagName = child.tagName;
3014
+ if (tagName === "unit") {
3015
+ const unitId = child.getAttribute("id")?.trim();
3016
+ if (!unitId) return;
3017
+ const key = `resources/${fileId}/${currentPath}${unitId}/source`;
3018
+ index.set(key, child);
3019
+ } else if (tagName === "group") {
3020
+ const groupId = child.getAttribute("id")?.trim();
3021
+ const newPath = groupId ? `${currentPath}${groupId}/groupUnits/` : currentPath;
3022
+ indexUnitsV2(child, fileId, newPath, index);
3023
+ }
3024
+ });
3025
+ }
3026
+ function updateUnitV2(unit, value) {
3027
+ const document = unit.ownerDocument;
3028
+ let segment = unit.querySelector("segment");
3029
+ if (!segment) {
3030
+ segment = document.createElement("segment");
3031
+ unit.appendChild(segment);
3032
+ }
3033
+ let source = segment.querySelector("source");
3034
+ if (!source) {
3035
+ source = document.createElement("source");
3036
+ segment.appendChild(source);
3037
+ }
3038
+ setTextContent(source, value);
3039
+ }
3040
+ function getTransUnitKey(transUnit) {
3041
+ const resname = transUnit.getAttribute("resname")?.trim();
3042
+ if (resname) return resname;
3043
+ const id = transUnit.getAttribute("id")?.trim();
3044
+ if (id) return id;
3045
+ const sourceElement = transUnit.querySelector("source");
3046
+ if (sourceElement) {
3047
+ const sourceText = extractTextContent(sourceElement).trim();
3048
+ if (sourceText) return sourceText;
3049
+ }
3050
+ return "";
3051
+ }
3052
+ function extractTextContent(element) {
3053
+ const cdataNode = Array.from(element.childNodes).find(
3054
+ (node) => node.nodeType === element.CDATA_SECTION_NODE
3055
+ );
3056
+ if (cdataNode) {
3057
+ return cdataNode.nodeValue || "";
3058
+ }
3059
+ return element.textContent || "";
3060
+ }
3061
+ function setTextContent(element, content) {
3062
+ const document = element.ownerDocument;
3063
+ while (element.firstChild) {
3064
+ element.removeChild(element.firstChild);
3065
+ }
3066
+ if (/[<>&"']/.test(content)) {
3067
+ const cdataSection = document.createCDATASection(content);
3068
+ element.appendChild(cdataSection);
3069
+ } else {
3070
+ element.textContent = content;
3071
+ }
3072
+ }
3073
+ function extractXmlDeclaration(xmlContent) {
3074
+ const match2 = xmlContent.match(/^<\?xml[^>]*\?>/);
3075
+ return match2 ? match2[0] : "";
3076
+ }
3077
+ function serializeWithDeclaration(dom, declaration) {
3078
+ let serialized = dom.serialize();
3079
+ serialized = formatXml(serialized);
3080
+ if (declaration) {
3081
+ serialized = `${declaration}
3082
+ ${serialized}`;
3083
+ }
3084
+ return serialized;
3085
+ }
3086
+ function formatXml(xml) {
3087
+ const dom = new JSDOM2(xml, { contentType: "text/xml" });
3088
+ const doc = dom.window.document;
3089
+ function formatElement(element, depth = 0) {
3090
+ const indent2 = " ".repeat(depth);
3091
+ const tagName = element.tagName;
3092
+ const attributes = Array.from(element.attributes).map((attr) => `${attr.name}="${attr.value}"`).join(" ");
3093
+ const openTag = attributes ? `<${tagName} ${attributes}>` : `<${tagName}>`;
3094
+ const cdataNode = Array.from(element.childNodes).find(
3095
+ (node) => node.nodeType === element.CDATA_SECTION_NODE
3096
+ );
3097
+ if (cdataNode) {
3098
+ return `${indent2}${openTag}<![CDATA[${cdataNode.nodeValue}]]></${tagName}>`;
3099
+ }
3100
+ const textContent = element.textContent?.trim() || "";
3101
+ const hasOnlyText = element.childNodes.length === 1 && element.childNodes[0].nodeType === 3;
3102
+ if (hasOnlyText && textContent) {
3103
+ return `${indent2}${openTag}${textContent}</${tagName}>`;
3104
+ }
3105
+ const children = Array.from(element.children);
3106
+ if (children.length === 0) {
3107
+ return `${indent2}${openTag}</${tagName}>`;
3108
+ }
3109
+ let result = `${indent2}${openTag}
3110
+ `;
3111
+ for (const child of children) {
3112
+ result += formatElement(child, depth + 1) + "\n";
3113
+ }
3114
+ result += `${indent2}</${tagName}>`;
3115
+ return result;
3116
+ }
3117
+ return formatElement(doc.documentElement);
3118
+ }
3119
+ function createEmptyResult(originalLocale, locale) {
3120
+ return {};
3121
+ }
3122
+ function pushNewFile(locale, translations, originalLocale) {
3123
+ const skeleton = `<?xml version="1.0" encoding="utf-8"?>
3124
+ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
3125
+ <file original="" source-language="${originalLocale}" target-language="${locale}" datatype="plaintext">
3126
+ <header></header>
3127
+ <body></body>
3128
+ </file>
3129
+ </xliff>`;
3130
+ const dom = new JSDOM2(skeleton, { contentType: "text/xml" });
3131
+ const document = dom.window.document;
3132
+ const bodyElement = document.querySelector("body");
3133
+ Object.entries(translations).forEach(([key, value]) => {
3134
+ const unit = document.createElement("trans-unit");
3135
+ unit.setAttribute("id", key);
3136
+ unit.setAttribute("resname", key);
3137
+ unit.setAttribute("restype", "string");
3138
+ unit.setAttribute("datatype", "plaintext");
3139
+ const sourceElement = document.createElement("source");
3140
+ setTextContent(sourceElement, key);
3141
+ unit.appendChild(sourceElement);
3142
+ const targetElement = document.createElement("target");
3143
+ targetElement.setAttribute("state", value ? "translated" : "new");
3144
+ setTextContent(targetElement, value);
3145
+ unit.appendChild(targetElement);
3146
+ bodyElement.appendChild(unit);
3147
+ });
3148
+ return serializeWithDeclaration(
3149
+ dom,
3150
+ '<?xml version="1.0" encoding="utf-8"?>'
3151
+ );
2764
3152
  }
2765
3153
 
2766
3154
  // src/cli/loaders/xml.ts
@@ -9954,7 +10342,7 @@ async function renderHero2() {
9954
10342
  // package.json
9955
10343
  var package_default = {
9956
10344
  name: "lingo.dev",
9957
- version: "0.105.1",
10345
+ version: "0.105.2",
9958
10346
  description: "Lingo.dev CLI",
9959
10347
  private: false,
9960
10348
  publishConfig: {