@smartcat/sanity-plugin 1.2.0 → 1.3.0

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.
Files changed (42) hide show
  1. package/dist/_chunks-cjs/index.cjs +18 -5
  2. package/dist/_chunks-cjs/index.cjs.map +1 -1
  3. package/dist/_chunks-cjs/index2.cjs +8 -6
  4. package/dist/_chunks-cjs/index2.cjs.map +1 -1
  5. package/dist/_chunks-es/index.js +18 -5
  6. package/dist/_chunks-es/index.js.map +1 -1
  7. package/dist/_chunks-es/index2.js +8 -6
  8. package/dist/_chunks-es/index2.js.map +1 -1
  9. package/dist/export.cjs +21 -4
  10. package/dist/export.cjs.map +1 -1
  11. package/dist/export.d.cts +2 -0
  12. package/dist/export.d.ts +2 -0
  13. package/dist/export.js +22 -5
  14. package/dist/export.js.map +1 -1
  15. package/dist/index.cjs +156 -39
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.js +160 -43
  18. package/dist/index.js.map +1 -1
  19. package/dist/locjson.d.cts +15 -0
  20. package/dist/locjson.d.ts +15 -0
  21. package/dist/progress.d.cts +0 -19
  22. package/dist/progress.d.ts +0 -19
  23. package/dist/smartcat.cjs +14 -2
  24. package/dist/smartcat.cjs.map +1 -1
  25. package/dist/smartcat.d.cts +10 -1
  26. package/dist/smartcat.d.ts +10 -1
  27. package/dist/smartcat.js +14 -2
  28. package/dist/smartcat.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/actions/AddToProjectAction.tsx +23 -2
  31. package/src/export/export.test.ts +42 -5
  32. package/src/export/index.ts +63 -8
  33. package/src/lib/locjson/filename.ts +7 -2
  34. package/src/lib/locjson/locjson.test.ts +64 -0
  35. package/src/lib/locjson/serialize.ts +38 -2
  36. package/src/progress/core.ts +44 -3
  37. package/src/progress/progress.test.ts +40 -0
  38. package/src/schema/translationProject.ts +19 -0
  39. package/src/smartcat/client.ts +15 -1
  40. package/src/tool/ProjectView.tsx +108 -44
  41. package/src/tool/data.ts +7 -0
  42. package/src/tool/processing.ts +110 -4
package/dist/index.cjs CHANGED
@@ -88,7 +88,19 @@ function createTranslationProjectType() {
88
88
  // Set when the item was added from the published perspective, so the
89
89
  // export translates the published document. Absent/false (incl. all
90
90
  // legacy items) prefers the draft — see itemDocDerefRespectingDraft.
91
- sanity.defineField({ name: "sourceIsPublished", title: "Source is published", type: "boolean" })
91
+ sanity.defineField({ name: "sourceIsPublished", title: "Source is published", type: "boolean" }),
92
+ // When true, the export sends the item's *existing* translations to
93
+ // Smartcat: one bilingual LocJSON per target language (populated from
94
+ // field/linked-doc translations), grouped via `{name}_{lang}.locjson`.
95
+ // Absent/false (incl. all legacy items) ⇒ today's behaviour: a single
96
+ // source-only file targeting all languages. Note: imported bilingual
97
+ // targets land confirmed at every workflow stage (Smartcat ignores
98
+ // per-segment status on LocJSON import), i.e. they skip human review.
99
+ sanity.defineField({
100
+ name: "sendExistingTranslations",
101
+ title: "Send existing translations",
102
+ type: "boolean"
103
+ })
92
104
  ],
93
105
  preview: { select: { title: "docId", subtitle: "docType" } }
94
106
  })
@@ -198,8 +210,15 @@ function createTranslationProjectType() {
198
210
  type: "object",
199
211
  name: "smartcatOutboxItem",
200
212
  fields: [
213
+ sanity.defineField({ name: "sourceDocId", type: "string" }),
214
+ sanity.defineField({ name: "title", type: "string" }),
201
215
  sanity.defineField({ name: "filename", type: "string" }),
202
- sanity.defineField({ name: "locjson", type: "text" })
216
+ sanity.defineField({ name: "locjson", type: "text" }),
217
+ // Present only for per-language bilingual files (send-existing flow):
218
+ // the single target language this file carries, so the export Function
219
+ // uploads it with `documentModel targetLanguages:[lang]`. Absent ⇒
220
+ // upload targeting all project languages (the source-only default).
221
+ sanity.defineField({ name: "targetLanguage", type: "string" })
203
222
  ]
204
223
  })
