@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,4 +1,4 @@
1
- import { PDS } from "#pds";
1
+ import { PDS, msg } from "#pds";
2
2
 
3
3
  const EDITOR_TAG = "pds-live-edit";
4
4
  const STYLE_ID = "pds-live-editor-styles";
@@ -1631,6 +1631,462 @@ function setStoredConfig(nextConfig) {
1631
1631
  } catch (e) {}
1632
1632
  }
1633
1633
 
1634
+ function normalizeLocaleTag(locale) {
1635
+ return String(locale || "").trim().toLowerCase();
1636
+ }
1637
+
1638
+ let __startupLocalizationLocales = null;
1639
+ let __startupLocalizationLocalesPromise = null;
1640
+
1641
+ const LOCALE_PROBE_CANDIDATES = [
1642
+ "en", "nl", "de", "fr", "es", "it", "pt", "sv", "no", "da", "fi",
1643
+ "pl", "cs", "sk", "sl", "hu", "ro", "bg", "hr", "sr", "ru", "uk",
1644
+ "tr", "el", "he", "ar", "fa", "hi", "ja", "ko", "zh", "zh-cn", "zh-tw",
1645
+ ];
1646
+
1647
+ function toBaseLocale(locale) {
1648
+ const normalized = normalizeLocaleTag(locale);
1649
+ if (!normalized) return "";
1650
+ return normalized.split("-")[0] || normalized;
1651
+ }
1652
+
1653
+ function isLocalizationActive() {
1654
+ const configLocalization =
1655
+ PDS?.currentConfig?.localization && typeof PDS.currentConfig.localization === "object"
1656
+ ? PDS.currentConfig.localization
1657
+ : null;
1658
+
1659
+ const runtimeState =
1660
+ typeof PDS?.getLocalizationState === "function"
1661
+ ? PDS.getLocalizationState()
1662
+ : null;
1663
+
1664
+ const hasRuntimeProvider = Boolean(runtimeState?.hasProvider);
1665
+
1666
+ const hasConfigProvider = Boolean(
1667
+ configLocalization?.provider ||
1668
+ typeof configLocalization?.translate === "function" ||
1669
+ typeof configLocalization?.loadLocale === "function" ||
1670
+ typeof configLocalization?.setLocale === "function"
1671
+ );
1672
+
1673
+ const hasConfigMessages = Boolean(
1674
+ configLocalization?.messages &&
1675
+ typeof configLocalization.messages === "object" &&
1676
+ Object.keys(configLocalization.messages).length > 0
1677
+ );
1678
+
1679
+ const hasRuntimeMessages = Boolean(
1680
+ runtimeState?.messages &&
1681
+ typeof runtimeState.messages === "object" &&
1682
+ Object.keys(runtimeState.messages).length > 0
1683
+ );
1684
+
1685
+ return hasRuntimeProvider || hasConfigProvider || hasConfigMessages || hasRuntimeMessages;
1686
+ }
1687
+
1688
+ function isLocaleBundle(bundle) {
1689
+ if (!bundle || typeof bundle !== "object" || Array.isArray(bundle)) {
1690
+ return false;
1691
+ }
1692
+
1693
+ return Object.values(bundle).some((value) => {
1694
+ if (typeof value === "string") return true;
1695
+ return Boolean(value && typeof value === "object" && typeof value.content === "string");
1696
+ });
1697
+ }
1698
+
1699
+ function normalizeMessageBundle(bundle) {
1700
+ if (!bundle || typeof bundle !== "object" || Array.isArray(bundle)) {
1701
+ return {};
1702
+ }
1703
+
1704
+ const normalized = {};
1705
+ Object.entries(bundle).forEach(([key, value]) => {
1706
+ if (typeof value === "string") {
1707
+ normalized[key] = value;
1708
+ return;
1709
+ }
1710
+
1711
+ if (value && typeof value === "object" && typeof value.content === "string") {
1712
+ normalized[key] = value.content;
1713
+ }
1714
+ });
1715
+
1716
+ return normalized;
1717
+ }
1718
+
1719
+ function getLocalizationProviderLoader(configLocalization) {
1720
+ if (!configLocalization || typeof configLocalization !== "object") return null;
1721
+
1722
+ return (
1723
+ (typeof configLocalization?.loadLocale === "function"
1724
+ ? configLocalization.loadLocale
1725
+ : null) ||
1726
+ (typeof configLocalization?.provider?.loadLocale === "function"
1727
+ ? configLocalization.provider.loadLocale
1728
+ : null) ||
1729
+ (typeof configLocalization?.setLocale === "function"
1730
+ ? configLocalization.setLocale
1731
+ : null) ||
1732
+ (typeof configLocalization?.provider?.setLocale === "function"
1733
+ ? configLocalization.provider.setLocale
1734
+ : null)
1735
+ );
1736
+ }
1737
+
1738
+ function buildLocaleProbeList({ defaultLocale, runtimeState, knownLocales }) {
1739
+ const candidates = new Set();
1740
+
1741
+ const normalizedDefault = normalizeLocaleTag(defaultLocale);
1742
+ if (normalizedDefault) {
1743
+ candidates.add(normalizedDefault);
1744
+ }
1745
+
1746
+ if (Array.isArray(runtimeState?.loadedLocales)) {
1747
+ runtimeState.loadedLocales.forEach((locale) => {
1748
+ const normalized = normalizeLocaleTag(locale);
1749
+ if (normalized) {
1750
+ candidates.add(normalized);
1751
+ candidates.add(toBaseLocale(normalized));
1752
+ }
1753
+ });
1754
+ }
1755
+
1756
+ if (Array.isArray(knownLocales)) {
1757
+ knownLocales.forEach((locale) => {
1758
+ const normalized = normalizeLocaleTag(locale);
1759
+ if (normalized) {
1760
+ candidates.add(normalized);
1761
+ candidates.add(toBaseLocale(normalized));
1762
+ }
1763
+ });
1764
+ }
1765
+
1766
+ if (typeof navigator !== "undefined" && Array.isArray(navigator.languages)) {
1767
+ navigator.languages.forEach((locale) => {
1768
+ const normalized = normalizeLocaleTag(locale);
1769
+ if (normalized) {
1770
+ candidates.add(normalized);
1771
+ candidates.add(toBaseLocale(normalized));
1772
+ }
1773
+ });
1774
+ }
1775
+
1776
+ LOCALE_PROBE_CANDIDATES.forEach((locale) => {
1777
+ const normalized = normalizeLocaleTag(locale);
1778
+ if (normalized) {
1779
+ candidates.add(normalized);
1780
+ candidates.add(toBaseLocale(normalized));
1781
+ }
1782
+ });
1783
+
1784
+ candidates.delete("");
1785
+ return Array.from(candidates).sort((a, b) => a.localeCompare(b));
1786
+ }
1787
+
1788
+ function bundlesDifferFromOrigin(originBundle, candidateBundle) {
1789
+ const originEntries = normalizeMessageBundle(originBundle);
1790
+ const candidateEntries = normalizeMessageBundle(candidateBundle);
1791
+
1792
+ const originKeys = Object.keys(originEntries);
1793
+ const candidateKeys = Object.keys(candidateEntries);
1794
+ if (!originKeys.length || !candidateKeys.length) {
1795
+ return false;
1796
+ }
1797
+
1798
+ return originKeys.some((key) => {
1799
+ if (!Object.prototype.hasOwnProperty.call(candidateEntries, key)) return false;
1800
+ return String(candidateEntries[key]) !== String(originEntries[key]);
1801
+ });
1802
+ }
1803
+
1804
+ function collectLocalesFromMessageRows(source, locales) {
1805
+ if (!source || typeof source !== "object" || Array.isArray(source)) return;
1806
+
1807
+ Object.values(source).forEach((row) => {
1808
+ if (!row || typeof row !== "object" || Array.isArray(row)) return;
1809
+ if (typeof row.content === "string") return;
1810
+
1811
+ Object.entries(row).forEach(([localeKey, translatedValue]) => {
1812
+ const normalized = normalizeLocaleTag(localeKey);
1813
+ if (!normalized) return;
1814
+
1815
+ const hasValue =
1816
+ typeof translatedValue === "string" ||
1817
+ Boolean(
1818
+ translatedValue &&
1819
+ typeof translatedValue === "object" &&
1820
+ typeof translatedValue.content === "string"
1821
+ );
1822
+
1823
+ if (hasValue) {
1824
+ locales.add(normalized);
1825
+ }
1826
+ });
1827
+ });
1828
+ }
1829
+
1830
+ function collectLocalesFromLocaleBundles(source, locales) {
1831
+ if (!source || typeof source !== "object" || Array.isArray(source)) return;
1832
+
1833
+ Object.entries(source).forEach(([localeKey, bundle]) => {
1834
+ const normalized = normalizeLocaleTag(localeKey);
1835
+ if (!normalized) return;
1836
+ if (!isLocaleBundle(bundle)) return;
1837
+ locales.add(normalized);
1838
+ });
1839
+ }
1840
+
1841
+ function collectLocalesFromConfiguredList(source, locales) {
1842
+ if (!Array.isArray(source)) return;
1843
+
1844
+ source.forEach((localeValue) => {
1845
+ const normalized = normalizeLocaleTag(localeValue);
1846
+ if (normalized) {
1847
+ locales.add(normalized);
1848
+ }
1849
+ });
1850
+ }
1851
+
1852
+ async function detectStartupLocalizationLocales() {
1853
+ const locales = new Set();
1854
+
1855
+ const configLocalization =
1856
+ PDS?.currentConfig?.localization && typeof PDS.currentConfig.localization === "object"
1857
+ ? PDS.currentConfig.localization
1858
+ : null;
1859
+
1860
+ const defaultLocale = normalizeLocaleTag(configLocalization?.locale);
1861
+ if (defaultLocale) {
1862
+ locales.add(defaultLocale);
1863
+ }
1864
+
1865
+ collectLocalesFromConfiguredList(configLocalization?.locales, locales);
1866
+ collectLocalesFromConfiguredList(configLocalization?.provider?.locales, locales);
1867
+
1868
+ if (locales.size >= 2) {
1869
+ return Array.from(locales).sort((a, b) => a.localeCompare(b));
1870
+ }
1871
+
1872
+ const runtimeState =
1873
+ typeof PDS?.getLocalizationState === "function"
1874
+ ? PDS.getLocalizationState()
1875
+ : null;
1876
+ const runtimeDefaultLocale = normalizeLocaleTag(runtimeState?.locale);
1877
+ if (runtimeDefaultLocale) {
1878
+ locales.add(runtimeDefaultLocale);
1879
+ }
1880
+
1881
+ const messageSources = [
1882
+ configLocalization?.messages,
1883
+ configLocalization?.messagesByLocale,
1884
+ configLocalization?.i18n,
1885
+ configLocalization?.translations,
1886
+ configLocalization?.provider?.messages,
1887
+ configLocalization?.provider?.messagesByLocale,
1888
+ configLocalization?.provider?.i18n,
1889
+ configLocalization?.provider?.translations,
1890
+ ];
1891
+
1892
+ messageSources.forEach((source) => {
1893
+ collectLocalesFromMessageRows(source, locales);
1894
+ collectLocalesFromLocaleBundles(source, locales);
1895
+ });
1896
+
1897
+ const localeListFromRows = Array.from(locales).sort((a, b) => a.localeCompare(b));
1898
+ if (localeListFromRows.length >= 2) {
1899
+ return localeListFromRows;
1900
+ }
1901
+
1902
+ const providerLoader = getLocalizationProviderLoader(configLocalization);
1903
+ const runtimeLoadLocale =
1904
+ typeof PDS?.loadLocale === "function" ? PDS.loadLocale.bind(PDS) : null;
1905
+
1906
+ if (typeof providerLoader !== "function" && typeof runtimeLoadLocale !== "function") {
1907
+ return localeListFromRows;
1908
+ }
1909
+
1910
+ const originLocale =
1911
+ normalizeLocaleTag(configLocalization?.locale) ||
1912
+ normalizeLocaleTag(runtimeState?.locale) ||
1913
+ "en";
1914
+
1915
+ locales.add(originLocale);
1916
+
1917
+ let originBundle = normalizeMessageBundle(runtimeState?.messages || configLocalization?.messages);
1918
+ if (!Object.keys(originBundle).length) {
1919
+ try {
1920
+ let loadedOrigin = null;
1921
+
1922
+ if (typeof runtimeLoadLocale === "function") {
1923
+ loadedOrigin = await Promise.resolve(runtimeLoadLocale(originLocale));
1924
+ } else {
1925
+ loadedOrigin = await Promise.resolve(
1926
+ providerLoader({
1927
+ locale: originLocale,
1928
+ defaultLocale: originLocale,
1929
+ reason: "startup-locale-detect-origin",
1930
+ loadedLocales: Array.from(locales),
1931
+ messages: {},
1932
+ load: true,
1933
+ })
1934
+ );
1935
+ }
1936
+
1937
+ originBundle = normalizeMessageBundle(loadedOrigin);
1938
+ } catch (error) {}
1939
+ }
1940
+
1941
+ const probeLocales = buildLocaleProbeList({
1942
+ defaultLocale: originLocale,
1943
+ runtimeState,
1944
+ knownLocales: Array.from(locales),
1945
+ });
1946
+
1947
+ for (const candidateLocale of probeLocales) {
1948
+ if (!candidateLocale || candidateLocale === originLocale) {
1949
+ continue;
1950
+ }
1951
+
1952
+ try {
1953
+ let candidateBundle = null;
1954
+
1955
+ if (typeof runtimeLoadLocale === "function") {
1956
+ candidateBundle = await Promise.resolve(runtimeLoadLocale(candidateLocale));
1957
+ } else {
1958
+ candidateBundle = await Promise.resolve(
1959
+ providerLoader({
1960
+ locale: candidateLocale,
1961
+ defaultLocale: originLocale,
1962
+ reason: "startup-locale-detect-probe",
1963
+ loadedLocales: Array.from(locales),
1964
+ messages: {},
1965
+ load: true,
1966
+ })
1967
+ );
1968
+ }
1969
+
1970
+ if (bundlesDifferFromOrigin(originBundle, candidateBundle)) {
1971
+ locales.add(candidateLocale);
1972
+ if (locales.size >= 2) {
1973
+ break;
1974
+ }
1975
+ }
1976
+ } catch (error) {}
1977
+ }
1978
+
1979
+ return Array.from(locales).sort((a, b) => a.localeCompare(b));
1980
+ }
1981
+
1982
+ async function getStartupLocalizationLocales() {
1983
+ if (Array.isArray(__startupLocalizationLocales)) {
1984
+ return [...__startupLocalizationLocales];
1985
+ }
1986
+
1987
+ if (__startupLocalizationLocalesPromise) {
1988
+ const inFlight = await __startupLocalizationLocalesPromise;
1989
+ return [...inFlight];
1990
+ }
1991
+
1992
+ __startupLocalizationLocalesPromise = detectStartupLocalizationLocales()
1993
+ .then((locales) => {
1994
+ __startupLocalizationLocales = Array.isArray(locales) ? locales : [];
1995
+ return __startupLocalizationLocales;
1996
+ })
1997
+ .catch(() => {
1998
+ __startupLocalizationLocales = [];
1999
+ return __startupLocalizationLocales;
2000
+ })
2001
+ .finally(() => {
2002
+ __startupLocalizationLocalesPromise = null;
2003
+ });
2004
+
2005
+ const resolved = await __startupLocalizationLocalesPromise;
2006
+ return [...resolved];
2007
+ }
2008
+
2009
+ function localeOptionLabel(locale) {
2010
+ const normalized = normalizeLocaleTag(locale);
2011
+ if (!normalized) return "";
2012
+
2013
+ try {
2014
+ const currentDocLang =
2015
+ normalizeLocaleTag(document.documentElement?.getAttribute?.("lang")) || "en";
2016
+ const displayNames = new Intl.DisplayNames([currentDocLang], {
2017
+ type: "language",
2018
+ });
2019
+ const named = displayNames.of(normalized);
2020
+ if (named && String(named).trim()) {
2021
+ return named;
2022
+ }
2023
+ } catch (error) {}
2024
+
2025
+ return normalized;
2026
+ }
2027
+
2028
+ function localeMatches(selectedLocale, activeLocale) {
2029
+ const selected = normalizeLocaleTag(selectedLocale);
2030
+ const active = normalizeLocaleTag(activeLocale);
2031
+ if (!selected || !active) return false;
2032
+ return selected === active || toBaseLocale(selected) === toBaseLocale(active);
2033
+ }
2034
+
2035
+ async function buildQuickLanguageSelector() {
2036
+ if (typeof document === "undefined") return null;
2037
+ if (!isLocalizationActive()) return null;
2038
+
2039
+ const detectedLocales = await getStartupLocalizationLocales();
2040
+ if (detectedLocales.length < 2) return null;
2041
+
2042
+ const languageGroup = document.createElement("div");
2043
+ languageGroup.className = "stack-xs";
2044
+
2045
+ const languageText = document.createElement("span");
2046
+ languageText.setAttribute("data-label", "");
2047
+ languageText.textContent = msg("Language");
2048
+
2049
+ const languageFieldset = document.createElement("fieldset");
2050
+ languageFieldset.setAttribute("role", "radiogroup");
2051
+ languageFieldset.className = "buttons";
2052
+
2053
+ const activeDocumentLang =
2054
+ normalizeLocaleTag(document.documentElement?.getAttribute?.("lang")) ||
2055
+ detectedLocales[0];
2056
+
2057
+ const radioName = `pds-live-language-${Date.now()}`;
2058
+
2059
+ detectedLocales.forEach((locale) => {
2060
+ const optionLabel = document.createElement("label");
2061
+
2062
+ const optionInput = document.createElement("input");
2063
+ optionInput.type = "radio";
2064
+ optionInput.name = radioName;
2065
+ optionInput.value = locale;
2066
+ optionInput.checked = localeMatches(locale, activeDocumentLang);
2067
+
2068
+ const optionText = document.createElement("span");
2069
+
2070
+ optionText.textContent = localeOptionLabel(locale);
2071
+
2072
+ optionLabel.append(optionInput, optionText);
2073
+ languageFieldset.appendChild(optionLabel);
2074
+ });
2075
+
2076
+ languageFieldset.addEventListener("change", (event) => {
2077
+ const selected = event.target;
2078
+ if (!(selected instanceof HTMLInputElement)) return;
2079
+ if (selected.type !== "radio") return;
2080
+
2081
+ const nextLocale = normalizeLocaleTag(selected.value);
2082
+ if (!nextLocale) return;
2083
+ document.documentElement.setAttribute("lang", nextLocale);
2084
+ });
2085
+
2086
+ languageGroup.append(languageText, languageFieldset);
2087
+ return languageGroup;
2088
+ }
2089
+
1634
2090
  function getPresetOptions() {
1635
2091
  const presets = PDS?.presets || {};
1636
2092
  return Object.values(presets)
@@ -1792,7 +2248,7 @@ async function exportFromLiveEdit(format) {
1792
2248
  const config = getLiveEditExportConfig();
1793
2249
  const content = buildConfigModuleContent(config);
1794
2250
  downloadTextFile(content, "pds.config.js", "text/javascript");
1795
- await PDS?.toast?.("Exported config file", { type: "success" });
2251
+ await PDS?.toast?.(msg("Exported config file"), { type: "success" });
1796
2252
  return;
1797
2253
  }
1798
2254
 
@@ -1807,12 +2263,12 @@ async function exportFromLiveEdit(format) {
1807
2263
  const figmaTokens = figmafyTokens(rawTokens);
1808
2264
  const content = JSON.stringify(figmaTokens, null, 2);
1809
2265
  downloadTextFile(content, "design-tokens.figma.json", "application/json");
1810
- await PDS?.toast?.("Exported Figma tokens", { type: "success" });
2266
+ await PDS?.toast?.(msg("Exported Figma tokens"), { type: "success" });
1811
2267
  return;
1812
2268
  }
1813
2269
  } catch (error) {
1814
2270
  console.warn("[pds-live-edit] Export failed", error);
1815
- await PDS?.toast?.("Export failed", { type: "error" });
2271
+ await PDS?.toast?.(msg("Export failed"), { type: "error" });
1816
2272
  }
