@pure-ds/core 0.7.30 → 0.7.32

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 (76) hide show
  1. package/.github/copilot-instructions.md +20 -11
  2. package/LOCALIZATION.md +197 -0
  3. package/dist/types/pds.config.d.ts +1 -1
  4. package/dist/types/pds.config.d.ts.map +1 -1
  5. package/dist/types/pds.d.ts +150 -0
  6. package/dist/types/public/assets/pds/components/pds-form.d.ts +2 -2
  7. package/dist/types/public/assets/pds/components/pds-form.d.ts.map +1 -1
  8. package/dist/types/public/assets/pds/components/pds-omnibox.d.ts +2 -2
  9. package/dist/types/public/assets/pds/components/pds-omnibox.d.ts.map +1 -1
  10. package/dist/types/public/assets/pds/components/pds-rating.d.ts +1 -119
  11. package/dist/types/public/assets/pds/components/pds-rating.d.ts.map +1 -1
  12. package/dist/types/public/assets/pds/components/pds-treeview.d.ts.map +1 -1
  13. package/dist/types/public/assets/pds/components/pds-upload.d.ts.map +1 -1
  14. package/dist/types/src/js/common/ask.d.ts.map +1 -1
  15. package/dist/types/src/js/common/font-loader.d.ts.map +1 -1
  16. package/dist/types/src/js/common/localization-resource-provider.d.ts +49 -0
  17. package/dist/types/src/js/common/localization-resource-provider.d.ts.map +1 -0
  18. package/dist/types/src/js/common/localization.d.ts +25 -0
  19. package/dist/types/src/js/common/localization.d.ts.map +1 -0
  20. package/dist/types/src/js/common/msg.d.ts +1 -2
  21. package/dist/types/src/js/common/msg.d.ts.map +1 -1
  22. package/dist/types/src/js/common/pds-log.d.ts +3 -0
  23. package/dist/types/src/js/common/pds-log.d.ts.map +1 -0
  24. package/dist/types/src/js/lit.d.ts +0 -7
  25. package/dist/types/src/js/lit.d.ts.map +1 -1
  26. package/dist/types/src/js/pds-core/pds-config.d.ts +51 -0
  27. package/dist/types/src/js/pds-core/pds-config.d.ts.map +1 -1
  28. package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -1
  29. package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -1
  30. package/dist/types/src/js/pds-core/pds-registry.d.ts.map +1 -1
  31. package/dist/types/src/js/pds-core/pds-runtime.d.ts.map +1 -1
  32. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -1
  33. package/dist/types/src/js/pds-localization.d.ts +3 -0
  34. package/dist/types/src/js/pds-localization.d.ts.map +1 -0
  35. package/dist/types/src/js/pds-singleton.d.ts +13 -0
  36. package/dist/types/src/js/pds-singleton.d.ts.map +1 -0
  37. package/dist/types/src/js/pds.d.ts +9 -1
  38. package/dist/types/src/js/pds.d.ts.map +1 -1
  39. package/package.json +8 -4
  40. package/packages/pds-cli/README.md +2 -0
  41. package/packages/pds-cli/lib/pds-mcp-core.js +2 -2
  42. package/public/assets/js/app.js +9 -11
  43. package/public/assets/js/lit.js +3 -94
  44. package/public/assets/js/pds-ask.js +4 -4
  45. package/public/assets/js/pds-enhancers.js +1 -1
  46. package/public/assets/js/pds-localization.js +1 -0
  47. package/public/assets/js/pds-manager.js +118 -118
  48. package/public/assets/js/pds.js +2 -2
  49. package/public/assets/pds/components/pds-calendar.js +4 -4
  50. package/public/assets/pds/components/pds-daterange.js +11 -8
  51. package/public/assets/pds/components/pds-form.js +22 -22
  52. package/public/assets/pds/components/pds-live-edit.js +503 -42
  53. package/public/assets/pds/components/pds-live-importer.js +66 -66
  54. package/public/assets/pds/components/pds-live-template-canvas.js +3 -3
  55. package/public/assets/pds/components/pds-omnibox.js +4 -4
  56. package/public/assets/pds/components/pds-rating.js +5 -3
  57. package/public/assets/pds/components/pds-tags.js +5 -5
  58. package/public/assets/pds/components/pds-theme.js +4 -4
  59. package/public/assets/pds/components/pds-toaster.js +6 -6
  60. package/public/assets/pds/components/pds-treeview.js +8 -4
  61. package/public/assets/pds/components/pds-upload.js +7 -6
  62. package/public/assets/pds/core/pds-ask.js +4 -4
  63. package/public/assets/pds/core/pds-enhancers.js +1 -1
  64. package/public/assets/pds/core/pds-localization.js +1 -0
  65. package/public/assets/pds/core/pds-manager.js +118 -118
  66. package/public/assets/pds/core.js +2 -2
  67. package/public/assets/pds/external/lit.js +3 -94
  68. package/readme.md +34 -6
  69. package/src/js/pds-core/pds-config.js +34 -11
  70. package/src/js/pds-core/pds-enhancers.js +5 -3
  71. package/src/js/pds-core/pds-live.js +7 -5
  72. package/src/js/pds-core/pds-registry.js +6 -4
  73. package/src/js/pds-core/pds-runtime.js +12 -8
  74. package/src/js/pds-core/pds-start-helpers.js +9 -4
  75. package/src/js/pds.d.ts +150 -0
  76. package/src/js/pds.js +420 -40
