autumnnote 1.0.8 → 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.
@@ -63,7 +63,8 @@ export class LinkTooltip {
63
63
  // ---------------------------------------------------------------------------
64
64
 
65
65
  _buildTooltip() {
66
- const el = createElement('div', { class: 'an-link-tooltip', role: 'toolbar', 'aria-label': 'Link actions' });
66
+ const L = this.context.locale.tooltips.link;
67
+ const el = createElement('div', { class: 'an-link-tooltip', role: 'toolbar', 'aria-label': L.ariaLabel });
67
68
  el.style.display = 'none';
68
69
 
69
70
  // URL preview text (truncated)
@@ -74,10 +75,10 @@ export class LinkTooltip {
74
75
  el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
75
76
 
76
77
  // Action buttons
77
- this._openBtn = this._makeBtn(ICONS.open, 'Open link', () => this._openLink());
78
- this._copyBtn = this._makeBtn(ICONS.copy, 'Copy URL', () => this._copyLink());
79
- this._editBtn = this._makeBtn(ICONS.edit, 'Edit link', () => this._editLink());
80
- this._unlinkBtn = this._makeBtn(ICONS.unlink, 'Remove link', () => this._unlink());
78
+ this._openBtn = this._makeBtn(ICONS.open, L.openLink, () => this._openLink());
79
+ this._copyBtn = this._makeBtn(ICONS.copy, L.copyUrl, () => this._copyLink());
80
+ this._editBtn = this._makeBtn(ICONS.edit, L.editLink, () => this._editLink());
81
+ this._unlinkBtn = this._makeBtn(ICONS.unlink, L.removeLink, () => this._unlink());
81
82
 
82
83
  el.appendChild(this._openBtn);
83
84
  el.appendChild(this._copyBtn);
@@ -93,7 +93,7 @@ export class ShortcutsDialog {
93
93
  class: 'an-dialog-overlay',
94
94
  role: 'dialog',
95
95
  'aria-modal': 'true',
96
- 'aria-label': 'Keyboard Shortcuts',
96
+ 'aria-label': this.context.locale.shortcutsDialog.ariaLabel,
97
97
  });
98
98
 
99
99
  const box = createElement('div', { class: 'an-dialog-box an-shortcuts-box' });
@@ -101,18 +101,19 @@ export class ShortcutsDialog {
101
101
  // Title row with close button
102
102
  const titleRow = createElement('div', { class: 'an-icon-title-row' });
103
103
  const title = createElement('h3', { class: 'an-dialog-title' });
104
- title.textContent = 'Keyboard Shortcuts';
104
+ title.textContent = this.context.locale.shortcutsDialog.title;
105
105
  const closeBtn = createElement('button', {
106
106
  type: 'button',
107
107
  class: 'an-icon-close',
108
- 'aria-label': 'Close',
108
+ 'aria-label': this.context.locale.shortcutsDialog.close,
109
109
  });
110
110
  closeBtn.textContent = '×';
111
111
  this._closeBtn = closeBtn;
112
112
  titleRow.append(title, closeBtn);
113
113
  box.appendChild(titleRow);
114
114
 
115
- SHORTCUTS.forEach(({ category, items }) => {
115
+ const shortcuts = this.context.locale.shortcutsDialog.shortcuts || SHORTCUTS;
116
+ shortcuts.forEach(({ category, items }) => {
116
117
  const catEl = createElement('div', { class: 'an-shortcuts-cat' });
117
118
  catEl.textContent = category;
118
119
  box.appendChild(catEl);
@@ -82,7 +82,7 @@ export class Statusbar {
82
82
  if (this.options.resizable !== false) {
83
83
  const handle = createElement('div', {
84
84
  class: 'an-resize-handle',
85
- title: 'Resize editor',
85
+ title: this.context.locale.statusbar.resizeHandle,
86
86
  'aria-hidden': 'true',
87
87
  });
88
88
  this._bindResize(handle);
@@ -220,12 +220,13 @@ export class Statusbar {
220
220
  const maxWords = this.options.maxWords || 0;
221
221
  const maxChars = this.options.maxChars || 0;
222
222
 
223
+ const LS = this.context.locale.statusbar;
223
224
  this._wordCountEl.textContent = maxWords
224
- ? `Words: ${words}/${maxWords}`
225
- : `Words: ${words}`;
225
+ ? LS.wordsLimit(words, maxWords)
226
+ : LS.words(words);
226
227
  this._charCountEl.textContent = maxChars
227
- ? `Chars: ${chars}/${maxChars}`
228
- : `Chars: ${chars}`;
228
+ ? LS.charsLimit(chars, maxChars)
229
+ : LS.chars(chars);
229
230
 
230
231
  // Apply warning / exceeded styles
231
232
  _applyLimitClass(this._wordCountEl, words, maxWords);
@@ -376,55 +376,56 @@ export class TableTooltip {
376
376
  // ---------------------------------------------------------------------------
377
377
 
378
378
  _buildTooltip() {
379
+ const L = this.context.locale.tooltips.table;
379
380
  const el = createElement('div', {
380
381
  class: 'an-link-tooltip an-table-tooltip',
381
382
  role: 'toolbar',
382
- 'aria-label': 'Table actions',
383
+ 'aria-label': L.ariaLabel,
383
384
  });
384
385
  el.style.display = 'none';
385
386
 
386
387
  // Label
387
388
  this._label = createElement('span', { class: 'an-link-tooltip-url' });
388
- this._label.textContent = 'Table';
389
+ this._label.textContent = L.label;
389
390
  el.appendChild(this._label);
390
391
 
391
392
  el.appendChild(this._sep());
392
393
 
393
394
  // Select-cells toggle
394
- this._selectBtn = this._makeBtn(ICONS.selectCells, 'Select Cells', () => this._toggleSelectMode());
395
+ this._selectBtn = this._makeBtn(ICONS.selectCells, L.selectCells, () => this._toggleSelectMode());
395
396
  el.appendChild(this._selectBtn);
396
397
 
397
398
  el.appendChild(this._sep());
398
399
 
399
400
  // Row operations
400
- el.appendChild(this._makeBtn(ICONS.rowAbove, 'Add Row Above', () => this._addRow('above')));
401
- el.appendChild(this._makeBtn(ICONS.rowBelow, 'Add Row Below', () => this._addRow('below')));
402
- el.appendChild(this._makeBtn(ICONS.deleteRow, 'Delete Row', () => this._deleteRow()));
401
+ el.appendChild(this._makeBtn(ICONS.rowAbove, L.addRowAbove, () => this._addRow('above')));
402
+ el.appendChild(this._makeBtn(ICONS.rowBelow, L.addRowBelow, () => this._addRow('below')));
403
+ el.appendChild(this._makeBtn(ICONS.deleteRow, L.deleteRow, () => this._deleteRow()));
403
404
 
404
405
  el.appendChild(this._sep());
405
406
 
406
407
  // Column operations
407
- el.appendChild(this._makeBtn(ICONS.colLeft, 'Add Column Left', () => this._addColumn('left')));
408
- el.appendChild(this._makeBtn(ICONS.colRight, 'Add Column Right', () => this._addColumn('right')));
409
- el.appendChild(this._makeBtn(ICONS.deleteCol, 'Delete Column', () => this._deleteColumn()));
408
+ el.appendChild(this._makeBtn(ICONS.colLeft, L.addColumnLeft, () => this._addColumn('left')));
409
+ el.appendChild(this._makeBtn(ICONS.colRight, L.addColumnRight, () => this._addColumn('right')));
410
+ el.appendChild(this._makeBtn(ICONS.deleteCol, L.deleteColumn, () => this._deleteColumn()));
410
411
 
411
412
  el.appendChild(this._sep());
412
413
 
413
414
  // Merge cells
414
- el.appendChild(this._makeBtn(ICONS.mergeCells, 'Merge Cells', () => this._mergeCells()));
415
- el.appendChild(this._makeBtn(ICONS.unmergeCells, 'Unmerge Cells', () => this._unmergeCells()));
415
+ el.appendChild(this._makeBtn(ICONS.mergeCells, L.mergeCells, () => this._mergeCells()));
416
+ el.appendChild(this._makeBtn(ICONS.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
416
417
 
417
418
  el.appendChild(this._sep());
418
419
 
419
420
  // Resize
420
- el.appendChild(this._makeBtn(ICONS.colWidth, 'Column Width', () => this._openSizePopover('col')));
421
- el.appendChild(this._makeBtn(ICONS.rowHeight, 'Row Height', () => this._openSizePopover('row')));
422
- el.appendChild(this._makeBtn(ICONS.tableBorder,'Table Border Width',() => this._openSizePopover('border')));
421
+ el.appendChild(this._makeBtn(ICONS.colWidth, L.columnWidth, () => this._openSizePopover('col')));
422
+ el.appendChild(this._makeBtn(ICONS.rowHeight, L.rowHeight, () => this._openSizePopover('row')));
423
+ el.appendChild(this._makeBtn(ICONS.tableBorder,L.tableBorderWidth, () => this._openSizePopover('border')));
423
424
 
424
425
  el.appendChild(this._sep());
425
426
 
426
427
  // Delete table (danger)
427
- el.appendChild(this._makeBtn(ICONS.deleteTable, 'Delete Table', () => this._deleteTable(), true));
428
+ el.appendChild(this._makeBtn(ICONS.deleteTable, L.deleteTable, () => this._deleteTable(), true));
428
429
 
429
430
  // Keep tooltip alive while hovering.
430
431
  // Don't schedule hide on mouseleave when the size popover is open —
@@ -909,9 +910,9 @@ export class TableTooltip {
909
910
 
910
911
  const actionsEl = createElement('div', { class: 'an-size-popover-actions' });
911
912
  const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
912
- cancelBtn.textContent = 'Cancel';
913
+ cancelBtn.textContent = this.context.locale.tooltips.table.cancelBtn;
913
914
  const applyBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
914
- applyBtn.textContent = 'Apply';
915
+ applyBtn.textContent = this.context.locale.tooltips.table.applyBtn;
915
916
  actionsEl.appendChild(cancelBtn);
916
917
  actionsEl.appendChild(applyBtn);
917
918
 
@@ -960,7 +961,7 @@ export class TableTooltip {
960
961
  ? (parseInt(firstCell.style.borderWidth, 10) ||
961
962
  parseInt(window.getComputedStyle(firstCell).borderWidth, 10) || 1)
962
963
  : 1;
963
- this._sizeTitleEl.textContent = 'Table Border Width (px)';
964
+ this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
964
965
  this._sizeInputEl.min = '0';
965
966
  this._sizeInputEl.max = '10';
966
967
  this._sizeInputEl.value = currentPx;
@@ -979,7 +980,9 @@ export class TableTooltip {
979
980
  const t = c.closest('table');
980
981
  return t && t === cell.closest('table');
981
982
  });
982
- this._sizeTitleEl.textContent = isCol ? 'Column Width (px)' : 'Row Height (px)';
983
+ this._sizeTitleEl.textContent = isCol
984
+ ? this.context.locale.tooltips.table.columnWidthPx
985
+ : this.context.locale.tooltips.table.rowHeightPx;
983
986
  this._sizeInputEl.min = '1';
984
987
  this._sizeInputEl.max = '2000';
985
988
  this._sizeInputEl.value = isCol
@@ -182,9 +182,9 @@ export class Toolbar {
182
182
  const btn = createElement('button', {
183
183
  type: 'button',
184
184
  class: baseClass,
185
- title: def.tooltip || '',
185
+ title: this.context.locale.toolbar[def.name] || def.tooltip || '',
186
186
  'data-btn': def.name,
187
- 'aria-label': def.tooltip || def.name,
187
+ 'aria-label': this.context.locale.toolbar[def.name] || def.tooltip || def.name,
188
188
  'aria-haspopup': 'true',
189
189
  'aria-expanded': 'false',
190
190
  });
@@ -206,7 +206,7 @@ export class Toolbar {
206
206
  });
207
207
  const grid = createElement('div', { class: 'an-table-grid' });
208
208
  const label = createElement('div', { class: 'an-table-label' });
209
- label.textContent = 'Insert Table';
209
+ label.textContent = this.context.locale.toolbar.insertTableLabel || 'Insert Table';
210
210
 
211
211
  const cells = [];
212
212
  for (let r = 1; r <= ROWS; r++) {
@@ -232,7 +232,7 @@ export class Toolbar {
232
232
  const c = +cell.getAttribute('data-col');
233
233
  cell.classList.toggle('active', r <= rows && c <= cols);
234
234
  });
235
- label.textContent = (rows && cols) ? `${rows} × ${cols}` : 'Insert Table';
235
+ label.textContent = (rows && cols) ? `${rows} × ${cols}` : (this.context.locale.toolbar.insertTableLabel || 'Insert Table');
236
236
  };
237
237
 
238
238
  const openPopup = () => {
@@ -307,9 +307,9 @@ export class Toolbar {
307
307
  const applyBtn = createElement('button', {
308
308
  type: 'button',
309
309
  class: `${baseClass} an-color-btn`,
310
- title: def.tooltip || '',
310
+ title: this.context.locale.toolbar[def.name] || def.tooltip || '',
311
311
  'data-btn': def.name,
312
- 'aria-label': def.tooltip || def.name,
312
+ 'aria-label': this.context.locale.toolbar[def.name] || def.tooltip || def.name,
313
313
  });
314
314
 
315
315
  const S = 'stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';
@@ -326,7 +326,9 @@ export class Toolbar {
326
326
  const arrowBtn = createElement('button', {
327
327
  type: 'button',
328
328
  class: `${baseClass} an-color-arrow`,
329
- title: `Choose ${def.name === 'foreColor' ? 'text' : 'highlight'} color`,
329
+ title: def.name === 'foreColor'
330
+ ? (this.context.locale.toolbar.chooseTextColor || 'Choose text color')
331
+ : (this.context.locale.toolbar.chooseHighlightColor || 'Choose highlight color'),
330
332
  'aria-haspopup': 'true',
331
333
  'aria-expanded': 'false',
332
334
  });
@@ -346,8 +348,8 @@ export class Toolbar {
346
348
  });
347
349
 
348
350
  const customRow = createElement('div', { class: 'an-color-custom' });
349
- const colorInput = createElement('input', { type: 'color', value: currentColor, title: 'Custom color' });
350
- const customLabel = createElement('span', {}, ['Custom color']);
351
+ const colorInput = createElement('input', { type: 'color', value: currentColor, title: this.context.locale.toolbar.customColor || 'Custom color' });
352
+ const customLabel = createElement('span', {}, [this.context.locale.toolbar.customColor || 'Custom color']);
351
353
  customRow.appendChild(colorInput);
352
354
  customRow.appendChild(customLabel);
353
355
 
@@ -491,19 +493,23 @@ export class Toolbar {
491
493
  const cls = def.selectClass ? `an-select ${def.selectClass}` : 'an-select';
492
494
  const select = createElement('select', {
493
495
  class: cls,
494
- title: def.tooltip || '',
496
+ title: this.context.locale.toolbar[def.name] || def.tooltip || '',
495
497
  'data-btn': def.name,
496
- 'aria-label': def.tooltip || def.name,
498
+ 'aria-label': this.context.locale.toolbar[def.name] || def.tooltip || def.name,
497
499
  });
498
500
 
499
501
  // Blank "placeholder" option (non-selectable header)
500
- const placeholderText = def.placeholder || 'Font';
502
+ const placeholderText = this.context.locale.toolbar[def.name + 'Placeholder'] || def.placeholder || 'Font';
501
503
  const placeholder = createElement('option', { value: '', disabled: '', hidden: '' }, [placeholderText]);
502
504
  select.appendChild(placeholder);
503
505
 
504
506
  items.forEach((item) => {
505
507
  const value = (typeof item === 'object') ? item.value : item;
506
- const label = (typeof item === 'object') ? item.label : item;
508
+ const label = (typeof item === 'object')
509
+ ? (def.name === 'paragraphStyle'
510
+ ? ((this.context.locale.toolbar.paragraphItems || {})[item.value] || item.label)
511
+ : item.label)
512
+ : item;
507
513
  const isHeader = (typeof item === 'object') && !!item.disabled;
508
514
  const attrs = { value };
509
515
  if (isHeader) attrs.disabled = '';
@@ -558,9 +564,9 @@ export class Toolbar {
558
564
  const btn = createElement('button', {
559
565
  type: 'button',
560
566
  class: classAttr,
561
- title: btnDef.tooltip || '',
567
+ title: this.context.locale.toolbar[btnDef.name] || btnDef.tooltip || '',
562
568
  'data-btn': btnDef.name,
563
- 'aria-label': btnDef.tooltip || btnDef.name,
569
+ 'aria-label': this.context.locale.toolbar[btnDef.name] || btnDef.tooltip || btnDef.name,
564
570
  });
565
571
 
566
572
  // Render icon: prefer FontAwesome if enabled; otherwise fall back to SVG or text.
@@ -59,24 +59,25 @@ export class VideoDialog {
59
59
  // ---------------------------------------------------------------------------
60
60
 
61
61
  _buildDialog() {
62
+ const L = this.context.locale.videoDialog;
62
63
  const overlay = createElement('div', {
63
64
  class: 'an-dialog-overlay',
64
65
  role: 'dialog',
65
66
  'aria-modal': 'true',
66
- 'aria-label': 'Insert video',
67
+ 'aria-label': L.ariaLabel,
67
68
  });
68
69
  const box = createElement('div', { class: 'an-dialog-box' });
69
70
 
70
71
  const title = createElement('h3', { class: 'an-dialog-title' });
71
- title.textContent = 'Insert Video';
72
+ title.textContent = L.title;
72
73
 
73
74
  // URL input
74
75
  const urlLabel = createElement('label', { class: 'an-label' });
75
- urlLabel.textContent = 'Video URL';
76
+ urlLabel.textContent = L.videoUrl;
76
77
  const urlInput = /** @type {HTMLInputElement} */ (createElement('input', {
77
78
  type: 'url',
78
79
  class: 'an-input',
79
- placeholder: 'YouTube, Vimeo, or direct .mp4 URL',
80
+ placeholder: L.urlPlaceholder,
80
81
  autocomplete: 'off',
81
82
  }));
82
83
  this._urlInput = urlInput;
@@ -87,7 +88,7 @@ export class VideoDialog {
87
88
 
88
89
  // Width
89
90
  const widthLabel = createElement('label', { class: 'an-label' });
90
- widthLabel.textContent = 'Width (px)';
91
+ widthLabel.textContent = L.widthLabel;
91
92
  const widthInput = /** @type {HTMLInputElement} */ (createElement('input', {
92
93
  type: 'number',
93
94
  class: 'an-input',
@@ -101,9 +102,9 @@ export class VideoDialog {
101
102
  // Buttons
102
103
  const btnRow = createElement('div', { class: 'an-dialog-actions' });
103
104
  const insertBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
104
- insertBtn.textContent = 'Insert';
105
+ insertBtn.textContent = L.insertBtn;
105
106
  const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
106
- cancelBtn.textContent = 'Cancel';
107
+ cancelBtn.textContent = L.cancelBtn;
107
108
  btnRow.appendChild(insertBtn);
108
109
  btnRow.appendChild(cancelBtn);
109
110
 
@@ -113,7 +114,7 @@ export class VideoDialog {
113
114
  // Live URL hint
114
115
  const d0 = on(urlInput, 'input', () => {
115
116
  const info = this._parseVideoUrl(urlInput.value.trim());
116
- hintEl.textContent = info ? `Detected: ${info.type}` : (urlInput.value ? 'Unknown format — will try direct video embed' : '');
117
+ hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : (urlInput.value ? this.context.locale.videoDialog.unknownFormat : '');
117
118
  });
118
119
 
119
120
  const d1 = on(insertBtn, 'click', () => this._onInsert());
@@ -140,7 +141,7 @@ export class VideoDialog {
140
141
 
141
142
  const html = this._buildEmbedHtml(rawUrl, width);
142
143
  if (!html) {
143
- this._hintEl.textContent = 'Invalid URL — please enter a valid video link.';
144
+ this._hintEl.textContent = this.context.locale.videoDialog.invalidUrl;
144
145
  this._urlInput.focus();
145
146
  return;
146
147
  }
@@ -78,24 +78,25 @@ export class VideoTooltip {
78
78
  // ---------------------------------------------------------------------------
79
79
 
80
80
  _buildTooltip() {
81
+ const L = this.context.locale.tooltips.video;
81
82
  const el = createElement('div', {
82
83
  class: 'an-link-tooltip an-video-tooltip',
83
84
  role: 'toolbar',
84
- 'aria-label': 'Video actions',
85
+ 'aria-label': L.ariaLabel,
85
86
  });
86
87
  el.style.display = 'none';
87
88
 
88
89
  // Label
89
90
  this._label = createElement('span', { class: 'an-link-tooltip-url' });
90
- this._label.textContent = 'Video';
91
+ this._label.textContent = L.label;
91
92
  el.appendChild(this._label);
92
93
 
93
94
  el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
94
95
 
95
- this._floatLeftBtn = this._makeBtn(ICONS.floatLeft, 'Float Left', () => this._setFloat('left'));
96
- this._floatNoneBtn = this._makeBtn(ICONS.floatNone, 'No Float', () => this._setFloat(''));
97
- this._alignCenterBtn = this._makeBtn(ICONS.alignCenter, 'Align Center', () => this._setCenter());
98
- this._floatRightBtn = this._makeBtn(ICONS.floatRight, 'Float Right', () => this._setFloat('right'));
96
+ this._floatLeftBtn = this._makeBtn(ICONS.floatLeft, L.floatLeft, () => this._setFloat('left'));
97
+ this._floatNoneBtn = this._makeBtn(ICONS.floatNone, L.noFloat, () => this._setFloat(''));
98
+ this._alignCenterBtn = this._makeBtn(ICONS.alignCenter, L.alignCenter, () => this._setCenter());
99
+ this._floatRightBtn = this._makeBtn(ICONS.floatRight, L.floatRight, () => this._setFloat('right'));
99
100
 
100
101
  el.appendChild(this._floatLeftBtn);
101
102
  el.appendChild(this._floatNoneBtn);
@@ -104,18 +105,18 @@ export class VideoTooltip {
104
105
 
105
106
  el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
106
107
 
107
- this._originalBtn = this._makeBtn(ICONS.originalSize, 'Original Size', () => this._resetSize());
108
+ this._originalBtn = this._makeBtn(ICONS.originalSize, L.originalSize, () => this._resetSize());
108
109
 
109
110
  el.appendChild(this._originalBtn);
110
111
 
111
112
  el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
112
113
 
113
- this._previewBtn = this._makeBtn(ICONS.preview, 'Preview Video', () => this._togglePreview());
114
+ this._previewBtn = this._makeBtn(ICONS.preview, L.previewVideo, () => this._togglePreview());
114
115
  el.appendChild(this._previewBtn);
115
116
 
116
117
  el.appendChild(createElement('div', { class: 'an-link-tooltip-sep' }));
117
118
 
118
- this._deleteBtn = this._makeBtn(ICONS.deleteVideo, 'Delete Video', () => this._delete(), true);
119
+ this._deleteBtn = this._makeBtn(ICONS.deleteVideo, L.deleteVideo, () => this._delete(), true);
119
120
 
120
121
  el.appendChild(this._deleteBtn);
121
122
 
@@ -292,7 +293,7 @@ export class VideoTooltip {
292
293
 
293
294
  // Visual feedback: button turns primary-coloured
294
295
  this._previewBtn.classList.add('an-link-tooltip-btn--copied');
295
- this._previewBtn.title = 'Exit Preview';
296
+ this._previewBtn.title = this.context.locale.tooltips.video.exitPreview;
296
297
 
297
298
  // Exit preview on mousedown outside the wrapper.
298
299
  // Using 'mousedown' (not 'click') so clicks inside an iframe
@@ -319,7 +320,7 @@ export class VideoTooltip {
319
320
 
320
321
  // Reset button appearance
321
322
  this._previewBtn.classList.remove('an-link-tooltip-btn--copied');
322
- this._previewBtn.title = 'Preview Video';
323
+ this._previewBtn.title = this.context.locale.tooltips.video.previewVideo;
323
324
 
324
325
  if (this._previewClickOff) {
325
326
  document.removeEventListener('mousedown', this._previewClickOff, true);
@@ -135,4 +135,8 @@ export const defaultOptions = {
135
135
  // Custom focus ring colour — overrides the default blue when set.
136
136
  // Accepts any valid CSS colour string, e.g. '#f97316', 'hsl(25,90%,55%)'.
137
137
  focusColor: null,
138
+ // Display language for the editor UI.
139
+ // Built-in values: 'en' (default), 'vi', 'ja', 'zh', 'fr'.
140
+ // Pass a partial or full locale object to override individual strings.
141
+ lang: 'en',
138
142
  };
package/types/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * AutumnNote – TypeScript declarations
3
- * @version 1.0.4
3
+ * @version 1.0.9
4
4
  */
5
5
 
6
6
  // ---------------------------------------------------------------------------
@@ -104,6 +104,12 @@ export interface AsnOptions {
104
104
  onPaste?: (data: { text: string; html: string | null }) => void;
105
105
  /** Additional color swatches shown at the top of the color picker. */
106
106
  colorSwatches?: string[];
107
+ /**
108
+ * Display language for the editor UI.
109
+ * Built-in: 'en' (default) | 'vi' | 'ja' | 'zh' | 'fr'.
110
+ * Pass a partial locale object to override individual strings.
111
+ */
112
+ lang?: string | Partial<AsnLocale>;
107
113
  }
108
114
 
109
115
  // ---------------------------------------------------------------------------
@@ -179,6 +185,57 @@ export type ToolbarItemDef = ButtonDef | DropdownDef | GridButtonDef | ColorPick
179
185
  /** Runtime default options object (same export as `src/js/index.js`). */
180
186
  export declare const defaultOptions: Required<AsnOptions>;
181
187
 
188
+ // ---------------------------------------------------------------------------
189
+ // Locale / i18n
190
+ // ---------------------------------------------------------------------------
191
+
192
+ /** Full locale object shape used by the editor. All leaf values are strings or template functions. */
193
+ export interface AsnLocale {
194
+ toolbar: Record<string, string | ((n: string | number) => string) | Record<string, string>>;
195
+ linkDialog: Record<string, string>;
196
+ imageDialog: Record<string, string>;
197
+ videoDialog: Record<string, string | ((type: string) => string)>;
198
+ emojiDialog: Record<string, string | Record<string, string>>;
199
+ iconDialog: Record<string, string | Record<string, string>>;
200
+ findReplace: Record<string, string>;
201
+ shortcutsDialog: {
202
+ title: string;
203
+ ariaLabel: string;
204
+ close: string;
205
+ shortcuts: Array<{ category: string; items: Array<{ keys: string; action: string }> }>;
206
+ };
207
+ contextMenu: Record<string, string>;
208
+ statusbar: {
209
+ resizeHandle: string;
210
+ words: (n: number) => string;
211
+ wordsLimit: (n: number, max: number) => string;
212
+ chars: (n: number) => string;
213
+ charsLimit: (n: number, max: number) => string;
214
+ };
215
+ tooltips: {
216
+ link: Record<string, string>;
217
+ image: Record<string, string>;
218
+ code: Record<string, string>;
219
+ table: Record<string, string>;
220
+ video: Record<string, string>;
221
+ };
222
+ errors: {
223
+ imageFormat: (type: string) => string;
224
+ imageSize: (maxMb: number) => string;
225
+ };
226
+ }
227
+
228
+ /** Registry of all built-in locales (keys: 'en', 'vi', 'ja', 'zh', 'fr'). */
229
+ export declare const locales: Record<string, AsnLocale>;
230
+
231
+ /**
232
+ * Resolves a lang value to a full AsnLocale.
233
+ * - string key → looks up locales registry, deep-merges with 'en' fallback.
234
+ * - object → deep-merges with 'en' as base.
235
+ * - falsy / 'en' → returns the canonical English locale.
236
+ */
237
+ export declare function resolveLocale(lang: string | Partial<AsnLocale> | null | undefined): AsnLocale;
238
+
182
239
  // Re-export core utility modules exposed by the package entry-point.
183
240
  export * from '../src/js/core/dom.js';
184
241
  export * from '../src/js/core/range.js';