autumnnote 1.0.7 → 1.0.9

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 (40) hide show
  1. package/README.md +332 -638
  2. package/dist/autumnnote.css +40 -4
  3. package/dist/autumnnote.es.js +3375 -262
  4. package/dist/autumnnote.es.js.map +1 -1
  5. package/dist/autumnnote.umd.js +3374 -261
  6. package/dist/autumnnote.umd.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/js/Context.js +4 -0
  9. package/src/js/editing/Style.js +195 -44
  10. package/src/js/editing/Typing.js +2 -2
  11. package/src/js/i18n/de.js +320 -0
  12. package/src/js/i18n/en.js +328 -0
  13. package/src/js/i18n/es.js +320 -0
  14. package/src/js/i18n/fr.js +321 -0
  15. package/src/js/i18n/index.js +59 -0
  16. package/src/js/i18n/ja.js +321 -0
  17. package/src/js/i18n/ko.js +320 -0
  18. package/src/js/i18n/vi.js +321 -0
  19. package/src/js/i18n/zh.js +321 -0
  20. package/src/js/index.js +2 -1
  21. package/src/js/module/CodeTooltip.js +12 -9
  22. package/src/js/module/ContextMenu.js +13 -11
  23. package/src/js/module/Editor.js +14 -3
  24. package/src/js/module/EmojiDialog.js +19 -7
  25. package/src/js/module/FindReplace.js +14 -13
  26. package/src/js/module/IconDialog.js +25 -13
  27. package/src/js/module/ImageDialog.js +25 -20
  28. package/src/js/module/ImageTooltip.js +13 -12
  29. package/src/js/module/LinkDialog.js +10 -9
  30. package/src/js/module/LinkTooltip.js +6 -5
  31. package/src/js/module/Placeholder.js +6 -1
  32. package/src/js/module/ShortcutsDialog.js +5 -4
  33. package/src/js/module/Statusbar.js +6 -5
  34. package/src/js/module/TableTooltip.js +412 -102
  35. package/src/js/module/Toolbar.js +21 -15
  36. package/src/js/module/VideoDialog.js +10 -9
  37. package/src/js/module/VideoTooltip.js +12 -11
  38. package/src/js/settings.js +4 -0
  39. package/src/styles/autumnnote.scss +50 -5
  40. package/types/index.d.ts +58 -1
@@ -566,8 +566,24 @@ function underline() {
566
566
  }
567
567
  /**
568
568
  * Strikethrough / removes strikethrough.
569
+ * Falls back to manual DOM manipulation inside nested formats where
570
+ * execCommand's state detection is unreliable (mirrors underline() logic).
569
571
  */
570
- var strikethrough = () => execCommand("strikeThrough");
572
+ function strikethrough() {
573
+ const sel = window.getSelection();
574
+ if (!sel || !sel.rangeCount) return;
575
+ let sc = sel.getRangeAt(0).startContainer;
576
+ if (sc.nodeType === 3) sc = sc.parentElement;
577
+ const sEl = sc && sc.closest && (sc.closest("s") || sc.closest("strike"));
578
+ const nativeState = document.queryCommandState("strikeThrough");
579
+ if (sEl && !nativeState) {
580
+ const parent = sEl.parentNode;
581
+ while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
582
+ parent.removeChild(sEl);
583
+ return;
584
+ }
585
+ execCommand("strikeThrough");
586
+ }
571
587
  /**
572
588
  * Superscript toggle.
573
589
  */
@@ -600,6 +616,22 @@ var fontName = (name) => execCommand("fontName", name);
600
616
  function fontSize(size, editable = document) {
601
617
  const sel = window.getSelection();
602
618
  const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
619
+ if (wasCollapsed && sel && sel.rangeCount > 0) {
620
+ try {
621
+ const range = sel.getRangeAt(0);
622
+ const span = document.createElement("span");
623
+ span.style.fontSize = size;
624
+ const zwsNode = document.createTextNode("​");
625
+ span.appendChild(zwsNode);
626
+ range.insertNode(span);
627
+ const nr = document.createRange();
628
+ nr.setStart(zwsNode, zwsNode.textContent.length);
629
+ nr.collapse(true);
630
+ sel.removeAllRanges();
631
+ sel.addRange(nr);
632
+ } catch (_) {}
633
+ return;
634
+ }
603
635
  execCommand("fontSize", "7");
604
636
  const scope = editable instanceof HTMLElement ? editable : document;
605
637
  const newSpans = [];
@@ -611,27 +643,17 @@ function fontSize(size, editable = document) {
611
643
  el.parentNode.removeChild(el);
612
644
  newSpans.push(span);
613
645
  });
614
- if (sel && newSpans.length > 0) {
646
+ if (!wasCollapsed && sel && newSpans.length > 0) {
615
647
  const first = newSpans[0];
616
648
  const last = newSpans[newSpans.length - 1];
617
649
  try {
618
- if (wasCollapsed) {
619
- if (!first.firstChild) first.appendChild(document.createTextNode("​"));
620
- const nr = document.createRange();
621
- const anchor = first.firstChild;
622
- nr.setStart(anchor, anchor.textContent.length);
623
- nr.collapse(true);
624
- sel.removeAllRanges();
625
- sel.addRange(nr);
626
- } else {
627
- const nr = document.createRange();
628
- const startNode = first.firstChild || first;
629
- const endNode = last.lastChild || last;
630
- nr.setStart(startNode, 0);
631
- nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
632
- sel.removeAllRanges();
633
- sel.addRange(nr);
634
- }
650
+ const nr = document.createRange();
651
+ const startNode = first.firstChild || first;
652
+ const endNode = last.lastChild || last;
653
+ nr.setStart(startNode, 0);
654
+ nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
655
+ sel.removeAllRanges();
656
+ sel.addRange(nr);
635
657
  } catch (_) {}
636
658
  }
637
659
  }
@@ -662,8 +684,57 @@ var justifyFull = () => execCommand("justifyFull");
662
684
  var indent = () => execCommand("indent");
663
685
  /**
664
686
  * Outdents the list or block.
687
+ * G.5: When cursor is inside a checklist item, "outdent" means converting
688
+ * that item back to a regular <p> element rather than calling execCommand
689
+ * (which would destroy the ul > li checklist structure).
690
+ */
691
+ function outdent() {
692
+ const sel = window.getSelection();
693
+ if (sel && sel.rangeCount) {
694
+ let container = sel.getRangeAt(0).commonAncestorContainer;
695
+ if (container.nodeType === 3) container = container.parentElement;
696
+ const checkLi = container && container.closest && container.closest(".an-checklist li");
697
+ if (checkLi) {
698
+ _checklistItemToP(checkLi);
699
+ return;
700
+ }
701
+ }
702
+ execCommand("outdent");
703
+ }
704
+ /**
705
+ * G.5 helper: splits a checklist at checkLi, converts it to a <p>,
706
+ * and keeps items before/after as separate checklists.
707
+ * @param {HTMLElement} checkLi
665
708
  */
666
- var outdent = () => execCommand("outdent");
709
+ function _checklistItemToP(checkLi) {
710
+ const checkUl = checkLi.closest(".an-checklist");
711
+ if (!checkUl) return;
712
+ const allLis = Array.from(checkUl.children);
713
+ const liIndex = allLis.indexOf(checkLi);
714
+ const afterLis = allLis.slice(liIndex + 1);
715
+ const p = document.createElement("p");
716
+ p.textContent = Array.from(checkLi.childNodes).filter((n) => !(n.nodeType === 1 && n.tagName === "INPUT")).map((n) => n.textContent).join("").replace(/\u200B/g, "").trim() || "\xA0";
717
+ if (afterLis.length > 0) {
718
+ const newUl = document.createElement("ul");
719
+ newUl.className = "an-checklist";
720
+ afterLis.forEach((li) => newUl.appendChild(li));
721
+ checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
722
+ }
723
+ checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
724
+ checkUl.removeChild(checkLi);
725
+ if (checkUl.children.length === 0) checkUl.parentNode.removeChild(checkUl);
726
+ try {
727
+ const nr = document.createRange();
728
+ const firstChild = p.firstChild;
729
+ nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
730
+ nr.collapse(true);
731
+ const s = window.getSelection();
732
+ if (s) {
733
+ s.removeAllRanges();
734
+ s.addRange(nr);
735
+ }
736
+ } catch {}
737
+ }
667
738
  /**
668
739
  * Inserts an unordered list or converts selection.
669
740
  */
@@ -867,10 +938,60 @@ function toggleChecklist() {
867
938
  sel.addRange(nr);
868
939
  return;
869
940
  }
870
- const lines = sel.toString().split(/\r?\n/).filter((l) => l.trim().length > 0);
871
- if (lines.length === 0) return;
872
- const items = lines.map((l) => `<li><input type="checkbox" contenteditable="false">${l || "​"}</li>`).join("");
873
- document.execCommand("insertHTML", false, `<ul class="an-checklist">${items}</ul>`);
941
+ if (!sel.toString().replace(/[\u00a0\u200B]/g, " ").trim()) return;
942
+ const BLOCK_TAGS_MULTI = new Set([
943
+ "P",
944
+ "DIV",
945
+ "H1",
946
+ "H2",
947
+ "H3",
948
+ "H4",
949
+ "H5",
950
+ "H6",
951
+ "BLOCKQUOTE",
952
+ "PRE",
953
+ "LI"
954
+ ]);
955
+ const blocks = [];
956
+ const seenBlocks = /* @__PURE__ */ new Set();
957
+ const commonAncestor = range.commonAncestorContainer;
958
+ const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
959
+ let node;
960
+ while (node = iter.nextNode()) {
961
+ if (!range.intersectsNode(node)) continue;
962
+ let block = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
963
+ while (block && !BLOCK_TAGS_MULTI.has(block.tagName)) block = block.parentElement;
964
+ if (block && !seenBlocks.has(block)) {
965
+ seenBlocks.add(block);
966
+ blocks.push(block);
967
+ }
968
+ }
969
+ if (blocks.length === 0) return;
970
+ const newUl = document.createElement("ul");
971
+ newUl.className = "an-checklist";
972
+ let lastTextNode = null;
973
+ blocks.forEach((block) => {
974
+ const li = document.createElement("li");
975
+ const cb = document.createElement("input");
976
+ cb.type = "checkbox";
977
+ cb.setAttribute("contenteditable", "false");
978
+ li.appendChild(cb);
979
+ const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
980
+ const tn = document.createTextNode(blockText || "​");
981
+ li.appendChild(tn);
982
+ newUl.appendChild(li);
983
+ lastTextNode = tn;
984
+ });
985
+ const firstBlock = blocks[0];
986
+ firstBlock.parentNode.insertBefore(newUl, firstBlock);
987
+ blocks.forEach((block) => block.parentNode && block.parentNode.removeChild(block));
988
+ if (lastTextNode) {
989
+ const nr = document.createRange();
990
+ nr.setStart(lastTextNode, lastTextNode.textContent.length);
991
+ nr.collapse(true);
992
+ sel.removeAllRanges();
993
+ sel.addRange(nr);
994
+ }
874
995
  }