205
224
  ]
@@ -434,7 +453,7 @@ const PROJECTS_QUERY$1 = `*[_type == $type] | order(name asc){
434
453
  function createAddToProjectAction(config) {
435
454
  const sourceLanguage = config.sourceLanguage;
436
455
  return (props) => {
437
- const { id, draft, published, onComplete } = props, client = sanity.useClient({ apiVersion: constants.API_VERSION }), schema = sanity.useSchema(), toast = ui.useToast(), { selectedPerspectiveName } = sanity.usePerspective(), sourceIsPublished = selectedPerspectiveName === "published" || !draft, [open, setOpen] = react.useState(!1), [projects, setProjects] = react.useState(null), [selection, setSelection] = react.useState(CREATE_NEW), [newName, setNewName] = react.useState(""), [busy, setBusy] = react.useState(!1), [includeLinked, setIncludeLinked] = react.useState(!0), { templates, loaded, requestRefresh } = useTemplates(), [workflow2, setWorkflow] = useWorkflowSelection(open, templates, loaded), publishedId2 = id.replace(/^drafts\./, ""), doc = draft ?? published, docTitle = doc && resolveDocumentTitle(schema, doc) || UNTITLED;
456
+ const { id, draft, published, onComplete } = props, client = sanity.useClient({ apiVersion: constants.API_VERSION }), schema = sanity.useSchema(), toast = ui.useToast(), { selectedPerspectiveName } = sanity.usePerspective(), sourceIsPublished = selectedPerspectiveName === "published" || !draft, [open, setOpen] = react.useState(!1), [projects, setProjects] = react.useState(null), [selection, setSelection] = react.useState(CREATE_NEW), [newName, setNewName] = react.useState(""), [busy, setBusy] = react.useState(!1), [includeLinked, setIncludeLinked] = react.useState(!0), [sendExisting, setSendExisting] = react.useState(!1), { templates, loaded, requestRefresh } = useTemplates(), [workflow2, setWorkflow] = useWorkflowSelection(open, templates, loaded), publishedId2 = id.replace(/^drafts\./, ""), doc = draft ?? published, docTitle = doc && resolveDocumentTitle(schema, doc) || UNTITLED;
438
457
  react.useEffect(() => {
439
458
  if (!open) return;
440
459
  requestRefresh();
@@ -457,7 +476,8 @@ function createAddToProjectAction(config) {
457
476
  _key: uuid.uuid(),
458
477
  docId,
459
478
  docType,
460
- sourceIsPublished
479
+ sourceIsPublished,
480
+ sendExistingTranslations: sendExisting
461
481
  }), collected = /* @__PURE__ */ new Map([[publishedId2, doc?._type]]);
462
482
  if (includeLinked) {
463
483
  const linked = await gatherLinkedDocuments({ client, rootId: publishedId2, isRoot: (type) => config.rootTypes.includes(type), isTranslatable: (type) => locjson.getTranslatableFields(schema.get(type), { exclude: [config.documentI18nLanguageField] }).length > 0 });
@@ -499,7 +519,7 @@ function createAddToProjectAction(config) {
499
519
  } finally {
500
520
  setBusy(!1);
501
521
  }
502
- }, [selection, newName, workflow2, publishedId2, doc, includeLinked, sourceIsPublished, schema, client, projects, close]), content = react.useMemo(() => projects === null ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { align: "center", justify: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { muted: !0 }) }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 4, children: [
522
+ }, [selection, newName, workflow2, publishedId2, doc, includeLinked, sendExisting, sourceIsPublished, schema, client, projects, close]), content = react.useMemo(() => projects === null ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { align: "center", justify: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { muted: !0 }) }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 4, children: [
503
523
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
504
524
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: 1, children: "Project" }),
505
525
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -580,6 +600,20 @@ function createAddToProjectAction(config) {
580
600
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "medium", children: "Include linked content" }),
581
601
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, children: "Also add documents this one references, recursively \u2014 skipping any already in the project." })
582
602
  ] })
603
+ ] }),
604
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "flex-start", gap: 3, children: [
605
+ /* @__PURE__ */ jsxRuntime.jsx(
606
+ ui.Switch,
607
+ {
608
+ checked: sendExisting,
609
+ onChange: (e) => setSendExisting(e.currentTarget.checked),
610
+ disabled: busy
611
+ }
612
+ ),
613
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
614
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "medium", children: "Send existing translations" }),
615
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, children: "Upload current translations alongside the source, so Smartcat starts pre-filled. Imported translations are marked confirmed at every stage \u2014 they skip human review." })
616
+ ] })
583
617
  ] })
584
618
  ] }) }),
585
619
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "flex-end", gap: 2, children: [
@@ -596,7 +630,7 @@ function createAddToProjectAction(config) {
596
630
  }
597
631
  )
598
632
  ] })
599
- ] }), [projects, selection, newName, workflow2, templates, busy, includeLinked, docTitle, sourceIsPublished, close, handleConfirm]);
633
+ ] }), [projects, selection, newName, workflow2, templates, busy, includeLinked, sendExisting, docTitle, sourceIsPublished, close, handleConfirm]);
600
634
  return {
601
635
  label: "Add to translation project",
602
636
  icon: icons.TranslateIcon,
@@ -633,6 +667,7 @@ const PROJECTS_QUERY = `*[_type == "${constants.TRANSLATION_PROJECT_TYPE}"] | or
633
667
  _key,