1817
2273
  }
1818
2274
 
@@ -1999,7 +2455,7 @@ async function createConfiguredForm({
1999
2455
  const applyBtn = document.createElement("button");
2000
2456
  applyBtn.className = "btn-primary btn-sm";
2001
2457
  applyBtn.type = "button";
2002
- applyBtn.textContent = "Apply";
2458
+ applyBtn.textContent = msg("Apply");
2003
2459
  applyBtn.addEventListener("click", async () => {
2004
2460
  if (typeof form.getValuesFlat === "function") {
2005
2461
  if (!customElements.get("pds-form")) {
@@ -2027,8 +2483,8 @@ async function createConfiguredForm({
2027
2483
  const undoBtn = document.createElement("button");
2028
2484
  undoBtn.className = "btn-secondary btn-sm icon-only";
2029
2485
  undoBtn.type = "button";
2030
- undoBtn.setAttribute("aria-label", "Undo");
2031
- undoBtn.setAttribute("title", "Undo");
2486
+ undoBtn.setAttribute("aria-label", msg("Undo"));
2487
+ undoBtn.setAttribute("title", msg("Undo"));
2032
2488
  const undoIcon = document.createElement("pds-icon");
2033
2489
  undoIcon.setAttribute("icon", "arrow-counter-clockwise");
2034
2490
  undoIcon.setAttribute("size", "sm");
@@ -2185,8 +2641,8 @@ function withThemeConditionalSchema(schema, uiSchema) {
2185
2641
  baseSchema.properties[FORM_THEME_CONTEXT_FIELD] = {
2186
2642
  type: "string",
2187
2643
  oneOf: [
2188
- { const: "light", title: "Light" },
2189
- { const: "dark", title: "Dark" },
2644
+ { const: "light", title: msg("Light") },
2645
+ { const: "dark", title: msg("Dark") },
2190
2646
  ],
2191
2647
  };
2192
2648
 
@@ -2342,7 +2798,7 @@ function buildGroupedFullConfigPayload(payload, design) {
2342
2798
  ...Object.fromEntries(scalarKeys.map((key) => [key, rootProperties[key]])),
2343
2799
  [FULL_CONFIG_GROUPS_KEY]: {
2344
2800
  type: "object",
2345
- title: "Design Groups",
2801
+ title: msg("Design Groups"),
2346
2802
  properties: Object.fromEntries(
2347
2803
  groupedKeys.map((key) => [key, rootProperties[key]])
2348
2804
  ),
@@ -2984,7 +3440,7 @@ class PdsLiveEdit extends HTMLElement {
2984
3440
  button.className = `context-edit btn-primary btn-xs icon-only ${MARKER_CLASS}`;
2985
3441
  button.setAttribute("type", "button");
2986
3442
  button.setAttribute("data-direction", "auto");
2987
- button.setAttribute("aria-label", "Edit design settings");
3443
+ button.setAttribute("aria-label", msg("Edit design settings"));
2988
3444
 
2989
3445
  const icon = document.createElement("pds-icon");
2990
3446
  icon.setAttribute("icon", "pencil");
@@ -3000,7 +3456,7 @@ class PdsLiveEdit extends HTMLElement {
3000
3456
 
3001
3457
  const title = document.createElement("span");
3002
3458
  title.className = "pds-live-editor-title";
3003
- title.textContent = "Quick edit";
3459
+ title.textContent = msg("Quick edit");
3004
3460
  header.appendChild(title);
3005
3461
 
3006
3462
  quickItem.appendChild(header);
@@ -3055,7 +3511,7 @@ class PdsLiveEdit extends HTMLElement {
3055
3511
  } catch (error) {
3056
3512
  const fallback = document.createElement("p");
3057
3513
  fallback.className = "text-muted";
3058
- fallback.textContent = "Editor form unavailable. Lazy component definition did not complete in time.";
3514
+ fallback.textContent = msg("Editor form unavailable. Lazy component definition did not complete in time.");
3059
3515
  container.appendChild(fallback);
3060
3516
  console.warn("[PDS Live Edit] Failed to render quick form:", error);
3061
3517
  return;
@@ -3074,7 +3530,7 @@ class PdsLiveEdit extends HTMLElement {
3074
3530
  const gearBtn = document.createElement("button");
3075
3531
  gearBtn.className = "btn-outline btn-sm icon-only";
3076
3532
  gearBtn.type = "button";
3077
- gearBtn.setAttribute("aria-label", "More settings");
3533
+ gearBtn.setAttribute("aria-label", msg("More settings"));
3078
3534
  const gearIcon = document.createElement("pds-icon");
3079
3535
  gearIcon.setAttribute("icon", "caret-right");
3080
3536
  gearIcon.setAttribute("size", "sm");
@@ -3104,7 +3560,7 @@ class PdsLiveEdit extends HTMLElement {
3104
3560
  const button = document.createElement("button");
3105
3561
  button.type = "button";
3106
3562
  button.className = "btn-outline btn-sm icon-only";
3107
- button.setAttribute("aria-label", "Quick theme and preset");
3563
+ button.setAttribute("aria-label", msg("Quick theme, preset and language"));
3108
3564
 
3109
3565
  const icon = document.createElement("pds-icon");
3110
3566
  icon.setAttribute("icon", "palette");
@@ -3125,15 +3581,20 @@ class PdsLiveEdit extends HTMLElement {
3125
3581
  const themeLabel = document.createElement("label");
3126
3582
  themeLabel.className = "stack-xs";
3127
3583
  const themeText = document.createElement("span");
3128
- themeText.textContent = "Theme";
3584
+ themeText.textContent = msg("Theme");
3129
3585
  const themeToggle = document.createElement("pds-theme");
3130
3586
  themeLabel.append(themeText, themeToggle);
3131
3587
  content.appendChild(themeLabel);
3132
3588
 
3589
+ const languageSelector = await buildQuickLanguageSelector();
3590
+ if (languageSelector) {
3591
+ content.appendChild(languageSelector);
3592
+ }
3593
+
3133
3594
  const presetLabel = document.createElement("label");
3134
3595
  presetLabel.className = "stack-xs";
3135
3596
  const presetText = document.createElement("span");
3136
- presetText.textContent = "Preset";
3597
+ presetText.textContent = msg("Preset");
3137
3598
  presetLabel.appendChild(presetText);
3138
3599
 
3139
3600
  let presetControlRendered = false;
@@ -3142,7 +3603,7 @@ class PdsLiveEdit extends HTMLElement {
3142
3603
 
3143
3604
  const presetOmnibox = document.createElement("pds-omnibox");
3144
3605
  presetOmnibox.setAttribute("item-grid", "0 1fr 0");
3145
- presetOmnibox.setAttribute("placeholder", "Search presets...");
3606
+ presetOmnibox.setAttribute("placeholder", msg("Search presets..."));
3146
3607
 
3147
3608
  const activePresetId = getActivePresetId();
3148
3609
  const activePresetName = getPresetNameById(activePresetId);
@@ -3238,7 +3699,7 @@ class PdsLiveEdit extends HTMLElement {
3238
3699
  const header = document.createElement("div");
3239
3700
  header.setAttribute("slot", "drawer-header");
3240
3701
  header.className = "flex items-center justify-between";
3241
- header.textContent = "Design settings";
3702
+ header.textContent = msg("Design settings");
3242
3703
 
3243
3704
  const content = document.createElement("div");
3244
3705
  content.setAttribute("slot", "drawer-content");
@@ -3248,14 +3709,14 @@ class PdsLiveEdit extends HTMLElement {
3248
3709
  presetCard.className = "card surface-elevated stack-sm";
3249
3710
 
3250
3711
  const presetTitle = document.createElement("h4");
3251
- presetTitle.textContent = "Preset";
3712
+ presetTitle.textContent = msg("Preset");
3252
3713
  presetCard.appendChild(presetTitle);
3253
3714
 
3254
3715
  const presetLabel = document.createElement("label");
3255
3716
  presetLabel.className = "stack-xs";
3256
3717
 
3257
3718
  const presetText = document.createElement("span");
3258
- presetText.textContent = "Choose a base style";
3719
+ presetText.textContent = msg("Choose a base style");
3259
3720
  presetLabel.appendChild(presetText);
3260
3721
 
3261
3722
  let presetControlRendered = false;
@@ -3264,7 +3725,7 @@ class PdsLiveEdit extends HTMLElement {
3264
3725
 
3265
3726
  const presetOmnibox = document.createElement("pds-omnibox");
3266
3727
  presetOmnibox.setAttribute("item-grid", "0 1fr 0");
3267
- presetOmnibox.setAttribute("placeholder", "Search presets...");
3728
+ presetOmnibox.setAttribute("placeholder", msg("Search presets..."));
3268
3729
 
3269
3730
  const activePresetId = getActivePresetId();
3270
3731
  const activePresetName = getPresetNameById(activePresetId);
@@ -3330,7 +3791,7 @@ class PdsLiveEdit extends HTMLElement {
3330
3791
  themeCard.className = "card surface-elevated stack-sm";
3331
3792
 
3332
3793
  const themeTitle = document.createElement("h4");
3333
- themeTitle.textContent = "Theme";
3794
+ themeTitle.textContent = msg("Theme");
3334
3795
  themeCard.appendChild(themeTitle);
3335
3796
 
3336
3797
  const themeToggle = document.createElement("pds-theme");
@@ -3340,13 +3801,13 @@ class PdsLiveEdit extends HTMLElement {
3340
3801
  configCard.className = "card surface-elevated stack-sm";
3341
3802
 
3342
3803
  const configTitle = document.createElement("h4");
3343
- configTitle.textContent = "Configuration";
3804
+ configTitle.textContent = msg("Configuration");
3344
3805
  configCard.appendChild(configTitle);
3345
3806
 
3346
3807
  const configDescription = document.createElement("p");
3347
3808
  configDescription.className = "text-muted";
3348
3809
  configDescription.textContent =
3349
- "Edit the full design config generated from PDS metadata.";
3810
+ msg("Edit the full design config generated from PDS metadata.");
3350
3811
  configCard.appendChild(configDescription);
3351
3812
 
3352
3813
  const configFormContainer = document.createElement("div");
@@ -3376,7 +3837,7 @@ class PdsLiveEdit extends HTMLElement {
3376
3837
  const unavailable = document.createElement("p");
3377
3838
  unavailable.className = "text-muted";
3378
3839
  unavailable.textContent =
3379
- "Full config metadata is unavailable in this runtime.";
3840
+ msg("Full config metadata is unavailable in this runtime.");
3380
3841
  configFormContainer.appendChild(unavailable);
3381
3842
  }
3382
3843
 
@@ -3384,7 +3845,7 @@ class PdsLiveEdit extends HTMLElement {
3384
3845
  templateCard.className = "card surface-elevated stack-sm";
3385
3846
 
3386
3847
  const templateTitle = document.createElement("h4");
3387
- templateTitle.textContent = "Canvas Templates";
3848
+ templateTitle.textContent = msg("Canvas Templates");
3388
3849
  templateCard.appendChild(templateTitle);
3389
3850
 
3390
3851
  const templateCanvas = document.createElement("pds-live-template-canvas");
@@ -3397,7 +3858,7 @@ class PdsLiveEdit extends HTMLElement {
3397
3858
  importCard.className = "card surface-elevated stack-sm";
3398
3859
 
3399
3860
  const importTitle = document.createElement("h4");
3400
- importTitle.textContent = "Import & Convert";
3861
+ importTitle.textContent = msg("Import & Convert");
3401
3862
  importCard.appendChild(importTitle);
3402
3863
 
3403
3864
  const importer = document.createElement("pds-live-importer");
@@ -3418,7 +3879,7 @@ class PdsLiveEdit extends HTMLElement {
3418
3879
  exportCard.className = "card surface-elevated stack-sm";
3419
3880
 
3420
3881
  const exportTitle = document.createElement("h4");
3421
- exportTitle.textContent = "Export";
3882
+ exportTitle.textContent = msg("Export");
3422
3883
  exportCard.appendChild(exportTitle);
3423
3884
 
3424
3885
  const exportNav = document.createElement("nav");
@@ -3431,7 +3892,7 @@ class PdsLiveEdit extends HTMLElement {
3431
3892
  exportIcon.setAttribute("icon", "download");
3432
3893
  exportIcon.setAttribute("size", "sm");
3433
3894
  const exportLabel = document.createElement("span");
3434
- exportLabel.textContent = "Download";
3895
+ exportLabel.textContent = msg("Download");
3435
3896
  const exportCaret = document.createElement("pds-icon");
3436
3897
  exportCaret.setAttribute("icon", "caret-down");
3437
3898
  exportCaret.setAttribute("size", "sm");
@@ -3450,7 +3911,7 @@ class PdsLiveEdit extends HTMLElement {
3450
3911
  configIcon.setAttribute("icon", "file-js");
3451
3912
  configIcon.setAttribute("size", "sm");
3452
3913
  const configLabel = document.createElement("span");
3453
- configLabel.textContent = "Config File";
3914
+ configLabel.textContent = msg("Config File");
3454
3915
  configLink.append(configIcon, configLabel);
3455
3916
  configItem.appendChild(configLink);
3456
3917
 
@@ -3465,7 +3926,7 @@ class PdsLiveEdit extends HTMLElement {
3465
3926
  figmaIcon.setAttribute("icon", "brackets-curly");
3466
3927
  figmaIcon.setAttribute("size", "sm");
3467
3928
  const figmaLabel = document.createElement("span");
3468
- figmaLabel.textContent = "Figma Tokens (JSON)";
3929
+ figmaLabel.textContent = msg("Figma Tokens (JSON)");
3469
3930
  figmaLink.append(figmaIcon, figmaLabel);
3470
3931
  figmaItem.appendChild(figmaLink);
3471
3932
 
@@ -3476,7 +3937,7 @@ class PdsLiveEdit extends HTMLElement {
3476
3937
  const resetButton = document.createElement("button");
3477
3938
  resetButton.type = "button";
3478
3939
  resetButton.className = "btn-outline pds-live-editor-reset-btn";
3479
- resetButton.textContent = "Reset Config";
3940
+ resetButton.textContent = msg("Reset Config");
3480
3941
  resetButton.addEventListener("click", async () => {
3481
3942
  await this.resetConfig();
3482
3943
  });
@@ -3487,7 +3948,7 @@ class PdsLiveEdit extends HTMLElement {
3487
3948
 
3488
3949
  const accordion = document.createElement("section");
3489
3950
  accordion.className = "accordion";
3490
- accordion.setAttribute("aria-label", "Design settings groups");
3951
+ accordion.setAttribute("aria-label", msg("Design settings groups"));
3491
3952
 
3492
3953
  const buildAccordionGroup = (title, nodes = [], open = false, sectionId = "") => {
3493
3954
  const details = document.createElement("details");
@@ -3507,11 +3968,11 @@ class PdsLiveEdit extends HTMLElement {
3507
3968
  return details;
3508
3969
  };
3509
3970
 
3510
- accordion.appendChild(buildAccordionGroup("Preset & Theme", [presetCard, themeCard], true, "preset-theme"));
3511
- accordion.appendChild(buildAccordionGroup("Configuration", [configCard], false, "configuration"));
3512
- accordion.appendChild(buildAccordionGroup("Canvas", [templateCard], false, "canvas"));
3971
+ accordion.appendChild(buildAccordionGroup(msg("Preset & Theme"), [presetCard, themeCard], true, "preset-theme"));
3972
+ accordion.appendChild(buildAccordionGroup(msg("Configuration"), [configCard], false, "configuration"));
3973
+ accordion.appendChild(buildAccordionGroup(msg("Canvas"), [templateCard], false, "canvas"));
3513
3974
  accordion.appendChild(
3514
- buildAccordionGroup("Import & Export", [importCard, exportCard], false, "import-convert-export")
3975
+ buildAccordionGroup(msg("Import & Export"), [importCard, exportCard], false, "import-convert-export")
3515
3976
  );
3516
3977
 
3517
3978
  content.appendChild(accordion);
@@ -3582,13 +4043,13 @@ class PdsLiveEdit extends HTMLElement {
3582
4043
  let confirmed = true;
3583
4044
  if (typeof PDS?.ask === "function") {
3584
4045
  confirmed = await PDS.ask(
3585
- "This clears your saved local configuration and reloads the page.",
4046
+ msg("This clears your saved local configuration and reloads the page."),
3586
4047
  {
3587
- title: "Reset Config?",
4048
+ title: msg("Reset Config?"),
3588
4049
  type: "confirm",
3589
4050
  buttons: {
3590
- ok: { name: "Reset", variant: "danger" },
3591
- cancel: { name: "Cancel", cancel: true },
4051
+ ok: { name: msg("Reset"), variant: "danger" },
4052
+ cancel: { name: msg("Cancel"), cancel: true },
3592
4053
  },
3593
4054
  }
3594
4055
  );