@@ -1,10 +1,10 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg, str } from "#pds";
2
2
 
3
3
  const COMPONENT_TAG = "pds-live-importer";
4
4
 
5
5
  const IMPORT_MODES = [
6
- { id: "convert-only", label: "Convert to PDS HTML only" },
7
- { id: "adopt-design-and-convert", label: "Adopt design language + convert" },
6
+ { id: "convert-only", label: msg("Convert to PDS HTML only") },
7
+ { id: "adopt-design-and-convert", label: msg("Adopt design language + convert") },
8
8
  ];
9
9
 
10
10
  let managerPromise = null;
@@ -46,7 +46,7 @@ function formatBytes(size) {
46
46
 
47
47
  function formatDateTime(value) {
48
48
  const date = value ? new Date(value) : null;
49
- if (!date || Number.isNaN(date.getTime())) return "Unknown date";
49
+ if (!date || Number.isNaN(date.getTime())) return msg("Unknown date");
50
50
  return date.toLocaleString();
51
51
  }
52
52
 
@@ -116,24 +116,24 @@ class PdsLiveImporter extends HTMLElement {
116
116
  this.className = "card surface-subtle stack-sm";
117
117
  this.innerHTML = `
118
118
  <label class="stack-xs">
119
- <span>File</span>
119
+ <span>${msg("File")}</span>
120
120
  <pds-upload class="pds-live-import-upload" accept=".html,.htm,.txt,.md,.json,.css,.js,.ts,.tsx,.jsx" max-files="1"></pds-upload>
121
121
  </label>
122
122
  <label class="stack-xs">
123
- <span>Import mode</span>
123
+ <span>${msg("Import mode")}</span>
124
124
  <select class="pds-live-import-mode">
125
125
  ${IMPORT_MODES.map((mode) => `<option value="${mode.id}">${escapeHtml(mode.label)}</option>`).join("")}
126
126
  </select>
127
127
  </label>
128
128
  <div class="flex gap-sm justify-end">
129
- <button type="button" class="btn-secondary btn-sm pds-live-import-run" disabled>Re-import</button>
129
+ <button type="button" class="btn-secondary btn-sm pds-live-import-run" disabled>${msg("Re-import")}</button>
130
130
  </div>
131
131
  <section class="card surface-base stack-xs pds-live-import-history">
132
132
  <div class="flex items-center justify-between gap-sm">
133
- <strong>Import History</strong>
133
+ <strong>${msg("Import History")}</strong>
134
134
  <div class="flex gap-xs">
135
- <button type="button" class="btn-outline btn-sm pds-live-history-refresh">Refresh</button>
136
- <button type="button" class="btn-outline btn-sm pds-live-history-clear">Clear</button>
135
+ <button type="button" class="btn-outline btn-sm pds-live-history-refresh">${msg("Refresh")}</button>
136
+ <button type="button" class="btn-outline btn-sm pds-live-history-clear">${msg("Clear")}</button>
137
137
  </div>
138
138
  </div>
139
139
  <div class="stack-xs pds-live-history-list"></div>
@@ -213,10 +213,10 @@ class PdsLiveImporter extends HTMLElement {
213
213
 
214
214
  const dialog = document.createElement("dialog");
215
215
  dialog.className = "card surface-elevated stack-sm";
216
- dialog.setAttribute("aria-label", "Import details");
216
+ dialog.setAttribute("aria-label", msg("Import details"));
217
217
 
218
218
  const title = document.createElement("h4");
219
- title.textContent = "Import Details";
219
+ title.textContent = msg("Import Details");
220
220
 
221
221
  const content = document.createElement("div");
222
222
  content.className = "stack-sm";
@@ -228,7 +228,7 @@ class PdsLiveImporter extends HTMLElement {
228
228
  const copyBtn = document.createElement("button");
229
229
  copyBtn.type = "button";
230
230
  copyBtn.className = "btn-outline btn-sm icon-only";
231
- copyBtn.setAttribute("aria-label", "Copy to clipboard");
231
+ copyBtn.setAttribute("aria-label", msg("Copy to clipboard"));
232
232
  const copyIcon = document.createElement("pds-icon");
233
233
  copyIcon.setAttribute("icon", "copy");
234
234
  copyIcon.setAttribute("size", "sm");
@@ -240,7 +240,7 @@ class PdsLiveImporter extends HTMLElement {
240
240
  const closeBtn = document.createElement("button");
241
241
  closeBtn.type = "button";
242
242
  closeBtn.className = "btn-outline btn-sm";
243
- closeBtn.textContent = "Close";
243
+ closeBtn.textContent = msg("Close");
244
244
  closeBtn.addEventListener("click", () => dialog.close());
245
245
 
246
246
  footer.appendChild(copyBtn);
@@ -265,7 +265,7 @@ class PdsLiveImporter extends HTMLElement {
265
265
  ? `<ul>${notes
266
266
  .map((note) => `<li>${escapeHtml(note)}</li>`)
267
267
  .join("")}</ul>`
268
- : "none";
268
+ : msg("none");
269
269
  const issuesHtml = issueCount
270
270
  ? `<ul>${entry.issues
271
271
  .map(
@@ -273,33 +273,33 @@ class PdsLiveImporter extends HTMLElement {
273
273
  `<li><strong>${escapeHtml(issue?.severity || "info")}</strong>: ${escapeHtml(issue?.message || "")}</li>`
274
274
  )
275
275
  .join("")}</ul>`
276
- : "none";
276
+ : msg("none");
277
277
  const unknownHtml = unknownTokens.length
278
278
  ? `<code>${escapeHtml(unknownTokens.join(", "))}</code>`
279
- : "none";
279
+ : msg("none");
280
280
 
281
281
  return `
282
282
  <table class="table-bordered table-compact">
283
283
  <tbody>
284
- <tr><th scope="row">File</th><td>${escapeHtml(entry?.fileName || "(untitled import)")}</td></tr>
285
- <tr><th scope="row">Source</th><td>${escapeHtml(entry?.sourceType || "unknown")}</td></tr>
286
- <tr><th scope="row">Mode</th><td>${escapeHtml(getImportModeLabel(entry?.importMode || entry?.meta?.importMode))}</td></tr>
287
- <tr><th scope="row">Date</th><td>${escapeHtml(formatDateTime(entry?.createdAt || entry?.createdAtIso))}</td></tr>
288
- <tr><th scope="row">Confidence</th><td>${confidence}</td></tr>
289
- <tr><th scope="row">Tailwind</th><td>${Number(coverage.tailwind ?? 0)}</td></tr>
290
- <tr><th scope="row">Mapped</th><td>${Number(coverage.mapped ?? 0)}</td></tr>
291
- <tr><th scope="row">Ignored</th><td>${Number(coverage.ignored ?? 0)}</td></tr>
292
- <tr><th scope="row">Policy Skipped</th><td>${Number(coverage.policySkipped ?? 0)}</td></tr>
293
- <tr><th scope="row">Unknown</th><td>${Number(coverage.unknown ?? 0)}</td></tr>
294
- <tr><th scope="row">Imported Styles</th><td>${Number(coverage.importedStyles ?? 0)}</td></tr>
295
- <tr><th scope="row">Unknown Tokens</th><td>${unknownHtml}</td></tr>
296
- <tr><th scope="row">Notes</th><td>${notesHtml}</td></tr>
297
- <tr><th scope="row">Issues</th><td>${issuesHtml}</td></tr>
284
+ <tr><th scope="row">${msg("File")}</th><td>${escapeHtml(entry?.fileName || msg("(untitled import)"))}</td></tr>
285
+ <tr><th scope="row">${msg("Source")}</th><td>${escapeHtml(entry?.sourceType || msg("unknown"))}</td></tr>
286
+ <tr><th scope="row">${msg("Mode")}</th><td>${escapeHtml(getImportModeLabel(entry?.importMode || entry?.meta?.importMode))}</td></tr>
287
+ <tr><th scope="row">${msg("Date")}</th><td>${escapeHtml(formatDateTime(entry?.createdAt || entry?.createdAtIso))}</td></tr>
288
+ <tr><th scope="row">${msg("Confidence")}</th><td>${confidence}</td></tr>
289
+ <tr><th scope="row">${msg("Tailwind")}</th><td>${Number(coverage.tailwind ?? 0)}</td></tr>
290
+ <tr><th scope="row">${msg("Mapped")}</th><td>${Number(coverage.mapped ?? 0)}</td></tr>
291
+ <tr><th scope="row">${msg("Ignored")}</th><td>${Number(coverage.ignored ?? 0)}</td></tr>
292
+ <tr><th scope="row">${msg("Policy Skipped")}</th><td>${Number(coverage.policySkipped ?? 0)}</td></tr>
293
+ <tr><th scope="row">${msg("Unknown")}</th><td>${Number(coverage.unknown ?? 0)}</td></tr>
294
+ <tr><th scope="row">${msg("Imported Styles")}</th><td>${Number(coverage.importedStyles ?? 0)}</td></tr>
295
+ <tr><th scope="row">${msg("Unknown Tokens")}</th><td>${unknownHtml}</td></tr>
296
+ <tr><th scope="row">${msg("Notes")}</th><td>${notesHtml}</td></tr>
297
+ <tr><th scope="row">${msg("Issues")}</th><td>${issuesHtml}</td></tr>
298
298
  </tbody>
299
299
  </table>
300
300
  <div class="flex gap-sm justify-end">
301
- ${String(entry?.convertedHtml || "").trim() ? '<button type="button" class="btn-secondary btn-sm pds-live-history-apply" data-history-id="' + Number(entry?.id || 0) + '">Apply stored result</button>' : ""}
302
- <button type="button" class="btn-outline btn-sm pds-live-history-reimport" data-history-id="${Number(entry?.id || 0)}">Re-import input</button>
301
+ ${String(entry?.convertedHtml || "").trim() ? '<button type="button" class="btn-secondary btn-sm pds-live-history-apply" data-history-id="' + Number(entry?.id || 0) + '">' + msg("Apply stored result") + '</button>' : ""}
302
+ <button type="button" class="btn-outline btn-sm pds-live-history-reimport" data-history-id="${Number(entry?.id || 0)}">${msg("Re-import input")}</button>
303
303
  </div>
304
304
  `;
305
305
  }
@@ -338,7 +338,7 @@ class PdsLiveImporter extends HTMLElement {
338
338
  entry?.convertedHtml || entry?.resultSnapshot?.template?.html || ""
339
339
  ).trim();
340
340
  if (!text) {
341
- await this._toast("No converted PDS HTML available to copy", "warning");
341
+ await this._toast(msg("No converted PDS HTML available to copy"), "warning");
342
342
  return;
343
343
  }
344
344
 
@@ -356,9 +356,9 @@ class PdsLiveImporter extends HTMLElement {
356
356
  document.execCommand("copy");
357
357
  area.remove();
358
358
  }
359
- await this._toast("Copied converted PDS HTML to clipboard", "success");
359
+ await this._toast(msg("Copied converted PDS HTML to clipboard"), "success");
360
360
  } catch (error) {
361
- await this._toast("Failed to copy converted PDS HTML", "error");
361
+ await this._toast(msg("Failed to copy converted PDS HTML"), "error");
362
362
  }
363
363
  }
364
364
 
@@ -393,12 +393,12 @@ class PdsLiveImporter extends HTMLElement {
393
393
  if (!list) return;
394
394
 
395
395
  if (this._isHistoryLoading) {
396
- list.innerHTML = `<p class="text-muted">Loading import history...</p>`;
396
+ list.innerHTML = `<p class="text-muted">${msg("Loading import history...")}</p>`;
397
397
  return;
398
398
  }
399
399
 
400
400
  if (!Array.isArray(this._history) || this._history.length === 0) {
401
- list.innerHTML = `<p class="text-muted">No imports yet.</p>`;
401
+ list.innerHTML = `<p class="text-muted">${msg("No imports yet.")}</p>`;
402
402
  return;
403
403
  }
404
404
 
@@ -412,8 +412,8 @@ class PdsLiveImporter extends HTMLElement {
412
412
  const failedCount = Number(coverage.unknown ?? 0);
413
413
  const mappedCount = Number(coverage.mapped ?? 0);
414
414
  const totalTailwind = Number(coverage.tailwind ?? 0);
415
- const statusIssueText = `${failedCount} failed / ${issueCount} issues`;
416
- const fileLabel = `${entry?.fileName || "(untitled import)"} (${entry?.sourceType || "unknown"})`;
415
+ const statusIssueText = msg(str`${failedCount} failed / ${issueCount} issues`);
416
+ const fileLabel = msg(str`${entry?.fileName || msg("(untitled import)")} (${entry?.sourceType || msg("unknown")})`);
417
417
  const importMode = getImportModeLabel(entry?.importMode || entry?.meta?.importMode);
418
418
  return `
419
419
  <tr data-history-id="${Number(entry?.id || 0)}" data-history-file-name="${escapeHtml(entry?.fileName || "")}" data-history-source-type="${escapeHtml(entry?.sourceType || "")}">
@@ -421,12 +421,12 @@ class PdsLiveImporter extends HTMLElement {
421
421
  <td>${escapeHtml(fileLabel)}</td>
422
422
  <td class="flex flex-wrap gap-xs items-center">
423
423
  <span class="badge badge-outline badge-info badge-sm">${escapeHtml(importMode)}</span>
424
- <span class="badge badge-outline badge-success badge-sm">${escapeHtml(confidence)} success</span>
424
+ <span class="badge badge-outline badge-success badge-sm">${escapeHtml(msg(str`${confidence} success`))}</span>
425
425
  <span class="badge badge-outline badge-warning badge-sm">${escapeHtml(statusIssueText)}</span>
426
- <span class="badge badge-outline badge-info badge-sm">${mappedCount} of ${totalTailwind} mapped</span>
426
+ <span class="badge badge-outline badge-info badge-sm">${escapeHtml(msg(str`${mappedCount} of ${totalTailwind} mapped`))}</span>
427
427
  </td>
428
428
  <td>
429
- <button type="button" class="btn-outline btn-sm icon-only pds-live-history-details" data-history-id="${Number(entry?.id || 0)}" aria-label="Details" title="Details">
429
+ <button type="button" class="btn-outline btn-sm icon-only pds-live-history-details" data-history-id="${Number(entry?.id || 0)}" aria-label="${msg("Details")}" title="${msg("Details")}">
430
430
  <pds-icon icon="info" size="sm"></pds-icon>
431
431
  </button>
432
432
  </td>
@@ -439,10 +439,10 @@ class PdsLiveImporter extends HTMLElement {
439
439
  <table class="table-bordered table-compact">
440
440
  <thead>
441
441
  <tr>
442
- <th>Date</th>
443
- <th>File</th>
444
- <th>Status</th>
445
- <th>Info</th>
442
+ <th>${msg("Date")}</th>
443
+ <th>${msg("File")}</th>
444
+ <th>${msg("Status")}</th>
445
+ <th>${msg("Info")}</th>
446
446
  </tr>
447
447
  </thead>
448
448
  <tbody>
@@ -485,7 +485,7 @@ class PdsLiveImporter extends HTMLElement {
485
485
 
486
486
  let proceed = true;
487
487
  if (typeof PDS?.ask === "function") {
488
- const answer = await PDS.ask("Clear import history?", { type: "confirm" });
488
+ const answer = await PDS.ask(msg("Clear import history?"), { type: "confirm" });
489
489
  proceed = Boolean(answer);
490
490
  }
491
491
 
@@ -591,13 +591,13 @@ class PdsLiveImporter extends HTMLElement {
591
591
  const coverage = result?.meta?.coverage || {};
592
592
 
593
593
  return [
594
- `Import complete: ${fileName || "(no file)"}`,
594
+ msg(str`Import complete: ${fileName || msg("(no file)")}`),
595
595
  `source=${sourceType || "unknown"}`,
596
596
  `mode=${normalizeImportMode(result?.meta?.importMode || this._selectedImportMode)}`,
597
597
  `confidence=${confidence}`,
598
598
  `issues=${issues.length}`,
599
599
  `mapped=${coverage.mapped ?? 0}/${coverage.tailwind ?? 0}`,
600
- `report=Open Import History > Metadata table`,
600
+ msg("report=Open Import History > Metadata table"),
601
601
  ].join("\n");
602
602
  }
603
603
 
@@ -610,21 +610,21 @@ class PdsLiveImporter extends HTMLElement {
610
610
  const failedCount = Number(coverage.unknown ?? 0);
611
611
  const totalTailwind = Number(coverage.tailwind ?? 0);
612
612
  const mappedCount = Number(coverage.mapped ?? 0);
613
- const fileLabel = `${fileName || "(no file)"} (${sourceType || result?.type || "unknown"})`;
614
- const issueText = `${failedCount} failed / ${issues.length} issues`;
613
+ const fileLabel = msg(str`${fileName || msg("(no file)")} (${sourceType || result?.type || msg("unknown")})`);
614
+ const issueText = msg(str`${failedCount} failed / ${issues.length} issues`);
615
615
  const modeLabel = getImportModeLabel(importMode || result?.meta?.importMode);
616
616
 
617
617
  return `
618
618
  <table class="table-bordered table-compact">
619
619
  <tbody>
620
- <tr><th scope="row">File</th><td>${escapeHtml(fileLabel)}</td></tr>
621
- <tr><th scope="row">Mode</th><td>${escapeHtml(modeLabel)}</td></tr>
620
+ <tr><th scope="row">${msg("File")}</th><td>${escapeHtml(fileLabel)}</td></tr>
621
+ <tr><th scope="row">${msg("Mode")}</th><td>${escapeHtml(modeLabel)}</td></tr>
622
622
  <tr>
623
- <th scope="row">Status</th>
623
+ <th scope="row">${msg("Status")}</th>
624
624
  <td class="flex gap-xs items-center">
625
- <span class="badge badge-outline badge-success badge-sm">${confidencePct}% success</span>
625
+ <span class="badge badge-outline badge-success badge-sm">${escapeHtml(msg(str`${confidencePct}% success`))}</span>
626
626
  <span class="badge badge-outline badge-warning badge-sm">${escapeHtml(issueText)}</span>
627
- <span class="badge badge-outline badge-info badge-sm">${mappedCount} of ${totalTailwind} mapped</span>
627
+ <span class="badge badge-outline badge-info badge-sm">${escapeHtml(msg(str`${mappedCount} of ${totalTailwind} mapped`))}</span>
628
628
  </td>
629
629
  </tr>
630
630
  </tbody>
@@ -669,7 +669,9 @@ class PdsLiveImporter extends HTMLElement {
669
669
  this._updateSelectionUI();
670
670
  await this._runImport({ autoTriggered: true });
671
671
  } catch (error) {
672
- await this._toast(`Import failed\nreason=Could not read selected file\nerror=${error?.message || "Unknown error"}`, "error");
672
+ await this._toast(msg(str`Import failed
673
+ reason=Could not read selected file
674
+ error=${error?.message || msg("Unknown error")}`), "error");
673
675
  this._selectedFile = null;
674
676
  this._selectedText = "";
675
677
  this._selectedSourceType = "";
@@ -698,7 +700,7 @@ class PdsLiveImporter extends HTMLElement {
698
700
 
699
701
  const manager = await getManagerModule();
700
702
  if (typeof manager?.runLiveImport !== "function") {
701
- await this._toast("Import failed\nreason=Import service unavailable", "error");
703
+ await this._toast(msg("Import failed\nreason=Import service unavailable"), "error");
702
704
  return;
703
705
  }
704
706
 
@@ -731,12 +733,10 @@ class PdsLiveImporter extends HTMLElement {
731
733
  );
732
734
  } catch (error) {
733
735
  await this._toast(
734
- [
735
- `Import failed: ${this._selectedFile?.name || "selected file"}`,
736
- `source=${sourceType || "unknown"}`,
737
- `mode=${importMode}`,
738
- `error=${error?.message || "Unknown error"}`,
739
- ].join("\n"),
736
+ msg(str`Import failed: ${this._selectedFile?.name || msg("selected file")}
737
+ source=${sourceType || "unknown"}
738
+ mode=${importMode}
739
+ error=${error?.message || msg("Unknown error")}`),
740
740
  "error"
741
741
  );
742
742
  } finally {
@@ -750,7 +750,7 @@ class PdsLiveImporter extends HTMLElement {
750
750
  {
751
751
  html: true,
752
752
  action: {
753
- label: "Open details",
753
+ label: msg("Open details"),
754
754
  icon: "caret-right",
755
755
  onClick: () =>
756
756
  this._openLatestHistoryMetadata({
@@ -1,4 +1,4 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg } from "#pds";
2
2
 
3
3
  const COMPONENT_TAG = "pds-live-template-canvas";
4
4
 
@@ -46,7 +46,7 @@ class PdsLiveTemplateCanvas extends HTMLElement {
46
46
  this.className = "stack-sm";
47
47
  this.innerHTML = `
48
48
  <label class="stack-xs">
49
- <span>Template</span>
49
+ <span>${msg("Template")}</span>
50
50
  <div class="pds-live-template-omnibox"></div>
51
51
  </label>
52
52
  <p class="text-muted pds-live-template-description"></p>
@@ -68,7 +68,7 @@ class PdsLiveTemplateCanvas extends HTMLElement {
68
68
  try {
69
69
  const omnibox = document.createElement("pds-omnibox");
70
70
  omnibox.setAttribute("item-grid", "45px 1fr 0");
71
- omnibox.setAttribute("placeholder", "Search canvas templates...");
71
+ omnibox.setAttribute("placeholder", msg("Search canvas templates..."));
72
72
  omnibox.value = "";
73
73
 
74
74
  const templates = this._templates;
@@ -31,7 +31,7 @@
31
31
  *
32
32
  * @property {PdsOmniboxSettings} settings - AutoComplete settings object (required by consumer)
33
33
  */
34
- import { PDS } from "#pds";
34
+ import { PDS, msg } from "#pds";
35
35
 
36
36
  const LAYERS = ["tokens", "primitives", "components", "utilities"];
37
37
  const DEFAULT_PLACEHOLDER = "Search...";
@@ -134,7 +134,7 @@ export class PdsOmnibox extends HTMLElement {
134
134
  }
135
135
 
136
136
  get placeholder() {
137
- return this.getAttribute("placeholder") || DEFAULT_PLACEHOLDER;
137
+ return this.getAttribute("placeholder") || msg(DEFAULT_PLACEHOLDER);
138
138
  }
139
139
 
140
140
  set placeholder(value) {
@@ -215,7 +215,7 @@ export class PdsOmnibox extends HTMLElement {
215
215
  this.#root.innerHTML = `
216
216
  <div class="ac-container input-icon">
217
217
  <pds-icon morph icon="${DEFAULT_ICON}"></pds-icon>
218
- <input class="ac-input" type="search" placeholder="${DEFAULT_PLACEHOLDER}" autocomplete="off" />
218
+ <input class="ac-input" type="search" placeholder="${msg(DEFAULT_PLACEHOLDER)}" autocomplete="off" />
219
219
  </div>
220
220
  `;
221
221
 
@@ -568,7 +568,7 @@ export class PdsOmnibox extends HTMLElement {
568
568
  }
569
569
  this.#internals.setValidity(
570
570
  { customError: true },
571
- this.#input.validationMessage || "Invalid value",
571
+ this.#input.validationMessage || msg("Invalid value"),
572
572
  this.#input,
573
573
  );
574
574
  }
@@ -1,3 +1,5 @@
1
+ import { msg, str } from "#pds";
2
+
1
3
  const STAR_PATH =
2
4
  "M12 .9l3.1 6.3 7 1-5 4.9 1.2 6.9L12 16.8 5.7 20l1.2-6.9-5-4.9 7-1L12 .9z";
3
5
 
@@ -499,7 +501,7 @@ class PdsRating extends HTMLElement {
499
501
  this.#input.name = this.name;
500
502
 
501
503
  if (!this.#input.hasAttribute("aria-label") && !this.#input.hasAttribute("aria-labelledby")) {
502
- this.#input.setAttribute("aria-label", "Rating");
504
+ this.#input.setAttribute("aria-label", msg("Rating"));
503
505
  }
504
506
 
505
507
  if (!this.hasAttribute("role")) {
@@ -538,8 +540,8 @@ class PdsRating extends HTMLElement {
538
540
  this.style.removeProperty("--rating-star-on");
539
541
  }
540
542
 
541
- this.#input.setAttribute("aria-valuetext", `${current} out of ${max} stars`);
542
- this.setAttribute("aria-label", this.#input.getAttribute("aria-label") || "Rating");
543
+ this.#input.setAttribute("aria-valuetext", msg(str`${current} out of ${max} stars`));
544
+ this.setAttribute("aria-label", this.#input.getAttribute("aria-label") || msg("Rating"));
543
545
  this.setAttribute("aria-disabled", String(this.disabled));
544
546
  this.setAttribute("aria-readonly", String(this.readOnly));
545
547
  this.setAttribute("aria-valuemin", "0");
@@ -1,4 +1,4 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg, str } from "#pds";
2
2
  import "./pds-omnibox.js";
3
3
 
4
4
  const DEFAULT_OMNIBOX_OPTIONS = {
@@ -190,7 +190,7 @@ class PdsTags extends HTMLElement {
190
190
  }
191
191
 
192
192
  get placeholder() {
193
- return this.getAttribute("placeholder") || "Search tags...";
193
+ return this.getAttribute("placeholder") || msg("Search tags...");
194
194
  }
195
195
 
196
196
  set placeholder(value) {
@@ -312,7 +312,7 @@ class PdsTags extends HTMLElement {
312
312
  this.#root.innerHTML = /* html */ `
313
313
  <div class="stack-sm" part="wrap">
314
314
  <div class="chips" part="chips" aria-live="polite"></div>
315
- <span class="empty" part="empty">No tags selected.</span>
315
+ <span class="empty" part="empty">${msg("No tags selected.")}</span>
316
316
  <pds-omnibox part="omnibox"></pds-omnibox>
317
317
  </div>
318
318
  `;
@@ -583,7 +583,7 @@ class PdsTags extends HTMLElement {
583
583
  chip.dataset.tagId = item.id;
584
584
  chip.innerHTML = /* html */ `${item.text} <pds-icon icon="x" size="xs" aria-hidden="true"></pds-icon>`;
585
585
  chip.disabled = this.disabled;
586
- chip.setAttribute("aria-label", `Remove ${item.text}`);
586
+ chip.setAttribute("aria-label", msg(str`Remove ${item.text}`));
587
587
  chip.addEventListener("click", () => {
588
588
  this.#toggleSelection(item.id);
589
589
  });
@@ -640,7 +640,7 @@ class PdsTags extends HTMLElement {
640
640
  this.#debug("syncFormValue:invalid", { reason: "valueMissing" });
641
641
  this.#internals.setValidity(
642
642
  { valueMissing: true },
643
- "Please select at least one option.",
643
+ msg("Please select at least one option."),
644
644
  this.#omnibox || this,
645
645
  );
646
646
  return;
@@ -1,4 +1,4 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg, str } from "#pds";
2
2
 
3
3
  /**
4
4
  * `<pds-theme>` exposes a zero-config theme toggle that updates `PDS.theme` and
@@ -103,7 +103,7 @@ class PdsTheme extends HTMLElement {
103
103
  <input type="radio" name="theme" value="${option.value}" />
104
104
  <span>
105
105
  <pds-icon icon="${option.icon}" size="sm"></pds-icon>
106
- ${option.label}
106
+ ${msg(option.label)}
107
107
  </span>
108
108
  </label>`,
109
109
  ).join("");
@@ -134,9 +134,9 @@ class PdsTheme extends HTMLElement {
134
134
  currentPreset?.name ||
135
135
  PDS.currentPreset?.name ||
136
136
  PDS.currentConfig?.preset ||
137
- "current preset";
137
+ msg("current preset");
138
138
  console.warn(
139
- `PDS theme "${resolvedTheme}" not supported by preset "${presetName}".`
139
+ msg(str`PDS theme "${resolvedTheme}" not supported by preset "${presetName}".`)
140
140
  );
141
141
  this.#syncCheckedState();
142
142
  return;
@@ -1,4 +1,4 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg } from "#pds";
2
2
 
3
3
  /**
4
4
  * @element pds-toaster
@@ -339,7 +339,7 @@ export class AppToaster extends HTMLElement {
339
339
  if (closable) {
340
340
  const closeBtn = document.createElement("button");
341
341
  closeBtn.className = "callout-close";
342
- closeBtn.setAttribute("aria-label", "Close");
342
+ closeBtn.setAttribute("aria-label", msg("Close"));
343
343
  closeBtn.textContent = "×";
344
344
  closeBtn.onclick = () => this.dismissToast(id);
345
345
  toast.appendChild(closeBtn);
@@ -452,10 +452,10 @@ export class AppToaster extends HTMLElement {
452
452
  */
453
453
  #getToastTitle(type) {
454
454
  const titles = {
455
- information: "Information",
456
- success: "Success!",
457
- warning: "Warning",
458
- error: "Error",
455
+ information: msg("Information"),
456
+ success: msg("Success!"),
457
+ warning: msg("Warning"),
458
+ error: msg("Error"),
459
459
  };
460
460
  return titles[type] || titles.information;
461
461
  }
@@ -1,4 +1,4 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg, str } from "#pds";
2
2
 
3
3
  /**
4
4
  * Accessible, form-associated treeview with nested UL output.
@@ -583,7 +583,7 @@ export class PdsTreeview extends HTMLElement {
583
583
  #renderShell() {
584
584
  this.#root.innerHTML = `
585
585
  <div class="tv-host" data-state="ready">
586
- <ul class="tv-tree" role="tree" aria-label="Treeview"></ul>
586
+ <ul class="tv-tree" role="tree" aria-label="${this.#escapeAttribute(msg("Treeview"))}"></ul>
587
587
  </div>
588
588
  `;
589
589
 
@@ -988,11 +988,15 @@ export class PdsTreeview extends HTMLElement {
988
988
  const hasPrefix = Boolean(node.image || node.icon);
989
989
  const selected = this.#selectedIds.has(node.id);
990
990
  const toggleGlyph = node.loadingChildren ? "…" : expanded ? "−" : "+";
991
+ const toggleAriaLabel = this.#escapeAttribute(
992
+ expanded ? msg(str`Collapse ${node.text}`) : msg(str`Expand ${node.text}`),
993
+ );
994
+ const selectAriaLabel = this.#escapeAttribute(msg(str`Select ${node.text}`));
991
995
  const toggle = hasChildren
992
- ? `<button type="button" class="tv-toggle icon-only" data-node-id="${this.#escapeAttribute(node.id)}" aria-label="${expanded ? "Collapse" : "Expand"} ${this.#escapeAttribute(node.text)}" ${node.loadingChildren ? "disabled" : ""}>${toggleGlyph}</button>`
996
+ ? `<button type="button" class="tv-toggle icon-only" data-node-id="${this.#escapeAttribute(node.id)}" aria-label="${toggleAriaLabel}" ${node.loadingChildren ? "disabled" : ""}>${toggleGlyph}</button>`
993
997
  : `<span class="tv-toggle-gap" aria-hidden="true"></span>`;
994
998
  const checkbox = useCheckboxes
995
- ? `<span class="tv-check"><input class="tv-checkbox-input" type="checkbox" data-node-id="${this.#escapeAttribute(node.id)}" aria-label="Select ${this.#escapeAttribute(node.text)}" ${selected ? "checked" : ""} ${this.disabled ? "disabled" : ""}></span>`
999
+ ? `<span class="tv-check"><input class="tv-checkbox-input" type="checkbox" data-node-id="${this.#escapeAttribute(node.id)}" aria-label="${selectAriaLabel}" ${selected ? "checked" : ""} ${this.disabled ? "disabled" : ""}></span>`
996
1000
  : "";
997
1001
  const prefix = this.#renderPrefix(node);
998
1002
  const rowClassParts = ["tv-row", hasPrefix ? "tv-row-has-prefix" : "tv-row-no-prefix"];
@@ -1,4 +1,4 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg, str } from "#pds";
2
2
 
3
3
  /**
4
4
  * Drag-and-drop file uploader that participates in native forms.
@@ -52,10 +52,10 @@ class UploadArea extends HTMLElement {
52
52
  this.#root.innerHTML = `
53
53
  <div class="container" role="button" tabindex="0" aria-disabled="false">
54
54
  <div class="instructions">
55
- <div class="headline">Drop files here or</div>
56
- <div class="sub">Click to browse, or drag & drop files to upload.</div>
55
+ <div class="headline">${msg("Drop files here or")}</div>
56
+ <div class="sub">${msg("Click to browse, or drag & drop files to upload.")}</div>
57
57
  </div>
58
- <button type="button" class="btn-select">Select</button>
58
+ <button type="button" class="btn-select">${msg("Select")}</button>
59
59
  </div>
60
60
 
61
61
  <div class="tiles" aria-live="polite" aria-relevant="additions removals"></div>
@@ -572,8 +572,9 @@ class UploadArea extends HTMLElement {
572
572
  const remove = document.createElement("button");
573
573
  remove.className = "remove";
574
574
  remove.type = "button";
575
- remove.title = `Remove ${file.name}`;
576
- remove.setAttribute("aria-label", `Remove ${file.name}`);
575
+ const removeLabel = msg(str`Remove ${file.name}`);
576
+ remove.title = removeLabel;
577
+ remove.setAttribute("aria-label", removeLabel);
577
578
  remove.textContent = "✕";
578
579
  remove.addEventListener("click", (e) => {
579
580
  e.stopPropagation();