634
668
  "docId": ${progress.ITEM_ID},
635
669
  sourceIsPublished,
670
+ sendExistingTranslations,
636
671
  "doc": ${progress.itemDocDeref("{_id, _type}")},
637
672
  "translations": *[_type == "translation.metadata" && references(${progress.ITEM_ID_FROM_SUBQUERY})][0]
638
673
  .translations[]{language, "id": value._ref}
@@ -830,8 +865,11 @@ const ITEMS_QUERY = `*[_id == $id][0]{
830
865
  items[]{
831
866
  "docId": ${progress.ITEM_ID},
832
867
  "docType": docType,
868
+ sendExistingTranslations,
833
869
  "doc": ${progress.itemDocDerefRespectingDraft()}
834
870
  }
871
+ }`, SIBLINGS_QUERY = `*[_type == "translation.metadata" && references($ids)]{
872
+ "variants": translations[]{ "language": _key, "doc": value-> }
835
873
  }`;
836
874
  async function prepareExport({
837
875
  client,
@@ -856,7 +894,22 @@ async function prepareExport({
856
894
  }
857
895
  const deletedDocIds = new Set(deletions.map((d) => d.sourceDocId)), items = (project?.items ?? []).filter(
858
896
  (item) => !(item.docId && deletedDocIds.has(item.docId))
859
- ), unresolved = items.filter((item) => !item.doc), docs = items.map((item) => item.doc).filter((d) => !!d), fieldsByType = /* @__PURE__ */ new Map(), fieldsFor = (type) => {
897
+ ), unresolved = items.filter((item) => !item.doc), docs = items.map((item) => item.doc).filter((d) => !!d), siblingsByDocId = /* @__PURE__ */ new Map(), flaggedDocLevelIds = items.filter(
898
+ (item) => item.doc && item.sendExistingTranslations === !0 && locjson.localizationMode(schema.get(item.doc._type)) === "document"
899
+ ).map((item) => publishedId(item.doc._id));
900
+ if (flaggedDocLevelIds.length > 0) {
901
+ const groups = await client.fetch(
902
+ SIBLINGS_QUERY,
903
+ { ids: flaggedDocLevelIds }
904
+ );
905
+ for (const group of groups ?? []) {
906
+ const byLang = /* @__PURE__ */ new Map(), groupSourceIds = [];
907
+ for (const variant of group.variants ?? [])
908
+ variant.doc && (byLang.set(variant.language, variant.doc), groupSourceIds.push(publishedId(variant.doc._id)));
909
+ for (const sid of groupSourceIds) siblingsByDocId.set(sid, byLang);
910
+ }
911
+ }
912
+ const fieldsByType = /* @__PURE__ */ new Map(), fieldsFor = (type) => {
860
913
  let fields = fieldsByType.get(type);
861
914
  return fields || (fields = locjson.getTranslatableFields(schema.get(type), { exclude }), fieldsByType.set(type, fields)), fields;
862
915
  }, configuredLanguages = languages.map((l) => l.id).join(", ") || "(none)", configuredTypes = translatableTypes === void 0 ? "all types" : translatableTypes.join(", ") || "(none)";
@@ -866,12 +919,46 @@ async function prepareExport({
866
919
  level: "error",
867
920
  message: `${item.docId ?? "unknown document"} (${item.docType ?? "unknown type"}): no document found \u2014 it may be unpublished or deleted; skipped`
868
921
  });
869
- const outbox = [];
870
- for (const rawDoc of docs) {
871
- const isDraft = rawDoc._id.startsWith("drafts."), doc = isDraft ? { ...rawDoc, _id: publishedId(rawDoc._id) } : rawDoc, name = resolveDocumentTitle(schema, doc);
872
- log({ level: "info", message: `${name || doc._id} \xB7 ${doc._type}${isDraft ? " (draft)" : ""}` });
922
+ const outbox = [], queueBilingual = (doc, name, fields, isDraft) => {
923
+ const mode = locjson.localizationMode(schema.get(doc._type)), byLang = mode === "document" ? siblingsByDocId.get(doc._id) : void 0;
924
+ let queued = 0;
925
+ for (const targetSanityId of targetLanguages) {
926
+ const smartcatCode = progress.toSmartcatLanguage(smartcatLanguages, targetSanityId), target = mode === "field" ? { language: targetSanityId } : { language: targetSanityId, doc: byLang?.get(targetSanityId) }, locjson$1 = locjson.serializeToLocjson(doc, fields, { sourceLanguage: source, fieldLanguageKey, target });
927
+ if (locjson$1.units.length === 0) {
928
+ log({ level: "skip", indent: !0, message: `${targetSanityId}: no translatable content \u2014 not sent` });
929
+ continue;
930
+ }
931
+ const prefilled = locjson$1.units.filter((u) => u.target.length > 0).length;
932
+ outbox.push({
933
+ _key: uuid.uuid(),
934
+ sourceDocId: doc._id,
935
+ title: name,
936
+ filename: locjson.buildLocjsonFilename(doc._type, doc._id, name, { draft: isDraft, language: smartcatCode }),
937
+ locjson: JSON.stringify(locjson$1),
938
+ targetLanguage: smartcatCode
939
+ }), queued++, log({
940
+ level: "success",
941
+ indent: !0,
942
+ message: `${targetSanityId} \u2192 ${smartcatCode}: queued ${locjson$1.units.length} unit(s), ${prefilled} with existing translation`
943
+ });
944
+ }
945
+ queued === 0 && log({ level: "skip", indent: !0, message: "no translatable content \u2014 not sent" });
946
+ };
947
+ for (const item of items) {
948
+ const rawDoc = item.doc;
949
+ if (!rawDoc) continue;
950
+ const isDraft = rawDoc._id.startsWith("drafts."), doc = isDraft ? { ...rawDoc, _id: publishedId(rawDoc._id) } : rawDoc, name = resolveDocumentTitle(schema, doc), sendExisting = item.sendExistingTranslations === !0;
951
+ log({
952
+ level: "info",
953
+ message: `${name || doc._id} \xB7 ${doc._type}${isDraft ? " (draft)" : ""}${sendExisting ? " \xB7 sending existing translations" : ""}`
954
+ });
873
955
  try {
874
- const fields = fieldsFor(doc._type), skippedBlockTypes = /* @__PURE__ */ new Map(), locjson$1 = locjson.serializeToLocjson(doc, fields, {
956
+ const fields = fieldsFor(doc._type);
957
+ if (sendExisting) {
958
+ queueBilingual(doc, name, fields, isDraft);
959
+ continue;
960
+ }
961
+ const skippedBlockTypes = /* @__PURE__ */ new Map(), locjson$1 = locjson.serializeToLocjson(doc, fields, {
875
962
  sourceLanguage: source,
876
963
  fieldLanguageKey,
877
964
  onSkippedField: ({ fieldPath, types }) => skippedBlockTypes.set(fieldPath, types)
@@ -1462,39 +1549,55 @@ const CodeArea = styled__default.default.textarea`
1462
1549
  line-height: 1.5;
1463
1550
  white-space: pre;
1464
1551
  tab-size: 2;
1465
- `;
1552
+ `, VIEW_SIBLINGS_QUERY = '*[_type == "translation.metadata" && references($id)][0].translations[]{"language": _key, "doc": value->}';
1466
1553
  function ViewLocjsonButton(props) {
1467
- const { docId, type, sourceIsPublished, client, schema, sourceLanguage, documentI18nLanguageField, fieldI18nLanguageField } = props, [open, setOpen] = react.useState(!1), [content, setContent] = react.useState(null), [filename, setFilename] = react.useState(null), [error, setError] = react.useState(null), build = react.useCallback(async () => {
1468
- setOpen(!0), setContent(null), setFilename(null), setError(null);
1469
- try {
1470
- const raw = await client.fetch(
1471
- sourceIsPublished ? "*[_id == $id][0]" : 'coalesce(*[_id == "drafts." + $id][0], *[_id == $id][0])',
1472
- { id: docId }
1473
- );
1474
- if (!raw) throw new Error(`Document not found: ${docId}`);
1475
- const isDraft = typeof raw._id == "string" && raw._id.startsWith("drafts."), doc = isDraft ? { ...raw, _id: docId } : raw, fields = locjson.getTranslatableFields(schema.get(type), { exclude: [documentI18nLanguageField] }), locjson$1 = locjson.serializeToLocjson(doc, fields, {
1476
- sourceLanguage,
1477
- fieldLanguageKey: fieldI18nLanguageField
1478
- }), smartcatName = locjson.buildLocjsonFilename(type, docId, resolveDocumentTitle(schema, doc), { draft: isDraft });
1479
- setFilename(smartcatName.split("/").pop() || smartcatName), setContent(JSON.stringify(locjson$1, null, 2));
1480
- } catch (err) {
1481
- setError(err instanceof Error ? err.message : String(err));
1482
- }
1483
- }, [client, schema, docId, type, sourceIsPublished, sourceLanguage, documentI18nLanguageField, fieldI18nLanguageField]), download = react.useCallback(() => {
1554
+ const { docId, type, sourceIsPublished, client, schema, sourceLanguage, documentI18nLanguageField, fieldI18nLanguageField, targetLanguages, languages } = props, [open, setOpen] = react.useState(!1), [content, setContent] = react.useState(null), [filename, setFilename] = react.useState(null), [title, setTitle] = react.useState("Source only"), [error, setError] = react.useState(null), build = react.useCallback(
1555
+ async (targetLanguage) => {
1556
+ setOpen(!0), setContent(null), setFilename(null), setError(null), setTitle(targetLanguage ? `Source + ${languageTitle(languages, targetLanguage)}` : "Source only");
1557
+ try {
1558
+ const raw = await client.fetch(
1559
+ sourceIsPublished ? "*[_id == $id][0]" : 'coalesce(*[_id == "drafts." + $id][0], *[_id == $id][0])',
1560
+ { id: docId }
1561
+ );
1562
+ if (!raw) throw new Error(`Document not found: ${docId}`);
1563
+ const isDraft = typeof raw._id == "string" && raw._id.startsWith("drafts."), doc = isDraft ? { ...raw, _id: docId } : raw, fields = locjson.getTranslatableFields(schema.get(type), { exclude: [documentI18nLanguageField] });
1564
+ let target;
1565
+ if (targetLanguage)
1566
+ if (locjson.localizationMode(schema.get(type)) === "document") {
1567
+ const sibling = (await client.fetch(VIEW_SIBLINGS_QUERY, { id: docId }) ?? []).find((v) => v.language === targetLanguage)?.doc ?? void 0;
1568
+ target = { language: targetLanguage, doc: sibling };
1569
+ } else
1570
+ target = { language: targetLanguage };
1571
+ const locjson$1 = locjson.serializeToLocjson(doc, fields, {
1572
+ sourceLanguage,
1573
+ fieldLanguageKey: fieldI18nLanguageField,
1574
+ target
1575
+ }), smartcatCode = targetLanguage ? languages.find((l) => l.id === targetLanguage)?.smartcatLanguage || targetLanguage : void 0, smartcatName = locjson.buildLocjsonFilename(type, docId, resolveDocumentTitle(schema, doc), {
1576
+ draft: isDraft,
1577
+ language: smartcatCode
1578
+ });
1579
+ setFilename(smartcatName.split("/").pop() || smartcatName), setContent(JSON.stringify(locjson$1, null, 2));
1580
+ } catch (err) {
1581
+ setError(err instanceof Error ? err.message : String(err));
1582
+ }
1583
+ },
1584
+ [client, schema, docId, type, sourceIsPublished, sourceLanguage, documentI18nLanguageField, fieldI18nLanguageField, languages]
1585
+ ), download = react.useCallback(() => {
1484
1586
  if (content == null || !filename) return;
1485
1587
  const url = URL.createObjectURL(new Blob([content], { type: "application/json" })), anchor = document.createElement("a");
1486
1588
  anchor.href = url, anchor.download = filename, anchor.click(), URL.revokeObjectURL(url);
1487
1589
  }, [content, filename]);
1488
1590
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1489
1591
  /* @__PURE__ */ jsxRuntime.jsx(
1490
- ui.Button,
1592
+ ui.MenuButton,
1491
1593
  {
1492
- mode: "bleed",
1493
- icon: icons.JsonIcon,
1494
- title: "View LocJSON",
1495
- fontSize: 0,
1496
- padding: 2,
1497
- onClick: build
1594
+ id: `locjson-menu-${docId}`,
1595
+ button: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { mode: "bleed", icon: icons.JsonIcon, iconRight: icons.ChevronDownIcon, title: "View LocJSON", fontSize: 0, padding: 2 }),
1596
+ menu: /* @__PURE__ */ jsxRuntime.jsxs(ui.Menu, { children: [
1597
+ /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, { text: "Source only", onClick: () => build() }),
1598
+ targetLanguages.map((lang) => /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, { text: `Source + ${languageTitle(languages, lang)}`, onClick: () => build(lang) }, lang))
1599
+ ] }),
1600
+ popover: { portal: !0 }
1498
1601
  }
1499
1602
  ),
1500
1603
  open && /* @__PURE__ */ jsxRuntime.jsx(
@@ -1504,7 +1607,9 @@ function ViewLocjsonButton(props) {
1504
1607
  header: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 3, children: [
1505
1608
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { weight: "semibold", children: [
1506
1609
  "LocJSON \xB7 ",
1507
- type
1610
+ type,
1611
+ " \xB7 ",
1612
+ title
1508
1613
  ] }),
1509
1614
  /* @__PURE__ */ jsxRuntime.jsx(
1510
1615
  ui.Button,
@@ -1918,6 +2023,16 @@ Smartcat language code: '${l.smartcatLanguage || l.id}'`,
1918
2023
  children: modeByType.get(item.doc._type) === "field" ? "fields" : "document"
1919
2024
  }
1920
2025
  ),
2026
+ /* @__PURE__ */ jsxRuntime.jsx(
2027
+ ui.Badge,
2028
+ {
2029
+ tone: item.sendExistingTranslations ? "primary" : "default",
2030
+ fontSize: 0,
2031
+ paddingX: 2,
2032
+ title: item.sendExistingTranslations ? "Existing translations are uploaded to Smartcat (one bilingual file per language) and marked confirmed" : "Only source text is sent; Smartcat translates from scratch",
2033
+ children: item.sendExistingTranslations ? "source + translations" : "source only"
2034
+ }
2035
+ ),
1921
2036
  item.doc && /* @__PURE__ */ jsxRuntime.jsx(OpenInStudioButton, { id: item.doc._id, type: item.doc._type }),
1922
2037
  item.doc && /* @__PURE__ */ jsxRuntime.jsx(
1923
2038
  ViewLocjsonButton,
@@ -1929,7 +2044,9 @@ Smartcat language code: '${l.smartcatLanguage || l.id}'`,
1929
2044
  schema,
1930
2045
  sourceLanguage,
1931
2046
  documentI18nLanguageField,
1932
- fieldI18nLanguageField
2047
+ fieldI18nLanguageField,
2048
+ targetLanguages: targets,
2049
+ languages
1933
2050
  }
1934
2051
  )
1935
2052
  ] }),