@uniweb/core 0.11.0 → 0.11.2
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/package.json +2 -2
- package/src/locale-config.js +66 -0
- package/src/tracker.js +28 -3
- package/src/website.js +18 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/core",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"vitest": "^4.1.7"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@uniweb/semantic-parser": "^1.
|
|
43
|
+
"@uniweb/semantic-parser": "^1.3.0",
|
|
44
44
|
"@uniweb/theming": "^0.1.15"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
package/src/locale-config.js
CHANGED
|
@@ -41,6 +41,72 @@ function codeOf(entry) {
|
|
|
41
41
|
* @param {*} value - The authored `languages` value.
|
|
42
42
|
* @returns {boolean}
|
|
43
43
|
*/
|
|
44
|
+
/**
|
|
45
|
+
* Human-readable display names for the locales a site is likely to declare.
|
|
46
|
+
*
|
|
47
|
+
* This lives in core, not kit, because core is what BUILDS the locale objects a
|
|
48
|
+
* foundation reads (`website.getLocales()`, `website.langs`). It used to live
|
|
49
|
+
* only in kit, so core could not resolve a label and left the field absent for
|
|
50
|
+
* a plain-string `languages:` entry — which meant the very form this module
|
|
51
|
+
* tells authors to migrate TO produced worse labels than the legacy object form
|
|
52
|
+
* it warns about. Measured 2026-08-23 on a site with 11 locales: dropping the
|
|
53
|
+
* `label:` keys silently turned "Français" into "fr" in the switcher, because
|
|
54
|
+
* the common foundation idiom is `locale.label || locale.code`.
|
|
55
|
+
*
|
|
56
|
+
* Kit re-exports this so `@uniweb/kit`'s `LOCALE_DISPLAY_NAMES` keeps working.
|
|
57
|
+
*/
|
|
58
|
+
export const LOCALE_DISPLAY_NAMES = {
|
|
59
|
+
en: 'English',
|
|
60
|
+
es: 'Español',
|
|
61
|
+
fr: 'Français',
|
|
62
|
+
de: 'Deutsch',
|
|
63
|
+
it: 'Italiano',
|
|
64
|
+
pt: 'Português',
|
|
65
|
+
nl: 'Nederlands',
|
|
66
|
+
pl: 'Polski',
|
|
67
|
+
ru: 'Русский',
|
|
68
|
+
ja: '日本語',
|
|
69
|
+
ko: '한국어',
|
|
70
|
+
zh: '中文',
|
|
71
|
+
'zh-CN': '简体中文',
|
|
72
|
+
'zh-TW': '繁體中文',
|
|
73
|
+
ar: 'العربية',
|
|
74
|
+
he: 'עברית',
|
|
75
|
+
hi: 'हिन्दी',
|
|
76
|
+
th: 'ไทย',
|
|
77
|
+
vi: 'Tiếng Việt',
|
|
78
|
+
tr: 'Türkçe',
|
|
79
|
+
uk: 'Українська',
|
|
80
|
+
cs: 'Čeština',
|
|
81
|
+
el: 'Ελληνικά',
|
|
82
|
+
hu: 'Magyar',
|
|
83
|
+
ro: 'Română',
|
|
84
|
+
sv: 'Svenska',
|
|
85
|
+
da: 'Dansk',
|
|
86
|
+
fi: 'Suomi',
|
|
87
|
+
no: 'Norsk',
|
|
88
|
+
id: 'Bahasa Indonesia',
|
|
89
|
+
ms: 'Bahasa Melayu'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve a locale's display label.
|
|
94
|
+
*
|
|
95
|
+
* Priority: an explicitly configured `label` -> the display-name table -> the
|
|
96
|
+
* code uppercased. Accepts either a bare code string or a `{ code, label? }`
|
|
97
|
+
* object, so callers do not have to branch on which form the config used.
|
|
98
|
+
*
|
|
99
|
+
* @param {string|{code: string, label?: string}} entry
|
|
100
|
+
* @returns {string} Display label, or '' when the entry carries no code.
|
|
101
|
+
*/
|
|
102
|
+
export function localeLabel(entry) {
|
|
103
|
+
if (typeof entry === 'string') {
|
|
104
|
+
return LOCALE_DISPLAY_NAMES[entry] || entry.toUpperCase()
|
|
105
|
+
}
|
|
106
|
+
if (!entry || typeof entry.code !== 'string' || !entry.code) return ''
|
|
107
|
+
return entry.label || LOCALE_DISPLAY_NAMES[entry.code] || entry.code.toUpperCase()
|
|
108
|
+
}
|
|
109
|
+
|
|
44
110
|
export function isWildcardLanguages(value) {
|
|
45
111
|
return value === '*' || (Array.isArray(value) && value.includes('*'))
|
|
46
112
|
}
|
package/src/tracker.js
CHANGED
|
@@ -49,9 +49,10 @@
|
|
|
49
49
|
* ## Field lifetime — captured once, replayed on every page view
|
|
50
50
|
*
|
|
51
51
|
* `document.referrer` and the landing `utm_*` params exist **at arrival and
|
|
52
|
-
* nowhere afterwards
|
|
53
|
-
* the
|
|
54
|
-
*
|
|
52
|
+
* nowhere afterwards** — as does `continues`, which is derived from the same
|
|
53
|
+
* read: the referrer never changes across SPA navigation, and the params leave
|
|
54
|
+
* the URL on the first navigation. So they are captured once, here, and
|
|
55
|
+
* attached to every `page_view` of the document.
|
|
55
56
|
*
|
|
56
57
|
* ⇒ **Consequence worth knowing when reading the numbers:** a per-view facet
|
|
57
58
|
* built on them is *derived, not observed*. `utm_source` counts "views by
|
|
@@ -113,11 +114,35 @@ function captureAcquisition() {
|
|
|
113
114
|
|
|
114
115
|
// Same-origin referrers are dropped: internal navigation is not a referral,
|
|
115
116
|
// and counting it would make a site its own top referrer on every page.
|
|
117
|
+
//
|
|
118
|
+
// ⭐ **But dropping it destroys the only thing separating two different
|
|
119
|
+
// events, so the fact that it WAS same-origin is kept as one bit.** A full
|
|
120
|
+
// document load happens either because someone arrived from outside, or
|
|
121
|
+
// because a visitor already on the site triggered a real navigation — a
|
|
122
|
+
// locale switch through kit's `<Link reload>` being the shipped case. Both
|
|
123
|
+
// reach a collector with no referrer: the first never had one, the second had
|
|
124
|
+
// it discarded here. ⇒ An "entry pages" metric built on that **invents**
|
|
125
|
+
// arrivals, counting a locale switch as somebody landing on the Spanish page.
|
|
126
|
+
//
|
|
127
|
+
// ⚖️ `continues` states the FACT, not the conclusion. Whether a continuation
|
|
128
|
+
// disqualifies an entry is the consumer's call; a field named for one metric
|
|
129
|
+
// ages badly the moment a second one wants it.
|
|
130
|
+
//
|
|
131
|
+
// ⛔ **It carries no identity and links nothing.** It says only *this document
|
|
132
|
+
// continues a visit*, never *which* — so the categorical claim in this file's
|
|
133
|
+
// header, that nothing persistent is minted, is untouched. Correlating two
|
|
134
|
+
// visits would be a session, which is exactly what is refused.
|
|
116
135
|
const referrer = document.referrer
|
|
117
136
|
if (referrer) {
|
|
118
137
|
try {
|
|
119
138
|
if (new URL(referrer).origin !== window.location.origin) {
|
|
120
139
|
context.referrer = referrer
|
|
140
|
+
} else {
|
|
141
|
+
// Emitted only when true. Absent means "not a continuation" AND "an
|
|
142
|
+
// older runtime that never sent it" — indistinguishable on purpose,
|
|
143
|
+
// because that collapses to today's behaviour rather than to a wrong
|
|
144
|
+
// answer, and it keeps the common payload the size it already was.
|
|
145
|
+
context.continues = true
|
|
121
146
|
}
|
|
122
147
|
} catch {
|
|
123
148
|
// Unparseable — treat as absent rather than forwarding a malformed value.
|
package/src/website.js
CHANGED
|
@@ -10,7 +10,7 @@ import EntityStore from './entity-store.js'
|
|
|
10
10
|
import FetcherDispatcher from './fetcher-dispatcher.js'
|
|
11
11
|
import ObservableState from './observable-state.js'
|
|
12
12
|
import { normalizeSeo } from './seo.js'
|
|
13
|
-
import { resolveDefaultLocale } from './locale-config.js'
|
|
13
|
+
import { resolveDefaultLocale, localeLabel } from './locale-config.js'
|
|
14
14
|
import { matchDynamicRoute, decodeRouteValue } from './route-match.js'
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -226,8 +226,12 @@ export default class Website {
|
|
|
226
226
|
/**
|
|
227
227
|
* Build locales list from config
|
|
228
228
|
* Supports both string codes and objects: ['es', 'fr'] or [{code: 'es', label: 'Español'}]
|
|
229
|
-
*
|
|
230
|
-
*
|
|
229
|
+
* Every returned locale carries a resolved `label`: an explicitly configured
|
|
230
|
+
* one wins, otherwise it comes from LOCALE_DISPLAY_NAMES, otherwise the code
|
|
231
|
+
* uppercased. It used to be passed through only when configured, which made
|
|
232
|
+
* the common foundation idiom `locale.label || locale.code` render "fr"
|
|
233
|
+
* instead of "Français" for a plain-string `languages:` entry — i.e. the form
|
|
234
|
+
* locale-config tells authors to migrate to was the one that read worse.
|
|
231
235
|
* @private
|
|
232
236
|
*/
|
|
233
237
|
buildLocalesList(config) {
|
|
@@ -262,6 +266,7 @@ export default class Website {
|
|
|
262
266
|
// Build final array with isDefault flag
|
|
263
267
|
return Array.from(localeMap.values()).map(locale => ({
|
|
264
268
|
...locale,
|
|
269
|
+
label: localeLabel(locale),
|
|
265
270
|
isDefault: locale.code === defaultLocale
|
|
266
271
|
}))
|
|
267
272
|
}
|
|
@@ -857,9 +862,16 @@ export default class Website {
|
|
|
857
862
|
// ─────────────────────────────────────────────────────────────────
|
|
858
863
|
|
|
859
864
|
/**
|
|
860
|
-
* Get all available locales
|
|
861
|
-
*
|
|
862
|
-
*
|
|
865
|
+
* Get all available locales.
|
|
866
|
+
*
|
|
867
|
+
* `label` is always present and always displayable: an explicitly configured
|
|
868
|
+
* label wins, otherwise it is resolved from LOCALE_DISPLAY_NAMES, otherwise it
|
|
869
|
+
* is the code uppercased. So `locale.label` needs no fallback at the call site
|
|
870
|
+
* — the idiom `locale.label || locale.code` is now redundant rather than
|
|
871
|
+
* load-bearing, and kit's `getLocaleLabel()` is only needed for a code that
|
|
872
|
+
* did not come from here.
|
|
873
|
+
*
|
|
874
|
+
* @returns {Array<{code: string, label: string, isDefault: boolean}>}
|
|
863
875
|
*/
|
|
864
876
|
getLocales() {
|
|
865
877
|
return this.locales
|