@translationstudio/translationstudio-strapi-extension 1.1.0 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/_chunks/{App-jiozoQyG.mjs → App-Cq1SRR-L.mjs} +148 -82
- package/dist/_chunks/{App-rdXR_woe.js → App-DkdbSZwI.js} +146 -80
- package/dist/admin/index.js +7 -13
- package/dist/admin/index.mjs +7 -13
- package/dist/server/index.js +330 -220
- package/dist/server/index.mjs +330 -220
- package/dist/server/src/controllers/controller.d.ts +2 -0
- package/dist/server/src/controllers/index.d.ts +2 -0
- package/dist/server/src/index.d.ts +5 -0
- 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/index.d.ts +3 -0
- package/dist/server/src/services/service.d.ts +5 -2
- package/package.json +5 -5
package/dist/server/index.mjs
CHANGED
|
@@ -80,6 +80,27 @@ const controller = ({ strapi: strapi2 }) => ({
|
|
|
80
80
|
ctx.status = 200;
|
|
81
81
|
ctx.body = [{ fields: result }];
|
|
82
82
|
},
|
|
83
|
+
async setDevelopmentUrl(ctx) {
|
|
84
|
+
const url = ctx.request.body.url;
|
|
85
|
+
const result = await strapi2.plugin(APP_NAME$1).service("service").setDevelopmentUrl(url);
|
|
86
|
+
if (result) {
|
|
87
|
+
ctx.status = 200;
|
|
88
|
+
ctx.body = { success: true };
|
|
89
|
+
} else {
|
|
90
|
+
ctx.status = 500;
|
|
91
|
+
ctx.body = { success: false };
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
async getDevelopmentUrl(ctx) {
|
|
95
|
+
const url = await strapi2.plugin(APP_NAME$1).service("service").getDevelopmentUrl();
|
|
96
|
+
if (url) {
|
|
97
|
+
ctx.status = 200;
|
|
98
|
+
ctx.body = { url };
|
|
99
|
+
} else {
|
|
100
|
+
ctx.status = 404;
|
|
101
|
+
ctx.body = { url: "" };
|
|
102
|
+
}
|
|
103
|
+
},
|
|
83
104
|
async importData(ctx) {
|
|
84
105
|
if (!await this.validateToken(ctx)) {
|
|
85
106
|
ctx.status = 400;
|
|
@@ -143,6 +164,22 @@ const routes = [
|
|
|
143
164
|
policies: []
|
|
144
165
|
}
|
|
145
166
|
},
|
|
167
|
+
{
|
|
168
|
+
method: "GET",
|
|
169
|
+
path: "/devurl",
|
|
170
|
+
handler: "controller.getDevelopmentUrl",
|
|
171
|
+
config: {
|
|
172
|
+
policies: []
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
method: "POST",
|
|
177
|
+
path: "/devurl",
|
|
178
|
+
handler: "controller.setDevelopmentUrl",
|
|
179
|
+
config: {
|
|
180
|
+
policies: []
|
|
181
|
+
}
|
|
182
|
+
},
|
|
146
183
|
{
|
|
147
184
|
method: "GET",
|
|
148
185
|
path: "/getToken",
|
|
@@ -288,49 +325,54 @@ const getEntry = async (contentTypeID, entryID, locale) => {
|
|
|
288
325
|
const transformResponse = (data) => data.map(
|
|
289
326
|
(item) => item.realType === "blocks" && Array.isArray(item.translatableValue[0]) ? { ...item, translatableValue: item.translatableValue[0] } : item
|
|
290
327
|
);
|
|
328
|
+
function jsonToHtml(json) {
|
|
329
|
+
if (!json || !Array.isArray(json)) {
|
|
330
|
+
return "";
|
|
331
|
+
}
|
|
332
|
+
return json.map((node) => processNode(node)).join("");
|
|
333
|
+
}
|
|
334
|
+
function processNode(node) {
|
|
335
|
+
if (!node) return "";
|
|
336
|
+
switch (node.type) {
|
|
337
|
+
case "paragraph":
|
|
338
|
+
return `<p>${node.children.map(processNode).join("")}</p>`;
|
|
339
|
+
case "heading":
|
|
340
|
+
const level = node.level || 1;
|
|
341
|
+
return `<h${level}>${node.children.map(processNode).join("")}</h${level}>`;
|
|
342
|
+
case "link":
|
|
343
|
+
const url = node.url || "#";
|
|
344
|
+
return `<a href="${url}">${node.children.map(processNode).join("")}</a>`;
|
|
345
|
+
case "list":
|
|
346
|
+
const listType = node.format === "ordered" ? "ol" : "ul";
|
|
347
|
+
return `<${listType}>${node.children.map(processNode).join("")}</${listType}>`;
|
|
348
|
+
case "list-item":
|
|
349
|
+
return `<li>${node.children.map(processNode).join("")}</li>`;
|
|
350
|
+
case "quote":
|
|
351
|
+
return `<blockquote>${node.children.map(processNode).join("")}</blockquote>`;
|
|
352
|
+
case "code":
|
|
353
|
+
return `<pre><code>${node.children.map((child) => child.text).join("")}</code></pre>`;
|
|
354
|
+
case "image":
|
|
355
|
+
return `<img src="${node.url}" alt="${node.alt || ""}" />`;
|
|
356
|
+
case "text":
|
|
357
|
+
return formatText(node);
|
|
358
|
+
default:
|
|
359
|
+
return node.children && Array.isArray(node.children) ? node.children.map(processNode).join("") : node.text || "";
|
|
360
|
+
}
|
|
361
|
+
}
|
|
291
362
|
function formatText(child) {
|
|
292
363
|
if (child.type === "link") {
|
|
293
|
-
return `<a href="${child.url}">${child.children.map((sub) => sub
|
|
364
|
+
return `<a href="${child.url}">${child.children.map((sub) => formatText(sub)).join("")}</a>`;
|
|
294
365
|
}
|
|
295
366
|
let text = child.text || "";
|
|
367
|
+
if (child.code) text = `<code>${text}</code>`;
|
|
296
368
|
if (child.bold) text = `<strong>${text}</strong>`;
|
|
297
369
|
if (child.italic) text = `<em>${text}</em>`;
|
|
298
370
|
if (child.underline) text = `<u>${text}</u>`;
|
|
299
|
-
if (child.strikethrough)
|
|
371
|
+
if (child.strikethrough) {
|
|
372
|
+
text = `~~${text}~~`;
|
|
373
|
+
}
|
|
300
374
|
return text;
|
|
301
375
|
}
|
|
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
376
|
const processComponent = async (fieldName, componentName, value, schemaName, componentId) => {
|
|
335
377
|
const contentFields = [];
|
|
336
378
|
const componentSchema = await strapi.components[componentName];
|
|
@@ -528,149 +570,197 @@ const isDynamicZone = (fieldSchema, value, schema) => {
|
|
|
528
570
|
const isComponent = (fieldSchema, value, schema) => {
|
|
529
571
|
return fieldSchema.type === "component" && isFieldLocalizable(fieldSchema, schema);
|
|
530
572
|
};
|
|
531
|
-
function
|
|
532
|
-
|
|
533
|
-
|
|
573
|
+
function htmlToJson(html) {
|
|
574
|
+
function parseHTML(html2) {
|
|
575
|
+
const elements2 = [];
|
|
576
|
+
const tagRegex = /<([a-z0-9]+)((?:\s+[a-z-]+="[^"]*")*)\s*>([\s\S]*?)<\/\1>/gi;
|
|
577
|
+
let match;
|
|
578
|
+
while ((match = tagRegex.exec(html2)) !== null) {
|
|
579
|
+
const [, tag, attributes, content] = match;
|
|
580
|
+
const attrs = {};
|
|
581
|
+
const attrRegex = /([a-z-]+)="([^"]*)"/gi;
|
|
582
|
+
let attrMatch;
|
|
583
|
+
while ((attrMatch = attrRegex.exec(attributes)) !== null) {
|
|
584
|
+
attrs[attrMatch[1]] = attrMatch[2];
|
|
585
|
+
}
|
|
586
|
+
elements2.push({ tag, attrs, content });
|
|
587
|
+
}
|
|
588
|
+
return elements2;
|
|
534
589
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
let
|
|
590
|
+
function parseInlineContent(content) {
|
|
591
|
+
const segments = [];
|
|
592
|
+
let currentText = "";
|
|
593
|
+
let formatStack = [];
|
|
594
|
+
let currentFormat = {
|
|
595
|
+
bold: false,
|
|
596
|
+
italic: false,
|
|
597
|
+
underline: false,
|
|
598
|
+
code: false,
|
|
599
|
+
strikethrough: false
|
|
600
|
+
};
|
|
601
|
+
const pushSegment = () => {
|
|
602
|
+
if (currentText) {
|
|
603
|
+
segments.push({
|
|
604
|
+
type: "text",
|
|
605
|
+
text: currentText,
|
|
606
|
+
...Object.fromEntries(Object.entries(currentFormat).filter(([_, value]) => value))
|
|
607
|
+
});
|
|
608
|
+
currentText = "";
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
const tags = content.split(/(<[^>]+>|~~)/);
|
|
612
|
+
for (const tag of tags) {
|
|
613
|
+
if (!tag) continue;
|
|
614
|
+
if (tag === "~~") {
|
|
615
|
+
pushSegment();
|
|
616
|
+
currentFormat.strikethrough = !currentFormat.strikethrough;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
if (tag.startsWith("<")) {
|
|
620
|
+
pushSegment();
|
|
621
|
+
if (tag.startsWith("</")) {
|
|
622
|
+
const tagName = tag.slice(2, -1).toLowerCase();
|
|
623
|
+
const lastTag = formatStack.pop();
|
|
624
|
+
if (lastTag && lastTag.type === tagName) {
|
|
625
|
+
switch (tagName) {
|
|
626
|
+
case "strong":
|
|
627
|
+
currentFormat.bold = false;
|
|
628
|
+
break;
|
|
629
|
+
case "em":
|
|
630
|
+
currentFormat.italic = false;
|
|
631
|
+
break;
|
|
632
|
+
case "u":
|
|
633
|
+
currentFormat.underline = false;
|
|
634
|
+
break;
|
|
635
|
+
case "code":
|
|
636
|
+
currentFormat.code = false;
|
|
637
|
+
break;
|
|
638
|
+
case "del":
|
|
639
|
+
currentFormat.strikethrough = false;
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
const tagName = tag.slice(1, -1).toLowerCase();
|
|
645
|
+
formatStack.push({ type: tagName, index: segments.length });
|
|
646
|
+
switch (tagName) {
|
|
647
|
+
case "strong":
|
|
648
|
+
currentFormat.bold = true;
|
|
649
|
+
break;
|
|
650
|
+
case "em":
|
|
651
|
+
currentFormat.italic = true;
|
|
652
|
+
break;
|
|
653
|
+
case "u":
|
|
654
|
+
currentFormat.underline = true;
|
|
655
|
+
break;
|
|
656
|
+
case "code":
|
|
657
|
+
currentFormat.code = true;
|
|
658
|
+
break;
|
|
659
|
+
case "del":
|
|
660
|
+
currentFormat.strikethrough = true;
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} else {
|
|
665
|
+
currentText += tag;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
pushSegment();
|
|
669
|
+
return segments.filter((segment) => segment.text.length > 0);
|
|
670
|
+
}
|
|
671
|
+
function parseList(html2, format) {
|
|
672
|
+
const listItems = html2.match(/<li>([\s\S]*?)<\/li>/g) || [];
|
|
673
|
+
return {
|
|
674
|
+
type: "list",
|
|
675
|
+
format,
|
|
676
|
+
children: listItems.map((item) => ({
|
|
677
|
+
type: "list-item",
|
|
678
|
+
children: parseListContent(item.replace(/<li>|<\/li>/g, ""))
|
|
679
|
+
}))
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
function parseListContent(content) {
|
|
683
|
+
const children = [];
|
|
684
|
+
const linkRegex = /<a\s+href="([^"]+)">([\s\S]*?)<\/a>/g;
|
|
538
685
|
let lastIndex = 0;
|
|
539
|
-
|
|
540
|
-
while ((
|
|
541
|
-
const [fullMatch,
|
|
542
|
-
if (
|
|
543
|
-
const
|
|
544
|
-
if (
|
|
545
|
-
|
|
686
|
+
let match;
|
|
687
|
+
while ((match = linkRegex.exec(content)) !== null) {
|
|
688
|
+
const [fullMatch, href, linkText] = match;
|
|
689
|
+
if (match.index > lastIndex) {
|
|
690
|
+
const textBefore = content.slice(lastIndex, match.index);
|
|
691
|
+
if (textBefore.trim()) {
|
|
692
|
+
children.push(...parseInlineContent(textBefore));
|
|
546
693
|
}
|
|
547
694
|
}
|
|
548
|
-
|
|
695
|
+
children.push({
|
|
549
696
|
type: "link",
|
|
550
|
-
url,
|
|
551
|
-
children:
|
|
697
|
+
url: href,
|
|
698
|
+
children: parseInlineContent(linkText)
|
|
552
699
|
});
|
|
553
|
-
lastIndex =
|
|
700
|
+
lastIndex = match.index + fullMatch.length;
|
|
554
701
|
}
|
|
555
|
-
if (lastIndex <
|
|
556
|
-
const
|
|
557
|
-
if (
|
|
558
|
-
|
|
702
|
+
if (lastIndex < content.length) {
|
|
703
|
+
const remainingText = content.slice(lastIndex);
|
|
704
|
+
if (remainingText.trim()) {
|
|
705
|
+
children.push(...parseInlineContent(remainingText));
|
|
559
706
|
}
|
|
560
707
|
}
|
|
561
|
-
return
|
|
708
|
+
return children;
|
|
562
709
|
}
|
|
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;
|
|
710
|
+
function parseParagraph(content) {
|
|
711
|
+
return {
|
|
712
|
+
type: "paragraph",
|
|
713
|
+
children: parseListContent(content)
|
|
714
|
+
};
|
|
585
715
|
}
|
|
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
|
-
});
|
|
716
|
+
const blocks = [];
|
|
717
|
+
const elements = parseHTML(html);
|
|
718
|
+
if (elements.length === 0 && html.trim()) {
|
|
719
|
+
blocks.push(parseParagraph(html));
|
|
720
|
+
return blocks;
|
|
612
721
|
}
|
|
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) {
|
|
722
|
+
for (const element of elements) {
|
|
723
|
+
switch (element.tag.toLowerCase()) {
|
|
626
724
|
case "h1":
|
|
627
725
|
case "h2":
|
|
628
726
|
case "h3":
|
|
629
|
-
|
|
727
|
+
case "h4":
|
|
728
|
+
case "h5":
|
|
729
|
+
case "h6":
|
|
730
|
+
blocks.push({
|
|
731
|
+
type: "heading",
|
|
732
|
+
level: parseInt(element.tag.slice(1)),
|
|
733
|
+
children: parseListContent(element.content)
|
|
734
|
+
});
|
|
630
735
|
break;
|
|
631
736
|
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
|
-
}
|
|
737
|
+
blocks.push(parseParagraph(element.content));
|
|
666
738
|
break;
|
|
667
739
|
case "ul":
|
|
740
|
+
blocks.push(parseList(element.content, "unordered"));
|
|
741
|
+
break;
|
|
668
742
|
case "ol":
|
|
669
|
-
|
|
743
|
+
blocks.push(parseList(element.content, "ordered"));
|
|
744
|
+
break;
|
|
745
|
+
case "blockquote":
|
|
746
|
+
blocks.push({
|
|
747
|
+
type: "quote",
|
|
748
|
+
children: [parseParagraph(element.content)]
|
|
749
|
+
});
|
|
670
750
|
break;
|
|
751
|
+
case "pre":
|
|
752
|
+
if (element.content.includes("<code>")) {
|
|
753
|
+
blocks.push({
|
|
754
|
+
type: "code",
|
|
755
|
+
children: parseInlineContent(element.content)
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
break;
|
|
759
|
+
default:
|
|
760
|
+
blocks.push(parseParagraph(element.content));
|
|
671
761
|
}
|
|
672
762
|
}
|
|
673
|
-
return
|
|
763
|
+
return blocks;
|
|
674
764
|
}
|
|
675
765
|
async function updateEntry(contentTypeID, entryID, sourceLocale, targetLocale, data, attributes) {
|
|
676
766
|
if (!entryID) {
|
|
@@ -682,6 +772,14 @@ async function updateEntry(contentTypeID, entryID, sourceLocale, targetLocale, d
|
|
|
682
772
|
locale: sourceLocale
|
|
683
773
|
});
|
|
684
774
|
const processedData = processDataRecursively(data);
|
|
775
|
+
for (const [key, value] of Object.entries(processedData)) {
|
|
776
|
+
if (attributes[key]?.type === "blocks" && typeof value === "string") {
|
|
777
|
+
console.warn(
|
|
778
|
+
`Field ${key} is a blocks field but received string value. Converting to blocks format.`
|
|
779
|
+
);
|
|
780
|
+
processedData[key] = htmlToJson(value);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
685
783
|
const localizedData = {};
|
|
686
784
|
for (const field in processedData) {
|
|
687
785
|
if (attributes[field] && (!attributes[field].pluginOptions?.i18n || attributes[field].pluginOptions?.i18n?.localized !== false)) {
|
|
@@ -700,23 +798,29 @@ async function updateEntry(contentTypeID, entryID, sourceLocale, targetLocale, d
|
|
|
700
798
|
});
|
|
701
799
|
}
|
|
702
800
|
}
|
|
703
|
-
function processDataRecursively(data) {
|
|
801
|
+
function processDataRecursively(data, schema) {
|
|
704
802
|
if (!data || typeof data !== "object") {
|
|
705
803
|
return data;
|
|
706
804
|
}
|
|
707
805
|
if (Array.isArray(data)) {
|
|
806
|
+
if (data[0]?.fields) {
|
|
807
|
+
const processedFields = {};
|
|
808
|
+
for (const fieldData of data[0].fields) {
|
|
809
|
+
if (fieldData.realType === "blocks") {
|
|
810
|
+
if (fieldData.translatableValue?.[0]) {
|
|
811
|
+
processedFields[fieldData.field] = htmlToJson(fieldData.translatableValue[0]);
|
|
812
|
+
}
|
|
813
|
+
} else {
|
|
814
|
+
processedFields[fieldData.field] = fieldData.translatableValue?.[0] || null;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return processedFields;
|
|
818
|
+
}
|
|
708
819
|
return data.map((item) => processDataRecursively(item));
|
|
709
820
|
}
|
|
710
821
|
const result = {};
|
|
711
822
|
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
|
-
}
|
|
823
|
+
result[key] = processDataRecursively(data[key]);
|
|
720
824
|
}
|
|
721
825
|
return result;
|
|
722
826
|
}
|
|
@@ -761,16 +865,15 @@ function processRepeatableComponents(fields, existingEntry, rootPath) {
|
|
|
761
865
|
}
|
|
762
866
|
const componentId = field.componentInfo.id;
|
|
763
867
|
if (!componentsById.has(componentId)) {
|
|
764
|
-
const existingComponent = existingComponents.find(
|
|
765
|
-
|
|
766
|
-
);
|
|
767
|
-
componentsById.set(
|
|
768
|
-
componentId,
|
|
769
|
-
existingComponent ? { ...existingComponent } : {}
|
|
770
|
-
);
|
|
868
|
+
const existingComponent = existingComponents.find((c) => c.id === componentId);
|
|
869
|
+
componentsById.set(componentId, existingComponent ? { ...existingComponent } : {});
|
|
771
870
|
}
|
|
772
871
|
const component = componentsById.get(componentId);
|
|
773
|
-
|
|
872
|
+
if (field.realType === "blocks") {
|
|
873
|
+
component[field.field] = htmlToJson(field.translatableValue[0] || "");
|
|
874
|
+
} else {
|
|
875
|
+
component[field.field] = field.translatableValue[0];
|
|
876
|
+
}
|
|
774
877
|
});
|
|
775
878
|
return Array.from(componentsById.values()).map((comp) => {
|
|
776
879
|
if (!existingComponents.find((ec) => ec.id === comp.id)) {
|
|
@@ -792,7 +895,11 @@ function processNestedComponents(fields, pathParts, existingEntry, acc) {
|
|
|
792
895
|
}
|
|
793
896
|
if (index2 === pathParts.length - 1) {
|
|
794
897
|
fields.forEach((field) => {
|
|
795
|
-
|
|
898
|
+
if (field.realType === "blocks") {
|
|
899
|
+
current[part][field.field] = htmlToJson(field.translatableValue[0] || "");
|
|
900
|
+
} else {
|
|
901
|
+
current[part][field.field] = field.translatableValue[0];
|
|
902
|
+
}
|
|
796
903
|
});
|
|
797
904
|
} else {
|
|
798
905
|
current = current[part];
|
|
@@ -807,11 +914,7 @@ function processComponentFields(componentFieldsMap, acc, existingEntry, targetSc
|
|
|
807
914
|
const rootPath = pathParts[0];
|
|
808
915
|
const schema = targetSchema.attributes?.[rootPath];
|
|
809
916
|
if (schema?.repeatable) {
|
|
810
|
-
acc[rootPath] = processRepeatableComponents(
|
|
811
|
-
fields,
|
|
812
|
-
existingEntry,
|
|
813
|
-
rootPath
|
|
814
|
-
);
|
|
917
|
+
acc[rootPath] = processRepeatableComponents(fields, existingEntry, rootPath);
|
|
815
918
|
} else {
|
|
816
919
|
processNestedComponents(fields, pathParts, existingEntry, acc);
|
|
817
920
|
}
|
|
@@ -876,9 +979,9 @@ function prepareImportData(translatables, existingEntry, targetSchema) {
|
|
|
876
979
|
return withDynamicZones;
|
|
877
980
|
}, {});
|
|
878
981
|
}
|
|
879
|
-
|
|
982
|
+
require("jsonwebtoken");
|
|
880
983
|
const crypto = require("crypto");
|
|
881
|
-
const TRANSLATIONTUDIO_URL = "https://
|
|
984
|
+
const TRANSLATIONTUDIO_URL = "https://strapi.translationstudio.tech";
|
|
882
985
|
const APP_NAME = "translationstudio";
|
|
883
986
|
const service = ({ strapi: strapi2 }) => {
|
|
884
987
|
const pluginStore = strapi2.store({
|
|
@@ -891,6 +994,16 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
891
994
|
const result = await pluginStore.get({ key: "license" });
|
|
892
995
|
return { license: result };
|
|
893
996
|
},
|
|
997
|
+
async getTranslationstudioUrl() {
|
|
998
|
+
try {
|
|
999
|
+
const result = await pluginStore.get({ key: "developurl" });
|
|
1000
|
+
if (typeof result === "string" && result !== "")
|
|
1001
|
+
return result;
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
console.warn(err);
|
|
1004
|
+
}
|
|
1005
|
+
return TRANSLATIONTUDIO_URL;
|
|
1006
|
+
},
|
|
894
1007
|
async setLicense(license) {
|
|
895
1008
|
try {
|
|
896
1009
|
await pluginStore.set({
|
|
@@ -902,6 +1015,26 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
902
1015
|
return { success: false };
|
|
903
1016
|
}
|
|
904
1017
|
},
|
|
1018
|
+
async setDevelopmentUrl(url) {
|
|
1019
|
+
try {
|
|
1020
|
+
await pluginStore.set({
|
|
1021
|
+
key: "developurl",
|
|
1022
|
+
value: url
|
|
1023
|
+
});
|
|
1024
|
+
return true;
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
async getDevelopmentUrl() {
|
|
1030
|
+
try {
|
|
1031
|
+
const result = await pluginStore.get({ key: "developurl" });
|
|
1032
|
+
if (typeof result === "string")
|
|
1033
|
+
return result;
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
}
|
|
1036
|
+
return "";
|
|
1037
|
+
},
|
|
905
1038
|
// Access Token
|
|
906
1039
|
async getToken() {
|
|
907
1040
|
try {
|
|
@@ -913,28 +1046,18 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
913
1046
|
},
|
|
914
1047
|
async generateToken() {
|
|
915
1048
|
const secretKey = crypto.randomBytes(64).toString("hex");
|
|
916
|
-
const token = jwt.sign(
|
|
917
|
-
{
|
|
918
|
-
app: APP_NAME,
|
|
919
|
-
iat: Math.floor(Date.now() / 1e3)
|
|
920
|
-
},
|
|
921
|
-
secretKey,
|
|
922
|
-
{ expiresIn: "10y" }
|
|
923
|
-
);
|
|
924
1049
|
await pluginStore.set({
|
|
925
1050
|
key: "token",
|
|
926
|
-
value:
|
|
1051
|
+
value: secretKey
|
|
927
1052
|
});
|
|
928
|
-
return { token };
|
|
1053
|
+
return { token: secretKey };
|
|
929
1054
|
},
|
|
930
1055
|
async getLanguageOptions() {
|
|
931
1056
|
const { license } = await this.getLicense();
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
{
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
);
|
|
1057
|
+
const url = await this.getTranslationstudioUrl();
|
|
1058
|
+
const response = await fetch(url + "/mappings", {
|
|
1059
|
+
headers: { Authorization: `${license}` }
|
|
1060
|
+
});
|
|
938
1061
|
const responseData = await response.json();
|
|
939
1062
|
return responseData;
|
|
940
1063
|
},
|
|
@@ -942,10 +1065,7 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
942
1065
|
const { contentTypeID, entryID, locale } = parsePayload(payload);
|
|
943
1066
|
const contentType = await getContentType(contentTypeID);
|
|
944
1067
|
const entry = await getEntry(contentTypeID, entryID, locale);
|
|
945
|
-
const contentFields = await processEntryFields(
|
|
946
|
-
entry,
|
|
947
|
-
contentType.attributes
|
|
948
|
-
);
|
|
1068
|
+
const contentFields = await processEntryFields(entry, contentType.attributes);
|
|
949
1069
|
return transformResponse(contentFields);
|
|
950
1070
|
},
|
|
951
1071
|
async importData(payload) {
|
|
@@ -953,17 +1073,9 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
953
1073
|
const sourceLocale = payload.source;
|
|
954
1074
|
const targetLocale = payload.target;
|
|
955
1075
|
try {
|
|
956
|
-
const existingEntry = await getEntry(
|
|
957
|
-
contentTypeID,
|
|
958
|
-
entryID,
|
|
959
|
-
targetLocale
|
|
960
|
-
);
|
|
1076
|
+
const existingEntry = await getEntry(contentTypeID, entryID, targetLocale);
|
|
961
1077
|
const targetSchema = await getContentType(contentTypeID);
|
|
962
|
-
const data = prepareImportData(
|
|
963
|
-
payload.document[0].fields,
|
|
964
|
-
existingEntry,
|
|
965
|
-
targetSchema
|
|
966
|
-
);
|
|
1078
|
+
const data = prepareImportData(payload.document[0].fields, existingEntry, targetSchema);
|
|
967
1079
|
if (targetSchema.pluginOptions.i18n.localized === true) {
|
|
968
1080
|
await updateEntry(
|
|
969
1081
|
contentTypeID,
|
|
@@ -981,17 +1093,15 @@ const service = ({ strapi: strapi2 }) => {
|
|
|
981
1093
|
},
|
|
982
1094
|
async requestTranslation(payload) {
|
|
983
1095
|
const { license } = await this.getLicense();
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
}
|
|
994
|
-
);
|
|
1096
|
+
const url = await this.getTranslationstudioUrl();
|
|
1097
|
+
const response = await fetch(url + "/translate", {
|
|
1098
|
+
method: "POST",
|
|
1099
|
+
headers: {
|
|
1100
|
+
Authorization: `${license}`,
|
|
1101
|
+
"Content-Type": "application/json"
|
|
1102
|
+
},
|
|
1103
|
+
body: JSON.stringify(payload)
|
|
1104
|
+
});
|
|
995
1105
|
if (response.status === 204) return true;
|
|
996
1106
|
},
|
|
997
1107
|
async getEmail(ctx) {
|