875
996
  /**
876
997
  * Returns true when the cursor is inside a checklist item.
@@ -1322,9 +1443,2699 @@ var defaultOptions = {
1322
1443
  onDestroy: null,
1323
1444
  onCharLimitReached: null,
1324
1445
  onWordLimitReached: null,
1325
- focusColor: null
1446
+ focusColor: null,
1447
+ lang: "en"
1326
1448
  };
1327
1449
  //#endregion
1450
+ //#region src/js/i18n/en.js
1451
+ /**
1452
+ * en.js - English locale (canonical reference)
1453
+ * All other locales deep-merge against this to fill missing keys.
1454
+ */
1455
+ /** @type {import('../../../types/index.js').AsnLocale} */
1456
+ var en = {
1457
+ toolbar: {
1458
+ bold: "Bold (Ctrl+B)",
1459
+ italic: "Italic (Ctrl+I)",
1460
+ underline: "Underline (Ctrl+U)",
1461
+ strikethrough: "Strikethrough",
1462
+ superscript: "Superscript",
1463
+ subscript: "Subscript",
1464
+ alignLeft: "Align Left",
1465
+ alignCenter: "Align Center",
1466
+ alignRight: "Align Right",
1467
+ alignJustify: "Justify",
1468
+ ul: "Unordered List",
1469
+ ol: "Ordered List",
1470
+ checklist: "Checklist",
1471
+ indent: "Indent",
1472
+ outdent: "Outdent",
1473
+ undo: "Undo (Ctrl+Z)",
1474
+ redo: "Redo (Ctrl+Y)",
1475
+ hr: "Horizontal Rule",
1476
+ link: "Insert Link",
1477
+ image: "Insert Image",
1478
+ video: "Insert Video",
1479
+ emoji: "Insert Emoji",
1480
+ icon: "Insert FA Icon",
1481
+ table: "Insert Table",
1482
+ fontSize: "Font Size",
1483
+ fontSizePlaceholder: "Size",
1484
+ removeFormat: "Remove Format",
1485
+ direction: "Toggle Text Direction (LTR / RTL)",
1486
+ fontFamily: "Font Family",
1487
+ paragraphStyle: "Paragraph Style",
1488
+ paragraphStylePlaceholder: "Style",
1489
+ lineHeight: "Line Height",
1490
+ lineHeightPlaceholder: "↕ Line",
1491
+ codeview: "HTML Code View",
1492
+ fullscreen: "Fullscreen",
1493
+ shortcuts: "Keyboard Shortcuts (Ctrl+Shift+/)",
1494
+ find: "Find (Ctrl+F)",
1495
+ findReplace: "Find & Replace (Ctrl+H)",
1496
+ inlineCode: "Inline Code (Ctrl+`)",
1497
+ print: "Print",
1498
+ foreColor: "Text Color",
1499
+ backColor: "Highlight Color",
1500
+ chooseTextColor: "Choose text color",
1501
+ chooseHighlightColor: "Choose highlight color",
1502
+ customColor: "Custom color",
1503
+ insertTableLabel: "Insert Table",
1504
+ paragraphItems: {
1505
+ p: "Normal",
1506
+ blockquote: "Quote",
1507
+ pre: "Code"
1508
+ }
1509
+ },
1510
+ linkDialog: {
1511
+ ariaLabel: "Insert link",
1512
+ title: "Insert Link",
1513
+ url: "URL",
1514
+ urlPlaceholder: "https://",
1515
+ displayText: "Display Text",
1516
+ textPlaceholder: "Link text",
1517
+ openInNewTab: "Open in new tab",
1518
+ insertBtn: "Insert",
1519
+ cancelBtn: "Cancel"
1520
+ },
1521
+ imageDialog: {
1522
+ ariaLabel: "Insert image",
1523
+ title: "Insert Image",
1524
+ imageUrl: "Image URL",
1525
+ urlPlaceholder: "https://example.com/image.png",
1526
+ altText: "Alt Text",
1527
+ altPlaceholder: "Describe the image",
1528
+ alignment: "Alignment",
1529
+ alignNone: "None",
1530
+ alignLeft: "Left",
1531
+ alignCenter: "Center",
1532
+ alignRight: "Right",
1533
+ uploadLabel: "Or upload a file",
1534
+ insertBtn: "Insert",
1535
+ cancelBtn: "Cancel"
1536
+ },
1537
+ videoDialog: {
1538
+ ariaLabel: "Insert video",
1539
+ title: "Insert Video",
1540
+ videoUrl: "Video URL",
1541
+ urlPlaceholder: "YouTube, Vimeo, or direct .mp4 URL",
1542
+ widthLabel: "Width (px)",
1543
+ widthPlaceholder: "560",
1544
+ insertBtn: "Insert",
1545
+ cancelBtn: "Cancel",
1546
+ detected: (type) => `Detected: ${type}`,
1547
+ unknownFormat: "Unknown format — will try direct video embed",
1548
+ invalidUrl: "Invalid URL — please enter a valid video link."
1549
+ },
1550
+ emojiDialog: {
1551
+ ariaLabel: "Insert emoji",
1552
+ title: "Insert Emoji",
1553
+ searchPlaceholder: "Search emojis…",
1554
+ all: "All",
1555
+ cancelBtn: "Cancel",
1556
+ close: "Close",
1557
+ categories: {
1558
+ smileys: "Smileys",
1559
+ people: "People",
1560
+ animals: "Animals",
1561
+ food: "Food",
1562
+ travel: "Travel",
1563
+ objects: "Objects",
1564
+ symbols: "Symbols"
1565
+ }
1566
+ },
1567
+ iconDialog: {
1568
+ ariaLabel: "Insert FA icon",
1569
+ title: "Insert FA Icon",
1570
+ searchPlaceholder: "Search icons…",
1571
+ all: "All",
1572
+ style: "Style",
1573
+ size: "Size",
1574
+ color: "Color",
1575
+ useColor: " Use color",
1576
+ selectHint: "Select an icon",
1577
+ insertBtn: "Insert FA Icon",
1578
+ cancelBtn: "Cancel",
1579
+ close: "Close",
1580
+ categories: {
1581
+ popular: "Popular",
1582
+ interface: "Interface",
1583
+ navigation: "Navigation",
1584
+ media: "Media",
1585
+ communication: "Communication",
1586
+ files: "Files",
1587
+ people: "People",
1588
+ objects: "Objects"
1589
+ }
1590
+ },
1591
+ findReplace: {
1592
+ findTitle: "Find",
1593
+ findReplaceTitle: "Find & Replace",
1594
+ findPlaceholder: "Find…",
1595
+ searchAriaLabel: "Search text",
1596
+ caseSensitive: "\xA0Case sensitive",
1597
+ prevBtn: "← Prev",
1598
+ nextBtn: "Next →",
1599
+ replacePlaceholder: "Replace with…",
1600
+ replaceAriaLabel: "Replace with",
1601
+ replaceBtn: "Replace",
1602
+ replaceAllBtn: "Replace All",
1603
+ close: "×"
1604
+ },
1605
+ shortcutsDialog: {
1606
+ title: "Keyboard Shortcuts",
1607
+ ariaLabel: "Keyboard Shortcuts",
1608
+ close: "Close",
1609
+ shortcuts: [
1610
+ {
1611
+ category: "Text Formatting",
1612
+ items: [
1613
+ {
1614
+ keys: "Ctrl + B",
1615
+ action: "Bold"
1616
+ },
1617
+ {
1618
+ keys: "Ctrl + I",
1619
+ action: "Italic"
1620
+ },
1621
+ {
1622
+ keys: "Ctrl + U",
1623
+ action: "Underline"
1624
+ },
1625
+ {
1626
+ keys: "Ctrl + K",
1627
+ action: "Insert / edit link"
1628
+ }
1629
+ ]
1630
+ },
1631
+ {
1632
+ category: "History",
1633
+ items: [{
1634
+ keys: "Ctrl + Z",
1635
+ action: "Undo"
1636
+ }, {
1637
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
1638
+ action: "Redo"
1639
+ }]
1640
+ },
1641
+ {
1642
+ category: "Selection & Navigation",
1643
+ items: [
1644
+ {
1645
+ keys: "Ctrl + A",
1646
+ action: "Select all content"
1647
+ },
1648
+ {
1649
+ keys: "Tab",
1650
+ action: "Indent list item / insert spaces"
1651
+ },
1652
+ {
1653
+ keys: "Shift + Tab",
1654
+ action: "Outdent list item"
1655
+ }
1656
+ ]
1657
+ },
1658
+ {
1659
+ category: "Clipboard",
1660
+ items: [{
1661
+ keys: "Ctrl + Shift + V",
1662
+ action: "Paste as plain text"
1663
+ }]
1664
+ },
1665
+ {
1666
+ category: "Find & Replace",
1667
+ items: [{
1668
+ keys: "Ctrl + F",
1669
+ action: "Find in document"
1670
+ }, {
1671
+ keys: "Ctrl + H",
1672
+ action: "Find & Replace"
1673
+ }]
1674
+ },
1675
+ {
1676
+ category: "Editor",
1677
+ items: [{
1678
+ keys: "Ctrl + Shift + /",
1679
+ action: "Show this keyboard shortcuts dialog"
1680
+ }]
1681
+ }
1682
+ ]
1683
+ },
1684
+ contextMenu: {
1685
+ cut: "Cut",
1686
+ copy: "Copy",
1687
+ paste: "Paste",
1688
+ bold: "Bold",
1689
+ italic: "Italic",
1690
+ underline: "Underline",
1691
+ textColor: "Text Color",
1692
+ highlightColor: "Highlight Color",
1693
+ copyFormat: "Copy Format",
1694
+ pasteFormat: "Paste Format",
1695
+ removeFormat: "Remove Format",
1696
+ link: "Insert Link",
1697
+ image: "Insert Image",
1698
+ video: "Insert Video",
1699
+ table: "Insert Table",
1700
+ back: "Back",
1701
+ noHighlight: "No highlight",
1702
+ customColor: "Custom color",
1703
+ customColorLabel: "Custom…"
1704
+ },
1705
+ statusbar: {
1706
+ resizeHandle: "Resize editor",
1707
+ words: (n) => `Words: ${n}`,
1708
+ wordsLimit: (n, max) => `Words: ${n}/${max}`,
1709
+ chars: (n) => `Chars: ${n}`,
1710
+ charsLimit: (n, max) => `Chars: ${n}/${max}`
1711
+ },
1712
+ tooltips: {
1713
+ link: {
1714
+ ariaLabel: "Link actions",
1715
+ openLink: "Open link",
1716
+ copyUrl: "Copy URL",
1717
+ editLink: "Edit link",
1718
+ removeLink: "Remove link"
1719
+ },
1720
+ image: {
1721
+ ariaLabel: "Image actions",
1722
+ label: "Image",
1723
+ floatLeft: "Float Left",
1724
+ noFloat: "No Float",
1725
+ alignCenter: "Align Center",
1726
+ floatRight: "Float Right",
1727
+ originalSize: "Original Size",
1728
+ rotateLeft: "Rotate Left",
1729
+ rotateRight: "Rotate Right",
1730
+ cropImage: "Crop Image",
1731
+ addCaption: "Add / Edit Caption",
1732
+ deleteImage: "Delete Image"
1733
+ },
1734
+ code: {
1735
+ ariaLabel: "Code block actions",
1736
+ label: "Code",
1737
+ syntaxLanguage: "Syntax Language",
1738
+ syntaxAriaLabel: "Syntax language",
1739
+ copyCode: "Copy Code",
1740
+ toggleWordWrap: "Toggle Word Wrap",
1741
+ enableWordWrap: "Enable Word Wrap",
1742
+ disableWordWrap: "Disable Word Wrap",
1743
+ convertToParagraph: "Convert to Paragraph",
1744
+ deleteCodeBlock: "Delete Code Block"
1745
+ },
1746
+ table: {
1747
+ ariaLabel: "Table actions",
1748
+ label: "Table",
1749
+ selectCells: "Select Cells",
1750
+ addRowAbove: "Add Row Above",
1751
+ addRowBelow: "Add Row Below",
1752
+ deleteRow: "Delete Row",
1753
+ addColumnLeft: "Add Column Left",
1754
+ addColumnRight: "Add Column Right",
1755
+ deleteColumn: "Delete Column",
1756
+ mergeCells: "Merge Cells",
1757
+ unmergeCells: "Unmerge Cells",
1758
+ columnWidth: "Column Width",
1759
+ rowHeight: "Row Height",
1760
+ tableBorderWidth: "Table Border Width",
1761
+ deleteTable: "Delete Table",
1762
+ columnWidthPx: "Column Width (px)",
1763
+ rowHeightPx: "Row Height (px)",
1764
+ tableBorderWidthPx: "Table Border Width (px)",
1765
+ cancelBtn: "Cancel",
1766
+ applyBtn: "Apply"
1767
+ },
1768
+ video: {
1769
+ ariaLabel: "Video actions",
1770
+ label: "Video",
1771
+ floatLeft: "Float Left",
1772
+ noFloat: "No Float",
1773
+ alignCenter: "Align Center",
1774
+ floatRight: "Float Right",
1775
+ originalSize: "Original Size",
1776
+ previewVideo: "Preview Video",
1777
+ exitPreview: "Exit Preview",
1778
+ deleteVideo: "Delete Video"
1779
+ }
1780
+ },
1781
+ errors: {
1782
+ imageFormat: (type) => `Format "${type}" is not supported for display in web browsers. Please convert to JPEG, PNG, or WebP first.`,
1783
+ imageSize: (maxSize) => `Image file is too large. Maximum allowed size is ${maxSize} MB.`
1784
+ }
1785
+ };
1786
+ //#endregion
1787
+ //#region src/js/i18n/index.js
1788
+ /**
1789
+ * i18n/index.js — Locale registry and resolver for autumn-note-ce.
1790
+ *
1791
+ * Usage:
1792
+ * lang: 'en' → built-in English (default)
1793
+ * lang: 'vi' → built-in Vietnamese
1794
+ * lang: 'ja' → built-in Japanese
1795
+ * lang: 'zh' → built-in Simplified Chinese
1796
+ * lang: 'fr' → built-in French
1797
+ * lang: 'de' → built-in German
1798
+ * lang: 'es' → built-in Spanish
1799
+ * lang: 'ko' → built-in Korean
1800
+ * lang: { ... } → custom locale object, deep-merged over English
1801
+ */
1802
+ /**
1803
+ * All built-in locales keyed by their language code.
1804
+ * @type {Record<string, Partial<AsnLocale>>}
1805
+ */
1806
+ var locales = {
1807
+ en,
1808
+ vi: {
1809
+ toolbar: {
1810
+ bold: "Đậm (Ctrl+B)",
1811
+ italic: "Nghiêng (Ctrl+I)",
1812
+ underline: "Gạch chân (Ctrl+U)",
1813
+ strikethrough: "Gạch ngang",
1814
+ superscript: "Chỉ số trên",
1815
+ subscript: "Chỉ số dưới",
1816
+ alignLeft: "Căn trái",
1817
+ alignCenter: "Căn giữa",
1818
+ alignRight: "Căn phải",
1819
+ alignJustify: "Căn đều",
1820
+ ul: "Danh sách không thứ tự",
1821
+ ol: "Danh sách có thứ tự",
1822
+ checklist: "Danh sách kiểm tra",
1823
+ indent: "Tăng thụt đầu dòng",
1824
+ outdent: "Giảm thụt đầu dòng",
1825
+ undo: "Hoàn tác (Ctrl+Z)",
1826
+ redo: "Làm lại (Ctrl+Y)",
1827
+ hr: "Đường kẻ ngang",
1828
+ link: "Chèn liên kết",
1829
+ image: "Chèn hình ảnh",
1830
+ video: "Chèn video",
1831
+ emoji: "Chèn biểu tượng cảm xúc",
1832
+ icon: "Chèn biểu tượng FA",
1833
+ table: "Chèn bảng",
1834
+ fontSize: "Cỡ chữ",
1835
+ fontSizePlaceholder: "Cỡ",
1836
+ removeFormat: "Xóa định dạng",
1837
+ direction: "Chuyển hướng văn bản (LTR / RTL)",
1838
+ fontFamily: "Phông chữ",
1839
+ paragraphStyle: "Kiểu đoạn văn",
1840
+ paragraphStylePlaceholder: "Kiểu",
1841
+ lineHeight: "Khoảng cách dòng",
1842
+ lineHeightPlaceholder: "↕ Dòng",
1843
+ codeview: "Xem mã HTML",
1844
+ fullscreen: "Toàn màn hình",
1845
+ shortcuts: "Phím tắt (Ctrl+Shift+/)",
1846
+ find: "Tìm kiếm (Ctrl+F)",
1847
+ findReplace: "Tìm & Thay thế (Ctrl+H)",
1848
+ inlineCode: "Mã nội tuyến (Ctrl+`)",
1849
+ print: "In",
1850
+ foreColor: "Màu chữ",
1851
+ backColor: "Màu nền chữ",
1852
+ chooseTextColor: "Chọn màu chữ",
1853
+ chooseHighlightColor: "Chọn màu nền",
1854
+ customColor: "Màu tùy chỉnh",
1855
+ insertTableLabel: "Chèn bảng",
1856
+ paragraphItems: {
1857
+ p: "Bình thường",
1858
+ blockquote: "Trích dẫn",
1859
+ pre: "Mã"
1860
+ }
1861
+ },
1862
+ linkDialog: {
1863
+ ariaLabel: "Chèn liên kết",
1864
+ title: "Chèn liên kết",
1865
+ url: "Địa chỉ URL",
1866
+ urlPlaceholder: "https://",
1867
+ displayText: "Văn bản hiển thị",
1868
+ textPlaceholder: "Nội dung liên kết",
1869
+ openInNewTab: "Mở trong tab mới",
1870
+ insertBtn: "Chèn",
1871
+ cancelBtn: "Hủy"
1872
+ },
1873
+ imageDialog: {
1874
+ ariaLabel: "Chèn hình ảnh",
1875
+ title: "Chèn hình ảnh",
1876
+ imageUrl: "URL hình ảnh",
1877
+ urlPlaceholder: "https://example.com/anh.png",
1878
+ altText: "Văn bản thay thế",
1879
+ altPlaceholder: "Mô tả hình ảnh",
1880
+ alignment: "Căn chỉnh",
1881
+ alignNone: "Không",
1882
+ alignLeft: "Trái",
1883
+ alignCenter: "Giữa",
1884
+ alignRight: "Phải",
1885
+ uploadLabel: "Hoặc tải lên tệp",
1886
+ insertBtn: "Chèn",
1887
+ cancelBtn: "Hủy"
1888
+ },
1889
+ videoDialog: {
1890
+ ariaLabel: "Chèn video",
1891
+ title: "Chèn video",
1892
+ videoUrl: "URL video",
1893
+ urlPlaceholder: "YouTube, Vimeo, hoặc URL .mp4 trực tiếp",
1894
+ widthLabel: "Chiều rộng (px)",
1895
+ widthPlaceholder: "560",
1896
+ insertBtn: "Chèn",
1897
+ cancelBtn: "Hủy",
1898
+ detected: (type) => `Đã phát hiện: ${type}`,
1899
+ unknownFormat: "Định dạng không xác định — sẽ thử nhúng video trực tiếp",
1900
+ invalidUrl: "URL không hợp lệ — vui lòng nhập đường dẫn video hợp lệ."
1901
+ },
1902
+ emojiDialog: {
1903
+ ariaLabel: "Chèn biểu tượng cảm xúc",
1904
+ title: "Chèn biểu tượng cảm xúc",
1905
+ searchPlaceholder: "Tìm kiếm biểu tượng…",
1906
+ all: "Tất cả",
1907
+ cancelBtn: "Hủy",
1908
+ close: "Đóng",
1909
+ categories: {
1910
+ smileys: "Mặt cười",
1911
+ people: "Con người",
1912
+ animals: "Động vật",
1913
+ food: "Thức ăn",
1914
+ travel: "Du lịch",
1915
+ objects: "Đồ vật",
1916
+ symbols: "Ký hiệu"
1917
+ }
1918
+ },
1919
+ iconDialog: {
1920
+ ariaLabel: "Chèn biểu tượng FA",
1921
+ title: "Chèn biểu tượng FA",
1922
+ searchPlaceholder: "Tìm kiếm biểu tượng…",
1923
+ all: "Tất cả",
1924
+ style: "Kiểu",
1925
+ size: "Kích thước",
1926
+ color: "Màu sắc",
1927
+ useColor: " Dùng màu",
1928
+ selectHint: "Chọn một biểu tượng",
1929
+ insertBtn: "Chèn biểu tượng FA",
1930
+ cancelBtn: "Hủy",
1931
+ close: "Đóng",
1932
+ categories: {
1933
+ popular: "Phổ biến",
1934
+ interface: "Giao diện",
1935
+ navigation: "Điều hướng",
1936
+ media: "Phương tiện",
1937
+ communication: "Liên lạc",
1938
+ files: "Tệp tin",
1939
+ people: "Con người",
1940
+ objects: "Đồ vật"
1941
+ }
1942
+ },
1943
+ findReplace: {
1944
+ findTitle: "Tìm kiếm",
1945
+ findReplaceTitle: "Tìm & Thay thế",
1946
+ findPlaceholder: "Tìm…",
1947
+ searchAriaLabel: "Văn bản tìm kiếm",
1948
+ caseSensitive: "\xA0Phân biệt hoa thường",
1949
+ prevBtn: "← Trước",
1950
+ nextBtn: "Tiếp →",
1951
+ replacePlaceholder: "Thay thế bằng…",
1952
+ replaceAriaLabel: "Thay thế bằng",
1953
+ replaceBtn: "Thay thế",
1954
+ replaceAllBtn: "Thay thế tất cả",
1955
+ close: "×"
1956
+ },
1957
+ shortcutsDialog: {
1958
+ title: "Phím tắt bàn phím",
1959
+ ariaLabel: "Phím tắt bàn phím",
1960
+ close: "Đóng",
1961
+ shortcuts: [
1962
+ {
1963
+ category: "Định dạng văn bản",
1964
+ items: [
1965
+ {
1966
+ keys: "Ctrl + B",
1967
+ action: "Đậm"
1968
+ },
1969
+ {
1970
+ keys: "Ctrl + I",
1971
+ action: "Nghiêng"
1972
+ },
1973
+ {
1974
+ keys: "Ctrl + U",
1975
+ action: "Gạch chân"
1976
+ },
1977
+ {
1978
+ keys: "Ctrl + K",
1979
+ action: "Chèn / sửa liên kết"
1980
+ }
1981
+ ]
1982
+ },
1983
+ {
1984
+ category: "Lịch sử",
1985
+ items: [{
1986
+ keys: "Ctrl + Z",
1987
+ action: "Hoàn tác"
1988
+ }, {
1989
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
1990
+ action: "Làm lại"
1991
+ }]
1992
+ },
1993
+ {
1994
+ category: "Chọn & Điều hướng",
1995
+ items: [
1996
+ {
1997
+ keys: "Ctrl + A",
1998
+ action: "Chọn tất cả"
1999
+ },
2000
+ {
2001
+ keys: "Tab",
2002
+ action: "Tăng thụt đầu dòng / chèn khoảng trắng"
2003
+ },
2004
+ {
2005
+ keys: "Shift + Tab",
2006
+ action: "Giảm thụt đầu dòng"
2007
+ }
2008
+ ]
2009
+ },
2010
+ {
2011
+ category: "Bảng nhớ tạm",
2012
+ items: [{
2013
+ keys: "Ctrl + Shift + V",
2014
+ action: "Dán dưới dạng văn bản thuần"
2015
+ }]
2016
+ },
2017
+ {
2018
+ category: "Tìm & Thay thế",
2019
+ items: [{
2020
+ keys: "Ctrl + F",
2021
+ action: "Tìm trong tài liệu"
2022
+ }, {
2023
+ keys: "Ctrl + H",
2024
+ action: "Tìm & Thay thế"
2025
+ }]
2026
+ },
2027
+ {
2028
+ category: "Trình soạn thảo",
2029
+ items: [{
2030
+ keys: "Ctrl + Shift + /",
2031
+ action: "Hiện hộp thoại phím tắt"
2032
+ }]
2033
+ }
2034
+ ]
2035
+ },
2036
+ contextMenu: {
2037
+ cut: "Cắt",
2038
+ copy: "Sao chép",
2039
+ paste: "Dán",
2040
+ bold: "Đậm",
2041
+ italic: "Nghiêng",
2042
+ underline: "Gạch chân",
2043
+ textColor: "Màu chữ",
2044
+ highlightColor: "Màu nền chữ",
2045
+ copyFormat: "Sao chép định dạng",
2046
+ pasteFormat: "Dán định dạng",
2047
+ removeFormat: "Xóa định dạng",
2048
+ link: "Chèn liên kết",
2049
+ image: "Chèn hình ảnh",
2050
+ video: "Chèn video",
2051
+ table: "Chèn bảng",
2052
+ back: "Quay lại",
2053
+ noHighlight: "Không tô màu",
2054
+ customColor: "Màu tùy chỉnh",
2055
+ customColorLabel: "Tùy chỉnh…"
2056
+ },
2057
+ statusbar: {
2058
+ resizeHandle: "Kéo để thay đổi kích thước",
2059
+ words: (n) => `Từ: ${n}`,
2060
+ wordsLimit: (n, max) => `Từ: ${n}/${max}`,
2061
+ chars: (n) => `Ký tự: ${n}`,
2062
+ charsLimit: (n, max) => `Ký tự: ${n}/${max}`
2063
+ },
2064
+ tooltips: {
2065
+ link: {
2066
+ ariaLabel: "Hành động liên kết",
2067
+ openLink: "Mở liên kết",
2068
+ copyUrl: "Sao chép URL",
2069
+ editLink: "Sửa liên kết",
2070
+ removeLink: "Xóa liên kết"
2071
+ },
2072
+ image: {
2073
+ ariaLabel: "Hành động hình ảnh",
2074
+ label: "Hình ảnh",
2075
+ floatLeft: "Nổi trái",
2076
+ noFloat: "Không nổi",
2077
+ alignCenter: "Căn giữa",
2078
+ floatRight: "Nổi phải",
2079
+ originalSize: "Kích thước gốc",
2080
+ rotateLeft: "Xoay trái",
2081
+ rotateRight: "Xoay phải",
2082
+ cropImage: "Cắt ảnh",
2083
+ addCaption: "Thêm / Sửa chú thích",
2084
+ deleteImage: "Xóa hình ảnh"
2085
+ },
2086
+ code: {
2087
+ ariaLabel: "Hành động khối mã",
2088
+ label: "Mã",
2089
+ syntaxLanguage: "Ngôn ngữ cú pháp",
2090
+ syntaxAriaLabel: "Ngôn ngữ cú pháp",
2091
+ copyCode: "Sao chép mã",
2092
+ toggleWordWrap: "Chuyển đổi xuống dòng",
2093
+ enableWordWrap: "Bật xuống dòng",
2094
+ disableWordWrap: "Tắt xuống dòng",
2095
+ convertToParagraph: "Chuyển thành đoạn văn",
2096
+ deleteCodeBlock: "Xóa khối mã"
2097
+ },
2098
+ table: {
2099
+ ariaLabel: "Hành động bảng",
2100
+ label: "Bảng",
2101
+ selectCells: "Chọn ô",
2102
+ addRowAbove: "Thêm hàng phía trên",
2103
+ addRowBelow: "Thêm hàng phía dưới",
2104
+ deleteRow: "Xóa hàng",
2105
+ addColumnLeft: "Thêm cột bên trái",
2106
+ addColumnRight: "Thêm cột bên phải",
2107
+ deleteColumn: "Xóa cột",
2108
+ mergeCells: "Gộp ô",
2109
+ unmergeCells: "Tách ô",
2110
+ columnWidth: "Chiều rộng cột",
2111
+ rowHeight: "Chiều cao hàng",
2112
+ tableBorderWidth: "Độ rộng viền bảng",
2113
+ deleteTable: "Xóa bảng",
2114
+ columnWidthPx: "Chiều rộng cột (px)",
2115
+ rowHeightPx: "Chiều cao hàng (px)",
2116
+ tableBorderWidthPx: "Độ rộng viền bảng (px)",
2117
+ cancelBtn: "Hủy",
2118
+ applyBtn: "Áp dụng"
2119
+ },
2120
+ video: {
2121
+ ariaLabel: "Hành động video",
2122
+ label: "Video",
2123
+ floatLeft: "Nổi trái",
2124
+ noFloat: "Không nổi",
2125
+ alignCenter: "Căn giữa",
2126
+ floatRight: "Nổi phải",
2127
+ originalSize: "Kích thước gốc",
2128
+ previewVideo: "Xem trước",
2129
+ exitPreview: "Thoát xem trước",
2130
+ deleteVideo: "Xóa video"
2131
+ }
2132
+ },
2133
+ errors: {
2134
+ imageFormat: (type) => `Định dạng "${type}" không được hỗ trợ hiển thị trên trình duyệt. Vui lòng chuyển đổi sang JPEG, PNG hoặc WebP.`,
2135
+ imageSize: (maxSize) => `Tệp hình ảnh quá lớn. Kích thước tối đa cho phép là ${maxSize} MB.`
2136
+ }
2137
+ },
2138
+ ja: {
2139
+ toolbar: {
2140
+ bold: "太字 (Ctrl+B)",
2141
+ italic: "斜体 (Ctrl+I)",
2142
+ underline: "下線 (Ctrl+U)",
2143
+ strikethrough: "取り消し線",
2144
+ superscript: "上付き文字",
2145
+ subscript: "下付き文字",
2146
+ alignLeft: "左揃え",
2147
+ alignCenter: "中央揃え",
2148
+ alignRight: "右揃え",
2149
+ alignJustify: "両端揃え",
2150
+ ul: "箇条書きリスト",
2151
+ ol: "番号付きリスト",
2152
+ checklist: "チェックリスト",
2153
+ indent: "インデントを増やす",
2154
+ outdent: "インデントを減らす",
2155
+ undo: "元に戻す (Ctrl+Z)",
2156
+ redo: "やり直す (Ctrl+Y)",
2157
+ hr: "水平線",
2158
+ link: "リンクを挿入",
2159
+ image: "画像を挿入",
2160
+ video: "動画を挿入",
2161
+ emoji: "絵文字を挿入",
2162
+ icon: "FAアイコンを挿入",
2163
+ table: "テーブルを挿入",
2164
+ fontSize: "フォントサイズ",
2165
+ fontSizePlaceholder: "サイズ",
2166
+ removeFormat: "書式をリセット",
2167
+ direction: "文字方向を切り替え (LTR / RTL)",
2168
+ fontFamily: "フォント",
2169
+ paragraphStyle: "段落スタイル",
2170
+ paragraphStylePlaceholder: "スタイル",
2171
+ lineHeight: "行の高さ",
2172
+ lineHeightPlaceholder: "↕ 行間",
2173
+ codeview: "HTMLソースを表示",
2174
+ fullscreen: "全画面表示",
2175
+ shortcuts: "キーボードショートカット (Ctrl+Shift+/)",
2176
+ find: "検索 (Ctrl+F)",
2177
+ findReplace: "検索と置換 (Ctrl+H)",
2178
+ inlineCode: "インラインコード (Ctrl+`)",
2179
+ print: "印刷",
2180
+ foreColor: "文字色",
2181
+ backColor: "ハイライト色",
2182
+ chooseTextColor: "文字色を選択",
2183
+ chooseHighlightColor: "ハイライト色を選択",
2184
+ customColor: "カスタムカラー",
2185
+ insertTableLabel: "テーブルを挿入",
2186
+ paragraphItems: {
2187
+ p: "標準",
2188
+ blockquote: "引用",
2189
+ pre: "コード"
2190
+ }
2191
+ },
2192
+ linkDialog: {
2193
+ ariaLabel: "リンクを挿入",
2194
+ title: "リンクを挿入",
2195
+ url: "URL",
2196
+ urlPlaceholder: "https://",
2197
+ displayText: "表示テキスト",
2198
+ textPlaceholder: "リンクテキスト",
2199
+ openInNewTab: "新しいタブで開く",
2200
+ insertBtn: "挿入",
2201
+ cancelBtn: "キャンセル"
2202
+ },
2203
+ imageDialog: {
2204
+ ariaLabel: "画像を挿入",
2205
+ title: "画像を挿入",
2206
+ imageUrl: "画像URL",
2207
+ urlPlaceholder: "https://example.com/image.png",
2208
+ altText: "代替テキスト",
2209
+ altPlaceholder: "画像の説明",
2210
+ alignment: "配置",
2211
+ alignNone: "なし",
2212
+ alignLeft: "左",
2213
+ alignCenter: "中央",
2214
+ alignRight: "右",
2215
+ uploadLabel: "ファイルをアップロード",
2216
+ insertBtn: "挿入",
2217
+ cancelBtn: "キャンセル"
2218
+ },
2219
+ videoDialog: {
2220
+ ariaLabel: "動画を挿入",
2221
+ title: "動画を挿入",
2222
+ videoUrl: "動画URL",
2223
+ urlPlaceholder: "YouTube、Vimeo、または直接動画URL",
2224
+ widthLabel: "幅 (px)",
2225
+ widthPlaceholder: "560",
2226
+ insertBtn: "挿入",
2227
+ cancelBtn: "キャンセル",
2228
+ detected: (type) => `検出: ${type}`,
2229
+ unknownFormat: "不明な形式 — 直接動画埋め込みを試みます",
2230
+ invalidUrl: "URLが無効です — 有効な動画URLを入力してください。"
2231
+ },
2232
+ emojiDialog: {
2233
+ ariaLabel: "絵文字を挿入",
2234
+ title: "絵文字を挿入",
2235
+ searchPlaceholder: "絵文字を検索…",
2236
+ all: "すべて",
2237
+ cancelBtn: "キャンセル",
2238
+ close: "閉じる",
2239
+ categories: {
2240
+ smileys: "顔文字",
2241
+ people: "人物",
2242
+ animals: "動物",
2243
+ food: "食べ物",
2244
+ travel: "旅行",
2245
+ objects: "モノ",
2246
+ symbols: "記号"
2247
+ }
2248
+ },
2249
+ iconDialog: {
2250
+ ariaLabel: "FAアイコンを挿入",
2251
+ title: "FAアイコンを挿入",
2252
+ searchPlaceholder: "アイコンを検索…",
2253
+ all: "すべて",
2254
+ style: "スタイル",
2255
+ size: "サイズ",
2256
+ color: "カラー",
2257
+ useColor: " カラーを使用",
2258
+ selectHint: "アイコンを選択してください",
2259
+ insertBtn: "FAアイコンを挿入",
2260
+ cancelBtn: "キャンセル",
2261
+ close: "閉じる",
2262
+ categories: {
2263
+ popular: "人気",
2264
+ interface: "インターフェース",
2265
+ navigation: "ナビゲーション",
2266
+ media: "メディア",
2267
+ communication: "通信",
2268
+ files: "ファイル",
2269
+ people: "人物",
2270
+ objects: "モノ"
2271
+ }
2272
+ },
2273
+ findReplace: {
2274
+ findTitle: "検索",
2275
+ findReplaceTitle: "検索と置換",
2276
+ findPlaceholder: "検索…",
2277
+ searchAriaLabel: "検索テキスト",
2278
+ caseSensitive: "\xA0大文字/小文字を区別",
2279
+ prevBtn: "← 前へ",
2280
+ nextBtn: "次へ →",
2281
+ replacePlaceholder: "置換後…",
2282
+ replaceAriaLabel: "置換後のテキスト",
2283
+ replaceBtn: "置換",
2284
+ replaceAllBtn: "すべて置換",
2285
+ close: "×"
2286
+ },
2287
+ shortcutsDialog: {
2288
+ title: "キーボードショートカット",
2289
+ ariaLabel: "キーボードショートカット",
2290
+ close: "閉じる",
2291
+ shortcuts: [
2292
+ {
2293
+ category: "テキスト書式",
2294
+ items: [
2295
+ {
2296
+ keys: "Ctrl + B",
2297
+ action: "太字"
2298
+ },
2299
+ {
2300
+ keys: "Ctrl + I",
2301
+ action: "斜体"
2302
+ },
2303
+ {
2304
+ keys: "Ctrl + U",
2305
+ action: "下線"
2306
+ },
2307
+ {
2308
+ keys: "Ctrl + K",
2309
+ action: "リンクの挿入 / 編集"
2310
+ }
2311
+ ]
2312
+ },
2313
+ {
2314
+ category: "履歴",
2315
+ items: [{
2316
+ keys: "Ctrl + Z",
2317
+ action: "元に戻す"
2318
+ }, {
2319
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
2320
+ action: "やり直す"
2321
+ }]
2322
+ },
2323
+ {
2324
+ category: "選択と移動",
2325
+ items: [
2326
+ {
2327
+ keys: "Ctrl + A",
2328
+ action: "すべて選択"
2329
+ },
2330
+ {
2331
+ keys: "Tab",
2332
+ action: "インデント増加 / スペース挿入"
2333
+ },
2334
+ {
2335
+ keys: "Shift + Tab",
2336
+ action: "インデント減少"
2337
+ }
2338
+ ]
2339
+ },
2340
+ {
2341
+ category: "クリップボード",
2342
+ items: [{
2343
+ keys: "Ctrl + Shift + V",
2344
+ action: "プレーンテキストとして貼り付け"
2345
+ }]
2346
+ },
2347
+ {
2348
+ category: "検索と置換",
2349
+ items: [{
2350
+ keys: "Ctrl + F",
2351
+ action: "文書内を検索"
2352
+ }, {
2353
+ keys: "Ctrl + H",
2354
+ action: "検索と置換"
2355
+ }]
2356
+ },
2357
+ {
2358
+ category: "エディター",
2359
+ items: [{
2360
+ keys: "Ctrl + Shift + /",
2361
+ action: "ショートカットダイアログを表示"
2362
+ }]
2363
+ }
2364
+ ]
2365
+ },
2366
+ contextMenu: {
2367
+ cut: "切り取り",
2368
+ copy: "コピー",
2369
+ paste: "貼り付け",
2370
+ bold: "太字",
2371
+ italic: "斜体",
2372
+ underline: "下線",
2373
+ textColor: "文字色",
2374
+ highlightColor: "ハイライト色",
2375
+ copyFormat: "書式をコピー",
2376
+ pasteFormat: "書式を貼り付け",
2377
+ removeFormat: "書式をリセット",
2378
+ link: "リンクを挿入",
2379
+ image: "画像を挿入",
2380
+ video: "動画を挿入",
2381
+ table: "テーブルを挿入",
2382
+ back: "戻る",
2383
+ noHighlight: "ハイライトなし",
2384
+ customColor: "カスタムカラー",
2385
+ customColorLabel: "カスタム…"
2386
+ },
2387
+ statusbar: {
2388
+ resizeHandle: "ドラッグしてリサイズ",
2389
+ words: (n) => `単語数: ${n}`,
2390
+ wordsLimit: (n, max) => `単語数: ${n}/${max}`,
2391
+ chars: (n) => `文字数: ${n}`,
2392
+ charsLimit: (n, max) => `文字数: ${n}/${max}`
2393
+ },
2394
+ tooltips: {
2395
+ link: {
2396
+ ariaLabel: "リンク操作",
2397
+ openLink: "リンクを開く",
2398
+ copyUrl: "URLをコピー",
2399
+ editLink: "リンクを編集",
2400
+ removeLink: "リンクを削除"
2401
+ },
2402
+ image: {
2403
+ ariaLabel: "画像操作",
2404
+ label: "画像",
2405
+ floatLeft: "左に回り込み",
2406
+ noFloat: "回り込みなし",
2407
+ alignCenter: "中央揃え",
2408
+ floatRight: "右に回り込み",
2409
+ originalSize: "元のサイズ",
2410
+ rotateLeft: "左に回転",
2411
+ rotateRight: "右に回転",
2412
+ cropImage: "画像をトリミング",
2413
+ addCaption: "キャプションを追加 / 編集",
2414
+ deleteImage: "画像を削除"
2415
+ },
2416
+ code: {
2417
+ ariaLabel: "コードブロック操作",
2418
+ label: "コード",
2419
+ syntaxLanguage: "言語",
2420
+ syntaxAriaLabel: "構文言語",
2421
+ copyCode: "コードをコピー",
2422
+ toggleWordWrap: "折り返しを切り替え",
2423
+ enableWordWrap: "折り返しを有効にする",
2424
+ disableWordWrap: "折り返しを無効にする",
2425
+ convertToParagraph: "段落に変換",
2426
+ deleteCodeBlock: "コードブロックを削除"
2427
+ },
2428
+ table: {
2429
+ ariaLabel: "テーブル操作",
2430
+ label: "テーブル",
2431
+ selectCells: "セルを選択",
2432
+ addRowAbove: "上に行を追加",
2433
+ addRowBelow: "下に行を追加",
2434
+ deleteRow: "行を削除",
2435
+ addColumnLeft: "左に列を追加",
2436
+ addColumnRight: "右に列を追加",
2437
+ deleteColumn: "列を削除",
2438
+ mergeCells: "セルを結合",
2439
+ unmergeCells: "セルの結合を解除",
2440
+ columnWidth: "列幅",
2441
+ rowHeight: "行の高さ",
2442
+ tableBorderWidth: "テーブルの枠幅",
2443
+ deleteTable: "テーブルを削除",
2444
+ columnWidthPx: "列幅 (px)",
2445
+ rowHeightPx: "行の高さ (px)",
2446
+ tableBorderWidthPx: "テーブルの枠幅 (px)",
2447
+ cancelBtn: "キャンセル",
2448
+ applyBtn: "適用"
2449
+ },
2450
+ video: {
2451
+ ariaLabel: "動画操作",
2452
+ label: "動画",
2453
+ floatLeft: "左に回り込み",
2454
+ noFloat: "回り込みなし",
2455
+ alignCenter: "中央揃え",
2456
+ floatRight: "右に回り込み",
2457
+ originalSize: "元のサイズ",
2458
+ previewVideo: "プレビュー",
2459
+ exitPreview: "プレビューを終了",
2460
+ deleteVideo: "動画を削除"
2461
+ }
2462
+ },
2463
+ errors: {
2464
+ imageFormat: (type) => `形式 "${type}" はブラウザでの表示をサポートしていません。JPEG、PNG、または WebP に変換してください。`,
2465
+ imageSize: (maxSize) => `画像ファイルが大きすぎます。最大許容サイズは ${maxSize} MB です。`
2466
+ }
2467
+ },
2468
+ zh: {
2469
+ toolbar: {
2470
+ bold: "粗体 (Ctrl+B)",
2471
+ italic: "斜体 (Ctrl+I)",
2472
+ underline: "下划线 (Ctrl+U)",
2473
+ strikethrough: "删除线",
2474
+ superscript: "上标",
2475
+ subscript: "下标",
2476
+ alignLeft: "左对齐",
2477
+ alignCenter: "居中",
2478
+ alignRight: "右对齐",
2479
+ alignJustify: "两端对齐",
2480
+ ul: "无序列表",
2481
+ ol: "有序列表",
2482
+ checklist: "待办列表",
2483
+ indent: "增加缩进",
2484
+ outdent: "减少缩进",
2485
+ undo: "撤销 (Ctrl+Z)",
2486
+ redo: "重做 (Ctrl+Y)",
2487
+ hr: "水平分割线",
2488
+ link: "插入链接",
2489
+ image: "插入图片",
2490
+ video: "插入视频",
2491
+ emoji: "插入表情",
2492
+ icon: "插入 FA 图标",
2493
+ table: "插入表格",
2494
+ fontSize: "字号",
2495
+ fontSizePlaceholder: "大小",
2496
+ removeFormat: "清除格式",
2497
+ direction: "切换文字方向 (LTR / RTL)",
2498
+ fontFamily: "字体",
2499
+ paragraphStyle: "段落样式",
2500
+ paragraphStylePlaceholder: "样式",
2501
+ lineHeight: "行高",
2502
+ lineHeightPlaceholder: "↕ 行距",
2503
+ codeview: "查看 HTML 源码",
2504
+ fullscreen: "全屏",
2505
+ shortcuts: "键盘快捷键 (Ctrl+Shift+/)",
2506
+ find: "搜索 (Ctrl+F)",
2507
+ findReplace: "查找和替换 (Ctrl+H)",
2508
+ inlineCode: "行内代码 (Ctrl+`)",
2509
+ print: "打印",
2510
+ foreColor: "文字颜色",
2511
+ backColor: "高亮颜色",
2512
+ chooseTextColor: "选择文字颜色",
2513
+ chooseHighlightColor: "选择高亮颜色",
2514
+ customColor: "自定义颜色",
2515
+ insertTableLabel: "插入表格",
2516
+ paragraphItems: {
2517
+ p: "正文",
2518
+ blockquote: "引用",
2519
+ pre: "代码"
2520
+ }
2521
+ },
2522
+ linkDialog: {
2523
+ ariaLabel: "插入链接",
2524
+ title: "插入链接",
2525
+ url: "链接地址",
2526
+ urlPlaceholder: "https://",
2527
+ displayText: "显示文字",
2528
+ textPlaceholder: "链接文字",
2529
+ openInNewTab: "在新标签页中打开",
2530
+ insertBtn: "插入",
2531
+ cancelBtn: "取消"
2532
+ },
2533
+ imageDialog: {
2534
+ ariaLabel: "插入图片",
2535
+ title: "插入图片",
2536
+ imageUrl: "图片地址",
2537
+ urlPlaceholder: "https://example.com/image.png",
2538
+ altText: "替代文字",
2539
+ altPlaceholder: "图片描述",
2540
+ alignment: "对齐方式",
2541
+ alignNone: "无",
2542
+ alignLeft: "左",
2543
+ alignCenter: "居中",
2544
+ alignRight: "右",
2545
+ uploadLabel: "或上传文件",
2546
+ insertBtn: "插入",
2547
+ cancelBtn: "取消"
2548
+ },
2549
+ videoDialog: {
2550
+ ariaLabel: "插入视频",
2551
+ title: "插入视频",
2552
+ videoUrl: "视频地址",
2553
+ urlPlaceholder: "YouTube、Vimeo 或直接 .mp4 链接",
2554
+ widthLabel: "宽度 (px)",
2555
+ widthPlaceholder: "560",
2556
+ insertBtn: "插入",
2557
+ cancelBtn: "取消",
2558
+ detected: (type) => `已识别: ${type}`,
2559
+ unknownFormat: "未知格式 — 将尝试直接嵌入视频",
2560
+ invalidUrl: "URL 无效 — 请输入有效的视频链接。"
2561
+ },
2562
+ emojiDialog: {
2563
+ ariaLabel: "插入表情",
2564
+ title: "插入表情",
2565
+ searchPlaceholder: "搜索表情…",
2566
+ all: "全部",
2567
+ cancelBtn: "取消",
2568
+ close: "关闭",
2569
+ categories: {
2570
+ smileys: "笑脸",
2571
+ people: "人物",
2572
+ animals: "动物",
2573
+ food: "食物",
2574
+ travel: "旅行",
2575
+ objects: "物品",
2576
+ symbols: "符号"
2577
+ }
2578
+ },
2579
+ iconDialog: {
2580
+ ariaLabel: "插入 FA 图标",
2581
+ title: "插入 FA 图标",
2582
+ searchPlaceholder: "搜索图标…",
2583
+ all: "全部",
2584
+ style: "样式",
2585
+ size: "大小",
2586
+ color: "颜色",
2587
+ useColor: " 使用颜色",
2588
+ selectHint: "请选择一个图标",
2589
+ insertBtn: "插入 FA 图标",
2590
+ cancelBtn: "取消",
2591
+ close: "关闭",
2592
+ categories: {
2593
+ popular: "热门",
2594
+ interface: "界面",
2595
+ navigation: "导航",
2596
+ media: "媒体",
2597
+ communication: "通讯",
2598
+ files: "文件",
2599
+ people: "人物",
2600
+ objects: "物品"
2601
+ }
2602
+ },
2603
+ findReplace: {
2604
+ findTitle: "搜索",
2605
+ findReplaceTitle: "查找和替换",
2606
+ findPlaceholder: "查找…",
2607
+ searchAriaLabel: "搜索文字",
2608
+ caseSensitive: "\xA0区分大小写",
2609
+ prevBtn: "← 上一个",
2610
+ nextBtn: "下一个 →",
2611
+ replacePlaceholder: "替换为…",
2612
+ replaceAriaLabel: "替换为",
2613
+ replaceBtn: "替换",
2614
+ replaceAllBtn: "全部替换",
2615
+ close: "×"
2616
+ },
2617
+ shortcutsDialog: {
2618
+ title: "键盘快捷键",
2619
+ ariaLabel: "键盘快捷键",
2620
+ close: "关闭",
2621
+ shortcuts: [
2622
+ {
2623
+ category: "文字格式",
2624
+ items: [
2625
+ {
2626
+ keys: "Ctrl + B",
2627
+ action: "粗体"
2628
+ },
2629
+ {
2630
+ keys: "Ctrl + I",
2631
+ action: "斜体"
2632
+ },
2633
+ {
2634
+ keys: "Ctrl + U",
2635
+ action: "下划线"
2636
+ },
2637
+ {
2638
+ keys: "Ctrl + K",
2639
+ action: "插入 / 编辑链接"
2640
+ }
2641
+ ]
2642
+ },
2643
+ {
2644
+ category: "历史记录",
2645
+ items: [{
2646
+ keys: "Ctrl + Z",
2647
+ action: "撤销"
2648
+ }, {
2649
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
2650
+ action: "重做"
2651
+ }]
2652
+ },
2653
+ {
2654
+ category: "选择与导航",
2655
+ items: [
2656
+ {
2657
+ keys: "Ctrl + A",
2658
+ action: "全选"
2659
+ },
2660
+ {
2661
+ keys: "Tab",
2662
+ action: "增加缩进 / 插入空格"
2663
+ },
2664
+ {
2665
+ keys: "Shift + Tab",
2666
+ action: "减少缩进"
2667
+ }
2668
+ ]
2669
+ },
2670
+ {
2671
+ category: "剪贴板",
2672
+ items: [{
2673
+ keys: "Ctrl + Shift + V",
2674
+ action: "粘贴为纯文本"
2675
+ }]
2676
+ },
2677
+ {
2678
+ category: "查找和替换",
2679
+ items: [{
2680
+ keys: "Ctrl + F",
2681
+ action: "在文档中搜索"
2682
+ }, {
2683
+ keys: "Ctrl + H",
2684
+ action: "查找和替换"
2685
+ }]
2686
+ },
2687
+ {
2688
+ category: "编辑器",
2689
+ items: [{
2690
+ keys: "Ctrl + Shift + /",
2691
+ action: "显示快捷键对话框"
2692
+ }]
2693
+ }
2694
+ ]
2695
+ },
2696
+ contextMenu: {
2697
+ cut: "剪切",
2698
+ copy: "复制",
2699
+ paste: "粘贴",
2700
+ bold: "粗体",
2701
+ italic: "斜体",
2702
+ underline: "下划线",
2703
+ textColor: "文字颜色",
2704
+ highlightColor: "高亮颜色",
2705
+ copyFormat: "复制格式",
2706
+ pasteFormat: "粘贴格式",
2707
+ removeFormat: "清除格式",
2708
+ link: "插入链接",
2709
+ image: "插入图片",
2710
+ video: "插入视频",
2711
+ table: "插入表格",
2712
+ back: "返回",
2713
+ noHighlight: "无高亮",
2714
+ customColor: "自定义颜色",
2715
+ customColorLabel: "自定义…"
2716
+ },
2717
+ statusbar: {
2718
+ resizeHandle: "拖动以调整大小",
2719
+ words: (n) => `字数: ${n}`,
2720
+ wordsLimit: (n, max) => `字数: ${n}/${max}`,
2721
+ chars: (n) => `字符数: ${n}`,
2722
+ charsLimit: (n, max) => `字符数: ${n}/${max}`
2723
+ },
2724
+ tooltips: {
2725
+ link: {
2726
+ ariaLabel: "链接操作",
2727
+ openLink: "打开链接",
2728
+ copyUrl: "复制 URL",
2729
+ editLink: "编辑链接",
2730
+ removeLink: "删除链接"
2731
+ },
2732
+ image: {
2733
+ ariaLabel: "图片操作",
2734
+ label: "图片",
2735
+ floatLeft: "左浮动",
2736
+ noFloat: "不浮动",
2737
+ alignCenter: "居中对齐",
2738
+ floatRight: "右浮动",
2739
+ originalSize: "原始尺寸",
2740
+ rotateLeft: "向左旋转",
2741
+ rotateRight: "向右旋转",
2742
+ cropImage: "裁剪图片",
2743
+ addCaption: "添加 / 编辑说明",
2744
+ deleteImage: "删除图片"
2745
+ },
2746
+ code: {
2747
+ ariaLabel: "代码块操作",
2748
+ label: "代码",
2749
+ syntaxLanguage: "语法语言",
2750
+ syntaxAriaLabel: "语法语言",
2751
+ copyCode: "复制代码",
2752
+ toggleWordWrap: "切换自动换行",
2753
+ enableWordWrap: "启用自动换行",
2754
+ disableWordWrap: "禁用自动换行",
2755
+ convertToParagraph: "转换为段落",
2756
+ deleteCodeBlock: "删除代码块"
2757
+ },
2758
+ table: {
2759
+ ariaLabel: "表格操作",
2760
+ label: "表格",
2761
+ selectCells: "选择单元格",
2762
+ addRowAbove: "在上方插入行",
2763
+ addRowBelow: "在下方插入行",
2764
+ deleteRow: "删除行",
2765
+ addColumnLeft: "在左侧插入列",
2766
+ addColumnRight: "在右侧插入列",
2767
+ deleteColumn: "删除列",
2768
+ mergeCells: "合并单元格",
2769
+ unmergeCells: "拆分单元格",
2770
+ columnWidth: "列宽",
2771
+ rowHeight: "行高",
2772
+ tableBorderWidth: "表格边框宽度",
2773
+ deleteTable: "删除表格",
2774
+ columnWidthPx: "列宽 (px)",
2775
+ rowHeightPx: "行高 (px)",
2776
+ tableBorderWidthPx: "表格边框宽度 (px)",
2777
+ cancelBtn: "取消",
2778
+ applyBtn: "应用"
2779
+ },
2780
+ video: {
2781
+ ariaLabel: "视频操作",
2782
+ label: "视频",
2783
+ floatLeft: "左浮动",
2784
+ noFloat: "不浮动",
2785
+ alignCenter: "居中对齐",
2786
+ floatRight: "右浮动",
2787
+ originalSize: "原始尺寸",
2788
+ previewVideo: "预览视频",
2789
+ exitPreview: "退出预览",
2790
+ deleteVideo: "删除视频"
2791
+ }
2792
+ },
2793
+ errors: {
2794
+ imageFormat: (type) => `格式 "${type}" 不支持在浏览器中显示。请转换为 JPEG、PNG 或 WebP。`,
2795
+ imageSize: (maxSize) => `图片文件过大。最大允许大小为 ${maxSize} MB。`
2796
+ }
2797
+ },
2798
+ fr: {
2799
+ toolbar: {
2800
+ bold: "Gras (Ctrl+B)",
2801
+ italic: "Italique (Ctrl+I)",
2802
+ underline: "Souligné (Ctrl+U)",
2803
+ strikethrough: "Barré",
2804
+ superscript: "Exposant",
2805
+ subscript: "Indice",
2806
+ alignLeft: "Aligner à gauche",
2807
+ alignCenter: "Centrer",
2808
+ alignRight: "Aligner à droite",
2809
+ alignJustify: "Justifier",
2810
+ ul: "Liste à puces",
2811
+ ol: "Liste numérotée",
2812
+ checklist: "Liste de tâches",
2813
+ indent: "Augmenter le retrait",
2814
+ outdent: "Diminuer le retrait",
2815
+ undo: "Annuler (Ctrl+Z)",
2816
+ redo: "Rétablir (Ctrl+Y)",
2817
+ hr: "Ligne horizontale",
2818
+ link: "Insérer un lien",
2819
+ image: "Insérer une image",
2820
+ video: "Insérer une vidéo",
2821
+ emoji: "Insérer un emoji",
2822
+ icon: "Insérer une icône FA",
2823
+ table: "Insérer un tableau",
2824
+ fontSize: "Taille de police",
2825
+ fontSizePlaceholder: "Taille",
2826
+ removeFormat: "Effacer la mise en forme",
2827
+ direction: "Basculer la direction du texte (LTR / RTL)",
2828
+ fontFamily: "Police",
2829
+ paragraphStyle: "Style de paragraphe",
2830
+ paragraphStylePlaceholder: "Style",
2831
+ lineHeight: "Interligne",
2832
+ lineHeightPlaceholder: "↕ Ligne",
2833
+ codeview: "Afficher le code HTML",
2834
+ fullscreen: "Plein écran",
2835
+ shortcuts: "Raccourcis clavier (Ctrl+Shift+/)",
2836
+ find: "Rechercher (Ctrl+F)",
2837
+ findReplace: "Rechercher et remplacer (Ctrl+H)",
2838
+ inlineCode: "Code inline (Ctrl+`)",
2839
+ print: "Imprimer",
2840
+ foreColor: "Couleur du texte",
2841
+ backColor: "Couleur de surbrillance",
2842
+ chooseTextColor: "Choisir la couleur du texte",
2843
+ chooseHighlightColor: "Choisir la couleur de surbrillance",
2844
+ customColor: "Couleur personnalisée",
2845
+ insertTableLabel: "Insérer un tableau",
2846
+ paragraphItems: {
2847
+ p: "Normal",
2848
+ blockquote: "Citation",
2849
+ pre: "Code"
2850
+ }
2851
+ },
2852
+ linkDialog: {
2853
+ ariaLabel: "Insérer un lien",
2854
+ title: "Insérer un lien",
2855
+ url: "URL",
2856
+ urlPlaceholder: "https://",
2857
+ displayText: "Texte affiché",
2858
+ textPlaceholder: "Texte du lien",
2859
+ openInNewTab: "Ouvrir dans un nouvel onglet",
2860
+ insertBtn: "Insérer",
2861
+ cancelBtn: "Annuler"
2862
+ },
2863
+ imageDialog: {
2864
+ ariaLabel: "Insérer une image",
2865
+ title: "Insérer une image",
2866
+ imageUrl: "URL de l'image",
2867
+ urlPlaceholder: "https://example.com/image.png",
2868
+ altText: "Texte alternatif",
2869
+ altPlaceholder: "Description de l'image",
2870
+ alignment: "Alignement",
2871
+ alignNone: "Aucun",
2872
+ alignLeft: "Gauche",
2873
+ alignCenter: "Centre",
2874
+ alignRight: "Droite",
2875
+ uploadLabel: "Ou téléverser un fichier",
2876
+ insertBtn: "Insérer",
2877
+ cancelBtn: "Annuler"
2878
+ },
2879
+ videoDialog: {
2880
+ ariaLabel: "Insérer une vidéo",
2881
+ title: "Insérer une vidéo",
2882
+ videoUrl: "URL de la vidéo",
2883
+ urlPlaceholder: "YouTube, Vimeo ou URL .mp4 directe",
2884
+ widthLabel: "Largeur (px)",
2885
+ widthPlaceholder: "560",
2886
+ insertBtn: "Insérer",
2887
+ cancelBtn: "Annuler",
2888
+ detected: (type) => `Détecté\u00a0: ${type}`,
2889
+ unknownFormat: "Format inconnu — tentative d'intégration directe",
2890
+ invalidUrl: "URL invalide — veuillez saisir un lien vidéo valide."
2891
+ },
2892
+ emojiDialog: {
2893
+ ariaLabel: "Insérer un emoji",
2894
+ title: "Insérer un emoji",
2895
+ searchPlaceholder: "Rechercher des emojis…",
2896
+ all: "Tout",
2897
+ cancelBtn: "Annuler",
2898
+ close: "Fermer",
2899
+ categories: {
2900
+ smileys: "Smileys",
2901
+ people: "Personnes",
2902
+ animals: "Animaux",
2903
+ food: "Nourriture",
2904
+ travel: "Voyage",
2905
+ objects: "Objets",
2906
+ symbols: "Symboles"
2907
+ }
2908
+ },
2909
+ iconDialog: {
2910
+ ariaLabel: "Insérer une icône FA",
2911
+ title: "Insérer une icône FA",
2912
+ searchPlaceholder: "Rechercher des icônes…",
2913
+ all: "Tout",
2914
+ style: "Style",
2915
+ size: "Taille",
2916
+ color: "Couleur",
2917
+ useColor: " Utiliser la couleur",
2918
+ selectHint: "Sélectionner une icône",
2919
+ insertBtn: "Insérer une icône FA",
2920
+ cancelBtn: "Annuler",
2921
+ close: "Fermer",
2922
+ categories: {
2923
+ popular: "Populaire",
2924
+ interface: "Interface",
2925
+ navigation: "Navigation",
2926
+ media: "Média",
2927
+ communication: "Communication",
2928
+ files: "Fichiers",
2929
+ people: "Personnes",
2930
+ objects: "Objets"
2931
+ }
2932
+ },
2933
+ findReplace: {
2934
+ findTitle: "Rechercher",
2935
+ findReplaceTitle: "Rechercher et remplacer",
2936
+ findPlaceholder: "Rechercher…",
2937
+ searchAriaLabel: "Texte à rechercher",
2938
+ caseSensitive: "\xA0Respecter la casse",
2939
+ prevBtn: "← Préc.",
2940
+ nextBtn: "Suiv. →",
2941
+ replacePlaceholder: "Remplacer par…",
2942
+ replaceAriaLabel: "Remplacer par",
2943
+ replaceBtn: "Remplacer",
2944
+ replaceAllBtn: "Tout remplacer",
2945
+ close: "×"
2946
+ },
2947
+ shortcutsDialog: {
2948
+ title: "Raccourcis clavier",
2949
+ ariaLabel: "Raccourcis clavier",
2950
+ close: "Fermer",
2951
+ shortcuts: [
2952
+ {
2953
+ category: "Mise en forme du texte",
2954
+ items: [
2955
+ {
2956
+ keys: "Ctrl + B",
2957
+ action: "Gras"
2958
+ },
2959
+ {
2960
+ keys: "Ctrl + I",
2961
+ action: "Italique"
2962
+ },
2963
+ {
2964
+ keys: "Ctrl + U",
2965
+ action: "Souligné"
2966
+ },
2967
+ {
2968
+ keys: "Ctrl + K",
2969
+ action: "Insérer / modifier un lien"
2970
+ }
2971
+ ]
2972
+ },
2973
+ {
2974
+ category: "Historique",
2975
+ items: [{
2976
+ keys: "Ctrl + Z",
2977
+ action: "Annuler"
2978
+ }, {
2979
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
2980
+ action: "Rétablir"
2981
+ }]
2982
+ },
2983
+ {
2984
+ category: "Sélection et navigation",
2985
+ items: [
2986
+ {
2987
+ keys: "Ctrl + A",
2988
+ action: "Tout sélectionner"
2989
+ },
2990
+ {
2991
+ keys: "Tab",
2992
+ action: "Augmenter le retrait / insérer des espaces"
2993
+ },
2994
+ {
2995
+ keys: "Shift + Tab",
2996
+ action: "Diminuer le retrait"
2997
+ }
2998
+ ]
2999
+ },
3000
+ {
3001
+ category: "Presse-papiers",
3002
+ items: [{
3003
+ keys: "Ctrl + Shift + V",
3004
+ action: "Coller en texte brut"
3005
+ }]
3006
+ },
3007
+ {
3008
+ category: "Rechercher et remplacer",
3009
+ items: [{
3010
+ keys: "Ctrl + F",
3011
+ action: "Rechercher dans le document"
3012
+ }, {
3013
+ keys: "Ctrl + H",
3014
+ action: "Rechercher et remplacer"
3015
+ }]
3016
+ },
3017
+ {
3018
+ category: "Éditeur",
3019
+ items: [{
3020
+ keys: "Ctrl + Shift + /",
3021
+ action: "Afficher les raccourcis clavier"
3022
+ }]
3023
+ }
3024
+ ]
3025
+ },
3026
+ contextMenu: {
3027
+ cut: "Couper",
3028
+ copy: "Copier",
3029
+ paste: "Coller",
3030
+ bold: "Gras",
3031
+ italic: "Italique",
3032
+ underline: "Souligné",
3033
+ textColor: "Couleur du texte",
3034
+ highlightColor: "Couleur de surbrillance",
3035
+ copyFormat: "Copier la mise en forme",
3036
+ pasteFormat: "Coller la mise en forme",
3037
+ removeFormat: "Effacer la mise en forme",
3038
+ link: "Insérer un lien",
3039
+ image: "Insérer une image",
3040
+ video: "Insérer une vidéo",
3041
+ table: "Insérer un tableau",
3042
+ back: "Retour",
3043
+ noHighlight: "Sans surbrillance",
3044
+ customColor: "Couleur personnalisée",
3045
+ customColorLabel: "Personnaliser…"
3046
+ },
3047
+ statusbar: {
3048
+ resizeHandle: "Faire glisser pour redimensionner",
3049
+ words: (n) => `Mots\u00a0: ${n}`,
3050
+ wordsLimit: (n, max) => `Mots\u00a0: ${n}/${max}`,
3051
+ chars: (n) => `Caractères\u00a0: ${n}`,
3052
+ charsLimit: (n, max) => `Caractères\u00a0: ${n}/${max}`
3053
+ },
3054
+ tooltips: {
3055
+ link: {
3056
+ ariaLabel: "Actions du lien",
3057
+ openLink: "Ouvrir le lien",
3058
+ copyUrl: "Copier l'URL",
3059
+ editLink: "Modifier le lien",
3060
+ removeLink: "Supprimer le lien"
3061
+ },
3062
+ image: {
3063
+ ariaLabel: "Actions de l'image",
3064
+ label: "Image",
3065
+ floatLeft: "Flottant à gauche",
3066
+ noFloat: "Sans flottant",
3067
+ alignCenter: "Centré",
3068
+ floatRight: "Flottant à droite",
3069
+ originalSize: "Taille originale",
3070
+ rotateLeft: "Rotation à gauche",
3071
+ rotateRight: "Rotation à droite",
3072
+ cropImage: "Recadrer l'image",
3073
+ addCaption: "Ajouter / modifier la légende",
3074
+ deleteImage: "Supprimer l'image"
3075
+ },
3076
+ code: {
3077
+ ariaLabel: "Actions du bloc de code",
3078
+ label: "Code",
3079
+ syntaxLanguage: "Langage de syntaxe",
3080
+ syntaxAriaLabel: "Langage de syntaxe",
3081
+ copyCode: "Copier le code",
3082
+ toggleWordWrap: "Activer/désactiver le retour à la ligne",
3083
+ enableWordWrap: "Activer le retour à la ligne",
3084
+ disableWordWrap: "Désactiver le retour à la ligne",
3085
+ convertToParagraph: "Convertir en paragraphe",
3086
+ deleteCodeBlock: "Supprimer le bloc de code"
3087
+ },
3088
+ table: {
3089
+ ariaLabel: "Actions du tableau",
3090
+ label: "Tableau",
3091
+ selectCells: "Sélectionner des cellules",
3092
+ addRowAbove: "Ajouter une ligne au-dessus",
3093
+ addRowBelow: "Ajouter une ligne en-dessous",
3094
+ deleteRow: "Supprimer la ligne",
3095
+ addColumnLeft: "Ajouter une colonne à gauche",
3096
+ addColumnRight: "Ajouter une colonne à droite",
3097
+ deleteColumn: "Supprimer la colonne",
3098
+ mergeCells: "Fusionner les cellules",
3099
+ unmergeCells: "Scinder les cellules",
3100
+ columnWidth: "Largeur de colonne",
3101
+ rowHeight: "Hauteur de ligne",
3102
+ tableBorderWidth: "Épaisseur des bordures",
3103
+ deleteTable: "Supprimer le tableau",
3104
+ columnWidthPx: "Largeur de colonne (px)",
3105
+ rowHeightPx: "Hauteur de ligne (px)",
3106
+ tableBorderWidthPx: "Épaisseur des bordures (px)",
3107
+ cancelBtn: "Annuler",
3108
+ applyBtn: "Appliquer"
3109
+ },
3110
+ video: {
3111
+ ariaLabel: "Actions de la vidéo",
3112
+ label: "Vidéo",
3113
+ floatLeft: "Flottant à gauche",
3114
+ noFloat: "Sans flottant",
3115
+ alignCenter: "Centré",
3116
+ floatRight: "Flottant à droite",
3117
+ originalSize: "Taille originale",
3118
+ previewVideo: "Aperçu",
3119
+ exitPreview: "Quitter l'aperçu",
3120
+ deleteVideo: "Supprimer la vidéo"
3121
+ }
3122
+ },
3123
+ errors: {
3124
+ imageFormat: (type) => `Le format "${type}" n'est pas pris en charge par le navigateur. Veuillez le convertir en JPEG, PNG ou WebP.`,
3125
+ imageSize: (maxSize) => `Le fichier image est trop volumineux. La taille maximale autorisée est de ${maxSize}\u00a0Mo.`
3126
+ }
3127
+ },
3128
+ de: {
3129
+ toolbar: {
3130
+ bold: "Fett (Ctrl+B)",
3131
+ italic: "Kursiv (Ctrl+I)",
3132
+ underline: "Unterstrichen (Ctrl+U)",
3133
+ strikethrough: "Durchgestrichen",
3134
+ superscript: "Hochgestellt",
3135
+ subscript: "Tiefgestellt",
3136
+ alignLeft: "Linksbündig",
3137
+ alignCenter: "Zentriert",
3138
+ alignRight: "Rechtsbündig",
3139
+ alignJustify: "Blocksatz",
3140
+ ul: "Ungeordnete Liste",
3141
+ ol: "Geordnete Liste",
3142
+ checklist: "Checkliste",
3143
+ indent: "Einzug vergrößern",
3144
+ outdent: "Einzug verkleinern",
3145
+ undo: "Rückgängig (Ctrl+Z)",
3146
+ redo: "Wiederholen (Ctrl+Y)",
3147
+ hr: "Horizontale Linie",
3148
+ link: "Link einfügen",
3149
+ image: "Bild einfügen",
3150
+ video: "Video einfügen",
3151
+ emoji: "Emoji einfügen",
3152
+ icon: "FA-Symbol einfügen",
3153
+ table: "Tabelle einfügen",
3154
+ fontSize: "Schriftgröße",
3155
+ fontSizePlaceholder: "Größe",
3156
+ removeFormat: "Formatierung entfernen",
3157
+ direction: "Textrichtung umschalten (LTR / RTL)",
3158
+ fontFamily: "Schriftart",
3159
+ paragraphStyle: "Absatzstil",
3160
+ paragraphStylePlaceholder: "Stil",
3161
+ lineHeight: "Zeilenhöhe",
3162
+ lineHeightPlaceholder: "↕ Zeile",
3163
+ codeview: "HTML-Code-Ansicht",
3164
+ fullscreen: "Vollbild",
3165
+ shortcuts: "Tastenkürzel (Ctrl+Shift+/)",
3166
+ find: "Suchen (Ctrl+F)",
3167
+ findReplace: "Suchen & Ersetzen (Ctrl+H)",
3168
+ inlineCode: "Inline-Code (Ctrl+`)",
3169
+ print: "Drucken",
3170
+ foreColor: "Textfarbe",
3171
+ backColor: "Hervorhebungsfarbe",
3172
+ chooseTextColor: "Textfarbe auswählen",
3173
+ chooseHighlightColor: "Hervorhebungsfarbe auswählen",
3174
+ customColor: "Benutzerdefinierte Farbe",
3175
+ insertTableLabel: "Tabelle einfügen",
3176
+ paragraphItems: {
3177
+ p: "Normal",
3178
+ blockquote: "Zitat",
3179
+ pre: "Code"
3180
+ }
3181
+ },
3182
+ linkDialog: {
3183
+ ariaLabel: "Link einfügen",
3184
+ title: "Link einfügen",
3185
+ url: "URL",
3186
+ urlPlaceholder: "https://",
3187
+ displayText: "Anzeigetext",
3188
+ textPlaceholder: "Linktext",
3189
+ openInNewTab: "In neuem Tab öffnen",
3190
+ insertBtn: "Einfügen",
3191
+ cancelBtn: "Abbrechen"
3192
+ },
3193
+ imageDialog: {
3194
+ ariaLabel: "Bild einfügen",
3195
+ title: "Bild einfügen",
3196
+ imageUrl: "Bild-URL",
3197
+ urlPlaceholder: "https://example.com/image.png",
3198
+ altText: "Alt-Text",
3199
+ altPlaceholder: "Bild beschreiben",
3200
+ alignment: "Ausrichtung",
3201
+ alignNone: "Keine",
3202
+ alignLeft: "Links",
3203
+ alignCenter: "Mitte",
3204
+ alignRight: "Rechts",
3205
+ uploadLabel: "Oder Datei hochladen",
3206
+ insertBtn: "Einfügen",
3207
+ cancelBtn: "Abbrechen"
3208
+ },
3209
+ videoDialog: {
3210
+ ariaLabel: "Video einfügen",
3211
+ title: "Video einfügen",
3212
+ videoUrl: "Video-URL",
3213
+ urlPlaceholder: "YouTube, Vimeo oder direkte .mp4-URL",
3214
+ widthLabel: "Breite (px)",
3215
+ widthPlaceholder: "560",
3216
+ insertBtn: "Einfügen",
3217
+ cancelBtn: "Abbrechen",
3218
+ detected: (type) => `Erkannt: ${type}`,
3219
+ unknownFormat: "Unbekanntes Format — direktes Video-Einbetten wird versucht",
3220
+ invalidUrl: "Ungültige URL — bitte geben Sie einen gültigen Videolink ein."
3221
+ },
3222
+ emojiDialog: {
3223
+ ariaLabel: "Emoji einfügen",
3224
+ title: "Emoji einfügen",
3225
+ searchPlaceholder: "Emojis suchen…",
3226
+ all: "Alle",
3227
+ cancelBtn: "Abbrechen",
3228
+ close: "Schließen",
3229
+ categories: {
3230
+ smileys: "Smileys",
3231
+ people: "Menschen",
3232
+ animals: "Tiere",
3233
+ food: "Essen",
3234
+ travel: "Reisen",
3235
+ objects: "Objekte",
3236
+ symbols: "Symbole"
3237
+ }
3238
+ },
3239
+ iconDialog: {
3240
+ ariaLabel: "FA-Symbol einfügen",
3241
+ title: "FA-Symbol einfügen",
3242
+ searchPlaceholder: "Symbole suchen…",
3243
+ all: "Alle",
3244
+ style: "Stil",
3245
+ size: "Größe",
3246
+ color: "Farbe",
3247
+ useColor: " Farbe verwenden",
3248
+ selectHint: "Symbol auswählen",
3249
+ insertBtn: "FA-Symbol einfügen",
3250
+ cancelBtn: "Abbrechen",
3251
+ close: "Schließen",
3252
+ categories: {
3253
+ popular: "Beliebt",
3254
+ interface: "Benutzeroberfläche",
3255
+ navigation: "Navigation",
3256
+ media: "Medien",
3257
+ communication: "Kommunikation",
3258
+ files: "Dateien",
3259
+ people: "Menschen",
3260
+ objects: "Objekte"
3261
+ }
3262
+ },
3263
+ findReplace: {
3264
+ findTitle: "Suchen",
3265
+ findReplaceTitle: "Suchen & Ersetzen",
3266
+ findPlaceholder: "Suchen…",
3267
+ searchAriaLabel: "Suchtext",
3268
+ caseSensitive: "\xA0Groß-/Kleinschreibung",
3269
+ prevBtn: "← Zurück",
3270
+ nextBtn: "Weiter →",
3271
+ replacePlaceholder: "Ersetzen durch…",
3272
+ replaceAriaLabel: "Ersetzen durch",
3273
+ replaceBtn: "Ersetzen",
3274
+ replaceAllBtn: "Alle ersetzen",
3275
+ close: "×"
3276
+ },
3277
+ shortcutsDialog: {
3278
+ title: "Tastenkürzel",
3279
+ ariaLabel: "Tastenkürzel",
3280
+ close: "Schließen",
3281
+ shortcuts: [
3282
+ {
3283
+ category: "Textformatierung",
3284
+ items: [
3285
+ {
3286
+ keys: "Ctrl + B",
3287
+ action: "Fett"
3288
+ },
3289
+ {
3290
+ keys: "Ctrl + I",
3291
+ action: "Kursiv"
3292
+ },
3293
+ {
3294
+ keys: "Ctrl + U",
3295
+ action: "Unterstrichen"
3296
+ },
3297
+ {
3298
+ keys: "Ctrl + K",
3299
+ action: "Link einfügen / bearbeiten"
3300
+ }
3301
+ ]
3302
+ },
3303
+ {
3304
+ category: "Verlauf",
3305
+ items: [{
3306
+ keys: "Ctrl + Z",
3307
+ action: "Rückgängig"
3308
+ }, {
3309
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
3310
+ action: "Wiederholen"
3311
+ }]
3312
+ },
3313
+ {
3314
+ category: "Auswahl & Navigation",
3315
+ items: [
3316
+ {
3317
+ keys: "Ctrl + A",
3318
+ action: "Alles auswählen"
3319
+ },
3320
+ {
3321
+ keys: "Tab",
3322
+ action: "Listenebene erhöhen / Leerzeichen einfügen"
3323
+ },
3324
+ {
3325
+ keys: "Shift + Tab",
3326
+ action: "Listenebene verringern"
3327
+ }
3328
+ ]
3329
+ },
3330
+ {
3331
+ category: "Zwischenablage",
3332
+ items: [{
3333
+ keys: "Ctrl + Shift + V",
3334
+ action: "Als einfachen Text einfügen"
3335
+ }]
3336
+ },
3337
+ {
3338
+ category: "Suchen & Ersetzen",
3339
+ items: [{
3340
+ keys: "Ctrl + F",
3341
+ action: "Im Dokument suchen"
3342
+ }, {
3343
+ keys: "Ctrl + H",
3344
+ action: "Suchen & Ersetzen"
3345
+ }]
3346
+ },
3347
+ {
3348
+ category: "Editor",
3349
+ items: [{
3350
+ keys: "Ctrl + Shift + /",
3351
+ action: "Diesen Tastenkürzel-Dialog anzeigen"
3352
+ }]
3353
+ }
3354
+ ]
3355
+ },
3356
+ contextMenu: {
3357
+ cut: "Ausschneiden",
3358
+ copy: "Kopieren",
3359
+ paste: "Einfügen",
3360
+ bold: "Fett",
3361
+ italic: "Kursiv",
3362
+ underline: "Unterstrichen",
3363
+ textColor: "Textfarbe",
3364
+ highlightColor: "Hervorhebungsfarbe",
3365
+ copyFormat: "Format kopieren",
3366
+ pasteFormat: "Format einfügen",
3367
+ removeFormat: "Formatierung entfernen",
3368
+ link: "Link einfügen",
3369
+ image: "Bild einfügen",
3370
+ video: "Video einfügen",
3371
+ table: "Tabelle einfügen",
3372
+ back: "Zurück",
3373
+ noHighlight: "Keine Hervorhebung",
3374
+ customColor: "Benutzerdefinierte Farbe",
3375
+ customColorLabel: "Benutzerdefiniert…"
3376
+ },
3377
+ statusbar: {
3378
+ resizeHandle: "Editor-Größe ändern",
3379
+ words: (n) => `Wörter: ${n}`,
3380
+ wordsLimit: (n, max) => `Wörter: ${n}/${max}`,
3381
+ chars: (n) => `Zeichen: ${n}`,
3382
+ charsLimit: (n, max) => `Zeichen: ${n}/${max}`
3383
+ },
3384
+ tooltips: {
3385
+ link: {
3386
+ ariaLabel: "Link-Aktionen",
3387
+ openLink: "Link öffnen",
3388
+ copyUrl: "URL kopieren",
3389
+ editLink: "Link bearbeiten",
3390
+ removeLink: "Link entfernen"
3391
+ },
3392
+ image: {
3393
+ ariaLabel: "Bild-Aktionen",
3394
+ label: "Bild",
3395
+ floatLeft: "Links umfließen",
3396
+ noFloat: "Kein Umfluss",
3397
+ alignCenter: "Zentriert",
3398
+ floatRight: "Rechts umfließen",
3399
+ originalSize: "Originalgröße",
3400
+ rotateLeft: "Links drehen",
3401
+ rotateRight: "Rechts drehen",
3402
+ cropImage: "Bild zuschneiden",
3403
+ addCaption: "Beschriftung hinzufügen / bearbeiten",
3404
+ deleteImage: "Bild löschen"
3405
+ },
3406
+ code: {
3407
+ ariaLabel: "Codeblock-Aktionen",
3408
+ label: "Code",
3409
+ syntaxLanguage: "Syntaxsprache",
3410
+ syntaxAriaLabel: "Syntaxsprache",
3411
+ copyCode: "Code kopieren",
3412
+ toggleWordWrap: "Zeilenumbruch umschalten",
3413
+ enableWordWrap: "Zeilenumbruch aktivieren",
3414
+ disableWordWrap: "Zeilenumbruch deaktivieren",
3415
+ convertToParagraph: "In Absatz umwandeln",
3416
+ deleteCodeBlock: "Codeblock löschen"
3417
+ },
3418
+ table: {
3419
+ ariaLabel: "Tabellen-Aktionen",
3420
+ label: "Tabelle",
3421
+ selectCells: "Zellen auswählen",
3422
+ addRowAbove: "Zeile oben hinzufügen",
3423
+ addRowBelow: "Zeile unten hinzufügen",
3424
+ deleteRow: "Zeile löschen",
3425
+ addColumnLeft: "Spalte links hinzufügen",
3426
+ addColumnRight: "Spalte rechts hinzufügen",
3427
+ deleteColumn: "Spalte löschen",
3428
+ mergeCells: "Zellen zusammenführen",
3429
+ unmergeCells: "Zellen trennen",
3430
+ columnWidth: "Spaltenbreite",
3431
+ rowHeight: "Zeilenhöhe",
3432
+ tableBorderWidth: "Tabellenrahmenbreite",
3433
+ deleteTable: "Tabelle löschen",
3434
+ columnWidthPx: "Spaltenbreite (px)",
3435
+ rowHeightPx: "Zeilenhöhe (px)",
3436
+ tableBorderWidthPx: "Tabellenrahmenbreite (px)",
3437
+ cancelBtn: "Abbrechen",
3438
+ applyBtn: "Anwenden"
3439
+ },
3440
+ video: {
3441
+ ariaLabel: "Video-Aktionen",
3442
+ label: "Video",
3443
+ floatLeft: "Links umfließen",
3444
+ noFloat: "Kein Umfluss",
3445
+ alignCenter: "Zentriert",
3446
+ floatRight: "Rechts umfließen",
3447
+ originalSize: "Originalgröße",
3448
+ previewVideo: "Video-Vorschau",
3449
+ exitPreview: "Vorschau beenden",
3450
+ deleteVideo: "Video löschen"
3451
+ }
3452
+ },
3453
+ errors: {
3454
+ imageFormat: (type) => `Das Format „${type}" wird in Webbrowsern nicht unterstützt. Bitte konvertieren Sie es zuerst in JPEG, PNG oder WebP.`,
3455
+ imageSize: (maxSize) => `Die Bilddatei ist zu groß. Die maximal zulässige Größe beträgt ${maxSize} MB.`
3456
+ }
3457
+ },
3458
+ es: {
3459
+ toolbar: {
3460
+ bold: "Negrita (Ctrl+B)",
3461
+ italic: "Cursiva (Ctrl+I)",
3462
+ underline: "Subrayado (Ctrl+U)",
3463
+ strikethrough: "Tachado",
3464
+ superscript: "Superíndice",
3465
+ subscript: "Subíndice",
3466
+ alignLeft: "Alinear a la izquierda",
3467
+ alignCenter: "Centrar",
3468
+ alignRight: "Alinear a la derecha",
3469
+ alignJustify: "Justificar",
3470
+ ul: "Lista sin orden",
3471
+ ol: "Lista ordenada",
3472
+ checklist: "Lista de verificación",
3473
+ indent: "Aumentar sangría",
3474
+ outdent: "Reducir sangría",
3475
+ undo: "Deshacer (Ctrl+Z)",
3476
+ redo: "Rehacer (Ctrl+Y)",
3477
+ hr: "Línea horizontal",
3478
+ link: "Insertar enlace",
3479
+ image: "Insertar imagen",
3480
+ video: "Insertar vídeo",
3481
+ emoji: "Insertar emoji",
3482
+ icon: "Insertar icono FA",
3483
+ table: "Insertar tabla",
3484
+ fontSize: "Tamaño de fuente",
3485
+ fontSizePlaceholder: "Tamaño",
3486
+ removeFormat: "Eliminar formato",
3487
+ direction: "Cambiar dirección del texto (LTR / RTL)",
3488
+ fontFamily: "Fuente",
3489
+ paragraphStyle: "Estilo de párrafo",
3490
+ paragraphStylePlaceholder: "Estilo",
3491
+ lineHeight: "Interlineado",
3492
+ lineHeightPlaceholder: "↕ Línea",
3493
+ codeview: "Vista de código HTML",
3494
+ fullscreen: "Pantalla completa",
3495
+ shortcuts: "Atajos de teclado (Ctrl+Shift+/)",
3496
+ find: "Buscar (Ctrl+F)",
3497
+ findReplace: "Buscar y reemplazar (Ctrl+H)",
3498
+ inlineCode: "Código en línea (Ctrl+`)",
3499
+ print: "Imprimir",
3500
+ foreColor: "Color del texto",
3501
+ backColor: "Color de resaltado",
3502
+ chooseTextColor: "Elegir color del texto",
3503
+ chooseHighlightColor: "Elegir color de resaltado",
3504
+ customColor: "Color personalizado",
3505
+ insertTableLabel: "Insertar tabla",
3506
+ paragraphItems: {
3507
+ p: "Normal",
3508
+ blockquote: "Cita",
3509
+ pre: "Código"
3510
+ }
3511
+ },
3512
+ linkDialog: {
3513
+ ariaLabel: "Insertar enlace",
3514
+ title: "Insertar enlace",
3515
+ url: "URL",
3516
+ urlPlaceholder: "https://",
3517
+ displayText: "Texto a mostrar",
3518
+ textPlaceholder: "Texto del enlace",
3519
+ openInNewTab: "Abrir en nueva pestaña",
3520
+ insertBtn: "Insertar",
3521
+ cancelBtn: "Cancelar"
3522
+ },
3523
+ imageDialog: {
3524
+ ariaLabel: "Insertar imagen",
3525
+ title: "Insertar imagen",
3526
+ imageUrl: "URL de imagen",
3527
+ urlPlaceholder: "https://example.com/image.png",
3528
+ altText: "Texto alternativo",
3529
+ altPlaceholder: "Describir la imagen",
3530
+ alignment: "Alineación",
3531
+ alignNone: "Ninguna",
3532
+ alignLeft: "Izquierda",
3533
+ alignCenter: "Centro",
3534
+ alignRight: "Derecha",
3535
+ uploadLabel: "O subir un archivo",
3536
+ insertBtn: "Insertar",
3537
+ cancelBtn: "Cancelar"
3538
+ },
3539
+ videoDialog: {
3540
+ ariaLabel: "Insertar vídeo",
3541
+ title: "Insertar vídeo",
3542
+ videoUrl: "URL del vídeo",
3543
+ urlPlaceholder: "YouTube, Vimeo o URL directa .mp4",
3544
+ widthLabel: "Ancho (px)",
3545
+ widthPlaceholder: "560",
3546
+ insertBtn: "Insertar",
3547
+ cancelBtn: "Cancelar",
3548
+ detected: (type) => `Detectado: ${type}`,
3549
+ unknownFormat: "Formato desconocido — se intentará incrustar el vídeo directamente",
3550
+ invalidUrl: "URL no válida — introduzca un enlace de vídeo válido."
3551
+ },
3552
+ emojiDialog: {
3553
+ ariaLabel: "Insertar emoji",
3554
+ title: "Insertar emoji",
3555
+ searchPlaceholder: "Buscar emojis…",
3556
+ all: "Todos",
3557
+ cancelBtn: "Cancelar",
3558
+ close: "Cerrar",
3559
+ categories: {
3560
+ smileys: "Caritas",
3561
+ people: "Personas",
3562
+ animals: "Animales",
3563
+ food: "Comida",
3564
+ travel: "Viajes",
3565
+ objects: "Objetos",
3566
+ symbols: "Símbolos"
3567
+ }
3568
+ },
3569
+ iconDialog: {
3570
+ ariaLabel: "Insertar icono FA",
3571
+ title: "Insertar icono FA",
3572
+ searchPlaceholder: "Buscar iconos…",
3573
+ all: "Todos",
3574
+ style: "Estilo",
3575
+ size: "Tamaño",
3576
+ color: "Color",
3577
+ useColor: " Usar color",
3578
+ selectHint: "Seleccionar un icono",
3579
+ insertBtn: "Insertar icono FA",
3580
+ cancelBtn: "Cancelar",
3581
+ close: "Cerrar",
3582
+ categories: {
3583
+ popular: "Popular",
3584
+ interface: "Interfaz",
3585
+ navigation: "Navegación",
3586
+ media: "Medios",
3587
+ communication: "Comunicación",
3588
+ files: "Archivos",
3589
+ people: "Personas",
3590
+ objects: "Objetos"
3591
+ }
3592
+ },
3593
+ findReplace: {
3594
+ findTitle: "Buscar",
3595
+ findReplaceTitle: "Buscar y reemplazar",
3596
+ findPlaceholder: "Buscar…",
3597
+ searchAriaLabel: "Texto de búsqueda",
3598
+ caseSensitive: "\xA0Distinguir mayúsculas",
3599
+ prevBtn: "← Anterior",
3600
+ nextBtn: "Siguiente →",
3601
+ replacePlaceholder: "Reemplazar con…",
3602
+ replaceAriaLabel: "Reemplazar con",
3603
+ replaceBtn: "Reemplazar",
3604
+ replaceAllBtn: "Reemplazar todo",
3605
+ close: "×"
3606
+ },
3607
+ shortcutsDialog: {
3608
+ title: "Atajos de teclado",
3609
+ ariaLabel: "Atajos de teclado",
3610
+ close: "Cerrar",
3611
+ shortcuts: [
3612
+ {
3613
+ category: "Formato de texto",
3614
+ items: [
3615
+ {
3616
+ keys: "Ctrl + B",
3617
+ action: "Negrita"
3618
+ },
3619
+ {
3620
+ keys: "Ctrl + I",
3621
+ action: "Cursiva"
3622
+ },
3623
+ {
3624
+ keys: "Ctrl + U",
3625
+ action: "Subrayado"
3626
+ },
3627
+ {
3628
+ keys: "Ctrl + K",
3629
+ action: "Insertar / editar enlace"
3630
+ }
3631
+ ]
3632
+ },
3633
+ {
3634
+ category: "Historial",
3635
+ items: [{
3636
+ keys: "Ctrl + Z",
3637
+ action: "Deshacer"
3638
+ }, {
3639
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
3640
+ action: "Rehacer"
3641
+ }]
3642
+ },
3643
+ {
3644
+ category: "Selección y navegación",
3645
+ items: [
3646
+ {
3647
+ keys: "Ctrl + A",
3648
+ action: "Seleccionar todo"
3649
+ },
3650
+ {
3651
+ keys: "Tab",
3652
+ action: "Aumentar sangría / insertar espacios"
3653
+ },
3654
+ {
3655
+ keys: "Shift + Tab",
3656
+ action: "Reducir sangría"
3657
+ }
3658
+ ]
3659
+ },
3660
+ {
3661
+ category: "Portapapeles",
3662
+ items: [{
3663
+ keys: "Ctrl + Shift + V",
3664
+ action: "Pegar como texto plano"
3665
+ }]
3666
+ },
3667
+ {
3668
+ category: "Buscar y reemplazar",
3669
+ items: [{
3670
+ keys: "Ctrl + F",
3671
+ action: "Buscar en el documento"
3672
+ }, {
3673
+ keys: "Ctrl + H",
3674
+ action: "Buscar y reemplazar"
3675
+ }]
3676
+ },
3677
+ {
3678
+ category: "Editor",
3679
+ items: [{
3680
+ keys: "Ctrl + Shift + /",
3681
+ action: "Mostrar este diálogo de atajos"
3682
+ }]
3683
+ }
3684
+ ]
3685
+ },
3686
+ contextMenu: {
3687
+ cut: "Cortar",
3688
+ copy: "Copiar",
3689
+ paste: "Pegar",
3690
+ bold: "Negrita",
3691
+ italic: "Cursiva",
3692
+ underline: "Subrayado",
3693
+ textColor: "Color del texto",
3694
+ highlightColor: "Color de resaltado",
3695
+ copyFormat: "Copiar formato",
3696
+ pasteFormat: "Pegar formato",
3697
+ removeFormat: "Eliminar formato",
3698
+ link: "Insertar enlace",
3699
+ image: "Insertar imagen",
3700
+ video: "Insertar vídeo",
3701
+ table: "Insertar tabla",
3702
+ back: "Atrás",
3703
+ noHighlight: "Sin resaltado",
3704
+ customColor: "Color personalizado",
3705
+ customColorLabel: "Personalizado…"
3706
+ },
3707
+ statusbar: {
3708
+ resizeHandle: "Redimensionar editor",
3709
+ words: (n) => `Palabras: ${n}`,
3710
+ wordsLimit: (n, max) => `Palabras: ${n}/${max}`,
3711
+ chars: (n) => `Caracteres: ${n}`,
3712
+ charsLimit: (n, max) => `Caracteres: ${n}/${max}`
3713
+ },
3714
+ tooltips: {
3715
+ link: {
3716
+ ariaLabel: "Acciones de enlace",
3717
+ openLink: "Abrir enlace",
3718
+ copyUrl: "Copiar URL",
3719
+ editLink: "Editar enlace",
3720
+ removeLink: "Eliminar enlace"
3721
+ },
3722
+ image: {
3723
+ ariaLabel: "Acciones de imagen",
3724
+ label: "Imagen",
3725
+ floatLeft: "Flotar a la izquierda",
3726
+ noFloat: "Sin flotado",
3727
+ alignCenter: "Centrar",
3728
+ floatRight: "Flotar a la derecha",
3729
+ originalSize: "Tamaño original",
3730
+ rotateLeft: "Rotar a la izquierda",
3731
+ rotateRight: "Rotar a la derecha",
3732
+ cropImage: "Recortar imagen",
3733
+ addCaption: "Añadir / editar pie de foto",
3734
+ deleteImage: "Eliminar imagen"
3735
+ },
3736
+ code: {
3737
+ ariaLabel: "Acciones de bloque de código",
3738
+ label: "Código",
3739
+ syntaxLanguage: "Lenguaje de sintaxis",
3740
+ syntaxAriaLabel: "Lenguaje de sintaxis",
3741
+ copyCode: "Copiar código",
3742
+ toggleWordWrap: "Alternar ajuste de línea",
3743
+ enableWordWrap: "Activar ajuste de línea",
3744
+ disableWordWrap: "Desactivar ajuste de línea",
3745
+ convertToParagraph: "Convertir en párrafo",
3746
+ deleteCodeBlock: "Eliminar bloque de código"
3747
+ },
3748
+ table: {
3749
+ ariaLabel: "Acciones de tabla",
3750
+ label: "Tabla",
3751
+ selectCells: "Seleccionar celdas",
3752
+ addRowAbove: "Añadir fila encima",
3753
+ addRowBelow: "Añadir fila debajo",
3754
+ deleteRow: "Eliminar fila",
3755
+ addColumnLeft: "Añadir columna a la izquierda",
3756
+ addColumnRight: "Añadir columna a la derecha",
3757
+ deleteColumn: "Eliminar columna",
3758
+ mergeCells: "Combinar celdas",
3759
+ unmergeCells: "Separar celdas",
3760
+ columnWidth: "Ancho de columna",
3761
+ rowHeight: "Alto de fila",
3762
+ tableBorderWidth: "Grosor del borde de la tabla",
3763
+ deleteTable: "Eliminar tabla",
3764
+ columnWidthPx: "Ancho de columna (px)",
3765
+ rowHeightPx: "Alto de fila (px)",
3766
+ tableBorderWidthPx: "Grosor del borde (px)",
3767
+ cancelBtn: "Cancelar",
3768
+ applyBtn: "Aplicar"
3769
+ },
3770
+ video: {
3771
+ ariaLabel: "Acciones de vídeo",
3772
+ label: "Vídeo",
3773
+ floatLeft: "Flotar a la izquierda",
3774
+ noFloat: "Sin flotado",
3775
+ alignCenter: "Centrar",
3776
+ floatRight: "Flotar a la derecha",
3777
+ originalSize: "Tamaño original",
3778
+ previewVideo: "Vista previa de vídeo",
3779
+ exitPreview: "Salir de la vista previa",
3780
+ deleteVideo: "Eliminar vídeo"
3781
+ }
3782
+ },
3783
+ errors: {
3784
+ imageFormat: (type) => `El formato "${type}" no es compatible con los navegadores web. Por favor, conviértalo primero a JPEG, PNG o WebP.`,
3785
+ imageSize: (maxSize) => `El archivo de imagen es demasiado grande. El tamaño máximo permitido es ${maxSize} MB.`
3786
+ }
3787
+ },
3788
+ ko: {
3789
+ toolbar: {
3790
+ bold: "굵게 (Ctrl+B)",
3791
+ italic: "기울임꼴 (Ctrl+I)",
3792
+ underline: "밑줄 (Ctrl+U)",
3793
+ strikethrough: "취소선",
3794
+ superscript: "위 첨자",
3795
+ subscript: "아래 첨자",
3796
+ alignLeft: "왼쪽 정렬",
3797
+ alignCenter: "가운데 정렬",
3798
+ alignRight: "오른쪽 정렬",
3799
+ alignJustify: "양쪽 정렬",
3800
+ ul: "순서 없는 목록",
3801
+ ol: "순서 있는 목록",
3802
+ checklist: "체크리스트",
3803
+ indent: "들여쓰기",
3804
+ outdent: "내어쓰기",
3805
+ undo: "실행 취소 (Ctrl+Z)",
3806
+ redo: "다시 실행 (Ctrl+Y)",
3807
+ hr: "수평선",
3808
+ link: "링크 삽입",
3809
+ image: "이미지 삽입",
3810
+ video: "동영상 삽입",
3811
+ emoji: "이모지 삽입",
3812
+ icon: "FA 아이콘 삽입",
3813
+ table: "표 삽입",
3814
+ fontSize: "글꼴 크기",
3815
+ fontSizePlaceholder: "크기",
3816
+ removeFormat: "서식 제거",
3817
+ direction: "텍스트 방향 전환 (LTR / RTL)",
3818
+ fontFamily: "글꼴",
3819
+ paragraphStyle: "단락 스타일",
3820
+ paragraphStylePlaceholder: "스타일",
3821
+ lineHeight: "줄 간격",
3822
+ lineHeightPlaceholder: "↕ 줄",
3823
+ codeview: "HTML 코드 보기",
3824
+ fullscreen: "전체 화면",
3825
+ shortcuts: "키보드 단축키 (Ctrl+Shift+/)",
3826
+ find: "찾기 (Ctrl+F)",
3827
+ findReplace: "찾기 및 바꾸기 (Ctrl+H)",
3828
+ inlineCode: "인라인 코드 (Ctrl+`)",
3829
+ print: "인쇄",
3830
+ foreColor: "글자 색",
3831
+ backColor: "강조 색",
3832
+ chooseTextColor: "글자 색 선택",
3833
+ chooseHighlightColor: "강조 색 선택",
3834
+ customColor: "사용자 지정 색",
3835
+ insertTableLabel: "표 삽입",
3836
+ paragraphItems: {
3837
+ p: "기본",
3838
+ blockquote: "인용",
3839
+ pre: "코드"
3840
+ }
3841
+ },
3842
+ linkDialog: {
3843
+ ariaLabel: "링크 삽입",
3844
+ title: "링크 삽입",
3845
+ url: "URL",
3846
+ urlPlaceholder: "https://",
3847
+ displayText: "표시 텍스트",
3848
+ textPlaceholder: "링크 텍스트",
3849
+ openInNewTab: "새 탭에서 열기",
3850
+ insertBtn: "삽입",
3851
+ cancelBtn: "취소"
3852
+ },
3853
+ imageDialog: {
3854
+ ariaLabel: "이미지 삽입",
3855
+ title: "이미지 삽입",
3856
+ imageUrl: "이미지 URL",
3857
+ urlPlaceholder: "https://example.com/image.png",
3858
+ altText: "대체 텍스트",
3859
+ altPlaceholder: "이미지 설명",
3860
+ alignment: "정렬",
3861
+ alignNone: "없음",
3862
+ alignLeft: "왼쪽",
3863
+ alignCenter: "가운데",
3864
+ alignRight: "오른쪽",
3865
+ uploadLabel: "또는 파일 업로드",
3866
+ insertBtn: "삽입",
3867
+ cancelBtn: "취소"
3868
+ },
3869
+ videoDialog: {
3870
+ ariaLabel: "동영상 삽입",
3871
+ title: "동영상 삽입",
3872
+ videoUrl: "동영상 URL",
3873
+ urlPlaceholder: "YouTube, Vimeo 또는 직접 .mp4 URL",
3874
+ widthLabel: "너비 (px)",
3875
+ widthPlaceholder: "560",
3876
+ insertBtn: "삽입",
3877
+ cancelBtn: "취소",
3878
+ detected: (type) => `감지됨: ${type}`,
3879
+ unknownFormat: "알 수 없는 형식 — 직접 동영상 임베드를 시도합니다",
3880
+ invalidUrl: "유효하지 않은 URL — 올바른 동영상 링크를 입력하세요."
3881
+ },
3882
+ emojiDialog: {
3883
+ ariaLabel: "이모지 삽입",
3884
+ title: "이모지 삽입",
3885
+ searchPlaceholder: "이모지 검색…",
3886
+ all: "전체",
3887
+ cancelBtn: "취소",
3888
+ close: "닫기",
3889
+ categories: {
3890
+ smileys: "스마일",
3891
+ people: "사람",
3892
+ animals: "동물",
3893
+ food: "음식",
3894
+ travel: "여행",
3895
+ objects: "사물",
3896
+ symbols: "기호"
3897
+ }
3898
+ },
3899
+ iconDialog: {
3900
+ ariaLabel: "FA 아이콘 삽입",
3901
+ title: "FA 아이콘 삽입",
3902
+ searchPlaceholder: "아이콘 검색…",
3903
+ all: "전체",
3904
+ style: "스타일",
3905
+ size: "크기",
3906
+ color: "색상",
3907
+ useColor: " 색상 사용",
3908
+ selectHint: "아이콘을 선택하세요",
3909
+ insertBtn: "FA 아이콘 삽입",
3910
+ cancelBtn: "취소",
3911
+ close: "닫기",
3912
+ categories: {
3913
+ popular: "인기",
3914
+ interface: "인터페이스",
3915
+ navigation: "탐색",
3916
+ media: "미디어",
3917
+ communication: "커뮤니케이션",
3918
+ files: "파일",
3919
+ people: "사람",
3920
+ objects: "사물"
3921
+ }
3922
+ },
3923
+ findReplace: {
3924
+ findTitle: "찾기",
3925
+ findReplaceTitle: "찾기 및 바꾸기",
3926
+ findPlaceholder: "찾기…",
3927
+ searchAriaLabel: "검색 텍스트",
3928
+ caseSensitive: "\xA0대소문자 구분",
3929
+ prevBtn: "← 이전",
3930
+ nextBtn: "다음 →",
3931
+ replacePlaceholder: "바꿀 내용…",
3932
+ replaceAriaLabel: "바꿀 내용",
3933
+ replaceBtn: "바꾸기",
3934
+ replaceAllBtn: "모두 바꾸기",
3935
+ close: "×"
3936
+ },
3937
+ shortcutsDialog: {
3938
+ title: "키보드 단축키",
3939
+ ariaLabel: "키보드 단축키",
3940
+ close: "닫기",
3941
+ shortcuts: [
3942
+ {
3943
+ category: "텍스트 서식",
3944
+ items: [
3945
+ {
3946
+ keys: "Ctrl + B",
3947
+ action: "굵게"
3948
+ },
3949
+ {
3950
+ keys: "Ctrl + I",
3951
+ action: "기울임꼴"
3952
+ },
3953
+ {
3954
+ keys: "Ctrl + U",
3955
+ action: "밑줄"
3956
+ },
3957
+ {
3958
+ keys: "Ctrl + K",
3959
+ action: "링크 삽입 / 편집"
3960
+ }
3961
+ ]
3962
+ },
3963
+ {
3964
+ category: "실행 기록",
3965
+ items: [{
3966
+ keys: "Ctrl + Z",
3967
+ action: "실행 취소"
3968
+ }, {
3969
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
3970
+ action: "다시 실행"
3971
+ }]
3972
+ },
3973
+ {
3974
+ category: "선택 및 탐색",
3975
+ items: [
3976
+ {
3977
+ keys: "Ctrl + A",
3978
+ action: "전체 선택"
3979
+ },
3980
+ {
3981
+ keys: "Tab",
3982
+ action: "목록 들여쓰기 / 공백 삽입"
3983
+ },
3984
+ {
3985
+ keys: "Shift + Tab",
3986
+ action: "목록 내어쓰기"
3987
+ }
3988
+ ]
3989
+ },
3990
+ {
3991
+ category: "클립보드",
3992
+ items: [{
3993
+ keys: "Ctrl + Shift + V",
3994
+ action: "일반 텍스트로 붙여넣기"
3995
+ }]
3996
+ },
3997
+ {
3998
+ category: "찾기 및 바꾸기",
3999
+ items: [{
4000
+ keys: "Ctrl + F",
4001
+ action: "문서에서 찾기"
4002
+ }, {
4003
+ keys: "Ctrl + H",
4004
+ action: "찾기 및 바꾸기"
4005
+ }]
4006
+ },
4007
+ {
4008
+ category: "편집기",
4009
+ items: [{
4010
+ keys: "Ctrl + Shift + /",
4011
+ action: "이 단축키 대화상자 표시"
4012
+ }]
4013
+ }
4014
+ ]
4015
+ },
4016
+ contextMenu: {
4017
+ cut: "잘라내기",
4018
+ copy: "복사",
4019
+ paste: "붙여넣기",
4020
+ bold: "굵게",
4021
+ italic: "기울임꼴",
4022
+ underline: "밑줄",
4023
+ textColor: "글자 색",
4024
+ highlightColor: "강조 색",
4025
+ copyFormat: "서식 복사",
4026
+ pasteFormat: "서식 붙여넣기",
4027
+ removeFormat: "서식 제거",
4028
+ link: "링크 삽입",
4029
+ image: "이미지 삽입",
4030
+ video: "동영상 삽입",
4031
+ table: "표 삽입",
4032
+ back: "뒤로",
4033
+ noHighlight: "강조 없음",
4034
+ customColor: "사용자 지정 색",
4035
+ customColorLabel: "사용자 지정…"
4036
+ },
4037
+ statusbar: {
4038
+ resizeHandle: "편집기 크기 조정",
4039
+ words: (n) => `단어: ${n}`,
4040
+ wordsLimit: (n, max) => `단어: ${n}/${max}`,
4041
+ chars: (n) => `글자: ${n}`,
4042
+ charsLimit: (n, max) => `글자: ${n}/${max}`
4043
+ },
4044
+ tooltips: {
4045
+ link: {
4046
+ ariaLabel: "링크 작업",
4047
+ openLink: "링크 열기",
4048
+ copyUrl: "URL 복사",
4049
+ editLink: "링크 편집",
4050
+ removeLink: "링크 제거"
4051
+ },
4052
+ image: {
4053
+ ariaLabel: "이미지 작업",
4054
+ label: "이미지",
4055
+ floatLeft: "왼쪽 배치",
4056
+ noFloat: "배치 없음",
4057
+ alignCenter: "가운데 정렬",
4058
+ floatRight: "오른쪽 배치",
4059
+ originalSize: "원본 크기",
4060
+ rotateLeft: "왼쪽 회전",
4061
+ rotateRight: "오른쪽 회전",
4062
+ cropImage: "이미지 자르기",
4063
+ addCaption: "캡션 추가 / 편집",
4064
+ deleteImage: "이미지 삭제"
4065
+ },
4066
+ code: {
4067
+ ariaLabel: "코드 블록 작업",
4068
+ label: "코드",
4069
+ syntaxLanguage: "구문 언어",
4070
+ syntaxAriaLabel: "구문 언어",
4071
+ copyCode: "코드 복사",
4072
+ toggleWordWrap: "줄 바꿈 전환",
4073
+ enableWordWrap: "줄 바꿈 사용",
4074
+ disableWordWrap: "줄 바꿈 해제",
4075
+ convertToParagraph: "단락으로 변환",
4076
+ deleteCodeBlock: "코드 블록 삭제"
4077
+ },
4078
+ table: {
4079
+ ariaLabel: "표 작업",
4080
+ label: "표",
4081
+ selectCells: "셀 선택",
4082
+ addRowAbove: "위에 행 추가",
4083
+ addRowBelow: "아래에 행 추가",
4084
+ deleteRow: "행 삭제",
4085
+ addColumnLeft: "왼쪽에 열 추가",
4086
+ addColumnRight: "오른쪽에 열 추가",
4087
+ deleteColumn: "열 삭제",
4088
+ mergeCells: "셀 병합",
4089
+ unmergeCells: "셀 분할",
4090
+ columnWidth: "열 너비",
4091
+ rowHeight: "행 높이",
4092
+ tableBorderWidth: "표 테두리 너비",
4093
+ deleteTable: "표 삭제",
4094
+ columnWidthPx: "열 너비 (px)",
4095
+ rowHeightPx: "행 높이 (px)",
4096
+ tableBorderWidthPx: "표 테두리 너비 (px)",
4097
+ cancelBtn: "취소",
4098
+ applyBtn: "적용"
4099
+ },
4100
+ video: {
4101
+ ariaLabel: "동영상 작업",
4102
+ label: "동영상",
4103
+ floatLeft: "왼쪽 배치",
4104
+ noFloat: "배치 없음",
4105
+ alignCenter: "가운데 정렬",
4106
+ floatRight: "오른쪽 배치",
4107
+ originalSize: "원본 크기",
4108
+ previewVideo: "동영상 미리보기",
4109
+ exitPreview: "미리보기 종료",
4110
+ deleteVideo: "동영상 삭제"
4111
+ }
4112
+ },
4113
+ errors: {
4114
+ imageFormat: (type) => `"${type}" 형식은 웹 브라우저에서 지원되지 않습니다. JPEG, PNG 또는 WebP로 변환해 주세요.`,
4115
+ imageSize: (maxSize) => `이미지 파일이 너무 큽니다. 최대 허용 크기는 ${maxSize} MB입니다.`
4116
+ }
4117
+ }
4118
+ };
4119
+ /**
4120
+ * Resolve a locale object from a lang option value.
4121
+ *
4122
+ * @param {string | Partial<AsnLocale> | null | undefined} lang
4123
+ * @returns {AsnLocale} A fully-populated locale (always contains every key from en.js).
4124
+ */
4125
+ function resolveLocale(lang) {
4126
+ if (!lang || lang === "en") return en;
4127
+ if (typeof lang === "string") {
4128
+ const partial = locales[lang];
4129
+ if (!partial) return en;
4130
+ return mergeDeep(mergeDeep({}, en), partial);
4131
+ }
4132
+ if (typeof lang === "object") return mergeDeep(mergeDeep({}, en), lang);
4133
+ return en;
4134
+ }
4135
+ /**
4136
+ * @typedef {Object} AsnLocale (see types/index.d.ts for the full definition)
4137
+ */
4138
+ //#endregion
1328
4139
  //#region src/js/core/sanitise.js
