@pure-ds/core 0.7.36 → 0.7.38
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.
- package/dist/types/pds.d.ts +2 -0
- package/dist/types/src/js/common/localization-resource-provider.d.ts +2 -0
- package/dist/types/src/js/common/localization-resource-provider.d.ts.map +1 -1
- package/dist/types/src/js/common/localization.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -1
- package/package.json +1 -1
- package/public/assets/js/app.js +5 -5
- package/public/assets/js/pds-enhancers.js +1 -1
- package/public/assets/js/pds-localization.js +1 -1
- package/public/assets/js/pds-manager.js +17 -17
- package/public/assets/pds/components/pds-live-edit.js +18 -2
- package/public/assets/pds/core/pds-enhancers.js +1 -1
- package/public/assets/pds/core/pds-localization.js +1 -1
- package/public/assets/pds/core/pds-manager.js +17 -17
- package/src/js/common/localization-resource-provider.js +60 -15
- package/src/js/common/localization.js +164 -2
- package/src/js/pds-core/pds-live.js +4 -3
- package/src/js/pds.d.ts +2 -0
|
@@ -71,6 +71,59 @@ function normalizeAliasMap(aliases = {}) {
|
|
|
71
71
|
return normalized;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function resolveAliasTarget(aliasKey, aliases, localeSet) {
|
|
75
|
+
const values = Array.isArray(aliases?.[aliasKey]) ? aliases[aliasKey] : [];
|
|
76
|
+
for (const value of values) {
|
|
77
|
+
const normalizedValue = normalizeLocaleTag(value);
|
|
78
|
+
if (localeSet.has(normalizedValue)) {
|
|
79
|
+
return normalizedValue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createAliasLookup({ aliases, localeSet }) {
|
|
86
|
+
const lookup = new Map();
|
|
87
|
+
|
|
88
|
+
for (const locale of localeSet) {
|
|
89
|
+
lookup.set(locale, locale);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const key of Object.keys(aliases || {})) {
|
|
93
|
+
const target = resolveAliasTarget(key, aliases, localeSet);
|
|
94
|
+
if (!target) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`[i18n] Locale alias "${key}" does not map to any configured locale.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
lookup.set(key, target);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return lookup;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function resolveConfiguredLocale(locale, { defaultLocale, aliasLookup }) {
|
|
106
|
+
const normalized = normalizeLocaleTag(locale);
|
|
107
|
+
if (!normalized) {
|
|
108
|
+
return defaultLocale;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const direct = aliasLookup.get(normalized);
|
|
112
|
+
if (direct) {
|
|
113
|
+
return direct;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const base = toBaseLocale(normalized);
|
|
117
|
+
const baseMapped = base ? aliasLookup.get(base) : "";
|
|
118
|
+
if (baseMapped) {
|
|
119
|
+
return baseMapped;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
throw new Error(
|
|
123
|
+
`[i18n] Locale alias "${locale}" is not configured. Add an alias entry for "${base || normalized}".`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
74
127
|
function normalizeBundleShape(bundle) {
|
|
75
128
|
if (!bundle || typeof bundle !== "object" || Array.isArray(bundle)) {
|
|
76
129
|
return {};
|
|
@@ -177,6 +230,7 @@ function buildCandidateLocales({ locale, effectiveLocale, defaultLocale, aliases
|
|
|
177
230
|
* locales: string[],
|
|
178
231
|
* provider: {
|
|
179
232
|
* locales: string[],
|
|
233
|
+
* resolveLocale: (locale: string) => string,
|
|
180
234
|
* loadLocale: (context: { locale: string }) => Promise<Record<string, string | { content?: string }>>,
|
|
181
235
|
* },
|
|
182
236
|
* }}
|
|
@@ -186,6 +240,7 @@ export function createJSONLocalization(options = {}) {
|
|
|
186
240
|
const locales = toLocaleList(options?.locales, defaultLocale);
|
|
187
241
|
const localeSet = new Set(locales);
|
|
188
242
|
const aliases = normalizeAliasMap(options?.aliases || {});
|
|
243
|
+
const aliasLookup = createAliasLookup({ aliases, localeSet });
|
|
189
244
|
const cache = options?.cache instanceof Map ? options.cache : new Map();
|
|
190
245
|
const basePath = normalizeBasePath(options?.basePath);
|
|
191
246
|
const requestInit =
|
|
@@ -194,21 +249,10 @@ export function createJSONLocalization(options = {}) {
|
|
|
194
249
|
: {};
|
|
195
250
|
|
|
196
251
|
const resolveEffectiveLocale = (locale) => {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (localeSet.has(normalized)) {
|
|
203
|
-
return normalized;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const base = toBaseLocale(normalized);
|
|
207
|
-
if (localeSet.has(base)) {
|
|
208
|
-
return base;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return defaultLocale;
|
|
252
|
+
return resolveConfiguredLocale(locale, {
|
|
253
|
+
defaultLocale,
|
|
254
|
+
aliasLookup,
|
|
255
|
+
});
|
|
212
256
|
};
|
|
213
257
|
|
|
214
258
|
const loadLocale = async ({ locale }) => {
|
|
@@ -268,6 +312,7 @@ export function createJSONLocalization(options = {}) {
|
|
|
268
312
|
locales: [...locales],
|
|
269
313
|
provider: {
|
|
270
314
|
locales: [...locales],
|
|
315
|
+
resolveLocale: resolveEffectiveLocale,
|
|
271
316
|
loadLocale,
|
|
272
317
|
},
|
|
273
318
|
};
|
|
@@ -11,10 +11,22 @@ const __localizationState = {
|
|
|
11
11
|
reconcileTimer: null,
|
|
12
12
|
requestedKeys: new Set(),
|
|
13
13
|
textNodeKeyMap: new WeakMap(),
|
|
14
|
+
attributeKeyMap: new WeakMap(),
|
|
14
15
|
valueToKeys: new Map(),
|
|
15
16
|
missingWarnings: new Set(),
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
const __LOCALIZABLE_ATTRIBUTES = [
|
|
20
|
+
"title",
|
|
21
|
+
"placeholder",
|
|
22
|
+
"aria-label",
|
|
23
|
+
"aria-description",
|
|
24
|
+
"aria-placeholder",
|
|
25
|
+
"aria-roledescription",
|
|
26
|
+
"alt",
|
|
27
|
+
"label",
|
|
28
|
+
];
|
|
29
|
+
|
|
18
30
|
const __isStrTagged = (val) =>
|
|
19
31
|
Boolean(val) && typeof val !== "string" && typeof val === "object" && "strTag" in val;
|
|
20
32
|
|
|
@@ -33,6 +45,15 @@ function __resolveLocaleCandidate(locale) {
|
|
|
33
45
|
if (!normalized) {
|
|
34
46
|
return __localizationState.defaultLocale;
|
|
35
47
|
}
|
|
48
|
+
|
|
49
|
+
const resolveLocale = __localizationState.provider?.resolveLocale;
|
|
50
|
+
if (typeof resolveLocale === "function") {
|
|
51
|
+
const resolved = __normalizeLocale(resolveLocale(locale));
|
|
52
|
+
if (resolved) {
|
|
53
|
+
return resolved;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
return normalized;
|
|
37
58
|
}
|
|
38
59
|
|
|
@@ -98,7 +119,14 @@ function __resolveProvider(config) {
|
|
|
98
119
|
? provider.setLocale
|
|
99
120
|
: null;
|
|
100
121
|
|
|
101
|
-
|
|
122
|
+
const resolveLocale =
|
|
123
|
+
typeof config?.resolveLocale === "function"
|
|
124
|
+
? config.resolveLocale
|
|
125
|
+
: typeof provider?.resolveLocale === "function"
|
|
126
|
+
? provider.resolveLocale
|
|
127
|
+
: null;
|
|
128
|
+
|
|
129
|
+
if (!translate && !loadLocale && !setLocale && !resolveLocale) {
|
|
102
130
|
return null;
|
|
103
131
|
}
|
|
104
132
|
|
|
@@ -106,6 +134,7 @@ function __resolveProvider(config) {
|
|
|
106
134
|
translate,
|
|
107
135
|
loadLocale,
|
|
108
136
|
setLocale,
|
|
137
|
+
resolveLocale,
|
|
109
138
|
};
|
|
110
139
|
}
|
|
111
140
|
|
|
@@ -599,10 +628,135 @@ async function __localizeRequestedTextNodes() {
|
|
|
599
628
|
}
|
|
600
629
|
}
|
|
601
630
|
|
|
631
|
+
function __getElementAttributeKeyMap(element) {
|
|
632
|
+
let map = __localizationState.attributeKeyMap.get(element);
|
|
633
|
+
if (!map) {
|
|
634
|
+
map = new Map();
|
|
635
|
+
__localizationState.attributeKeyMap.set(element, map);
|
|
636
|
+
}
|
|
637
|
+
return map;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
async function __localizeAttribute(element, attrName) {
|
|
641
|
+
if (!element || typeof element.getAttribute !== "function") {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const rawValue = element.getAttribute(attrName);
|
|
646
|
+
if (typeof rawValue !== "string" || !rawValue.length) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const keyMap = __getElementAttributeKeyMap(element);
|
|
651
|
+
let key = keyMap.get(attrName) || null;
|
|
652
|
+
|
|
653
|
+
if (!key || !__localizationState.requestedKeys.has(key)) {
|
|
654
|
+
key = __findRequestedKeyForText(rawValue);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (!key) {
|
|
658
|
+
const segmentMatch = __findRequestedSubsegmentForText(rawValue);
|
|
659
|
+
if (!segmentMatch) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const scopedLocale = __resolveContextLocale({ element });
|
|
664
|
+
await __loadLocaleInternal(scopedLocale, "attribute");
|
|
665
|
+
|
|
666
|
+
const translated = __resolveTranslation(segmentMatch.key, segmentMatch.values, { element }, null);
|
|
667
|
+
const translatedText = segmentMatch.values.length
|
|
668
|
+
? __replacePlaceholders(translated, (index) => segmentMatch.values[index])
|
|
669
|
+
: translated;
|
|
670
|
+
|
|
671
|
+
const localizedValue =
|
|
672
|
+
rawValue.slice(0, segmentMatch.start) +
|
|
673
|
+
translatedText +
|
|
674
|
+
rawValue.slice(segmentMatch.end);
|
|
675
|
+
|
|
676
|
+
if (localizedValue !== rawValue) {
|
|
677
|
+
element.setAttribute(attrName, localizedValue);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
keyMap.set(attrName, segmentMatch.key);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
keyMap.set(attrName, key);
|
|
685
|
+
|
|
686
|
+
const scopedLocale = __resolveContextLocale({ element });
|
|
687
|
+
await __loadLocaleInternal(scopedLocale, "attribute");
|
|
688
|
+
|
|
689
|
+
const values = __resolveTemplateValuesForText(key, rawValue);
|
|
690
|
+
const translated = __resolveTranslation(key, values, { element }, null);
|
|
691
|
+
const translatedText = values.length
|
|
692
|
+
? __replacePlaceholders(translated, (index) => values[index])
|
|
693
|
+
: translated;
|
|
694
|
+
|
|
695
|
+
if (translatedText !== rawValue) {
|
|
696
|
+
element.setAttribute(attrName, translatedText);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function __localizeRequestedAttributes() {
|
|
701
|
+
if (typeof document === "undefined" || __localizationState.requestedKeys.size === 0) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const root = document.body || document.documentElement;
|
|
706
|
+
if (!root) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const roots = [];
|
|
711
|
+
const seenRoots = new Set();
|
|
712
|
+
|
|
713
|
+
const addRoot = (candidateRoot) => {
|
|
714
|
+
if (!candidateRoot || seenRoots.has(candidateRoot)) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
seenRoots.add(candidateRoot);
|
|
719
|
+
roots.push(candidateRoot);
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
addRoot(root);
|
|
723
|
+
|
|
724
|
+
for (let index = 0; index < roots.length; index += 1) {
|
|
725
|
+
const currentRoot = roots[index];
|
|
726
|
+
if (!currentRoot || typeof currentRoot.querySelectorAll !== "function") {
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const elements = currentRoot.querySelectorAll("*");
|
|
731
|
+
for (const element of elements) {
|
|
732
|
+
const shadowRoot = element?.shadowRoot;
|
|
733
|
+
if (shadowRoot) {
|
|
734
|
+
addRoot(shadowRoot);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
for (const scanRoot of roots) {
|
|
740
|
+
if (!scanRoot || typeof scanRoot.querySelectorAll !== "function") {
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const elements = scanRoot.querySelectorAll("*");
|
|
745
|
+
for (const element of elements) {
|
|
746
|
+
for (const attrName of __LOCALIZABLE_ATTRIBUTES) {
|
|
747
|
+
if (element.hasAttribute(attrName)) {
|
|
748
|
+
await __localizeAttribute(element, attrName);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
602
755
|
async function __reconcileLocalization() {
|
|
603
756
|
const detectedLocales = __collectDetectedLocales();
|
|
604
757
|
await __ensureDetectedLocalesLoaded(detectedLocales);
|
|
605
758
|
await __localizeRequestedTextNodes();
|
|
759
|
+
await __localizeRequestedAttributes();
|
|
606
760
|
__pruneUndetectedLocales(detectedLocales);
|
|
607
761
|
}
|
|
608
762
|
|
|
@@ -748,6 +902,7 @@ export function configureLocalization(config = null) {
|
|
|
748
902
|
__localizationState.loadingByLocale.clear();
|
|
749
903
|
__localizationState.requestedKeys.clear();
|
|
750
904
|
__localizationState.textNodeKeyMap = new WeakMap();
|
|
905
|
+
__localizationState.attributeKeyMap = new WeakMap();
|
|
751
906
|
__localizationState.valueToKeys.clear();
|
|
752
907
|
__localizationState.missingWarnings.clear();
|
|
753
908
|
|
|
@@ -767,11 +922,18 @@ export function configureLocalization(config = null) {
|
|
|
767
922
|
Object.prototype.hasOwnProperty.call(config, "provider") ||
|
|
768
923
|
Object.prototype.hasOwnProperty.call(config, "translate") ||
|
|
769
924
|
Object.prototype.hasOwnProperty.call(config, "loadLocale") ||
|
|
770
|
-
Object.prototype.hasOwnProperty.call(config, "setLocale")
|
|
925
|
+
Object.prototype.hasOwnProperty.call(config, "setLocale") ||
|
|
926
|
+
Object.prototype.hasOwnProperty.call(config, "resolveLocale")
|
|
771
927
|
) {
|
|
772
928
|
__localizationState.provider = __resolveProvider(config);
|
|
773
929
|
}
|
|
774
930
|
|
|
931
|
+
if (__localizationState.provider?.resolveLocale) {
|
|
932
|
+
__localizationState.defaultLocale = __resolveLocaleCandidate(
|
|
933
|
+
__localizationState.defaultLocale
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
|
|
775
937
|
__attachLangObserver();
|
|
776
938
|
__scheduleReconcile();
|
|
777
939
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Separated to keep the base runtime bundle lean for production/static usage.
|
|
4
4
|
*/
|
|
5
5
|
import { Generator } from "./pds-generator.js";
|
|
6
|
+
import { msg } from "../common/localization.js";
|
|
6
7
|
import { applyStyles, adoptLayers, adoptPrimitives } from "./pds-runtime.js";
|
|
7
8
|
import {
|
|
8
9
|
presets,
|
|
@@ -309,10 +310,10 @@ async function ensureLiveEditToggleButton() {
|
|
|
309
310
|
return li;
|
|
310
311
|
};
|
|
311
312
|
|
|
312
|
-
menu.appendChild(createItem("toggle", "Toggle live editing", "pencil"));
|
|
313
|
-
menu.appendChild(createItem("open-settings", "Open Settings", "gear"));
|
|
313
|
+
menu.appendChild(createItem("toggle", msg("Toggle live editing"), "pencil"));
|
|
314
|
+
menu.appendChild(createItem("open-settings", msg("Open Settings"), "gear"));
|
|
314
315
|
menu.appendChild(createSeparator());
|
|
315
|
-
menu.appendChild(createItem("reset-config", "Reset Config", "arrow-counter-clockwise"));
|
|
316
|
+
menu.appendChild(createItem("reset-config", msg("Reset Config"), "arrow-counter-clockwise"));
|
|
316
317
|
|
|
317
318
|
await ensureSharedQuickModeToggleMenuItem(menu);
|
|
318
319
|
|
package/src/js/pds.d.ts
CHANGED
|
@@ -124,6 +124,7 @@ export interface PDSLocalizationTranslateContext {
|
|
|
124
124
|
|
|
125
125
|
export interface PDSLocalizationProvider {
|
|
126
126
|
translate?: (context: PDSLocalizationTranslateContext) => string | undefined | null;
|
|
127
|
+
resolveLocale?: (locale: string) => string;
|
|
127
128
|
loadLocale?: (context: {
|
|
128
129
|
locale: string;
|
|
129
130
|
defaultLocale: string;
|
|
@@ -154,6 +155,7 @@ export interface PDSLocalizationConfig {
|
|
|
154
155
|
messages?: PDSLocalizationMessages;
|
|
155
156
|
provider?: PDSLocalizationProvider;
|
|
156
157
|
translate?: PDSLocalizationProvider["translate"];
|
|
158
|
+
resolveLocale?: PDSLocalizationProvider["resolveLocale"];
|
|
157
159
|
loadLocale?: PDSLocalizationProvider["loadLocale"];
|
|
158
160
|
setLocale?: PDSLocalizationProvider["setLocale"];
|
|
159
161
|
}
|