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