1329
4140
  /**
1330
4141
  * sanitise.js - Shared HTML and URL sanitisation utilities
@@ -1958,7 +4769,7 @@ function handleKeydown(event, editable, options = {}) {
1958
4769
  const para = closestPara(range.sc, editable);
1959
4770
  if (para && isLi(para)) {
1960
4771
  event.preventDefault();
1961
- if (event.shiftKey) execCommand("outdent");
4772
+ if (event.shiftKey) outdent();
1962
4773
  else execCommand("indent");
1963
4774
  return true;
1964
4775
  }
@@ -2379,7 +5190,12 @@ var Editor = class {
2379
5190
  };
2380
5191
  const isReadOnly = () => this.context.layoutInfo.container.classList.contains("an-disabled");
2381
5192
  this._disposers.push(on(editable, "keydown", onKeydown), on(editable, "beforeinput", onBeforeInput), on(editable, "input", onInput), on(document, "selectionchange", onSelChange), on(editable, "click", onCheckboxClick), on(editable, "mouseup", fixChecklistCursor), on(editable, "keyup", fixChecklistCursor), on(editable, "dragstart", (e) => {
2382
- if (isReadOnly()) e.preventDefault();
5193
+ if (isReadOnly()) {
5194
+ e.preventDefault();
5195
+ return;
5196
+ }
5197
+ const target = e.target;
5198
+ if (target && (target.nodeName === "IFRAME" || target.closest && target.closest(".an-video-wrapper"))) e.preventDefault();
2383
5199
  }), on(editable, "drop", (e) => {
2384
5200
  if (isReadOnly()) e.preventDefault();
2385
5201
  }));
@@ -2536,7 +5352,7 @@ var Editor = class {
2536
5352
  * @param {string} html - HTML string (will be sanitised)
2537
5353
  */
