@schukai/monster 3.96.2 → 3.97.0
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/CHANGELOG.md +23 -94
- package/package.json +1 -1
- package/source/components/accessibility/locale-picker.mjs +598 -0
- package/source/components/accessibility/style/locale-picker.css +1 -0
- package/source/components/accessibility/style/locale-picker.pcss +26 -0
- package/source/components/accessibility/stylesheet/locale-picker.mjs +31 -0
- package/source/components/content/stylesheet/copy.mjs +2 -2
- package/source/components/datatable/datasource/rest.mjs +33 -10
- package/source/components/datatable/filter.mjs +164 -63
- package/source/components/datatable/stylesheet/change-button.mjs +2 -2
- package/source/components/datatable/stylesheet/column-bar.mjs +2 -2
- package/source/components/datatable/stylesheet/dataset.mjs +2 -2
- package/source/components/datatable/stylesheet/datasource.mjs +1 -1
- package/source/components/datatable/stylesheet/datatable.mjs +1 -1
- package/source/components/datatable/stylesheet/embedded-pagination.mjs +2 -2
- package/source/components/datatable/stylesheet/filter-button.mjs +1 -1
- package/source/components/datatable/stylesheet/filter-controls-defaults.mjs +1 -1
- package/source/components/datatable/stylesheet/filter-date-range.mjs +1 -1
- package/source/components/datatable/stylesheet/filter-range.mjs +1 -1
- package/source/components/datatable/stylesheet/filter-select.mjs +2 -2
- package/source/components/datatable/stylesheet/filter.mjs +2 -2
- package/source/components/datatable/stylesheet/pagination.mjs +1 -1
- package/source/components/datatable/stylesheet/save-button.mjs +2 -2
- package/source/components/datatable/stylesheet/status.mjs +1 -1
- package/source/components/form/form.mjs +4 -2
- package/source/components/form/stylesheet/action-button.mjs +1 -1
- package/source/components/form/stylesheet/api-bar.mjs +1 -1
- package/source/components/form/stylesheet/api-button.mjs +1 -1
- package/source/components/form/stylesheet/button-bar.mjs +1 -1
- package/source/components/form/stylesheet/button.mjs +1 -1
- package/source/components/form/stylesheet/confirm-button.mjs +1 -1
- package/source/components/form/stylesheet/context-error.mjs +1 -1
- package/source/components/form/stylesheet/context-help.mjs +1 -1
- package/source/components/form/stylesheet/field-set.mjs +1 -1
- package/source/components/form/stylesheet/form.mjs +1 -1
- package/source/components/form/stylesheet/input-group.mjs +1 -1
- package/source/components/form/stylesheet/message-state-button.mjs +1 -1
- package/source/components/form/stylesheet/password.mjs +1 -1
- package/source/components/form/stylesheet/popper-button.mjs +1 -1
- package/source/components/form/stylesheet/select.mjs +1 -1
- package/source/components/form/stylesheet/state-button.mjs +1 -1
- package/source/components/form/stylesheet/toggle-switch.mjs +1 -1
- package/source/components/form/stylesheet/tree-select.mjs +1 -1
- package/source/components/host/stylesheet/call-button.mjs +2 -2
- package/source/components/host/stylesheet/config-manager.mjs +1 -1
- package/source/components/host/stylesheet/host.mjs +2 -2
- package/source/components/host/stylesheet/overlay.mjs +2 -2
- package/source/components/host/stylesheet/toggle-button.mjs +2 -2
- package/source/components/host/stylesheet/viewer.mjs +2 -2
- package/source/components/layout/stylesheet/collapse.mjs +2 -2
- package/source/components/layout/stylesheet/details.mjs +2 -2
- package/source/components/layout/stylesheet/iframe.mjs +1 -1
- package/source/components/layout/stylesheet/panel.mjs +2 -2
- package/source/components/layout/stylesheet/popper.mjs +1 -1
- package/source/components/layout/stylesheet/slider.mjs +2 -2
- package/source/components/layout/stylesheet/split-panel.mjs +1 -1
- package/source/components/layout/stylesheet/tabs.mjs +2 -2
- package/source/components/layout/stylesheet/width-toggle.mjs +1 -1
- package/source/components/navigation/stylesheet/table-of-content.mjs +2 -2
- package/source/components/notify/stylesheet/message.mjs +2 -2
- package/source/components/notify/stylesheet/notify.mjs +1 -1
- package/source/components/state/stylesheet/log.mjs +1 -1
- package/source/components/state/stylesheet/state.mjs +1 -1
- package/source/components/style/property.css +1 -0
- package/source/components/style/theme.css +4 -4
- package/source/components/stylesheet/badge.mjs +1 -1
- package/source/components/stylesheet/border.mjs +1 -1
- package/source/components/stylesheet/button.mjs +1 -1
- package/source/components/stylesheet/card.mjs +1 -1
- package/source/components/stylesheet/color.mjs +1 -1
- package/source/components/stylesheet/common.mjs +1 -1
- package/source/components/stylesheet/control.mjs +1 -1
- package/source/components/stylesheet/data-grid.mjs +1 -1
- package/source/components/stylesheet/display.mjs +1 -1
- package/source/components/stylesheet/floating-ui.mjs +1 -1
- package/source/components/stylesheet/form.mjs +1 -1
- package/source/components/stylesheet/host.mjs +1 -1
- package/source/components/stylesheet/icons.mjs +1 -1
- package/source/components/stylesheet/link.mjs +1 -1
- package/source/components/stylesheet/mixin/badge.mjs +1 -1
- package/source/components/stylesheet/mixin/button.mjs +1 -1
- package/source/components/stylesheet/mixin/hover.mjs +1 -1
- package/source/components/stylesheet/mixin/icon.mjs +1 -1
- package/source/components/stylesheet/mixin/media.mjs +1 -1
- package/source/components/stylesheet/mixin/property.mjs +1 -1
- package/source/components/stylesheet/mixin/skeleton.mjs +1 -1
- package/source/components/stylesheet/mixin/spinner.mjs +1 -1
- package/source/components/stylesheet/mixin/typography.mjs +1 -1
- package/source/components/stylesheet/normalize.mjs +1 -1
- package/source/components/stylesheet/popper.mjs +1 -1
- package/source/components/stylesheet/property.mjs +2 -2
- package/source/components/stylesheet/ripple.mjs +1 -1
- package/source/components/stylesheet/skeleton.mjs +1 -1
- package/source/components/stylesheet/space.mjs +1 -1
- package/source/components/stylesheet/spinner.mjs +1 -1
- package/source/components/stylesheet/table.mjs +1 -1
- package/source/components/stylesheet/theme.mjs +1 -1
- package/source/components/stylesheet/typography.mjs +1 -1
- package/source/components/tree-menu/stylesheet/tree-menu.mjs +1 -1
- package/source/constants.mjs +14 -1
- package/source/data/extend.mjs +2 -1
- package/source/data/transformer.mjs +2 -0
- package/source/dom/customelement.mjs +7 -3
- package/source/dom/updater.mjs +5 -1
- package/source/i18n/locale.mjs +151 -151
- package/source/i18n/map/languages.mjs +104 -0
- package/source/i18n/util.mjs +139 -0
- package/source/monster.mjs +1 -1
- package/source/text/formatter.mjs +5 -3
- package/source/types/is.mjs +13 -0
- package/source/types/proxyobserver.mjs +7 -2
- package/source/types/version.mjs +1 -1
- package/source/util/clone.mjs +9 -14
- package/test/cases/data/pathfinder.mjs +18 -0
- package/test/cases/i18n/util.mjs +295 -0
- package/test/cases/monster.mjs +1 -1
- package/test/cases/text/formatter.mjs +21 -1
- package/test/web/test.html +2 -2
- package/test/web/tests.js +115 -75
package/source/i18n/locale.mjs
CHANGED
@@ -12,12 +12,12 @@
|
|
12
12
|
* SPDX-License-Identifier: AGPL-3.0
|
13
13
|
*/
|
14
14
|
|
15
|
-
import {
|
16
|
-
import {
|
17
|
-
import {
|
18
|
-
import {
|
15
|
+
import {instanceSymbol} from "../constants.mjs";
|
16
|
+
import {Base} from "../types/base.mjs";
|
17
|
+
import {validateString} from "../types/validate.mjs";
|
18
|
+
import {clone} from "../util/clone.mjs";
|
19
19
|
|
20
|
-
export {
|
20
|
+
export {Locale, parseLocale};
|
21
21
|
|
22
22
|
/**
|
23
23
|
* @type {symbol}
|
@@ -69,116 +69,116 @@ const localeStringSymbol = Symbol("localeString");
|
|
69
69
|
* @see https://datatracker.ietf.org/doc/html/rfc3066
|
70
70
|
*/
|
71
71
|
class Locale extends Base {
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
72
|
+
/**
|
73
|
+
* @param {string} language
|
74
|
+
* @param {string} [region]
|
75
|
+
* @param {string} [script]
|
76
|
+
* @param {string} [variants]
|
77
|
+
* @param {string} [extlang]
|
78
|
+
* @param {string} [privateUse]
|
79
|
+
* @throws {Error} unsupported locale
|
80
|
+
*/
|
81
|
+
constructor(language, region, script, variants, extlang, privateUse) {
|
82
|
+
super();
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
84
|
+
this[propertiesSymbol] = {
|
85
|
+
language: language === undefined ? undefined : validateString(language),
|
86
|
+
script: script === undefined ? undefined : validateString(script),
|
87
|
+
region: region === undefined ? undefined : validateString(region),
|
88
|
+
variants: variants === undefined ? undefined : validateString(variants),
|
89
|
+
extlang: extlang === undefined ? undefined : validateString(extlang),
|
90
|
+
privateUse:
|
91
|
+
privateUse === undefined ? undefined : validateString(privateUse),
|
92
|
+
};
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
94
|
+
const s = [];
|
95
|
+
if (language !== undefined) s.push(language);
|
96
|
+
if (script !== undefined) s.push(script);
|
97
|
+
if (region !== undefined) s.push(region);
|
98
|
+
if (variants !== undefined) s.push(variants);
|
99
|
+
if (extlang !== undefined) s.push(extlang);
|
100
|
+
if (privateUse !== undefined) s.push(privateUse);
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
102
|
+
if (s.length === 0) {
|
103
|
+
throw new Error("unsupported locale");
|
104
|
+
}
|
105
105
|
|
106
|
-
|
107
|
-
|
106
|
+
this[localeStringSymbol] = s.join("-");
|
107
|
+
}
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
109
|
+
/**
|
110
|
+
* This method is called by the `instanceof` operator.
|
111
|
+
* @return {symbol}
|
112
|
+
* @since 3.27.0
|
113
|
+
*/
|
114
|
+
static get [instanceSymbol]() {
|
115
|
+
return Symbol.for("@schukai/monster/i18n/locale@@instance");
|
116
|
+
}
|
117
117
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
118
|
+
/**
|
119
|
+
* @return {string}
|
120
|
+
*/
|
121
|
+
get localeString() {
|
122
|
+
return this[localeStringSymbol];
|
123
|
+
}
|
124
124
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
125
|
+
/**
|
126
|
+
* @return {string|undefined}
|
127
|
+
*/
|
128
|
+
get language() {
|
129
|
+
return this[propertiesSymbol].language;
|
130
|
+
}
|
131
131
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
132
|
+
/**
|
133
|
+
* @return {string|undefined}
|
134
|
+
*/
|
135
|
+
get region() {
|
136
|
+
return this[propertiesSymbol].region;
|
137
|
+
}
|
138
138
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
139
|
+
/**
|
140
|
+
* @return {string|undefined}
|
141
|
+
*/
|
142
|
+
get script() {
|
143
|
+
return this[propertiesSymbol].script;
|
144
|
+
}
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
146
|
+
/**
|
147
|
+
* @return {string|undefined}
|
148
|
+
*/
|
149
|
+
get variants() {
|
150
|
+
return this[propertiesSymbol].variants;
|
151
|
+
}
|
152
152
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
153
|
+
/**
|
154
|
+
* @return {string|undefined}
|
155
|
+
*/
|
156
|
+
get extlang() {
|
157
|
+
return this[propertiesSymbol].extlang;
|
158
|
+
}
|
159
159
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
160
|
+
/**
|
161
|
+
* @return {string|undefined}
|
162
|
+
*/
|
163
|
+
get privateUse() {
|
164
|
+
return this[propertiesSymbol].privateValue;
|
165
|
+
}
|
166
166
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
167
|
+
/**
|
168
|
+
* @return {string}
|
169
|
+
*/
|
170
|
+
toString() {
|
171
|
+
return `${this.localeString}`;
|
172
|
+
}
|
173
173
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
174
|
+
/**
|
175
|
+
* The structure has the following: language, script, region, variants, extlang, privateUse
|
176
|
+
*
|
177
|
+
* @return {Monster.I18n.LocaleMap}
|
178
|
+
*/
|
179
|
+
getMap() {
|
180
|
+
return clone(this[propertiesSymbol]);
|
181
|
+
}
|
182
182
|
}
|
183
183
|
|
184
184
|
/**
|
@@ -260,63 +260,63 @@ class Locale extends Base {
|
|
260
260
|
* @throws {Error} unsupported locale
|
261
261
|
*/
|
262
262
|
function parseLocale(locale) {
|
263
|
-
|
263
|
+
locale = validateString(locale).replace(/_/g, "-");
|
264
264
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
265
|
+
let language;
|
266
|
+
let region;
|
267
|
+
let variants;
|
268
|
+
let parts;
|
269
|
+
let script;
|
270
|
+
let extlang;
|
271
|
+
const regexRegular =
|
272
|
+
"(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)";
|
273
|
+
const regexIrregular =
|
274
|
+
"(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)";
|
275
|
+
const regexGrandfathered = `(${regexIrregular}|${regexRegular})`;
|
276
|
+
const regexPrivateUse = "(x(-[A-Za-z0-9]{1,8})+)";
|
277
|
+
const regexSingleton = "[0-9A-WY-Za-wy-z]";
|
278
|
+
const regexExtension = `(${regexSingleton}(-[A-Za-z0-9]{2,8})+)`;
|
279
|
+
const regexVariant = "([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3})";
|
280
|
+
const regexRegion = "([A-Za-z]{2}|[0-9]{3})";
|
281
|
+
const regexScript = "([A-Za-z]{4})";
|
282
|
+
const regexExtlang = "([A-Za-z]{3}(-[A-Za-z]{3}){0,2})";
|
283
|
+
const regexLanguage = `(([A-Za-z]{2,3}(-${regexExtlang})?)|[A-Za-z]{4}|[A-Za-z]{5,8})`;
|
284
|
+
const regexLangtag = `(${regexLanguage}(-${regexScript})?(-${regexRegion})?(-${regexVariant})*(-${regexExtension})*(-${regexPrivateUse})?)`;
|
285
|
+
const regexLanguageTag = `^(${regexGrandfathered}|${regexLangtag}|${regexPrivateUse})$`;
|
286
|
+
const regex = new RegExp(regexLanguageTag);
|
287
|
+
let match;
|
288
288
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
289
|
+
if ((match = regex.exec(locale)) !== null) {
|
290
|
+
if (match.index === regex.lastIndex) {
|
291
|
+
regex.lastIndex++;
|
292
|
+
}
|
293
|
+
}
|
294
294
|
|
295
|
-
|
296
|
-
|
297
|
-
|
295
|
+
if (match === undefined || match === null) {
|
296
|
+
throw new Error("unsupported locale");
|
297
|
+
}
|
298
298
|
|
299
|
-
|
300
|
-
|
299
|
+
if (match[6] !== undefined) {
|
300
|
+
language = match[6];
|
301
301
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
302
|
+
parts = language.split("-");
|
303
|
+
if (parts.length > 1) {
|
304
|
+
language = parts[0];
|
305
|
+
extlang = parts[1];
|
306
|
+
}
|
307
|
+
}
|
308
308
|
|
309
|
-
|
310
|
-
|
311
|
-
|
309
|
+
if (match[14] !== undefined) {
|
310
|
+
region = match[14];
|
311
|
+
}
|
312
312
|
|
313
|
-
|
314
|
-
|
315
|
-
|
313
|
+
if (match[12] !== undefined) {
|
314
|
+
script = match[12];
|
315
|
+
}
|
316
316
|
|
317
|
-
|
318
|
-
|
319
|
-
|
317
|
+
if (match[16] !== undefined) {
|
318
|
+
variants = match[16];
|
319
|
+
}
|
320
320
|
|
321
|
-
|
321
|
+
return new Locale(language, region, script, variants, extlang);
|
322
322
|
}
|
@@ -0,0 +1,104 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
|
3
|
+
* Node module: @schukai/monster
|
4
|
+
*
|
5
|
+
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
|
6
|
+
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
|
7
|
+
*
|
8
|
+
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
|
9
|
+
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
|
10
|
+
* For more information about purchasing a commercial license, please contact schukai GmbH.
|
11
|
+
*
|
12
|
+
* SPDX-License-Identifier: AGPL-3.0
|
13
|
+
*/
|
14
|
+
|
15
|
+
export const languages = {
|
16
|
+
"en": "English",
|
17
|
+
"en-GB": "English (United Kingdom)",
|
18
|
+
"en-US": "English (United States)",
|
19
|
+
"en-CA": "English (Canada)",
|
20
|
+
"en-AU": "English (Australia)",
|
21
|
+
"es": "Español",
|
22
|
+
"es-ES": "Español (España)",
|
23
|
+
"es-MX": "Español (México)",
|
24
|
+
"es-AR": "Español (Argentina)",
|
25
|
+
"zh": "中文",
|
26
|
+
"zh-CN": "中文(简体)",
|
27
|
+
"zh-TW": "中文(繁體)",
|
28
|
+
"zh-HK": "中文(香港)",
|
29
|
+
"fr": "Français",
|
30
|
+
"fr-FR": "Français (France)",
|
31
|
+
"fr-CA": "Français (Canada)",
|
32
|
+
"fr-BE": "Français (Belgique)",
|
33
|
+
"de": "Deutsch",
|
34
|
+
"de-DE": "Deutsch (Deutschland)",
|
35
|
+
"de-AT": "Deutsch (Österreich)",
|
36
|
+
"de-CH": "Deutsch (Schweiz)",
|
37
|
+
"ja": "日本語",
|
38
|
+
"ru": "Русский",
|
39
|
+
"ru-RU": "Русский (Россия)",
|
40
|
+
"it": "Italiano",
|
41
|
+
"it-IT": "Italiano (Italia)",
|
42
|
+
"pt": "Português",
|
43
|
+
"pt-PT": "Português (Portugal)",
|
44
|
+
"pt-BR": "Português (Brasil)",
|
45
|
+
"ko": "한국어",
|
46
|
+
"ar": "العربية",
|
47
|
+
"hi": "हिन्दी",
|
48
|
+
"bn": "বাংলা",
|
49
|
+
"pa": "ਪੰਜਾਬੀ",
|
50
|
+
"id": "Bahasa Indonesia",
|
51
|
+
"vi": "Tiếng Việt",
|
52
|
+
"tr": "Türkçe",
|
53
|
+
"tr-TR": "Türkçe (Türkiye)",
|
54
|
+
"pl": "Polski",
|
55
|
+
"pl-PL": "Polski (Polska)",
|
56
|
+
"uk": "Українська",
|
57
|
+
"uk-UA": "Українська (Україна)",
|
58
|
+
"ro": "Română",
|
59
|
+
"ro-RO": "Română (România)",
|
60
|
+
"nl": "Nederlands",
|
61
|
+
"nl-NL": "Nederlands (Nederland)",
|
62
|
+
"el": "Ελληνικά",
|
63
|
+
"el-GR": "Ελληνικά (Ελλάδα)",
|
64
|
+
"hu": "Magyar",
|
65
|
+
"hu-HU": "Magyar (Magyarország)",
|
66
|
+
"sv": "Svenska",
|
67
|
+
"sv-SE": "Svenska (Sverige)",
|
68
|
+
"cs": "Čeština",
|
69
|
+
"cs-CZ": "Čeština (Česká republika)",
|
70
|
+
"bg": "Български",
|
71
|
+
"bg-BG": "Български (България)",
|
72
|
+
"da": "Dansk",
|
73
|
+
"da-DK": "Dansk (Danmark)",
|
74
|
+
"fi": "Suomi",
|
75
|
+
"fi-FI": "Suomi (Suomi)",
|
76
|
+
"sk": "Slovenčina",
|
77
|
+
"sk-SK": "Slovenčina (Slovensko)",
|
78
|
+
"he": "עברית",
|
79
|
+
"th": "ไทย",
|
80
|
+
"sr": "Српски",
|
81
|
+
"sr-RS": "Српски (Србија)",
|
82
|
+
"no": "Norsk",
|
83
|
+
"no-NO": "Norsk (Norge)",
|
84
|
+
"lt": "Lietuvių",
|
85
|
+
"lt-LT": "Lietuvių (Lietuva)",
|
86
|
+
"lv": "Latviešu",
|
87
|
+
"lv-LV": "Latviešu (Latvija)",
|
88
|
+
"et": "Eesti",
|
89
|
+
"et-EE": "Eesti (Eesti)",
|
90
|
+
"hr": "Hrvatski",
|
91
|
+
"hr-HR": "Hrvatski (Hrvatska)",
|
92
|
+
"sl": "Slovenščina",
|
93
|
+
"sl-SI": "Slovenščina (Slovenija)",
|
94
|
+
"mt": "Malti",
|
95
|
+
"mt-MT": "Malti (Malta)",
|
96
|
+
"is": "Íslenska",
|
97
|
+
"is-IS": "Íslenska (Ísland)",
|
98
|
+
"ga": "Gaeilge",
|
99
|
+
"ga-IE": "Gaeilge (Éire)",
|
100
|
+
"cy": "Cymraeg",
|
101
|
+
"cy-GB": "Cymraeg (Y Deyrnas Unedig)",
|
102
|
+
"sq": "Shqip",
|
103
|
+
"sq-AL": "Shqip (Shqipëria)"
|
104
|
+
};
|
@@ -0,0 +1,139 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
|
3
|
+
* Node module: @schukai/monster
|
4
|
+
*
|
5
|
+
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
|
6
|
+
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
|
7
|
+
*
|
8
|
+
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
|
9
|
+
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
|
10
|
+
* For more information about purchasing a commercial license, please contact schukai GmbH.
|
11
|
+
*
|
12
|
+
* SPDX-License-Identifier: AGPL-3.0
|
13
|
+
*/
|
14
|
+
|
15
|
+
import {languages} from "./map/languages.mjs";
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Determines the user's preferred language based on browser settings and available language options.
|
19
|
+
*
|
20
|
+
* It evaluates the current HTML document language, the browser's defined languages, and
|
21
|
+
* the language options from `<link>` elements with `hreflang` attributes in the document.
|
22
|
+
*
|
23
|
+
* @return {Object} An object containing information about the detected language, preferred language, and available languages.
|
24
|
+
*/
|
25
|
+
export function detectUserLanguagePreference() {
|
26
|
+
const currentLang = document.documentElement.lang;
|
27
|
+
|
28
|
+
let preferredLanguages = [];
|
29
|
+
|
30
|
+
if (typeof navigator.language === "string" && navigator.language.length > 0) {
|
31
|
+
preferredLanguages = [navigator.language];
|
32
|
+
}
|
33
|
+
|
34
|
+
if (Array.isArray(navigator.languages) && navigator.languages.length > 0) {
|
35
|
+
preferredLanguages = navigator.languages;
|
36
|
+
}
|
37
|
+
|
38
|
+
// add to preferredLanguages all the base languages of the preferred languages
|
39
|
+
preferredLanguages = preferredLanguages.concat(preferredLanguages.map(lang => lang.split("-")[0]));
|
40
|
+
|
41
|
+
|
42
|
+
if (!currentLang && preferredLanguages.length === 0) {
|
43
|
+
return {
|
44
|
+
message: "No language information available.",
|
45
|
+
};
|
46
|
+
}
|
47
|
+
|
48
|
+
const linkTags = document.querySelectorAll("link[hreflang]");
|
49
|
+
if (linkTags.length === 0) {
|
50
|
+
return {
|
51
|
+
current: currentLang || null,
|
52
|
+
message: "No <link> tags with hreflang available.",
|
53
|
+
};
|
54
|
+
}
|
55
|
+
|
56
|
+
const availableLanguages = [...linkTags].map((link) => {
|
57
|
+
const fullLang = link.hreflang;
|
58
|
+
const baseLang = fullLang.split("-")[0];
|
59
|
+
let label = link.getAttribute('data-monster-label')
|
60
|
+
if (!label) {
|
61
|
+
label = languages?.[fullLang]
|
62
|
+
if (!label) {
|
63
|
+
label = languages?.[baseLang]
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
return {
|
68
|
+
fullLang,
|
69
|
+
baseLang,
|
70
|
+
label,
|
71
|
+
href: link.href,
|
72
|
+
};
|
73
|
+
});
|
74
|
+
|
75
|
+
// filter availableLanguages to only include languages that are in the preferredLanguages array
|
76
|
+
const offerableLanguages = availableLanguages.filter(lang => preferredLanguages.includes(lang.fullLang) || preferredLanguages.includes(lang.baseLang));
|
77
|
+
|
78
|
+
if (offerableLanguages.length === 0) {
|
79
|
+
return {
|
80
|
+
current: currentLang || null,
|
81
|
+
message: "No available languages match the user's preferences.",
|
82
|
+
available: availableLanguages.map((lang) => ({
|
83
|
+
...lang,
|
84
|
+
weight: 1,
|
85
|
+
})),
|
86
|
+
};
|
87
|
+
}
|
88
|
+
|
89
|
+
// Helper function to determine the "weight" of a language match
|
90
|
+
function getWeight(langEntry) {
|
91
|
+
// Full match has priority 3
|
92
|
+
if (preferredLanguages.includes(langEntry.fullLang)) return 3;
|
93
|
+
// Base language match has priority 2
|
94
|
+
if (preferredLanguages.includes(langEntry.baseLang)) return 2;
|
95
|
+
// No match is priority 1
|
96
|
+
return 1;
|
97
|
+
}
|
98
|
+
|
99
|
+
// Sort the available languages by descending weight
|
100
|
+
offerableLanguages.sort((a, b) => getWeight(b) - getWeight(a));
|
101
|
+
|
102
|
+
// The best match is the first in the sorted list
|
103
|
+
const bestMatch = offerableLanguages[0];
|
104
|
+
const bestMatchWeight = getWeight(bestMatch);
|
105
|
+
|
106
|
+
const currentLabel = languages?.[currentLang] || currentLang
|
107
|
+
|
108
|
+
// If we found a language that matches user preferences (weight > 1)
|
109
|
+
if (bestMatchWeight > 0) {
|
110
|
+
return {
|
111
|
+
current: currentLang || null,
|
112
|
+
currentLabel: currentLabel,
|
113
|
+
preferred: {
|
114
|
+
full: bestMatch.fullLang,
|
115
|
+
base: bestMatch.baseLang,
|
116
|
+
label: bestMatch.label,
|
117
|
+
href : bestMatch.href,
|
118
|
+
},
|
119
|
+
available: availableLanguages.map((lang) => ({
|
120
|
+
...lang,
|
121
|
+
weight: getWeight(lang),
|
122
|
+
})),
|
123
|
+
offerable: offerableLanguages.map((lang) => ({
|
124
|
+
...lang,
|
125
|
+
weight: getWeight(lang),
|
126
|
+
})),
|
127
|
+
};
|
128
|
+
}
|
129
|
+
|
130
|
+
// If no language matched the user's preferences
|
131
|
+
return {
|
132
|
+
current: currentLang || null,
|
133
|
+
message: "None of the preferred languages are available.",
|
134
|
+
available: availableLanguages.map((lang) => ({
|
135
|
+
...lang,
|
136
|
+
weight: getWeight(lang),
|
137
|
+
})),
|
138
|
+
};
|
139
|
+
}
|
package/source/monster.mjs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* Copyright © schukai GmbH and all contributing authors,
|
2
|
+
* Copyright © schukai GmbH and all contributing authors, 2025. All rights reserved.
|
3
3
|
* Node module: @schukai/monster
|
4
4
|
*
|
5
5
|
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
|
@@ -276,7 +276,10 @@ function format(text) {
|
|
276
276
|
* @private
|
277
277
|
* @license AGPLv3
|
278
278
|
* @since 1.12.0
|
279
|
-
*
|
279
|
+
*
|
280
|
+
* @param {string} text
|
281
|
+
* @param {string} openMarker
|
282
|
+
* @param {string} closeMarker
|
280
283
|
* @return {string}
|
281
284
|
*/
|
282
285
|
function tokenize(text, openMarker, closeMarker) {
|
@@ -339,7 +342,7 @@ function tokenize(text, openMarker, closeMarker) {
|
|
339
342
|
const t1 = key.split("|").shift().trim(); // pipe symbol
|
340
343
|
const t2 = t1.split("::").shift().trim(); // key value delimiter
|
341
344
|
const t3 = t2.split(".").shift().trim(); // path delimiter
|
342
|
-
const prefix = this[workingDataSymbol]
|
345
|
+
const prefix = t3 in this[workingDataSymbol] ? "path:" : "static:";
|
343
346
|
|
344
347
|
let command = "";
|
345
348
|
if (
|
@@ -362,7 +365,6 @@ function tokenize(text, openMarker, closeMarker) {
|
|
362
365
|
}
|
363
366
|
|
364
367
|
formatted.push(validateString(pipe.run(this[workingDataSymbol])));
|
365
|
-
|
366
368
|
text = text.substring(endIndex + closeMarker.length);
|
367
369
|
}
|
368
370
|
|
package/source/types/is.mjs
CHANGED
@@ -12,6 +12,8 @@
|
|
12
12
|
* SPDX-License-Identifier: AGPL-3.0
|
13
13
|
*/
|
14
14
|
|
15
|
+
import { proxyInstanceMarker } from "../constants.mjs";
|
16
|
+
|
15
17
|
export {
|
16
18
|
isIterable,
|
17
19
|
isPrimitive,
|
@@ -23,8 +25,19 @@ export {
|
|
23
25
|
isArray,
|
24
26
|
isFunction,
|
25
27
|
isInteger,
|
28
|
+
isProxy,
|
26
29
|
};
|
27
30
|
|
31
|
+
/**
|
32
|
+
* Checks whether the value passed is a Proxy.
|
33
|
+
*
|
34
|
+
* @param {*} value
|
35
|
+
* @returns {boolean}
|
36
|
+
*/
|
37
|
+
function isProxy(value) {
|
38
|
+
return value?.[proxyInstanceMarker] === proxyInstanceMarker;
|
39
|
+
}
|
40
|
+
|
28
41
|
/**
|
29
42
|
* With this function you can check if a value is iterable.
|
30
43
|
*
|