@translationstudio/translationstudio-strapi-extension 1.1.0 → 1.1.1
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/dist/admin/index.js +6 -12
- package/dist/admin/index.mjs +6 -12
- package/dist/server/index.js +257 -208
- package/dist/server/index.mjs +257 -208
- package/dist/server/src/services/functions/exportData/jsonToHtml.d.ts +6 -2
- package/dist/server/src/services/functions/importData/htmlToJson.d.ts +1 -2
- package/dist/server/src/services/functions/importData/parseInlineElements.d.ts +21 -1
- package/dist/server/src/services/functions/importData/processComponentFields.d.ts +1 -1
- package/dist/server/src/services/functions/importData/updateEntry.d.ts +1 -1
- package/dist/server/src/services/service.d.ts +2 -2
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -288,49 +288,54 @@ const getEntry = async (contentTypeID, entryID, locale) => {
|
|
|
288
288
|
const transformResponse = (data) => data.map(
|
|
289
289
|
(item) => item.realType === "blocks" && Array.isArray(item.translatableValue[0]) ? { ...item, translatableValue: item.translatableValue[0] } : item
|
|
290
290
|
);
|
|
291
|
+
function jsonToHtml(json) {
|
|
292
|
+
if (!json || !Array.isArray(json)) {
|
|
293
|
+
return "";
|
|
294
|
+
}
|
|
295
|
+
return json.map((node) => processNode(node)).join("");
|
|
296
|
+
}
|
|
297
|
+
function processNode(node) {
|
|
298
|
+
if (!node) return "";
|
|
299
|
+
switch (node.type) {
|
|
300
|
+
case "paragraph":
|
|
301
|
+
return `<p>${node.children.map(processNode).join("")}</p>`;
|
|
302
|
+
case "heading":
|
|
303
|
+
const level = node.level || 1;
|
|
304
|
+
return `<h${level}>${node.children.map(processNode).join("")}</h${level}>`;
|
|
305
|
+
case "link":
|
|
306
|
+
const url = node.url || "#";
|
|
307
|
+
return `<a href="${url}">${node.children.map(processNode).join("")}</a>`;
|
|
308
|
+
case "list":
|
|
309
|
+
const listType = node.format === "ordered" ? "ol" : "ul";
|
|
310
|
+
return `<${listType}>${node.children.map(processNode).join("")}</${listType}>`;
|
|
311
|
+
case "list-item":
|
|
312
|
+
return `<li>${node.children.map(processNode).join("")}</li>`;
|
|
313
|
+
case "quote":
|
|
314
|
+
return `<blockquote>${node.children.map(processNode).join("")}</blockquote>`;
|
|
315
|
+
case "code":
|
|
316
|
+
return `<pre><code>${node.children.map((child) => child.text).join("")}</code></pre>`;
|
|
317
|
+
case "image":
|
|
318
|
+
return `<img src="${node.url}" alt="${node.alt || ""}" />`;
|
|
319
|
+
case "text":
|
|
320
|
+
return formatText(node);
|
|
321
|
+
default:
|
|
322
|
+
return node.children && Array.isArray(node.children) ? node.children.map(processNode).join("") : node.text || "";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
291
325
|
function formatText(child) {
|
|
292
326
|
if (child.type === "link") {
|
|
293
|
-
return `<a href="${child.url}">${child.children.map((sub) => sub
|
|
327
|
+
return `<a href="${child.url}">${child.children.map((sub) => formatText(sub)).join("")}</a>`;
|
|
294
328
|
}
|
|
295
329
|
let text = child.text || "";
|
|
330
|
+
if (child.code) text = `<code>${text}</code>`;
|
|
296
331
|
if (child.bold) text = `<strong>${text}</strong>`;
|
|
297
332
|
if (child.italic) text = `<em>${text}</em>`;
|
|
298
333
|
if (child.underline) text = `<u>${text}</u>`;
|
|
299
|
-
if (child.strikethrough)
|
|
334
|
+
if (child.strikethrough) {
|
|
335
|
+
text = `~~${text}~~`;
|
|
336
|
+
}
|
|
300
337
|
return text;
|
|
301
338
|
}
|
|
302
|
-
function renderChildren(children) {
|
|
303
|
-
return children.map(formatText).join("");
|
|
304
|
-
}
|
|
305
|
-
function renderHeading(element) {
|
|
306
|
-
return `<h${element.level}>${renderChildren(element.children)}</h${element.level}>`;
|
|
307
|
-
}
|
|
308
|
-
function renderParagraph(element) {
|
|
309
|
-
return `<p>${renderChildren(element.children)}</p>`;
|
|
310
|
-
}
|
|
311
|
-
function renderList(element) {
|
|
312
|
-
const tag = element.format === "unordered" ? "ul" : "ol";
|
|
313
|
-
const items = element.children.map(renderListItem).join("");
|
|
314
|
-
return `<${tag}>${items}</${tag}>`;
|
|
315
|
-
}
|
|
316
|
-
function renderListItem(item) {
|
|
317
|
-
const content = item.children.map(formatText).join("");
|
|
318
|
-
return `<li>${content}</li>`;
|
|
319
|
-
}
|
|
320
|
-
function jsonToHtml(jsonData) {
|
|
321
|
-
return jsonData.map((element) => {
|
|
322
|
-
switch (element.type) {
|
|
323
|
-
case "heading":
|
|
324
|
-
return renderHeading(element);
|
|
325
|
-
case "paragraph":
|
|
326
|
-
return renderParagraph(element);
|
|
327
|
-
case "list":
|
|
328
|
-
return renderList(element);
|
|
329
|
-
default:
|
|
330
|
-
return "";
|
|
331
|
-
}
|
|
332
|
-
}).join("");
|
|
333
|
-
}
|
|
334
339
|
const processComponent = async (fieldName, componentName, value, schemaName, componentId) => {
|
|
335
340
|
const contentFields = [];
|
|
336
341
|
const componentSchema = await strapi.components[componentName];
|
|
@@ -528,149 +533,197 @@ const isDynamicZone = (fieldSchema, value, schema) => {
|
|
|
528
533
|
const isComponent = (fieldSchema, value, schema) => {
|
|
529
534
|
return fieldSchema.type === "component" && isFieldLocalizable(fieldSchema, schema);
|
|
530
535
|
};
|
|
531
|
-
function
|
|
532
|
-
|
|
533
|
-
|
|
536
|
+
function htmlToJson(html) {
|
|
537
|
+
function parseHTML(html2) {
|
|
538
|
+
const elements2 = [];
|
|
539
|
+
const tagRegex = /<([a-z0-9]+)((?:\s+[a-z-]+="[^"]*")*)\s*>([\s\S]*?)<\/\1>/gi;
|
|
540
|
+
let match;
|
|
541
|
+
while ((match = tagRegex.exec(html2)) !== null) {
|
|
542
|
+
const [, tag, attributes, content] = match;
|
|
543
|
+
const attrs = {};
|
|
544
|
+
const attrRegex = /([a-z-]+)="([^"]*)"/gi;
|
|
545
|
+
let attrMatch;
|
|
546
|
+
while ((attrMatch = attrRegex.exec(attributes)) !== null) {
|
|
547
|
+
attrs[attrMatch[1]] = attrMatch[2];
|
|
548
|
+
}
|
|
549
|
+
elements2.push({ tag, attrs, content });
|
|
550
|
+
}
|
|
551
|
+
return elements2;
|
|
534
552
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
let
|
|
553
|
+
function parseInlineContent(content) {
|
|
554
|
+
const segments = [];
|
|
555
|
+
let currentText = "";
|
|
556
|
+
let formatStack = [];
|
|
557
|
+
let currentFormat = {
|
|
558
|
+
bold: false,
|
|
559
|
+
italic: false,
|
|
560
|
+
underline: false,
|
|
561
|
+
code: false,
|
|
562
|
+
strikethrough: false
|
|
563
|
+
};
|
|
564
|
+
const pushSegment = () => {
|
|
565
|
+
if (currentText) {
|
|
566
|
+
segments.push({
|
|
567
|
+
type: "text",
|
|
568
|
+
text: currentText,
|
|
569
|
+
...Object.fromEntries(Object.entries(currentFormat).filter(([_, value]) => value))
|
|
570
|
+
});
|
|
571
|
+
currentText = "";
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const tags = content.split(/(<[^>]+>|~~)/);
|
|
575
|
+
for (const tag of tags) {
|
|
576
|
+
if (!tag) continue;
|
|
577
|
+
if (tag === "~~") {
|
|
578
|
+
pushSegment();
|
|
579
|
+
currentFormat.strikethrough = !currentFormat.strikethrough;
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
if (tag.startsWith("<")) {
|
|
583
|
+
pushSegment();
|
|
584
|
+
if (tag.startsWith("</")) {
|
|
585
|
+
const tagName = tag.slice(2, -1).toLowerCase();
|
|
586
|
+
const lastTag = formatStack.pop();
|
|
587
|
+
if (lastTag && lastTag.type === tagName) {
|
|
588
|
+
switch (tagName) {
|
|
589
|
+
case "strong":
|
|
590
|
+
currentFormat.bold = false;
|
|
591
|
+
break;
|
|
592
|
+
case "em":
|
|
593
|
+
currentFormat.italic = false;
|
|
594
|
+
break;
|
|
595
|
+
case "u":
|
|
596
|
+
currentFormat.underline = false;
|
|
597
|
+
break;
|
|
598
|
+
case "code":
|
|
599
|
+
currentFormat.code = false;
|
|
600
|
+
break;
|
|
601
|
+
case "del":
|
|
602
|
+
currentFormat.strikethrough = false;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
const tagName = tag.slice(1, -1).toLowerCase();
|
|
608
|
+
formatStack.push({ type: tagName, index: segments.length });
|
|
609
|
+
switch (tagName) {
|
|
610
|
+
case "strong":
|
|
611
|
+
currentFormat.bold = true;
|
|
612
|
+
break;
|
|
613
|
+
case "em":
|
|
614
|
+
currentFormat.italic = true;
|
|
615
|
+
break;
|
|
616
|
+
case "u":
|
|
617
|
+
currentFormat.underline = true;
|
|
618
|
+
break;
|
|
619
|
+
case "code":
|
|
620
|
+
currentFormat.code = true;
|
|
621
|
+
break;
|
|
622
|
+
case "del":
|
|
623
|
+
currentFormat.strikethrough = true;
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
} else {
|
|
628
|
+
currentText += tag;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
pushSegment();
|
|
632
|
+
return segments.filter((segment) => segment.text.length > 0);
|
|
633
|
+
}
|
|
634
|
+
function parseList(html2, format) {
|
|
635
|
+
const listItems = html2.match(/<li>([\s\S]*?)<\/li>/g) || [];
|
|
636
|
+
return {
|
|
637
|
+
type: "list",
|
|
638
|
+
format,
|
|
639
|
+
children: listItems.map((item) => ({
|
|
640
|
+
type: "list-item",
|
|
641
|
+
children: parseListContent(item.replace(/<li>|<\/li>/g, ""))
|
|
642
|
+
}))
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function parseListContent(content) {
|
|
646
|
+
const children = [];
|
|
647
|
+
const linkRegex = /<a\s+href="([^"]+)">([\s\S]*?)<\/a>/g;
|
|
538
648
|
let lastIndex = 0;
|
|
539
|
-
|
|
540
|
-
while ((
|
|
541
|
-
const [fullMatch,
|
|
542
|
-
if (
|
|
543
|
-
const
|
|
544
|
-
if (
|
|
545
|
-
|
|
649
|
+
let match;
|
|
650
|
+
while ((match = linkRegex.exec(content)) !== null) {
|
|
651
|
+
const [fullMatch, href, linkText] = match;
|
|
652
|
+
if (match.index > lastIndex) {
|
|
653
|
+
const textBefore = content.slice(lastIndex, match.index);
|
|
654
|
+
if (textBefore.trim()) {
|
|
655
|
+
children.push(...parseInlineContent(textBefore));
|
|
546
656
|
}
|
|
547
657
|
}
|
|
548
|
-
|
|
658
|
+
children.push({
|
|
549
659
|
type: "link",
|
|
550
|
-
url,
|
|
551
|
-
children:
|
|
660
|
+
url: href,
|
|
661
|
+
children: parseInlineContent(linkText)
|
|
552
662
|
});
|
|
553
|
-
lastIndex =
|
|
663
|
+
lastIndex = match.index + fullMatch.length;
|
|
554
664
|
}
|
|
555
|
-
if (lastIndex <
|
|
556
|
-
const
|
|
557
|
-
if (
|
|
558
|
-
|
|
665
|
+
if (lastIndex < content.length) {
|
|
666
|
+
const remainingText = content.slice(lastIndex);
|
|
667
|
+
if (remainingText.trim()) {
|
|
668
|
+
children.push(...parseInlineContent(remainingText));
|
|
559
669
|
}
|
|
560
670
|
}
|
|
561
|
-
return
|
|
671
|
+
return children;
|
|
562
672
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const afterText = text.substring(match.index + fullMatch.length);
|
|
569
|
-
const elements = [];
|
|
570
|
-
if (beforeText) {
|
|
571
|
-
elements.push(...parseInlineElements(beforeText));
|
|
572
|
-
}
|
|
573
|
-
const nestedElements = parseInlineElements(content);
|
|
574
|
-
nestedElements.forEach((element) => {
|
|
575
|
-
if (tag === "strong") element.bold = true;
|
|
576
|
-
else if (tag === "em") element.italic = true;
|
|
577
|
-
else if (tag === "u") element.underline = true;
|
|
578
|
-
else if (tag === "del") element.strikethrough = true;
|
|
579
|
-
});
|
|
580
|
-
elements.push(...nestedElements);
|
|
581
|
-
if (afterText) {
|
|
582
|
-
elements.push(...parseInlineElements(afterText));
|
|
583
|
-
}
|
|
584
|
-
return elements;
|
|
673
|
+
function parseParagraph(content) {
|
|
674
|
+
return {
|
|
675
|
+
type: "paragraph",
|
|
676
|
+
children: parseListContent(content)
|
|
677
|
+
};
|
|
585
678
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
type: "heading",
|
|
592
|
-
level,
|
|
593
|
-
children: [{ type: "text", text: innerText.trim() }]
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
function parseParagraph(innerText) {
|
|
597
|
-
return {
|
|
598
|
-
type: "paragraph",
|
|
599
|
-
children: parseInlineElements(innerText)
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
function parseList(tag, innerText) {
|
|
603
|
-
const listType = tag === "ul" ? "unordered" : "ordered";
|
|
604
|
-
const listItems = [];
|
|
605
|
-
const listItemRegex = /<li>(.*?)<\/li>/g;
|
|
606
|
-
let itemMatch;
|
|
607
|
-
while ((itemMatch = listItemRegex.exec(innerText)) !== null) {
|
|
608
|
-
listItems.push({
|
|
609
|
-
type: "list-item",
|
|
610
|
-
children: parseInlineElements(itemMatch[1])
|
|
611
|
-
});
|
|
679
|
+
const blocks = [];
|
|
680
|
+
const elements = parseHTML(html);
|
|
681
|
+
if (elements.length === 0 && html.trim()) {
|
|
682
|
+
blocks.push(parseParagraph(html));
|
|
683
|
+
return blocks;
|
|
612
684
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
format: listType,
|
|
616
|
-
children: listItems
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
function htmlToJson(htmlData) {
|
|
620
|
-
const jsonData = [];
|
|
621
|
-
const blockRegex = /<(h[1-3]|p|ul|ol)(?:[^>]*?)>([\s\S]*?)<\/\1>/g;
|
|
622
|
-
let match;
|
|
623
|
-
while ((match = blockRegex.exec(htmlData)) !== null) {
|
|
624
|
-
const [, tag, content] = match;
|
|
625
|
-
switch (tag) {
|
|
685
|
+
for (const element of elements) {
|
|
686
|
+
switch (element.tag.toLowerCase()) {
|
|
626
687
|
case "h1":
|
|
627
688
|
case "h2":
|
|
628
689
|
case "h3":
|
|
629
|
-
|
|
690
|
+
case "h4":
|
|
691
|
+
case "h5":
|
|
692
|
+
case "h6":
|
|
693
|
+
blocks.push({
|
|
694
|
+
type: "heading",
|
|
695
|
+
level: parseInt(element.tag.slice(1)),
|
|
696
|
+
children: parseListContent(element.content)
|
|
697
|
+
});
|
|
630
698
|
break;
|
|
631
699
|
case "p":
|
|
632
|
-
|
|
633
|
-
const linkRegex = /<a\s+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g;
|
|
634
|
-
let linkMatch;
|
|
635
|
-
let lastIndex = 0;
|
|
636
|
-
const children = [];
|
|
637
|
-
children.push({ type: "text", text: "" });
|
|
638
|
-
while ((linkMatch = linkRegex.exec(content)) !== null) {
|
|
639
|
-
const [fullMatch, url, linkText] = linkMatch;
|
|
640
|
-
if (linkMatch.index > lastIndex) {
|
|
641
|
-
const beforeLinkText = content.substring(lastIndex, linkMatch.index);
|
|
642
|
-
if (beforeLinkText) {
|
|
643
|
-
children.push({ type: "text", text: beforeLinkText });
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
children.push({
|
|
647
|
-
type: "link",
|
|
648
|
-
url,
|
|
649
|
-
children: [{ type: "text", text: linkText }]
|
|
650
|
-
});
|
|
651
|
-
lastIndex = linkMatch.index + fullMatch.length;
|
|
652
|
-
}
|
|
653
|
-
const afterLastLink = content.substring(lastIndex);
|
|
654
|
-
if (afterLastLink) {
|
|
655
|
-
children.push({ type: "text", text: afterLastLink });
|
|
656
|
-
} else {
|
|
657
|
-
children.push({ type: "text", text: "" });
|
|
658
|
-
}
|
|
659
|
-
jsonData.push({
|
|
660
|
-
type: "paragraph",
|
|
661
|
-
children
|
|
662
|
-
});
|
|
663
|
-
} else {
|
|
664
|
-
jsonData.push(parseParagraph(content));
|
|
665
|
-
}
|
|
700
|
+
blocks.push(parseParagraph(element.content));
|
|
666
701
|
break;
|
|
667
702
|
case "ul":
|
|
703
|
+
blocks.push(parseList(element.content, "unordered"));
|
|
704
|
+
break;
|
|
668
705
|
case "ol":
|
|
669
|
-
|
|
706
|
+
blocks.push(parseList(element.content, "ordered"));
|
|
670
707
|
break;
|
|
708
|
+
case "blockquote":
|
|
709
|
+
blocks.push({
|
|
710
|
+
type: "quote",
|
|
711
|
+
children: [parseParagraph(element.content)]
|
|
712
|
+
});
|
|
713
|
+
break;
|
|
714
|
+
case "pre":
|
|
715
|
+
if (element.content.includes("<code>")) {
|
|
716
|
+
blocks.push({
|
|
717
|
+
type: "code",
|
|
718
|
+
children: parseInlineContent(element.content)
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
break;
|
|
722
|
+
default:
|
|
723
|
+
blocks.push(parseParagraph(element.content));
|
|
671
724
|
}
|
|
672
725
|
}
|
|
673
|
-
return
|
|
726
|
+
return blocks;
|
|
674
727
|
}
|
|
675
728
|
async function updateEntry(contentTypeID, entryID, sourceLocale, targetLocale, data, attributes) {
|
|
676
729
|
if (!entryID) {
|
|
@@ -682,6 +735,14 @@ async function updateEntry(contentTypeID, entryID, sourceLocale, targetLocale, d
|
|
|
682
735
|
locale: sourceLocale
|
|
683
736
|
});
|
|
684
737
|
const processedData = processDataRecursively(data);
|
|
738
|
+
for (const [key, value] of Object.entries(processedData)) {
|
|
739
|
+
if (attributes[key]?.type === "blocks" && typeof value === "string") {
|
|
740
|
+
console.warn(
|
|
741
|
+
`Field ${key} is a blocks field but received string value. Converting to blocks format.`
|
|
742
|
+
);
|
|
743
|
+
processedData[key] = htmlToJson(value);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
685
746
|
const localizedData = {};
|
|
686
747
|
for (const field in processedData) {
|
|
687
748
|
if (attributes[field] && (!attributes[field].pluginOptions?.i18n || attributes[field].pluginOptions?.i18n?.localized !== false)) {
|
|
@@ -700,23 +761,29 @@ async function updateEntry(contentTypeID, entryID, sourceLocale, targetLocale, d
|
|
|
700
761
|
});
|
|
701
762
|
}
|
|
702
763
|
}
|
|
703
|
-
function processDataRecursively(data) {
|
|
764
|
+
function processDataRecursively(data, schema) {
|
|
704
765
|
if (!data || typeof data !== "object") {
|
|
705
766
|
return data;
|
|
706
767
|
}
|
|
707
768
|
if (Array.isArray(data)) {
|
|
769
|
+
if (data[0]?.fields) {
|
|
770
|
+
const processedFields = {};
|
|
771
|
+
for (const fieldData of data[0].fields) {
|
|
772
|
+
if (fieldData.realType === "blocks") {
|
|
773
|
+
if (fieldData.translatableValue?.[0]) {
|
|
774
|
+
processedFields[fieldData.field] = htmlToJson(fieldData.translatableValue[0]);
|
|
775
|
+
}
|
|
776
|
+
} else {
|
|
777
|
+
processedFields[fieldData.field] = fieldData.translatableValue?.[0] || null;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return processedFields;
|
|
781
|
+
}
|
|
708
782
|
return data.map((item) => processDataRecursively(item));
|
|
709
783
|
}
|
|
710
784
|
const result = {};
|
|
711
785
|
for (const key in data) {
|
|
712
|
-
|
|
713
|
-
if (typeof value === "string" && (key.includes("Blocks") || key.includes("RTBlocks") || key === "blocks")) {
|
|
714
|
-
result[key] = htmlToJson(value);
|
|
715
|
-
} else if (value && typeof value === "object") {
|
|
716
|
-
result[key] = processDataRecursively(value);
|
|
717
|
-
} else {
|
|
718
|
-
result[key] = value;
|
|
719
|
-
}
|
|
786
|
+
result[key] = processDataRecursively(data[key]);
|
|
720
787
|
}
|
|
721
788
|
return result;
|
|
722
789
|
}
|
|
@@ -761,16 +828,15 @@ function processRepeatableComponents(fields, existingEntry, rootPath) {
|
|
|
761
828
|
}
|
|
762
829
|
const componentId = field.componentInfo.id;
|
|
763
830
|
if (!componentsById.has(componentId)) {
|
|
764
|
-
const existingComponent = existingComponents.find(
|
|
765
|
-
|
|
766
|
-
);
|
|
767
|
-
componentsById.set(
|
|
768
|
-
componentId,
|
|
769
|
-
existingComponent ? { ...existingComponent } : {}
|
|
770
|
-
);
|
|
831
|
+
const existingComponent = existingComponents.find((c) => c.id === componentId);
|
|
832
|
+
componentsById.set(componentId, existingComponent ? { ...existingComponent } : {});
|
|
771
833
|
}
|
|
772
834
|
const component = componentsById.get(componentId);
|
|
773
|
-
|
|
835
|
+
if (field.realType === "blocks") {
|
|
836
|
+
component[field.field] = htmlToJson(field.translatableValue[0] || "");
|
|
837
|
+
} else {
|
|
838
|
+
component[field.field] = field.translatableValue[0];
|
|
839
|
+
}
|
|
774
840
|
});
|
|
775
841
|
return Array.from(componentsById.values()).map((comp) => {
|
|
776
842
|
if (!existingComponents.find((ec) => ec.id === comp.id)) {
|
|
@@ -792,7 +858,11 @@ function processNestedComponents(fields, pathParts, existingEntry, acc) {
|
|
|
792
858
|
}
|
|
793
859
|
if (index2 === pathParts.length - 1) {
|
|
794
860
|
fields.forEach((field) => {
|
|
795
|
-
|
|
861
|
+
if (field.realType === "blocks") {
|
|
862
|
+
current[part][field.field] = htmlToJson(field.translatableValue[0] || "");
|
|
863
|
+
} else {
|
|
864
|
+
current[part][field.field] = field.translatableValue[0];
|
|
865
|
+
}
|
|
796
866
|
});
|
|
797
867
|
} else {
|
|
798
868
|
current = current[part];
|
|
@@ -807,11 +877,7 @@ function processComponentFields(componentFieldsMap, acc, existingEntry, targetSc
|
|
|
807
877
|
const rootPath = pathParts[0];
|
|
808
878
|
const schema = targetSchema.attributes?.[rootPath];
|
|
809
879
|
if (schema?.repeatable) {
|
|
810
|
-
acc[rootPath] = processRepeatableComponents(
|
|
811
|
-
fields,
|
|
812
|
-
existingEntry,
|
|
813
|
-
rootPath
|
|
814
|
-
);
|
|
880
|
+
acc[rootPath] = processRepeatableComponents(fields, existingEntry, rootPath);
|
|
815
881
|
} else {
|
|
816
882
|
processNestedComponents(fields, pathParts, existingEntry, acc);
|
|
817
883
|
}
|
|
@@ -929,12 +995,9 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
929
995
|
},
|
|
930
996
|
async getLanguageOptions() {
|
|
931
997
|
const { license } = await this.getLicense();
|
|
932
|
-
const response = await fetch(
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
headers: { Authorization: `${license}` }
|
|
936
|
-
}
|
|
937
|
-
);
|
|
998
|
+
const response = await fetch(TRANSLATIONTUDIO_URL + "/mappings", {
|
|
999
|
+
headers: { Authorization: `${license}` }
|
|
1000
|
+
});
|
|
938
1001
|
const responseData = await response.json();
|
|
939
1002
|
return responseData;
|
|
940
1003
|
},
|
|
@@ -942,10 +1005,7 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
942
1005
|
const { contentTypeID, entryID, locale } = parsePayload(payload);
|
|
943
1006
|
const contentType = await getContentType(contentTypeID);
|
|
944
1007
|
const entry = await getEntry(contentTypeID, entryID, locale);
|
|
945
|
-
const contentFields = await processEntryFields(
|
|
946
|
-
entry,
|
|
947
|
-
contentType.attributes
|
|
948
|
-
);
|
|
1008
|
+
const contentFields = await processEntryFields(entry, contentType.attributes);
|
|
949
1009
|
return transformResponse(contentFields);
|
|
950
1010
|
},
|
|
951
1011
|
async importData(payload) {
|
|
@@ -953,17 +1013,9 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
953
1013
|
const sourceLocale = payload.source;
|
|
954
1014
|
const targetLocale = payload.target;
|
|
955
1015
|
try {
|
|
956
|
-
const existingEntry = await getEntry(
|
|
957
|
-
contentTypeID,
|
|
958
|
-
entryID,
|
|
959
|
-
targetLocale
|
|
960
|
-
);
|
|
1016
|
+
const existingEntry = await getEntry(contentTypeID, entryID, targetLocale);
|
|
961
1017
|
const targetSchema = await getContentType(contentTypeID);
|
|
962
|
-
const data = prepareImportData(
|
|
963
|
-
payload.document[0].fields,
|
|
964
|
-
existingEntry,
|
|
965
|
-
targetSchema
|
|
966
|
-
);
|
|
1018
|
+
const data = prepareImportData(payload.document[0].fields, existingEntry, targetSchema);
|
|
967
1019
|
if (targetSchema.pluginOptions.i18n.localized === true) {
|
|
968
1020
|
await updateEntry(
|
|
969
1021
|
contentTypeID,
|
|
@@ -981,17 +1033,14 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
981
1033
|
},
|
|
982
1034
|
async requestTranslation(payload) {
|
|
983
1035
|
const { license } = await this.getLicense();
|
|
984
|
-
const response = await fetch(
|
|
985
|
-
|
|
986
|
-
{
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
body: JSON.stringify(payload)
|
|
993
|
-
}
|
|
994
|
-
);
|
|
1036
|
+
const response = await fetch(TRANSLATIONTUDIO_URL + "/translate", {
|
|
1037
|
+
method: "POST",
|
|
1038
|
+
headers: {
|
|
1039
|
+
Authorization: `${license}`,
|
|
1040
|
+
"Content-Type": "application/json"
|
|
1041
|
+
},
|
|
1042
|
+
body: JSON.stringify(payload)
|
|
1043
|
+
});
|
|
995
1044
|
if (response.status === 204) return true;
|
|
996
1045
|
},
|
|
997
1046
|
async getEmail(ctx) {
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Converts a richtext JSON structure to HTML
|
|
3
|
+
* @param {any} json - The JSON representation of richtext content
|
|
4
|
+
* @returns {string} - The converted HTML string
|
|
5
|
+
*/
|
|
6
|
+
export default function jsonToHtml(json: any): string;
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export default htmlToJson;
|
|
1
|
+
export default function htmlToJson(html: string): any[];
|
|
@@ -1,2 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Parses HTML and markdown-formatted text into a structured format for richtext fields
|
|
3
|
+
* @param {string} text - The HTML/markdown text to parse
|
|
4
|
+
* @returns {ContentNode[]} - Array of structured text elements
|
|
5
|
+
*/
|
|
6
|
+
interface TextNode {
|
|
7
|
+
type: 'text';
|
|
8
|
+
text: string;
|
|
9
|
+
bold?: boolean;
|
|
10
|
+
italic?: boolean;
|
|
11
|
+
underline?: boolean;
|
|
12
|
+
code?: boolean;
|
|
13
|
+
strikethrough?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface LinkNode {
|
|
16
|
+
type: 'link';
|
|
17
|
+
url: string;
|
|
18
|
+
children: TextNode[];
|
|
19
|
+
}
|
|
20
|
+
type ContentNode = TextNode | LinkNode;
|
|
21
|
+
declare function parseInlineElements(text: string): ContentNode[];
|
|
2
22
|
export default parseInlineElements;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { TranslationstudioTranslatable } from
|
|
1
|
+
import { TranslationstudioTranslatable } from '../../../../../Types';
|
|
2
2
|
export declare function processComponentFields(componentFieldsMap: Map<string, TranslationstudioTranslatable[]>, acc: Record<string, any>, existingEntry: any, targetSchema: any): Record<string, any>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function updateEntry(contentTypeID: string, entryID: string, sourceLocale: string, targetLocale: string, data: Record<string, any>, attributes: Record<string, any>): Promise<void>;
|
|
2
|
-
export declare function processDataRecursively(data: any): any;
|
|
2
|
+
export declare function processDataRecursively(data: any, schema?: any): any;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Core } from
|
|
2
|
-
import type { ExportPayload, ImportPayload, LocaleMap, TranslationRequest } from
|
|
1
|
+
import type { Core } from '@strapi/strapi';
|
|
2
|
+
import type { ExportPayload, ImportPayload, LocaleMap, TranslationRequest } from '../../../Types';
|
|
3
3
|
declare const service: ({ strapi }: {
|
|
4
4
|
strapi: Core.Strapi;
|
|
5
5
|
}) => {
|