2538
5354
  setHTML(html) {
2539
- this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html);
5355
+ this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html, { allowIframes: true });
2540
5356
  if (this._history) this._history.reset();
2541
5357
  this.afterCommand();
2542
5358
  }
@@ -2970,9 +5786,9 @@ var Toolbar = class {
2970
5786
  const btn = createElement("button", {
2971
5787
  type: "button",
2972
5788
  class: !!this.options.useBootstrap ? this.options.toolbarButtonClass || "btn btn-sm btn-light" : "an-btn",
2973
- title: def.tooltip || "",
5789
+ title: this.context.locale.toolbar[def.name] || def.tooltip || "",
2974
5790
  "data-btn": def.name,
2975
- "aria-label": def.tooltip || def.name,
5791
+ "aria-label": this.context.locale.toolbar[def.name] || def.tooltip || def.name,
2976
5792
  "aria-haspopup": "true",
2977
5793
  "aria-expanded": "false"
2978
5794
  });
@@ -2985,7 +5801,7 @@ var Toolbar = class {
2985
5801
  });
2986
5802
  const grid = createElement("div", { class: "an-table-grid" });
2987
5803
  const label = createElement("div", { class: "an-table-label" });
2988
- label.textContent = "Insert Table";
5804
+ label.textContent = this.context.locale.toolbar.insertTableLabel || "Insert Table";
2989
5805
  const cells = [];
2990
5806
  for (let r = 1; r <= ROWS; r++) for (let c = 1; c <= COLS; c++) {
2991
5807
  const cell = createElement("div", {
@@ -3005,7 +5821,7 @@ var Toolbar = class {
3005
5821
  const c = +cell.getAttribute("data-col");
3006
5822
  cell.classList.toggle("active", r <= rows && c <= cols);
3007
5823
  });
3008
- label.textContent = rows && cols ? `${rows} × ${cols}` : "Insert Table";
5824
+ label.textContent = rows && cols ? `${rows} × ${cols}` : this.context.locale.toolbar.insertTableLabel || "Insert Table";
3009
5825
  };
3010
5826
  const openPopup = () => {
3011
5827
  isOpen = true;
@@ -3085,9 +5901,9 @@ var Toolbar = class {
3085
5901
  const applyBtn = createElement("button", {
3086
5902
  type: "button",
3087
5903
  class: `${baseClass} an-color-btn`,
3088
- title: def.tooltip || "",
5904
+ title: this.context.locale.toolbar[def.name] || def.tooltip || "",
3089
5905
  "data-btn": def.name,
3090
- "aria-label": def.tooltip || def.name
5906
+ "aria-label": this.context.locale.toolbar[def.name] || def.tooltip || def.name
3091
5907
  });
3092
5908
  const S = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
3093
5909
  applyBtn.innerHTML = def.name === "foreColor" ? `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" ${S} style="display:block"><path d="M4 20L12 4L20 20"/><line x1="7.5" y1="14" x2="16.5" y2="14"/></svg>` : `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" ${S} style="display:block"><path d="M3 21v-4l9-9 4 4-9 9z"/><path d="M12 8l4 4"/></svg>`;
@@ -3097,7 +5913,7 @@ var Toolbar = class {
3097
5913
  const arrowBtn = createElement("button", {
3098
5914
  type: "button",
3099
5915
  class: `${baseClass} an-color-arrow`,
3100
- title: `Choose ${def.name === "foreColor" ? "text" : "highlight"} color`,
5916
+ title: def.name === "foreColor" ? this.context.locale.toolbar.chooseTextColor || "Choose text color" : this.context.locale.toolbar.chooseHighlightColor || "Choose highlight color",
3101
5917
  "aria-haspopup": "true",
3102
5918
  "aria-expanded": "false"
3103
5919
  });
@@ -3119,9 +5935,9 @@ var Toolbar = class {
3119
5935
  const colorInput = createElement("input", {
3120
5936
  type: "color",
3121
5937
  value: currentColor,
3122
- title: "Custom color"
5938
+ title: this.context.locale.toolbar.customColor || "Custom color"
3123
5939
  });
3124
- const customLabel = createElement("span", {}, ["Custom color"]);
5940
+ const customLabel = createElement("span", {}, [this.context.locale.toolbar.customColor || "Custom color"]);
3125
5941
  customRow.appendChild(colorInput);
3126
5942
  customRow.appendChild(customLabel);
3127
5943
  popup.appendChild(swatches);
@@ -3231,19 +6047,19 @@ var Toolbar = class {
3231
6047
  const items = def.name === "fontFamily" ? this.options.fontFamilies || [] : def.items || [];
3232
6048
  const select = createElement("select", {
3233
6049
  class: def.selectClass ? `an-select ${def.selectClass}` : "an-select",
3234
- title: def.tooltip || "",
6050
+ title: this.context.locale.toolbar[def.name] || def.tooltip || "",
3235
6051
  "data-btn": def.name,
3236
- "aria-label": def.tooltip || def.name
6052
+ "aria-label": this.context.locale.toolbar[def.name] || def.tooltip || def.name
3237
6053
  });
3238
6054
  const placeholder = createElement("option", {
3239
6055
  value: "",
3240
6056
  disabled: "",
3241
6057
  hidden: ""
3242
- }, [def.placeholder || "Font"]);
6058
+ }, [this.context.locale.toolbar[def.name + "Placeholder"] || def.placeholder || "Font"]);
3243
6059
  select.appendChild(placeholder);
3244
6060
  items.forEach((item) => {
3245
6061
  const value = typeof item === "object" ? item.value : item;
3246
- const label = typeof item === "object" ? item.label : item;
6062
+ const label = typeof item === "object" ? def.name === "paragraphStyle" ? (this.context.locale.toolbar.paragraphItems || {})[item.value] || item.label : item.label : item;
3247
6063
  const isHeader = typeof item === "object" && !!item.disabled;
3248
6064
  const attrs = { value };
3249
6065
  if (isHeader) attrs.disabled = "";
@@ -3283,9 +6099,9 @@ var Toolbar = class {
3283
6099
  const btn = createElement("button", {
3284
6100
  type: "button",
3285
6101
  class: `${!!this.options.useBootstrap ? this.options.toolbarButtonClass || "btn btn-sm btn-light" : `an-btn`}${btnDef.className ? ` ${btnDef.className}` : ""}`,
3286
- title: btnDef.tooltip || "",
6102
+ title: this.context.locale.toolbar[btnDef.name] || btnDef.tooltip || "",
3287
6103
  "data-btn": btnDef.name,
3288
- "aria-label": btnDef.tooltip || btnDef.name
6104
+ "aria-label": this.context.locale.toolbar[btnDef.name] || btnDef.tooltip || btnDef.name
3289
6105
  });
3290
6106
  const faPrefix = this.options.fontAwesomeClass || "fas";
3291
6107
  if (this._faReady) {
@@ -3410,7 +6226,7 @@ var Statusbar = class {
3410
6226
  if (this.options.resizable !== false) {
3411
6227
  const handle = createElement("div", {
3412
6228
  class: "an-resize-handle",
3413
- title: "Resize editor",
6229
+ title: this.context.locale.statusbar.resizeHandle,
3414
6230
  "aria-hidden": "true"
3415
6231
  });
3416
6232
  this._bindResize(handle);
@@ -3500,8 +6316,9 @@ var Statusbar = class {
3500
6316
  const chars = text.replace(/\n/g, "").length;
3501
6317
  const maxWords = this.options.maxWords || 0;
3502
6318
  const maxChars = this.options.maxChars || 0;
3503
- this._wordCountEl.textContent = maxWords ? `Words: ${words}/${maxWords}` : `Words: ${words}`;
3504
- this._charCountEl.textContent = maxChars ? `Chars: ${chars}/${maxChars}` : `Chars: ${chars}`;
6319
+ const LS = this.context.locale.statusbar;
6320
+ this._wordCountEl.textContent = maxWords ? LS.wordsLimit(words, maxWords) : LS.words(words);
6321
+ this._charCountEl.textContent = maxChars ? LS.charsLimit(chars, maxChars) : LS.chars(chars);
3505
6322
  _applyLimitClass(this._wordCountEl, words, maxWords);
3506
6323
  _applyLimitClass(this._charCountEl, chars, maxChars);
3507
6324
  }
@@ -3911,7 +6728,7 @@ var Placeholder = class {
3911
6728
  _update() {
3912
6729
  const editable = this.context.layoutInfo.editable;
3913
6730
  const isFocused = document.activeElement === editable;
3914
- const isEmpty = !editable.textContent.trim() && !editable.querySelector("img, table, hr, .an-video-wrapper");
6731
+ const isEmpty = !(editable.textContent.replace(/\u200B/g, "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
3915
6732
  editable.classList.toggle("an-placeholder", isEmpty && !isFocused);
3916
6733
  }
3917
6734
  };
@@ -4098,32 +6915,33 @@ var LinkDialog = class {
4098
6915
  this._open();
4099
6916
  }
4100
6917
  _buildDialog() {
6918
+ const L = this.context.locale.linkDialog;
4101
6919
  const overlay = createElement("div", {
4102
6920
  class: "an-dialog-overlay",
4103
6921
  role: "dialog",
4104
6922
  "aria-modal": "true",
4105
- "aria-label": "Insert link"
6923
+ "aria-label": L.ariaLabel
4106
6924
  });
4107
6925
  const box = createElement("div", { class: "an-dialog-box" });
4108
6926
  const title = createElement("h3", { class: "an-dialog-title" });
4109
- title.textContent = "Insert Link";
6927
+ title.textContent = L.title;
4110
6928
  const urlLabel = createElement("label", { class: "an-label" });
4111
- urlLabel.textContent = "URL";
6929
+ urlLabel.textContent = L.url;
4112
6930
  const urlInput = createElement("input", {
4113
6931
  type: "url",
4114
6932
  class: "an-input",
4115
- placeholder: "https://",
6933
+ placeholder: L.urlPlaceholder,
4116
6934
  id: "an-link-url",
4117
6935
  name: "url",
4118
6936
  autocomplete: "off"
4119
6937
  });
4120
6938
  this._urlInput = urlInput;
4121
6939
  const textLabel = createElement("label", { class: "an-label" });
4122
- textLabel.textContent = "Display Text";
6940
+ textLabel.textContent = L.displayText;
4123
6941
  const textInput = createElement("input", {
4124
6942
  type: "text",
4125
6943
  class: "an-input",
4126
- placeholder: "Link text",
6944
+ placeholder: L.textPlaceholder,
4127
6945
  id: "an-link-text",
4128
6946
  name: "linkText",
4129
6947
  autocomplete: "off"
@@ -4137,18 +6955,18 @@ var LinkDialog = class {
4137
6955
  });
4138
6956
  this._tabCheckbox = tabCheckbox;
4139
6957
  tabLabel.appendChild(tabCheckbox);
4140
- tabLabel.appendChild(document.createTextNode(" Open in new tab"));
6958
+ tabLabel.appendChild(document.createTextNode(" " + L.openInNewTab));
4141
6959
  const btnRow = createElement("div", { class: "an-dialog-actions" });
4142
6960
  const insertBtn = createElement("button", {
4143
6961
  type: "button",
4144
6962
  class: "an-btn an-btn-primary"
4145
6963
  });
4146
- insertBtn.textContent = "Insert";
6964
+ insertBtn.textContent = L.insertBtn;
4147
6965
  const cancelBtn = createElement("button", {
4148
6966
  type: "button",
4149
6967
  class: "an-btn"
4150
6968
  });
4151
- cancelBtn.textContent = "Cancel";
6969
+ cancelBtn.textContent = L.cancelBtn;
4152
6970
  btnRow.appendChild(insertBtn);
4153
6971
  btnRow.appendChild(cancelBtn);
4154
6972
  box.append(title, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
@@ -4274,53 +7092,54 @@ var ImageDialog = class {
4274
7092
  this._open();
4275
7093
  }
4276
7094
  _buildDialog() {
7095
+ const L = this.context.locale.imageDialog;
4277
7096
  const overlay = createElement("div", {
4278
7097
  class: "an-dialog-overlay",
4279
7098
  role: "dialog",
4280
7099
  "aria-modal": "true",
4281
- "aria-label": "Insert image"
7100
+ "aria-label": L.ariaLabel
4282
7101
  });
4283
7102
  const box = createElement("div", { class: "an-dialog-box" });
4284
7103
  const title = createElement("h3", { class: "an-dialog-title" });
4285
- title.textContent = "Insert Image";
7104
+ title.textContent = L.title;
4286
7105
  const urlLabel = createElement("label", { class: "an-label" });
4287
- urlLabel.textContent = "Image URL";
7106
+ urlLabel.textContent = L.imageUrl;
4288
7107
  const urlInput = createElement("input", {
4289
7108
  type: "url",
4290
7109
  class: "an-input",
4291
- placeholder: "https://example.com/image.png",
7110
+ placeholder: L.urlPlaceholder,
4292
7111
  autocomplete: "off"
4293
7112
  });
4294
7113
  this._urlInput = urlInput;
4295
7114
  const altLabel = createElement("label", { class: "an-label" });
4296
- altLabel.textContent = "Alt Text";
7115
+ altLabel.textContent = L.altText;
4297
7116
  const altInput = createElement("input", {
4298
7117
  type: "text",
4299
7118
  class: "an-input",
4300
- placeholder: "Describe the image",
7119
+ placeholder: L.altPlaceholder,
4301
7120
  autocomplete: "off"
4302
7121
  });
4303
7122
  this._altInput = altInput;
4304
7123
  box.append(title, urlLabel, urlInput, altLabel, altInput);
4305
7124
  const alignLabel = createElement("label", { class: "an-label" });
4306
- alignLabel.textContent = "Alignment";
7125
+ alignLabel.textContent = L.alignment;
4307
7126
  const alignRow = createElement("div", { class: "an-align-row" });
4308
7127
  [
4309
7128
  {
4310
7129
  value: "",
4311
- label: "None"
7130
+ label: L.alignNone
4312
7131
  },
4313
7132
  {
4314
7133
  value: "left",
4315
- label: "Left"
7134
+ label: L.alignLeft
4316
7135
  },
4317
7136
  {
4318
7137
  value: "center",
4319
- label: "Center"
7138
+ label: L.alignCenter
4320
7139
  },
4321
7140
  {
4322
7141
  value: "right",
4323
- label: "Right"
7142
+ label: L.alignRight
4324
7143
  }
4325
7144
  ].forEach(({ value, label }) => {
4326
7145
  const radioId = `an-align-${value || "none"}`;
@@ -4342,11 +7161,11 @@ var ImageDialog = class {
4342
7161
  box.append(alignLabel, alignRow);
4343
7162
  if (this.options.allowImageUpload !== false) {
4344
7163
  const fileLabel = createElement("label", { class: "an-label" });
4345
- fileLabel.textContent = "Or upload a file";
7164
+ fileLabel.textContent = L.uploadLabel;
4346
7165
  const fileInput = createElement("input", {
4347
7166
  type: "file",
4348
7167
  class: "an-input",
4349
- accept: "image/*"
7168
+ accept: "image/jpeg,image/png,image/gif,image/webp,image/svg+xml,image/avif"
4350
7169
  });
4351
7170
  this._fileInput = fileInput;
4352
7171
  const fileHint = createElement("p", { class: "an-dialog-hint" });
@@ -4360,12 +7179,12 @@ var ImageDialog = class {
4360
7179
  type: "button",
4361
7180
  class: "an-btn an-btn-primary"
4362
7181
  });
4363
- insertBtn.textContent = "Insert";
7182
+ insertBtn.textContent = L.insertBtn;
4364
7183
  const cancelBtn = createElement("button", {
4365
7184
  type: "button",
4366
7185
  class: "an-btn"
4367
7186
  });
4368
- cancelBtn.textContent = "Cancel";
7187
+ cancelBtn.textContent = L.cancelBtn;
4369
7188
  btnRow.appendChild(insertBtn);
4370
7189
  btnRow.appendChild(cancelBtn);
4371
7190
  box.append(btnRow);
@@ -4393,14 +7212,15 @@ var ImageDialog = class {
4393
7212
  _onFileChange() {
4394
7213
  const file = this._fileInput && this._fileInput.files && this._fileInput.files[0];
4395
7214
  if (!file || !file.type.startsWith("image/")) return;
4396
- if ([
4397
- "image/tiff",
4398
- "image/x-tiff",
4399
- "image/bmp",
4400
- "image/x-bmp",
4401
- "image/x-ms-bmp"
4402
- ].includes(file.type)) {
4403
- const message = `Format "${file.type}" is not supported for display in web browsers. Please convert to PNG, JPEG, or WebP first.`;
7215
+ if (!new Set([
7216
+ "image/jpeg",
7217
+ "image/png",
7218
+ "image/gif",
7219
+ "image/webp",
7220
+ "image/svg+xml",
7221
+ "image/avif"
7222
+ ]).has(file.type)) {
7223
+ const message = this.context.locale.errors.imageFormat(file.type);
4404
7224
  if (this._fileHint) this._fileHint.textContent = message;
4405
7225
  this.context.triggerEvent("imageError", {
4406
7226
  file,
@@ -4412,7 +7232,7 @@ var ImageDialog = class {
4412
7232
  if (this._fileHint) this._fileHint.textContent = "";
4413
7233
  const maxSize = (this.options.maxImageSize || 5) * 1024 * 1024;
4414
7234
  if (file.size > maxSize) {
4415
- const message = `Image file is too large. Maximum allowed size is ${this.options.maxImageSize || 5} MB.`;
7235
+ const message = this.context.locale.errors.imageSize(this.options.maxImageSize || 5);
4416
7236
  if (this._fileHint) this._fileHint.textContent = message;
4417
7237
  console.warn("[AutumnNote] ImageDialog:", message);
4418
7238
  this.context.triggerEvent("imageError", {
@@ -4498,28 +7318,29 @@ var VideoDialog = class {
4498
7318
  this._open();
4499
7319
  }
4500
7320
  _buildDialog() {
7321
+ const L = this.context.locale.videoDialog;
4501
7322
  const overlay = createElement("div", {
4502
7323
  class: "an-dialog-overlay",
4503
7324
  role: "dialog",
4504
7325
  "aria-modal": "true",
4505
- "aria-label": "Insert video"
7326
+ "aria-label": L.ariaLabel
4506
7327
  });
4507
7328
  const box = createElement("div", { class: "an-dialog-box" });
4508
7329
  const title = createElement("h3", { class: "an-dialog-title" });
4509
- title.textContent = "Insert Video";
7330
+ title.textContent = L.title;
4510
7331
  const urlLabel = createElement("label", { class: "an-label" });
4511
- urlLabel.textContent = "Video URL";
7332
+ urlLabel.textContent = L.videoUrl;
4512
7333
  const urlInput = createElement("input", {
4513
7334
  type: "url",
4514
7335
  class: "an-input",
4515
- placeholder: "YouTube, Vimeo, or direct .mp4 URL",
7336
+ placeholder: L.urlPlaceholder,
4516
7337
  autocomplete: "off"
4517
7338
  });
4518
7339
  this._urlInput = urlInput;
4519
7340
  const hintEl = createElement("p", { class: "an-dialog-hint" });
4520
7341
  this._hintEl = hintEl;
4521
7342
  const widthLabel = createElement("label", { class: "an-label" });
4522
- widthLabel.textContent = "Width (px)";
7343
+ widthLabel.textContent = L.widthLabel;
4523
7344
  const widthInput = createElement("input", {
4524
7345
  type: "number",
4525
7346
  class: "an-input",
@@ -4534,19 +7355,19 @@ var VideoDialog = class {
4534
7355
  type: "button",
4535
7356
  class: "an-btn an-btn-primary"
4536
7357
  });
4537
- insertBtn.textContent = "Insert";
7358
+ insertBtn.textContent = L.insertBtn;
4538
7359
  const cancelBtn = createElement("button", {
4539
7360
  type: "button",
4540
7361
  class: "an-btn"
4541
7362
  });
4542
- cancelBtn.textContent = "Cancel";
7363
+ cancelBtn.textContent = L.cancelBtn;
4543
7364
  btnRow.appendChild(insertBtn);
4544
7365
  btnRow.appendChild(cancelBtn);
4545
7366
  box.append(title, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
4546
7367
  overlay.appendChild(box);
4547
7368
  const d0 = on(urlInput, "input", () => {
4548
7369
  const info = this._parseVideoUrl(urlInput.value.trim());
4549
- hintEl.textContent = info ? `Detected: ${info.type}` : urlInput.value ? "Unknown format — will try direct video embed" : "";
7370
+ hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : urlInput.value ? this.context.locale.videoDialog.unknownFormat : "";
4550
7371
  });
4551
7372
  const d1 = on(insertBtn, "click", () => this._onInsert());
4552
7373
  const d2 = on(cancelBtn, "click", () => this._close());
@@ -4571,7 +7392,7 @@ var VideoDialog = class {
4571
7392
  }
4572
7393
  const html = this._buildEmbedHtml(rawUrl, width);
4573
7394
  if (!html) {
4574
- this._hintEl.textContent = "Invalid URL — please enter a valid video link.";
7395
+ this._hintEl.textContent = this.context.locale.videoDialog.invalidUrl;
4575
7396
  this._urlInput.focus();
4576
7397
  return;
4577
7398
  }
@@ -5143,19 +7964,20 @@ var LinkTooltip = class {
5143
7964
  this._el = null;
5144
7965
  }
5145
7966
  _buildTooltip() {
7967
+ const L = this.context.locale.tooltips.link;
5146
7968
  const el = createElement("div", {
5147
7969
  class: "an-link-tooltip",
5148
7970
  role: "toolbar",
5149
- "aria-label": "Link actions"
7971
+ "aria-label": L.ariaLabel
5150
7972
  });
5151
7973
  el.style.display = "none";
5152
7974
  this._urlLabel = createElement("span", { class: "an-link-tooltip-url" });
5153
7975
  el.appendChild(this._urlLabel);
5154
7976
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5155
- this._openBtn = this._makeBtn(ICONS$5.open, "Open link", () => this._openLink());
5156
- this._copyBtn = this._makeBtn(ICONS$5.copy, "Copy URL", () => this._copyLink());
5157
- this._editBtn = this._makeBtn(ICONS$5.edit, "Edit link", () => this._editLink());
5158
- this._unlinkBtn = this._makeBtn(ICONS$5.unlink, "Remove link", () => this._unlink());
7977
+ this._openBtn = this._makeBtn(ICONS$5.open, L.openLink, () => this._openLink());
7978
+ this._copyBtn = this._makeBtn(ICONS$5.copy, L.copyUrl, () => this._copyLink());
7979
+ this._editBtn = this._makeBtn(ICONS$5.edit, L.editLink, () => this._editLink());
7980
+ this._unlinkBtn = this._makeBtn(ICONS$5.unlink, L.removeLink, () => this._unlink());
5159
7981
  el.appendChild(this._openBtn);
5160
7982
  el.appendChild(this._copyBtn);
5161
7983
  el.appendChild(this._editBtn);
@@ -5334,38 +8156,39 @@ var ImageTooltip = class {
5334
8156
  this._el = null;
5335
8157
  }
5336
8158
  _buildTooltip() {
8159
+ const L = this.context.locale.tooltips.image;
5337
8160
  const el = createElement("div", {
5338
8161
  class: "an-link-tooltip an-image-tooltip",
5339
8162
  role: "toolbar",
5340
- "aria-label": "Image actions"
8163
+ "aria-label": L.ariaLabel
5341
8164
  });
5342
8165
  el.style.display = "none";
5343
8166
  this._label = createElement("span", { class: "an-link-tooltip-url" });
5344
- this._label.textContent = "Image";
8167
+ this._label.textContent = L.label;
5345
8168
  el.appendChild(this._label);
5346
8169
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5347
- this._floatLeftBtn = this._makeBtn(ICONS$4.floatLeft, "Float Left", () => this._setFloat("left"));
5348
- this._floatNoneBtn = this._makeBtn(ICONS$4.floatNone, "No Float", () => this._setFloat(""));
5349
- this._alignCenterBtn = this._makeBtn(ICONS$4.alignCenter, "Align Center", () => this._setCenter());
5350
- this._floatRightBtn = this._makeBtn(ICONS$4.floatRight, "Float Right", () => this._setFloat("right"));
8170
+ this._floatLeftBtn = this._makeBtn(ICONS$4.floatLeft, L.floatLeft, () => this._setFloat("left"));
8171
+ this._floatNoneBtn = this._makeBtn(ICONS$4.floatNone, L.noFloat, () => this._setFloat(""));
8172
+ this._alignCenterBtn = this._makeBtn(ICONS$4.alignCenter, L.alignCenter, () => this._setCenter());
8173
+ this._floatRightBtn = this._makeBtn(ICONS$4.floatRight, L.floatRight, () => this._setFloat("right"));
5351
8174
  el.appendChild(this._floatLeftBtn);
5352
8175
  el.appendChild(this._floatNoneBtn);
5353
8176
  el.appendChild(this._alignCenterBtn);
5354
8177
  el.appendChild(this._floatRightBtn);
5355
8178
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5356
- this._originalBtn = this._makeBtn(ICONS$4.originalSize, "Original Size", () => this._resetSize());
8179
+ this._originalBtn = this._makeBtn(ICONS$4.originalSize, L.originalSize, () => this._resetSize());
5357
8180
  el.appendChild(this._originalBtn);
5358
8181
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5359
- el.appendChild(this._makeBtn(ICONS$4.rotateLeft, "Rotate Left", () => this._rotate(-90)));
5360
- el.appendChild(this._makeBtn(ICONS$4.rotateRight, "Rotate Right", () => this._rotate(90)));
8182
+ el.appendChild(this._makeBtn(ICONS$4.rotateLeft, L.rotateLeft, () => this._rotate(-90)));
8183
+ el.appendChild(this._makeBtn(ICONS$4.rotateRight, L.rotateRight, () => this._rotate(90)));
5361
8184
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5362
- this._cropBtn = this._makeBtn(ICONS$4.crop, "Crop Image", () => this._crop());
8185
+ this._cropBtn = this._makeBtn(ICONS$4.crop, L.cropImage, () => this._crop());
5363
8186
  el.appendChild(this._cropBtn);
5364
8187
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5365
- this._captionBtn = this._makeBtn(ICONS$4.caption, "Add / Edit Caption", () => this._toggleCaption());
8188
+ this._captionBtn = this._makeBtn(ICONS$4.caption, L.addCaption, () => this._toggleCaption());
5366
8189
  el.appendChild(this._captionBtn);
5367
8190
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5368
- this._deleteBtn = this._makeBtn(ICONS$4.deleteImg, "Delete Image", () => this._delete(), true);
8191
+ this._deleteBtn = this._makeBtn(ICONS$4.deleteImg, L.deleteImage, () => this._delete(), true);
5369
8192
  el.appendChild(this._deleteBtn);
5370
8193
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => this._scheduleHide()));
5371
8194
  return el;
@@ -5617,32 +8440,33 @@ var VideoTooltip = class {
5617
8440
  this._el = null;
5618
8441
  }
5619
8442
  _buildTooltip() {
8443
+ const L = this.context.locale.tooltips.video;
5620
8444
  const el = createElement("div", {
5621
8445
  class: "an-link-tooltip an-video-tooltip",
5622
8446
  role: "toolbar",
5623
- "aria-label": "Video actions"
8447
+ "aria-label": L.ariaLabel
5624
8448
  });
5625
8449
  el.style.display = "none";
5626
8450
  this._label = createElement("span", { class: "an-link-tooltip-url" });
5627
- this._label.textContent = "Video";
8451
+ this._label.textContent = L.label;
5628
8452
  el.appendChild(this._label);
5629
8453
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5630
- this._floatLeftBtn = this._makeBtn(ICONS$3.floatLeft, "Float Left", () => this._setFloat("left"));
5631
- this._floatNoneBtn = this._makeBtn(ICONS$3.floatNone, "No Float", () => this._setFloat(""));
5632
- this._alignCenterBtn = this._makeBtn(ICONS$3.alignCenter, "Align Center", () => this._setCenter());
5633
- this._floatRightBtn = this._makeBtn(ICONS$3.floatRight, "Float Right", () => this._setFloat("right"));
8454
+ this._floatLeftBtn = this._makeBtn(ICONS$3.floatLeft, L.floatLeft, () => this._setFloat("left"));
8455
+ this._floatNoneBtn = this._makeBtn(ICONS$3.floatNone, L.noFloat, () => this._setFloat(""));
8456
+ this._alignCenterBtn = this._makeBtn(ICONS$3.alignCenter, L.alignCenter, () => this._setCenter());
8457
+ this._floatRightBtn = this._makeBtn(ICONS$3.floatRight, L.floatRight, () => this._setFloat("right"));
5634
8458
  el.appendChild(this._floatLeftBtn);
5635
8459
  el.appendChild(this._floatNoneBtn);
5636
8460
  el.appendChild(this._alignCenterBtn);
5637
8461
  el.appendChild(this._floatRightBtn);
5638
8462
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5639
- this._originalBtn = this._makeBtn(ICONS$3.originalSize, "Original Size", () => this._resetSize());
8463
+ this._originalBtn = this._makeBtn(ICONS$3.originalSize, L.originalSize, () => this._resetSize());
5640
8464
  el.appendChild(this._originalBtn);
5641
8465
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5642
- this._previewBtn = this._makeBtn(ICONS$3.preview, "Preview Video", () => this._togglePreview());
8466
+ this._previewBtn = this._makeBtn(ICONS$3.preview, L.previewVideo, () => this._togglePreview());
5643
8467
  el.appendChild(this._previewBtn);
5644
8468
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5645
- this._deleteBtn = this._makeBtn(ICONS$3.deleteVideo, "Delete Video", () => this._delete(), true);
8469
+ this._deleteBtn = this._makeBtn(ICONS$3.deleteVideo, L.deleteVideo, () => this._delete(), true);
5646
8470
  el.appendChild(this._deleteBtn);
5647
8471
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => this._scheduleHide()));
5648
8472
  return el;
@@ -5779,7 +8603,7 @@ var VideoTooltip = class {
5779
8603
  if (shield) shield.style.display = "none";
5780
8604
  this.context.invoke("videoResizer.deselect");
5781
8605
  this._previewBtn.classList.add("an-link-tooltip-btn--copied");
5782
- this._previewBtn.title = "Exit Preview";
8606
+ this._previewBtn.title = this.context.locale.tooltips.video.exitPreview;
5783
8607
  this._previewClickOff = (e) => {
5784
8608
  if (!wrapper.contains(e.target) && !this._el.contains(e.target)) this._exitPreview();
5785
8609
  };
@@ -5793,7 +8617,7 @@ var VideoTooltip = class {
5793
8617
  if (shield) shield.style.display = "";
5794
8618
  }
5795
8619
  this._previewBtn.classList.remove("an-link-tooltip-btn--copied");
5796
- this._previewBtn.title = "Preview Video";
8620
+ this._previewBtn.title = this.context.locale.tooltips.video.previewVideo;
5797
8621
  if (this._previewClickOff) {
5798
8622
  document.removeEventListener("mousedown", this._previewClickOff, true);
5799
8623
  this._previewClickOff = null;
@@ -5854,6 +8678,47 @@ function getCellAfterVisualCol(row, visualIdx) {
5854
8678
  }
5855
8679
  return null;
5856
8680
  }
8681
+ /**
8682
+ * Build a 2D grid map of the table, accounting for both rowspan and colspan.
8683
+ *
8684
+ * gridMap[r][c] = the DOM cell occupying visual grid position (r, c).
8685
+ * cellPos = WeakMap: cell → { r, c, rs, cs } (top-left grid origin + span).
8686
+ *
8687
+ * Uses HTMLTableElement.rows which is scoped to the table itself and never
8688
+ * includes rows from nested tables.
8689
+ *
8690
+ * @param {HTMLTableElement} table
8691
+ * @returns {{ gridMap: Object, cellPos: WeakMap }}
8692
+ */
8693
+ function buildGridMap(table) {
8694
+ const rows = Array.from(table.rows);
8695
+ const gridMap = {};
8696
+ const cellPos = /* @__PURE__ */ new WeakMap();
8697
+ rows.forEach((row, r) => {
8698
+ if (!gridMap[r]) gridMap[r] = {};
8699
+ let c = 0;
8700
+ for (const cell of row.cells) {
8701
+ while (gridMap[r][c]) c++;
8702
+ const rs = cell.rowSpan || 1;
8703
+ const cs = cell.colSpan || 1;
8704
+ cellPos.set(cell, {
8705
+ r,
8706
+ c,
8707
+ rs,
8708
+ cs
8709
+ });
8710
+ for (let dr = 0; dr < rs; dr++) {
8711
+ if (!gridMap[r + dr]) gridMap[r + dr] = {};
8712
+ for (let dc = 0; dc < cs; dc++) gridMap[r + dr][c + dc] = cell;
8713
+ }
8714
+ c += cs;
8715
+ }
8716
+ });
8717
+ return {
8718
+ gridMap,
8719
+ cellPos
8720
+ };
8721
+ }
5857
8722
  var ICONS$2 = {
5858
8723
  rowAbove: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="12" x2="21" y2="12"/><path d="M12 3v7"/><path d="M9 7l3-4 3 4"/></svg>`,
5859
8724
  rowBelow: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="12" x2="21" y2="12"/><path d="M12 12v7"/><path d="M9 17l3 4 3-4"/></svg>`,
@@ -5862,10 +8727,12 @@ var ICONS$2 = {
5862
8727
  colRight: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="12" y1="3" x2="12" y2="21"/><path d="M12 12h9"/><path d="M17 8l4 4-4 4"/></svg>`,
5863
8728
  deleteCol: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="12" y1="3" x2="12" y2="21"/><line x1="15" y1="6" x2="21" y2="12"/><line x1="21" y1="6" x2="15" y2="12"/></svg>`,
5864
8729
  mergeCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="8" height="10" rx="1"/><rect x="14" y="7" width="8" height="10" rx="1"/><path d="M10 12h4"/><path d="M12 10l2 2-2 2"/></svg>`,
8730
+ unmergeCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="14" rx="1"/><line x1="12" y1="5" x2="12" y2="19" stroke-dasharray="2.5 2"/><line x1="2" y1="12" x2="22" y2="12" stroke-dasharray="2.5 2"/><path d="M9 9 L6 12 L9 15"/><path d="M15 9 L18 12 L15 15"/></svg>`,
5865
8731
  colWidth: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="7" y1="4" x2="7" y2="20"/><line x1="17" y1="4" x2="17" y2="20"/><line x1="7" y1="12" x2="17" y2="12"/><path d="M10 9l-3 3 3 3"/><path d="M14 9l3 3-3 3"/></svg>`,
5866
8732
  rowHeight: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="17" x2="20" y2="17"/><line x1="12" y1="7" x2="12" y2="17"/><path d="M9 10l3-3 3 3"/><path d="M9 14l3 3 3-3"/></svg>`,
5867
8733
  tableBorder: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6" stroke-width="1"/><line x1="3" y1="13" x2="21" y2="13" stroke-width="2"/><line x1="3" y1="20" x2="21" y2="20" stroke-width="3"/></svg>`,
5868
- deleteTable: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="16" y1="16" x2="22" y2="22" stroke="#ef4444"/><line x1="22" y1="16" x2="16" y2="22" stroke="#ef4444"/></svg>`
8734
+ deleteTable: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="16" y1="16" x2="22" y2="22" stroke="#ef4444"/><line x1="22" y1="16" x2="16" y2="22" stroke="#ef4444"/></svg>`,
8735
+ selectCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z" fill="currentColor" opacity="0.15"/><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z"/></svg>`
5869
8736
  };
5870
8737
  var TableTooltip = class {
5871
8738
  /** @param {import('../Context.js').Context} context */
@@ -5881,6 +8748,12 @@ var TableTooltip = class {
5881
8748
  this._sizeApply = null;
5882
8749
  this._sizeTitleEl = null;
5883
8750
  this._sizeInputEl = null;
8751
+ this._selectMode = false;
8752
+ this._selectedCells = [];
8753
+ this._selectStart = null;
8754
+ this._selectDragging = false;
8755
+ this._selectBtn = null;
8756
+ this._editable = null;
5884
8757
  }
5885
8758
  initialize() {
5886
8759
  this._el = this._buildTooltip();
@@ -5888,6 +8761,29 @@ var TableTooltip = class {
5888
8761
  this._sizePopover = this._buildSizePopover();
5889
8762
  document.body.appendChild(this._sizePopover);
5890
8763
  const editable = this.context.layoutInfo.editable;
8764
+ this._editable = editable;
8765
+ const onSelMousedown = (e) => {
8766
+ if (!this._selectMode) return;
8767
+ const cell = e.target.closest("td, th");
8768
+ if (!cell || !editable.contains(cell)) return;
8769
+ if (cell.style.cursor === "col-resize" || cell.style.cursor === "row-resize") return;
8770
+ e.preventDefault();
8771
+ this._activeTable = cell.closest("table");
8772
+ this._selectStart = cell;
8773
+ this._selectDragging = true;
8774
+ this._setSelection([cell]);
8775
+ };
8776
+ const onSelMousemove = (e) => {
8777
+ if (!this._selectMode || !this._selectDragging || !this._selectStart) return;
8778
+ const cell = e.target.closest("td, th");
8779
+ if (!cell || !editable.contains(cell)) return;
8780
+ if (cell.closest("table") !== this._activeTable) return;
8781
+ this._setSelection(this._getRectCells(this._selectStart, cell));
8782
+ };
8783
+ const onSelMouseup = () => {
8784
+ this._selectDragging = false;
8785
+ };
8786
+ this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
5891
8787
  this._disposers.push(on(editable, "mouseover", (e) => {
5892
8788
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
5893
8789
  const table = e.target.closest("table");
@@ -5897,9 +8793,11 @@ var TableTooltip = class {
5897
8793
  this._scheduleShow(table);
5898
8794
  }
5899
8795
  }, { passive: true }), on(editable, "mouseout", (e) => {
8796
+ if (this._selectMode) return;
5900
8797
  const to = e.relatedTarget;
5901
8798
  if (!to || !editable.contains(to) && !this._el.contains(to) && !(this._sizePopover && this._sizePopover.contains(to))) this._scheduleHide();
5902
8799
  }, { passive: true }), on(document, "click", (e) => {
8800
+ if (this._selectMode && this._activeTable && this._activeTable.contains(e.target)) return;
5903
8801
  if (this._activeTable && !this._activeTable.contains(e.target) && !this._el.contains(e.target) && !(this._sizePopover && this._sizePopover.contains(e.target))) this._hide();
5904
8802
  }));
5905
8803
  this._initResize();
@@ -5972,7 +8870,7 @@ var TableTooltip = class {
5972
8870
  if (_edge === "col") {
5973
8871
  _startW = _nearCell.offsetWidth;
5974
8872
  _colIdx = getVisualColIndex(_nearCell);
5975
- _colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean) : [];
8873
+ _colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean).filter((c) => (c.colSpan || 1) === 1) : [];
5976
8874
  document.body.style.cursor = "col-resize";
5977
8875
  } else {
5978
8876
  _row = _nearCell.closest("tr");
@@ -6033,32 +8931,38 @@ var TableTooltip = class {
6033
8931
  this._sizePopover = null;
6034
8932
  }
6035
8933
  _buildTooltip() {
8934
+ const L = this.context.locale.tooltips.table;
6036
8935
  const el = createElement("div", {
6037
8936
  class: "an-link-tooltip an-table-tooltip",
6038
8937
  role: "toolbar",
6039
- "aria-label": "Table actions"
8938
+ "aria-label": L.ariaLabel
6040
8939
  });
6041
8940
  el.style.display = "none";
6042
8941
  this._label = createElement("span", { class: "an-link-tooltip-url" });
6043
- this._label.textContent = "Table";
8942
+ this._label.textContent = L.label;
6044
8943
  el.appendChild(this._label);
6045
8944
  el.appendChild(this._sep());
6046
- el.appendChild(this._makeBtn(ICONS$2.rowAbove, "Add Row Above", () => this._addRow("above")));
6047
- el.appendChild(this._makeBtn(ICONS$2.rowBelow, "Add Row Below", () => this._addRow("below")));
6048
- el.appendChild(this._makeBtn(ICONS$2.deleteRow, "Delete Row", () => this._deleteRow()));
8945
+ this._selectBtn = this._makeBtn(ICONS$2.selectCells, L.selectCells, () => this._toggleSelectMode());
8946
+ el.appendChild(this._selectBtn);
8947
+ el.appendChild(this._sep());
8948
+ el.appendChild(this._makeBtn(ICONS$2.rowAbove, L.addRowAbove, () => this._addRow("above")));
8949
+ el.appendChild(this._makeBtn(ICONS$2.rowBelow, L.addRowBelow, () => this._addRow("below")));
8950
+ el.appendChild(this._makeBtn(ICONS$2.deleteRow, L.deleteRow, () => this._deleteRow()));
6049
8951
  el.appendChild(this._sep());
6050
- el.appendChild(this._makeBtn(ICONS$2.colLeft, "Add Column Left", () => this._addColumn("left")));
6051
- el.appendChild(this._makeBtn(ICONS$2.colRight, "Add Column Right", () => this._addColumn("right")));
6052
- el.appendChild(this._makeBtn(ICONS$2.deleteCol, "Delete Column", () => this._deleteColumn()));
8952
+ el.appendChild(this._makeBtn(ICONS$2.colLeft, L.addColumnLeft, () => this._addColumn("left")));
8953
+ el.appendChild(this._makeBtn(ICONS$2.colRight, L.addColumnRight, () => this._addColumn("right")));
8954
+ el.appendChild(this._makeBtn(ICONS$2.deleteCol, L.deleteColumn, () => this._deleteColumn()));
6053
8955
  el.appendChild(this._sep());
6054
- el.appendChild(this._makeBtn(ICONS$2.mergeCells, "Merge Cells", () => this._mergeCells()));
8956
+ el.appendChild(this._makeBtn(ICONS$2.mergeCells, L.mergeCells, () => this._mergeCells()));
8957
+ el.appendChild(this._makeBtn(ICONS$2.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
6055
8958
  el.appendChild(this._sep());
6056
- el.appendChild(this._makeBtn(ICONS$2.colWidth, "Column Width", () => this._openSizePopover("col")));
6057
- el.appendChild(this._makeBtn(ICONS$2.rowHeight, "Row Height", () => this._openSizePopover("row")));
6058
- el.appendChild(this._makeBtn(ICONS$2.tableBorder, "Table Border Width", () => this._openSizePopover("border")));
8959
+ el.appendChild(this._makeBtn(ICONS$2.colWidth, L.columnWidth, () => this._openSizePopover("col")));
8960
+ el.appendChild(this._makeBtn(ICONS$2.rowHeight, L.rowHeight, () => this._openSizePopover("row")));
8961
+ el.appendChild(this._makeBtn(ICONS$2.tableBorder, L.tableBorderWidth, () => this._openSizePopover("border")));
6059
8962
  el.appendChild(this._sep());
6060
- el.appendChild(this._makeBtn(ICONS$2.deleteTable, "Delete Table", () => this._deleteTable(), true));
8963
+ el.appendChild(this._makeBtn(ICONS$2.deleteTable, L.deleteTable, () => this._deleteTable(), true));
6061
8964
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
8965
+ if (this._selectMode) return;
6062
8966
  if (this._sizePopover && this._sizePopover.style.display !== "none") return;
6063
8967
  this._scheduleHide();
6064
8968
  }));
@@ -6114,6 +9018,12 @@ var TableTooltip = class {
6114
9018
  this._el.style.display = "none";
6115
9019
  this._activeTable = null;
6116
9020
  this._activeCell = null;
9021
+ if (this._selectMode) {
9022
+ this._selectMode = false;
9023
+ if (this._selectBtn) this._selectBtn.classList.remove("an-link-tooltip-btn--active");
9024
+ if (this._editable) this._editable.classList.remove("an-table-select-mode");
9025
+ }
9026
+ this._clearSelection();
6117
9027
  this._clearTimers();
6118
9028
  this._hideSizePopover();
6119
9029
  }
@@ -6147,14 +9057,111 @@ var TableTooltip = class {
6147
9057
  }
6148
9058
  return this._activeCell || this._activeTable && this._activeTable.querySelector("td, th");
6149
9059
  }
9060
+ _toggleSelectMode() {
9061
+ this._selectMode = !this._selectMode;
9062
+ if (this._selectBtn) this._selectBtn.classList.toggle("an-link-tooltip-btn--active", this._selectMode);
9063
+ if (this._editable) this._editable.classList.toggle("an-table-select-mode", this._selectMode);
9064
+ if (!this._selectMode) this._clearSelection();
9065
+ }
9066
+ _clearSelection() {
9067
+ this._selectedCells.forEach((c) => c.classList.remove("an-cell-selected"));
9068
+ this._selectedCells = [];
9069
+ this._selectStart = null;
9070
+ }
9071
+ _setSelection(cells) {
9072
+ this._selectedCells.forEach((c) => {
9073
+ if (!cells.includes(c)) c.classList.remove("an-cell-selected");
9074
+ });
9075
+ this._selectedCells = cells;
9076
+ cells.forEach((c) => c.classList.add("an-cell-selected"));
9077
+ }
9078
+ /**
9079
+ * Returns all cells in the rectangular area between startCell and endCell,
9080
+ * correctly handling rowspan/colspan by using the grid map.
9081
+ * The rect is expanded iteratively until it is stable — this ensures any
9082
+ * merged cell that starts outside the initial rect but spans into it is
9083
+ * fully included.
9084
+ */
9085
+ _getRectCells(startCell, endCell) {
9086
+ if (!startCell) return [];
9087
+ if (!endCell || startCell === endCell) return [startCell];
9088
+ const table = startCell.closest("table");
9089
+ if (!table || !table.contains(endCell)) return [startCell];
9090
+ const { gridMap, cellPos } = buildGridMap(table);
9091
+ const sp = cellPos.get(startCell);
9092
+ const ep = cellPos.get(endCell);
9093
+ if (!sp || !ep) return [startCell];
9094
+ let minR = Math.min(sp.r, ep.r);
9095
+ let maxR = Math.max(sp.r + sp.rs - 1, ep.r + ep.rs - 1);
9096
+ let minC = Math.min(sp.c, ep.c);
9097
+ let maxC = Math.max(sp.c + sp.cs - 1, ep.c + ep.cs - 1);
9098
+ let changed = true;
9099
+ while (changed) {
9100
+ changed = false;
9101
+ for (let r = minR; r <= maxR; r++) {
9102
+ const rowMap = gridMap[r];
9103
+ if (!rowMap) continue;
9104
+ for (let c = minC; c <= maxC; c++) {
9105
+ const cell = rowMap[c];
9106
+ if (!cell) continue;
9107
+ const pos = cellPos.get(cell);
9108
+ if (!pos) continue;
9109
+ if (pos.r < minR) {
9110
+ minR = pos.r;
9111
+ changed = true;
9112
+ }
9113
+ if (pos.r + pos.rs - 1 > maxR) {
9114
+ maxR = pos.r + pos.rs - 1;
9115
+ changed = true;
9116
+ }
9117
+ if (pos.c < minC) {
9118
+ minC = pos.c;
9119
+ changed = true;
9120
+ }
9121
+ if (pos.c + pos.cs - 1 > maxC) {
9122
+ maxC = pos.c + pos.cs - 1;
9123
+ changed = true;
9124
+ }
9125
+ }
9126
+ }
9127
+ }
9128
+ const seen = /* @__PURE__ */ new Set();
9129
+ const result = [];
9130
+ for (let r = minR; r <= maxR; r++) {
9131
+ const rowMap = gridMap[r];
9132
+ if (!rowMap) continue;
9133
+ for (let c = minC; c <= maxC; c++) {
9134
+ const cell = rowMap[c];
9135
+ if (cell && !seen.has(cell)) {
9136
+ seen.add(cell);
9137
+ result.push(cell);
9138
+ }
9139
+ }
9140
+ }
9141
+ return result.length > 0 ? result : [startCell];
9142
+ }
9143
+ /**
9144
+ * Returns the active cell set: user-selected cells when available,
9145
+ * otherwise the single active/cursor cell.
9146
+ * @returns {HTMLTableCellElement[]}
9147
+ */
9148
+ _getSelectedCells() {
9149
+ return this._selectedCells.length > 0 ? this._selectedCells : [this._getCell()].filter(Boolean);
9150
+ }
6150
9151
  _addRow(position) {
6151
- const cell = this._getCell();
6152
- if (!cell) return;
6153
- const row = cell.closest("tr");
6154
- if (!row) return;
6155
- const colCount = Array.from(row.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
9152
+ const cells = this._getSelectedCells();
9153
+ if (!cells.length) return;
9154
+ const table = cells[0].closest("table");
9155
+ if (!table) return;
9156
+ const allRows = Array.from(table.querySelectorAll("tr"));
9157
+ const refRow = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))].reduce((best, r) => {
9158
+ const bi = allRows.indexOf(best);
9159
+ const ri = allRows.indexOf(r);
9160
+ return position === "above" ? ri < bi ? r : best : ri > bi ? r : best;
9161
+ });
9162
+ const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
6156
9163
  const newRow = document.createElement("tr");
6157
- const refCells = Array.from(row.cells);
9164
+ const refCells = Array.from(refRow.cells);
6158
9165
  for (let i = 0; i < colCount; i++) {
6159
9166
  const td = createElement("td", {}, ["\xA0"]);
6160
9167
  const ref = refCells[i];
@@ -6162,19 +9169,20 @@ var TableTooltip = class {
6162
9169
  if (ref && ref.style.minWidth) td.style.minWidth = ref.style.minWidth;
6163
9170
  newRow.appendChild(td);
6164
9171
  }
6165
- if (position === "above") row.parentElement?.insertBefore(newRow, row);
6166
- else row.insertAdjacentElement("afterend", newRow);
9172
+ if (position === "above") refRow.parentElement?.insertBefore(newRow, refRow);
9173
+ else refRow.insertAdjacentElement("afterend", newRow);
6167
9174
  requestAnimationFrame(() => this._positionNear(this._activeTable));
6168
9175
  this.context.invoke("editor.afterCommand");
6169
9176
  }
6170
9177
  _addColumn(position) {
6171
- const cell = this._getCell();
6172
- if (!cell) return;
6173
- const table = cell.closest("table");
9178
+ const cells = this._getSelectedCells();
9179
+ if (!cells.length) return;
9180
+ const table = cells[0].closest("table");
6174
9181
  if (!table) return;
6175
- const visualColIdx = getVisualColIndex(cell);
9182
+ const colIndices = cells.map((c) => getVisualColIndex(c));
9183
+ const targetColIdx = position === "left" ? Math.min(...colIndices) : Math.max(...colIndices);
6176
9184
  const rows = Array.from(table.querySelectorAll("tr"));
6177
- const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r, visualColIdx) : getCellAfterVisualCol(r, visualColIdx));
9185
+ const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r, targetColIdx) : getCellAfterVisualCol(r, targetColIdx));
6178
9186
  const isHeaders = rows.map((r) => r.closest("thead") !== null);
6179
9187
  rows.forEach((r, i) => {
6180
9188
  r.insertBefore(createElement(isHeaders[i] ? "th" : "td", {}, ["\xA0"]), refs[i]);
@@ -6183,68 +9191,93 @@ var TableTooltip = class {
6183
9191
  this.context.invoke("editor.afterCommand");
6184
9192
  }
6185
9193
  _deleteRow() {
6186
- const cell = this._getCell();
6187
- if (!cell) return;
6188
- const row = cell.closest("tr");
6189
- const table = cell.closest("table");
6190
- if (!row || !table) return;
9194
+ const cells = this._getSelectedCells();
9195
+ if (!cells.length) return;
9196
+ const table = cells[0].closest("table");
9197
+ if (!table) return;
6191
9198
  const tbody = table.querySelector("tbody");
6192
- if ((tbody ? tbody.querySelectorAll("tr").length : table.querySelectorAll("tr").length) <= 1 && row.closest("tbody")) return;
9199
+ const totalBodyRows = tbody ? tbody.querySelectorAll("tr").length : table.querySelectorAll("tr").length;
9200
+ const selectedRows = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))];
9201
+ if (selectedRows.filter((r) => r.closest("tbody")).length >= totalBodyRows) return;
6193
9202
  this._activeCell = null;
6194
- row.parentElement?.removeChild(row);
9203
+ this._clearSelection();
9204
+ selectedRows.forEach((r) => r.parentElement?.removeChild(r));
6195
9205
  requestAnimationFrame(() => this._positionNear(this._activeTable));
6196
9206
  this.context.invoke("editor.afterCommand");
6197
9207
  }
6198
9208
  _deleteColumn() {
6199
- const cell = this._getCell();
6200
- if (!cell) return;
6201
- const table = cell.closest("table");
9209
+ const cells = this._getSelectedCells();
9210
+ if (!cells.length) return;
9211
+ const table = cells[0].closest("table");
6202
9212
  if (!table) return;
6203
- const row = cell.closest("tr");
6204
- if (row && row.cells.length <= 1) return;
6205
- const visualColIdx = getVisualColIndex(cell);
6206
- this._activeCell = null;
6207
- const rows = Array.from(table.querySelectorAll("tr"));
6208
- rows.map((r) => getCellAtVisualCol(r, visualColIdx)).forEach((c, i) => {
6209
- if (c) rows[i].removeChild(c);
9213
+ const tableRows = Array.from(table.querySelectorAll("tr"));
9214
+ if (tableRows[0] && tableRows[0].cells.length <= 1) return;
9215
+ const colIndices = [...new Set(cells.map((c) => getVisualColIndex(c)))];
9216
+ if (colIndices.length >= (tableRows[0]?.cells.length ?? 1)) return;
9217
+ const cellsToDelete = [];
9218
+ colIndices.forEach((colIdx) => {
9219
+ tableRows.forEach((r) => {
9220
+ const c = getCellAtVisualCol(r, colIdx);
9221
+ if (c) cellsToDelete.push(c);
9222
+ });
6210
9223
  });
9224
+ this._activeCell = null;
9225
+ this._clearSelection();
9226
+ cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
6211
9227
  requestAnimationFrame(() => this._positionNear(this._activeTable));
6212
9228
  this.context.invoke("editor.afterCommand");
6213
9229
  }
6214
9230
  _mergeCells() {
6215
9231
  const cell = this._getCell();
6216
9232
  if (!cell) return;
6217
- const sel = window.getSelection();
6218
- if (!sel || sel.rangeCount === 0) return;
6219
- const range = sel.getRangeAt(0);
6220
9233
  const table = cell.closest("table");
6221
9234
  if (!table) return;
6222
- const selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
6223
- try {
6224
- return range.intersectsNode(c);
6225
- } catch {
6226
- return false;
6227
- }
6228
- });
6229
- if (selected.length < 2) return;
6230
- const rows = [...new Set(selected.map((c) => c.closest("tr")))];
6231
- if (rows.length === 1) {
6232
- const row = rows[0];
6233
- const rowSelected = Array.from(row.cells).filter((c) => selected.includes(c));
6234
- if (rowSelected.length < 2) return;
6235
- const first = rowSelected[0];
6236
- first.colSpan = rowSelected.reduce((sum, c) => sum + (c.colSpan || 1), 0);
6237
- first.innerHTML = rowSelected.map((c) => c.innerHTML).join("");
6238
- rowSelected.slice(1).forEach((c) => row.removeChild(c));
6239
- } else {
6240
- if ([...new Set(selected.map((c) => getVisualColIndex(c)))].length !== 1) return;
6241
- const first = selected[0];
6242
- first.rowSpan = selected.reduce((sum, c) => sum + (c.rowSpan || 1), 0);
6243
- first.innerHTML = selected.map((c) => c.innerHTML).join("");
6244
- selected.slice(1).forEach((c) => {
6245
- if (c.closest("tr")) c.closest("tr").removeChild(c);
9235
+ let selected = this._getSelectedCells().filter((c) => table.contains(c));
9236
+ if (selected.length < 2) {
9237
+ const sel = window.getSelection();
9238
+ if (!sel || sel.rangeCount === 0) return;
9239
+ const range = sel.getRangeAt(0);
9240
+ selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
9241
+ try {
9242
+ return range.intersectsNode(c);
9243
+ } catch {
9244
+ return false;
9245
+ }
6246
9246
  });
9247
+ if (selected.length < 2) return;
9248
+ }
9249
+ const { gridMap, cellPos } = buildGridMap(table);
9250
+ let minR = Infinity, maxR = -Infinity, minC = Infinity, maxC = -Infinity;
9251
+ selected.forEach((c) => {
9252
+ const pos = cellPos.get(c);
9253
+ if (!pos) return;
9254
+ if (pos.r < minR) minR = pos.r;
9255
+ if (pos.r + pos.rs - 1 > maxR) maxR = pos.r + pos.rs - 1;
9256
+ if (pos.c < minC) minC = pos.c;
9257
+ if (pos.c + pos.cs - 1 > maxC) maxC = pos.c + pos.cs - 1;
9258
+ });
9259
+ if (minR === Infinity) return;
9260
+ const seen = /* @__PURE__ */ new Set();
9261
+ const rectCells = [];
9262
+ for (let r = minR; r <= maxR; r++) {
9263
+ const rowMap = gridMap[r];
9264
+ if (!rowMap) continue;
9265
+ for (let c = minC; c <= maxC; c++) {
9266
+ const tc = rowMap[c];
9267
+ if (tc && !seen.has(tc)) {
9268
+ seen.add(tc);
9269
+ rectCells.push(tc);
9270
+ }
9271
+ }
6247
9272
  }
9273
+ if (rectCells.length < 2) return;
9274
+ const first = rectCells[0];
9275
+ first.colSpan = maxC - minC + 1;
9276
+ first.rowSpan = maxR - minR + 1;
9277
+ first.style.verticalAlign = "middle";
9278
+ first.innerHTML = rectCells.map((c) => c.innerHTML).join("");
9279
+ rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
9280
+ this._clearSelection();
6248
9281
  this.context.invoke("editor.afterCommand");
6249
9282
  }
6250
9283
  _deleteTable() {
@@ -6254,6 +9287,57 @@ var TableTooltip = class {
6254
9287
  if (table.parentNode) table.parentNode.removeChild(table);
6255
9288
  this.context.invoke("editor.afterCommand");
6256
9289
  }
9290
+ _unmergeCells() {
9291
+ const cells = this._getSelectedCells();
9292
+ if (!cells.length) return;
9293
+ const table = cells[0].closest("table");
9294
+ if (!table) return;
9295
+ const mergedCells = cells.filter((c) => table.contains(c) && ((c.colSpan || 1) > 1 || (c.rowSpan || 1) > 1));
9296
+ if (!mergedCells.length) return;
9297
+ mergedCells.forEach((cell) => {
9298
+ if (table.contains(cell)) this._unmergeOne(cell, table);
9299
+ });
9300
+ this._clearSelection();
9301
+ requestAnimationFrame(() => this._positionNear(this._activeTable));
9302
+ this.context.invoke("editor.afterCommand");
9303
+ }
9304
+ /**
9305
+ * Split a single merged cell (colspan/rowspan > 1) back into individual cells.
9306
+ * New cells are empty (&nbsp;); the original cell retains its content.
9307
+ * @param {HTMLTableCellElement} cell
9308
+ * @param {HTMLTableElement} table
9309
+ */
9310
+ _unmergeOne(cell, table) {
9311
+ const cs = cell.colSpan || 1;
9312
+ const rs = cell.rowSpan || 1;
9313
+ if (cs === 1 && rs === 1) return;
9314
+ const { cellPos } = buildGridMap(table);
9315
+ const pos = cellPos.get(cell);
9316
+ if (!pos) return;
9317
+ const { r, c } = pos;
9318
+ const tableRows = Array.from(table.rows);
9319
+ const tag = cell.tagName.toLowerCase();
9320
+ cell.rowSpan = 1;
9321
+ cell.colSpan = 1;
9322
+ cell.style.verticalAlign = "";
9323
+ if (cs > 1) {
9324
+ const insertRef = cell.nextElementSibling;
9325
+ for (let dc = 1; dc < cs; dc++) tableRows[r].insertBefore(createElement(tag, {}, ["\xA0"]), insertRef);
9326
+ }
9327
+ for (let dr = 1; dr < rs; dr++) {
9328
+ const targetRow = tableRows[r + dr];
9329
+ if (!targetRow) continue;
9330
+ let ref = null;
9331
+ for (const tc of targetRow.cells) {
9332
+ const tp = cellPos.get(tc);
9333
+ if (tp && tp.c > c) {
9334
+ ref = tc;
9335
+ break;
9336
+ }
9337
+ }
9338
+ for (let dc = 0; dc < cs; dc++) targetRow.insertBefore(createElement(tag, {}, ["\xA0"]), ref);
9339
+ }
9340
+ }
6257
9341
  _buildSizePopover() {
6258
9342
  const popover = createElement("div", { class: "an-size-popover" });
6259
9343
  popover.style.display = "none";
@@ -6274,12 +9358,12 @@ var TableTooltip = class {
6274
9358
  type: "button",
6275
9359
  class: "an-btn"
6276
9360
  });
6277
- cancelBtn.textContent = "Cancel";
9361
+ cancelBtn.textContent = this.context.locale.tooltips.table.cancelBtn;
6278
9362
  const applyBtn = createElement("button", {
6279
9363
  type: "button",
6280
9364
  class: "an-btn an-btn-primary"
6281
9365
  });
6282
- applyBtn.textContent = "Apply";
9366
+ applyBtn.textContent = this.context.locale.tooltips.table.applyBtn;
6283
9367
  actionsEl.appendChild(cancelBtn);
6284
9368
  actionsEl.appendChild(applyBtn);
6285
9369
  popover.appendChild(titleEl);
@@ -6317,7 +9401,7 @@ var TableTooltip = class {
6317
9401
  if (!table) return;
6318
9402
  const firstCell = table.querySelector("td, th");
6319
9403
  const currentPx = firstCell ? parseInt(firstCell.style.borderWidth, 10) || parseInt(window.getComputedStyle(firstCell).borderWidth, 10) || 1 : 1;
6320
- this._sizeTitleEl.textContent = "Table Border Width (px)";
9404
+ this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
6321
9405
  this._sizeInputEl.min = "0";
6322
9406
  this._sizeInputEl.max = "10";
6323
9407
  this._sizeInputEl.value = currentPx;
@@ -6335,27 +9419,35 @@ var TableTooltip = class {
6335
9419
  };
6336
9420
  } else {
6337
9421
  const isCol = type === "col";
6338
- this._sizeTitleEl.textContent = isCol ? "Column Width (px)" : "Row Height (px)";
9422
+ const activeCells = this._getSelectedCells().filter((c) => {
9423
+ const t = c.closest("table");
9424
+ return t && t === cell.closest("table");
9425
+ });
9426
+ this._sizeTitleEl.textContent = isCol ? this.context.locale.tooltips.table.columnWidthPx : this.context.locale.tooltips.table.rowHeightPx;
6339
9427
  this._sizeInputEl.min = "1";
6340
9428
  this._sizeInputEl.max = "2000";
6341
9429
  this._sizeInputEl.value = isCol ? cell.offsetWidth || 120 : cell.closest("tr") ? cell.closest("tr").offsetHeight || 40 : 40;
6342
9430
  this._sizeApply = (val) => {
9431
+ const table = cell.closest("table");
9432
+ if (!table) return;
6343
9433
  if (isCol) {
6344
- const table = cell.closest("table");
6345
- const visualColIdx = getVisualColIndex(cell);
6346
- Array.from(table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, visualColIdx)).forEach((c) => {
6347
- if (c) {
6348
- c.style.width = `${val}px`;
6349
- c.style.minWidth = `${val}px`;
6350
- }
9434
+ const colIndices = [...new Set(activeCells.map((c) => getVisualColIndex(c)))];
9435
+ const tableRows = Array.from(table.querySelectorAll("tr"));
9436
+ colIndices.forEach((colIdx) => {
9437
+ tableRows.forEach((r) => {
9438
+ const c = getCellAtVisualCol(r, colIdx);
9439
+ if (c && (c.colSpan || 1) === 1) {
9440
+ c.style.width = `${val}px`;
9441
+ c.style.minWidth = `${val}px`;
9442
+ }
9443
+ });
6351
9444
  });
6352
- } else {
6353
- const row = cell.closest("tr");
6354
- if (row) for (const c of row.cells) {
9445
+ } else [...new Set(activeCells.map((c) => c.closest("tr")).filter(Boolean))].forEach((row) => {
9446
+ for (const c of row.cells) {
6355
9447
  c.style.height = `${val}px`;
6356
9448
  c.style.minHeight = `${val}px`;
6357
9449
  }
6358
- }
9450
+ });
6359
9451
  this.context.invoke("editor.afterCommand");
6360
9452
  };
6361
9453
  }
@@ -6431,20 +9523,21 @@ var CodeTooltip = class {
6431
9523
  this._el = null;
6432
9524
  }
6433
9525
  _buildTooltip() {
9526
+ const L = this.context.locale.tooltips.code;
6434
9527
  const el = createElement("div", {
6435
9528
  class: "an-link-tooltip an-code-tooltip",
6436
9529
  role: "toolbar",
6437
- "aria-label": "Code block actions"
9530
+ "aria-label": L.ariaLabel
6438
9531
  });
6439
9532
  el.style.display = "none";
6440
9533
  this._label = createElement("span", { class: "an-link-tooltip-url" });
6441
- this._label.textContent = "Code";
9534
+ this._label.textContent = L.label;
6442
9535
  el.appendChild(this._label);
6443
9536
  el.appendChild(this._sep());
6444
9537
  this._langSelect = createElement("select", {
6445
9538
  class: "an-code-lang-select",
6446
- title: "Syntax Language",
6447
- "aria-label": "Syntax language"
9539
+ title: L.syntaxLanguage,
9540
+ "aria-label": L.syntaxAriaLabel
6448
9541
  });
6449
9542
  [
6450
9543
  ["", "Plain text"],
@@ -6475,15 +9568,15 @@ var CodeTooltip = class {
6475
9568
  this._disposers.push(on(this._langSelect, "change", () => this._onLangChange()));
6476
9569
  el.appendChild(this._langSelect);
6477
9570
  el.appendChild(this._sep());
6478
- this._copyBtn = this._makeBtn(ICONS$1.copy, "Copy Code", () => this._copyCode());
9571
+ this._copyBtn = this._makeBtn(ICONS$1.copy, L.copyCode, () => this._copyCode());
6479
9572
  el.appendChild(this._copyBtn);
6480
9573
  el.appendChild(this._sep());
6481
- this._wrapBtn = this._makeBtn(ICONS$1.wrapOn, "Toggle Word Wrap", () => this._toggleWrap());
9574
+ this._wrapBtn = this._makeBtn(ICONS$1.wrapOn, L.toggleWordWrap, () => this._toggleWrap());
6482
9575
  el.appendChild(this._wrapBtn);
6483
9576
  el.appendChild(this._sep());
6484
- el.appendChild(this._makeBtn(ICONS$1.toParagraph, "Convert to Paragraph", () => this._toParagraph()));
9577
+ el.appendChild(this._makeBtn(ICONS$1.toParagraph, L.convertToParagraph, () => this._toParagraph()));
6485
9578
  el.appendChild(this._sep());
6486
- el.appendChild(this._makeBtn(ICONS$1.deleteCode, "Delete Code Block", () => this._delete(), true));
9579
+ el.appendChild(this._makeBtn(ICONS$1.deleteCode, L.deleteCodeBlock, () => this._delete(), true));
6487
9580
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => this._scheduleHide()));
6488
9581
  return el;
6489
9582
  }
@@ -6560,7 +9653,7 @@ var CodeTooltip = class {
6560
9653
  if (!this._activePre || !this._wrapBtn) return;
6561
9654
  const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") || window.getComputedStyle(this._activePre).whiteSpace === "pre-wrap";
6562
9655
  this._wrapBtn.classList.toggle("active", wrapped);
6563
- this._wrapBtn.title = wrapped ? "Disable Word Wrap" : "Enable Word Wrap";
9656
+ this._wrapBtn.title = wrapped ? this.context.locale.tooltips.code.disableWordWrap : this.context.locale.tooltips.code.enableWordWrap;
6564
9657
  }
6565
9658
  _syncLangSelect() {
6566
9659
  if (!this._activePre || !this._langSelect) return;
@@ -9090,27 +12183,28 @@ var EmojiDialog = class {
9090
12183
  this._open();
9091
12184
  }
9092
12185
  _buildDialog() {
12186
+ const L = this.context.locale.emojiDialog;
9093
12187
  const overlay = createElement("div", {
9094
12188
  class: "an-dialog-overlay",
9095
12189
  role: "dialog",
9096
12190
  "aria-modal": "true",
9097
- "aria-label": "Insert emoji"
12191
+ "aria-label": L.ariaLabel
9098
12192
  });
9099
12193
  const box = createElement("div", { class: "an-dialog-box an-emoji-box" });
9100
12194
  const titleRow = createElement("div", { class: "an-icon-title-row" });
9101
12195
  const title = createElement("h3", { class: "an-dialog-title" });
9102
- title.textContent = "Insert Emoji";
12196
+ title.textContent = L.title;
9103
12197
  const closeBtn = createElement("button", {
9104
12198
  type: "button",
9105
12199
  class: "an-icon-close",
9106
- "aria-label": "Close"
12200
+ "aria-label": L.close
9107
12201
  });
9108
12202
  closeBtn.innerHTML = "&times;";
9109
12203
  titleRow.append(title, closeBtn);
9110
12204
  const searchInput = createElement("input", {
9111
12205
  type: "search",
9112
12206
  class: "an-input an-icon-search",
9113
- placeholder: "Search emojis…",
12207
+ placeholder: L.searchPlaceholder,
9114
12208
  autocomplete: "off"
9115
12209
  });
9116
12210
  this._searchInput = searchInput;
@@ -9120,7 +12214,7 @@ var EmojiDialog = class {
9120
12214
  class: "an-icon-cat active",
9121
12215
  "data-cat": "all"
9122
12216
  });
9123
- allTab.textContent = "All";
12217
+ allTab.textContent = L.all;
9124
12218
  catBar.appendChild(allTab);
9125
12219
  EMOJI_CATS.forEach(({ id, label }) => {
9126
12220
  const tab = createElement("button", {
@@ -9128,7 +12222,7 @@ var EmojiDialog = class {
9128
12222
  class: "an-icon-cat",
9129
12223
  "data-cat": id
9130
12224
  });
9131
- tab.textContent = label;
12225
+ tab.textContent = L.categories && L.categories[id] || label;
9132
12226
  catBar.appendChild(tab);
9133
12227
  });
9134
12228
  this._catBar = catBar;
@@ -9151,7 +12245,7 @@ var EmojiDialog = class {
9151
12245
  type: "button",
9152
12246
  class: "an-btn"
9153
12247
  });
9154
- cancelBtn.textContent = "Cancel";
12248
+ cancelBtn.textContent = L.cancelBtn;
9155
12249
  btnRow.appendChild(cancelBtn);
9156
12250
  box.append(titleRow, searchInput, catBar, grid, btnRow);
9157
12251
  overlay.appendChild(box);
@@ -9210,7 +12304,13 @@ var EmojiDialog = class {
9210
12304
  range.selectNodeContents(editable);
9211
12305
  range.collapse(false);
9212
12306
  }
12307
+ const _sc = range.startContainer;
12308
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
9213
12309
  range.deleteContents();
12310
+ if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
12311
+ range.setStart(_tdAnchor, 0);
12312
+ range.collapse(true);
12313
+ }
9214
12314
  const textNode = document.createTextNode(char);
9215
12315
  range.insertNode(textNode);
9216
12316
  range.setStartAfter(textNode);
@@ -9556,27 +12656,28 @@ var IconDialog = class {
9556
12656
  this._open();
9557
12657
  }
9558
12658
  _buildDialog() {
12659
+ const L = this.context.locale.iconDialog;
9559
12660
  const overlay = createElement("div", {
9560
12661
  class: "an-dialog-overlay",
9561
12662
  role: "dialog",
9562
12663
  "aria-modal": "true",
9563
- "aria-label": "Insert FA icon"
12664
+ "aria-label": L.ariaLabel
9564
12665
  });
9565
12666
  const box = createElement("div", { class: "an-dialog-box an-icon-box" });
9566
12667
  const titleRow = createElement("div", { class: "an-icon-title-row" });
9567
12668
  const title = createElement("h3", { class: "an-dialog-title" });
9568
- title.textContent = "Insert FA Icon";
12669
+ title.textContent = L.title;
9569
12670
  const closeBtn = createElement("button", {
9570
12671
  type: "button",
9571
12672
  class: "an-icon-close",
9572
- "aria-label": "Close"
12673
+ "aria-label": L.close
9573
12674
  });
9574
12675
  closeBtn.innerHTML = "&times;";
9575
12676
  titleRow.append(title, closeBtn);
9576
12677
  const searchInput = createElement("input", {
9577
12678
  type: "search",
9578
12679
  class: "an-input an-icon-search",
9579
- placeholder: "Search icons…",
12680
+ placeholder: L.searchPlaceholder,
9580
12681
  autocomplete: "off"
9581
12682
  });
9582
12683
  this._searchInput = searchInput;
@@ -9586,7 +12687,7 @@ var IconDialog = class {
9586
12687
  class: "an-icon-cat active",
9587
12688
  "data-cat": "all"
9588
12689
  });
9589
- allTab.textContent = "All";
12690
+ allTab.textContent = L.all;
9590
12691
  catBar.appendChild(allTab);
9591
12692
  ICON_CATEGORIES.forEach(({ id, label }) => {
9592
12693
  const tab = createElement("button", {
@@ -9594,7 +12695,7 @@ var IconDialog = class {
9594
12695
  class: "an-icon-cat",
9595
12696
  "data-cat": id
9596
12697
  });
9597
- tab.textContent = label;
12698
+ tab.textContent = L.categories && L.categories[id] || label;
9598
12699
  catBar.appendChild(tab);
9599
12700
  });
9600
12701
  this._catBar = catBar;
@@ -9619,7 +12720,7 @@ var IconDialog = class {
9619
12720
  this._grid = grid;
9620
12721
  const optRow = createElement("div", { class: "an-icon-options" });
9621
12722
  const styleLabel = createElement("label", { class: "an-label" });
9622
- styleLabel.textContent = "Style";
12723
+ styleLabel.textContent = L.style;
9623
12724
  const styleSelect = createElement("select", { class: "an-input an-icon-option-select" });
9624
12725
  [
9625
12726
  ["fa-solid", "Solid"],
@@ -9633,7 +12734,7 @@ var IconDialog = class {
9633
12734
  styleSelect.value = "fa-solid";
9634
12735
  this._styleSelect = styleSelect;
9635
12736
  const sizeLabel = createElement("label", { class: "an-label" });
9636
- sizeLabel.textContent = "Size";
12737
+ sizeLabel.textContent = L.size;
9637
12738
  const sizeSelect = createElement("select", { class: "an-input an-icon-option-select" });
9638
12739
  [
9639
12740
  ["", "Inherit"],
@@ -9651,7 +12752,7 @@ var IconDialog = class {
9651
12752
  });
9652
12753
  this._sizeSelect = sizeSelect;
9653
12754
  const colorLabel = createElement("label", { class: "an-label" });
9654
- colorLabel.textContent = "Color";
12755
+ colorLabel.textContent = L.color;
9655
12756
  const colorInput = createElement("input", {
9656
12757
  type: "color",
9657
12758
  class: "an-icon-color",
@@ -9664,11 +12765,11 @@ var IconDialog = class {
9664
12765
  checked: ""
9665
12766
  });
9666
12767
  this._useColorCb = useColorCb;
9667
- useColorLabel.append(useColorCb, document.createTextNode(" Use color"));
12768
+ useColorLabel.append(useColorCb, document.createTextNode(L.useColor));
9668
12769
  optRow.append(styleLabel, styleSelect, sizeLabel, sizeSelect, colorLabel, colorInput, useColorLabel);
9669
12770
  const preview = createElement("div", { class: "an-icon-preview" });
9670
12771
  const previewHint = createElement("span", { class: "an-icon-preview-hint" });
9671
- previewHint.textContent = "Select an icon";
12772
+ previewHint.textContent = L.selectHint;
9672
12773
  preview.appendChild(previewHint);
9673
12774
  this._preview = preview;
9674
12775
  const btnRow = createElement("div", { class: "an-dialog-actions" });
@@ -9677,12 +12778,12 @@ var IconDialog = class {
9677
12778
  class: "an-btn an-btn-primary",
9678
12779
  disabled: ""
9679
12780
  });
9680
- insertBtn.textContent = "Insert FA Icon";
12781
+ insertBtn.textContent = L.insertBtn;
9681
12782
  const cancelBtn = createElement("button", {
9682
12783
  type: "button",
9683
12784
  class: "an-btn"
9684
12785
  });
9685
- cancelBtn.textContent = "Cancel";
12786
+ cancelBtn.textContent = L.cancelBtn;
9686
12787
  btnRow.append(insertBtn, cancelBtn);
9687
12788
  this._insertBtn = insertBtn;
9688
12789
  box.append(titleRow, searchInput, catBar, grid, optRow, preview, btnRow);
@@ -9779,7 +12880,13 @@ var IconDialog = class {
9779
12880
  range.selectNodeContents(editable);
9780
12881
  range.collapse(false);
9781
12882
  }
12883
+ const _sc = range.startContainer;
12884
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
9782
12885
  range.deleteContents();
12886
+ if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
12887
+ range.setStart(_tdAnchor, 0);
12888
+ range.collapse(true);
12889
+ }
9783
12890
  range.insertNode(iconEl);
9784
12891
  let caretTextNode = iconEl.nextSibling;
9785
12892
  if (!caretTextNode || caretTextNode.nodeType !== Node.TEXT_NODE) {
@@ -9877,9 +12984,11 @@ var COLOR_PRESETS = [
9877
12984
  ];
9878
12985
  function makeColorSubItems(colorType) {
9879
12986
  const label = colorType === "foreColor" ? "Text Color" : "Highlight Color";
12987
+ const localeKey = colorType === "foreColor" ? "textColor" : "highlightColor";
9880
12988
  return () => [{
9881
12989
  back: true,
9882
12990
  label,
12991
+ localeKey,
9883
12992
  navigate: () => defaultItems
9884
12993
  }, {
9885
12994
  colorPalette: true,
@@ -10076,7 +13185,8 @@ var ContextMenu = class {
10076
13185
  });
10077
13186
  iconSpan.innerHTML = ICONS.back;
10078
13187
  backBtn.appendChild(iconSpan);
10079
- backBtn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || "Back"]));
13188
+ const backLabel = it.localeKey && this.context.locale.contextMenu[it.localeKey] || it.label || this.context.locale.contextMenu.back || "Back";
13189
+ backBtn.appendChild(createElement("span", { class: "an-context-label" }, [backLabel]));
10080
13190
  const off = on(backBtn, "click", (e) => {
10081
13191
  e.stopPropagation();
10082
13192
  const curLeft = parseFloat(this.el.style.left);
@@ -10114,7 +13224,7 @@ var ContextMenu = class {
10114
13224
  iconSpan.innerHTML = it.icon;
10115
13225
  btn.appendChild(iconSpan);
10116
13226
  }
10117
- btn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || it.name]));
13227
+ btn.appendChild(createElement("span", { class: "an-context-label" }, [this.context.locale.contextMenu[it.name] || it.label || it.name]));
10118
13228
  const chevron = createElement("span", {
10119
13229
  class: "an-context-chevron",
10120
13230
  "aria-hidden": "true"
@@ -10152,9 +13262,9 @@ var ContextMenu = class {
10152
13262
  if (it.colorType === "hiliteColor") {
10153
13263
  const noColor = createElement("div", {
10154
13264
  class: "an-context-color-swatch an-context-color-none",
10155
- title: "No highlight",
13265
+ title: this.context.locale.contextMenu.noHighlight || "No highlight",
10156
13266
  role: "button",
10157
- "aria-label": "No highlight"
13267
+ "aria-label": this.context.locale.contextMenu.noHighlight || "No highlight"
10158
13268
  });
10159
13269
  noColor.innerHTML = ICONS.noColor;
10160
13270
  const offNo = on(noColor, "click", (e) => {
@@ -10169,10 +13279,10 @@ var ContextMenu = class {
10169
13279
  const colorInput = createElement("input", {
10170
13280
  type: "color",
10171
13281
  value: it.colorType === "foreColor" ? "#000000" : "#ffff00",
10172
- title: "Custom color",
10173
- "aria-label": "Custom color"
13282
+ title: this.context.locale.contextMenu.customColor || "Custom color",
13283
+ "aria-label": this.context.locale.contextMenu.customColor || "Custom color"
10174
13284
  });
10175
- const customLabel = createElement("span", {}, ["Custom…"]);
13285
+ const customLabel = createElement("span", {}, [this.context.locale.contextMenu.customColorLabel || "Custom…"]);
10176
13286
  const offCustom = on(colorInput, "change", () => this._applyColor(it.colorType, colorInput.value));
10177
13287
  this._menuDisposers.push(offCustom);
10178
13288
  customRow.appendChild(colorInput);
@@ -10196,7 +13306,7 @@ var ContextMenu = class {
10196
13306
  iconSpan.innerHTML = it.icon;
10197
13307
  headerBtn.appendChild(iconSpan);
10198
13308
  }
10199
- headerBtn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || "Insert Table"]));
13309
+ headerBtn.appendChild(createElement("span", { class: "an-context-label" }, [this.context.locale.contextMenu.table || it.label || "Insert Table"]));
10200
13310
  const chevron = createElement("span", {
10201
13311
  class: "an-context-chevron",
10202
13312
  "aria-hidden": "true"
@@ -10225,7 +13335,7 @@ var ContextMenu = class {
10225
13335
  cells.forEach((cell) => {
10226
13336
  cell.classList.toggle("active", +cell.dataset.row <= rows && +cell.dataset.col <= cols);
10227
13337
  });
10228
- labelEl.textContent = rows && cols ? `${cols} × ${rows}` : "Insert Table";
13338
+ labelEl.textContent = rows && cols ? `${cols} × ${rows}` : this.context.locale.contextMenu.table || "Insert Table";
10229
13339
  };
10230
13340
  panel.appendChild(gridEl);
10231
13341
  panel.appendChild(labelEl);
@@ -10279,7 +13389,7 @@ var ContextMenu = class {
10279
13389
  iconSpan.innerHTML = it.icon;
10280
13390
  btn.appendChild(iconSpan);
10281
13391
  }
10282
- btn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || it.name]));
13392
+ btn.appendChild(createElement("span", { class: "an-context-label" }, [this.context.locale.contextMenu[it.name] || it.label || it.name]));
10283
13393
  const off = on(btn, "click", (e) => {
10284
13394
  e.stopPropagation();
10285
13395
  this.hide();
@@ -10593,22 +13703,22 @@ var ShortcutsDialog = class {
10593
13703
  class: "an-dialog-overlay",
10594
13704
  role: "dialog",
10595
13705
  "aria-modal": "true",
10596
- "aria-label": "Keyboard Shortcuts"
13706
+ "aria-label": this.context.locale.shortcutsDialog.ariaLabel
10597
13707
  });
10598
13708
  const box = createElement("div", { class: "an-dialog-box an-shortcuts-box" });
10599
13709
  const titleRow = createElement("div", { class: "an-icon-title-row" });
10600
13710
  const title = createElement("h3", { class: "an-dialog-title" });
10601
- title.textContent = "Keyboard Shortcuts";
13711
+ title.textContent = this.context.locale.shortcutsDialog.title;
10602
13712
  const closeBtn = createElement("button", {
10603
13713
  type: "button",
10604
13714
  class: "an-icon-close",
10605
- "aria-label": "Close"
13715
+ "aria-label": this.context.locale.shortcutsDialog.close
10606
13716
  });
10607
13717
  closeBtn.textContent = "×";
10608
13718
  this._closeBtn = closeBtn;
10609
13719
  titleRow.append(title, closeBtn);
10610
13720
  box.appendChild(titleRow);
10611
- SHORTCUTS.forEach(({ category, items }) => {
13721
+ (this.context.locale.shortcutsDialog.shortcuts || SHORTCUTS).forEach(({ category, items }) => {
10612
13722
  const catEl = createElement("div", { class: "an-shortcuts-cat" });
10613
13723
  catEl.textContent = category;
10614
13724
  box.appendChild(catEl);
@@ -10733,25 +13843,26 @@ var FindReplace = class {
10733
13843
  const isReplace = this._mode === "replace";
10734
13844
  if (replaceRow) replaceRow.style.display = isReplace ? "" : "none";
10735
13845
  if (replaceActions) replaceActions.style.display = isReplace ? "" : "none";
10736
- if (title) title.textContent = isReplace ? "Find & Replace" : "Find";
13846
+ if (title) title.textContent = isReplace ? this.context.locale.findReplace.findReplaceTitle : this.context.locale.findReplace.findTitle;
10737
13847
  }
10738
13848
  _buildDialog() {
13849
+ const L = this.context.locale.findReplace;
10739
13850
  const overlay = createElement("div", {
10740
13851
  class: "an-dialog-overlay an-fr-dialog",
10741
13852
  role: "dialog",
10742
13853
  "aria-modal": "true",
10743
- "aria-label": "Find and Replace"
13854
+ "aria-label": L.findReplaceTitle
10744
13855
  });
10745
13856
  const box = createElement("div", { class: "an-dialog-box" });
10746
13857
  const titleRow = createElement("div", { class: "an-icon-title-row" });
10747
13858
  const title = createElement("h3", { class: "an-dialog-title" });
10748
- title.textContent = "Find";
13859
+ title.textContent = L.findTitle;
10749
13860
  const closeBtn = createElement("button", {
10750
13861
  type: "button",
10751
13862
  class: "an-icon-close",
10752
13863
  "aria-label": "Close"
10753
13864
  });
10754
- closeBtn.textContent = "×";
13865
+ closeBtn.textContent = L.close;
10755
13866
  this._closeBtn = closeBtn;
10756
13867
  titleRow.append(title, closeBtn);
10757
13868
  box.appendChild(titleRow);
@@ -10759,8 +13870,8 @@ var FindReplace = class {
10759
13870
  const findInput = createElement("input", {
10760
13871
  type: "text",
10761
13872
  class: "an-input",
10762
- placeholder: "Find…",
10763
- "aria-label": "Search text"
13873
+ placeholder: L.findPlaceholder,
13874
+ "aria-label": L.searchAriaLabel
10764
13875
  });
10765
13876
  this._findInput = findInput;
10766
13877
  findRow.appendChild(findInput);
@@ -10772,7 +13883,7 @@ var FindReplace = class {
10772
13883
  "aria-label": "Case sensitive"
10773
13884
  });
10774
13885
  this._caseCheckbox = caseCheckbox;
10775
- caseLabel.append(caseCheckbox, document.createTextNode("\xA0Case sensitive"));
13886
+ caseLabel.append(caseCheckbox, document.createTextNode(L.caseSensitive));
10776
13887
  const counter = createElement("span", { class: "an-fr-counter" });
10777
13888
  this._counterEl = counter;
10778
13889
  optRow.append(caseLabel, counter);
@@ -10782,12 +13893,12 @@ var FindReplace = class {
10782
13893
  type: "button",
10783
13894
  class: "an-btn"
10784
13895
  });
10785
- prevBtn.textContent = "← Prev";
13896
+ prevBtn.textContent = L.prevBtn;
10786
13897
  const nextBtn = createElement("button", {
10787
13898
  type: "button",
10788
13899
  class: "an-btn an-btn-primary"
10789
13900
  });
10790
- nextBtn.textContent = "Next →";
13901
+ nextBtn.textContent = L.nextBtn;
10791
13902
  findActions.append(prevBtn, nextBtn);
10792
13903
  box.appendChild(findActions);
10793
13904
  const replaceRow = createElement("div", { class: "an-fr-replace-row" });
@@ -10795,8 +13906,8 @@ var FindReplace = class {
10795
13906
  const replaceInput = createElement("input", {
10796
13907
  type: "text",
10797
13908
  class: "an-input",
10798
- placeholder: "Replace with…",
10799
- "aria-label": "Replace with"
13909
+ placeholder: L.replacePlaceholder,
13910
+ "aria-label": L.replaceAriaLabel
10800
13911
  });
10801
13912
  this._replaceInput = replaceInput;
10802
13913
  replaceRow.appendChild(replaceInput);
@@ -10807,12 +13918,12 @@ var FindReplace = class {
10807
13918
  type: "button",
10808
13919
  class: "an-btn"
10809
13920
  });
10810
- replaceBtn.textContent = "Replace";
13921
+ replaceBtn.textContent = L.replaceBtn;
10811
13922
  const replaceAllBtn = createElement("button", {
10812
13923
  type: "button",
10813
13924
  class: "an-btn an-btn-primary"
10814
13925
  });
10815
- replaceAllBtn.textContent = "Replace All";
13926
+ replaceAllBtn.textContent = L.replaceAllBtn;
10816
13927
  replaceActions.append(replaceBtn, replaceAllBtn);
10817
13928
  box.appendChild(replaceActions);
10818
13929
  overlay.appendChild(box);
@@ -11473,6 +14584,8 @@ var Context = class {
11473
14584
  constructor(targetEl, userOptions = {}) {
11474
14585
  this.targetEl = targetEl;
11475
14586
  this.options = mergeDeep(defaultOptions, userOptions);
14587
+ /** @type {import('./i18n/index.js').AsnLocale} */
14588
+ this.locale = resolveLocale(this.options.lang);
11476
14589
  /** @type {{ container: HTMLElement, editable: HTMLElement, toolbar?: HTMLElement, statusbar?: HTMLElement }} */
11477
14590
  this.layoutInfo = {};
11478
14591
  /** @type {Map<string, Function[]>} */
@@ -11982,7 +15095,7 @@ var AutumnNote = {
11982
15095
  registerModule(name, ModuleClass) {
11983
15096
  _customModules.set(name, ModuleClass);
11984
15097
  },
11985
- version: "1.0.4"
15098
+ version: "1.0.9"
11986
15099
  };
11987
15100
  /**
11988
15101
  * @param {string|Element|NodeList|Element[]} selector
@@ -11995,6 +15108,6 @@ function resolveElements(selector) {
11995
15108
  return [];
11996
15109
  }
11997
15110
  //#endregion
11998
- export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, remove, removeFormatBtn, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
15111
+ export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, locales, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, remove, removeFormatBtn, resolveLocale, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
11999
15112
 
12000
15113
  //# sourceMappingURL=autumnnote.es.js.map