ds-one 0.2.0-alpha.3 → 0.2.5-alpha.10
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/DS1/0-face/{2025-04-23-device.ts → device.ts} +10 -6
- package/DS1/{utils/language.ts → 0-face/i18n.ts} +236 -92
- package/DS1/0-face/preferences.ts +23 -0
- package/DS1/0-face/pricing.ts +57 -0
- package/DS1/1-root/fonts/Iosevka-Regular.woff2 +0 -0
- package/DS1/1-root/one.css +76 -107
- package/DS1/2-core/ds-banner.ts +3 -0
- package/DS1/2-core/ds-button.ts +13 -16
- package/DS1/2-core/ds-cycle.ts +84 -39
- package/DS1/2-core/{ds-year.ts → ds-date.ts} +5 -6
- package/DS1/2-core/ds-icon.ts +4 -4
- package/DS1/2-core/ds-input.ts +1 -0
- package/DS1/2-core/ds-text.ts +27 -3
- package/DS1/2-core/ds-tooltip.ts +9 -14
- package/DS1/3-unit/ds-list.ts +7 -0
- package/DS1/3-unit/ds-row.ts +4 -5
- package/DS1/3-unit/ds-table.ts +5 -6
- package/DS1/4-page/ds-grid.ts +9 -59
- package/DS1/4-page/ds-layout.ts +123 -18
- package/DS1/index.ts +39 -37
- package/LICENSE +1 -1
- package/README.md +43 -133
- package/dist/0-face/{2025-04-23-device.d.ts → device.d.ts} +1 -1
- package/dist/0-face/device.d.ts.map +1 -0
- package/dist/0-face/{2025-04-23-device.js → device.js} +7 -3
- package/dist/{utils/language.d.ts → 0-face/i18n.d.ts} +1 -3
- package/dist/0-face/i18n.d.ts.map +1 -0
- package/dist/{utils/language.js → 0-face/i18n.js} +178 -76
- package/dist/0-face/preferences.d.ts +9 -0
- package/dist/0-face/preferences.d.ts.map +1 -0
- package/dist/0-face/preferences.js +14 -0
- package/dist/0-face/pricing.d.ts +15 -0
- package/dist/0-face/pricing.d.ts.map +1 -0
- package/dist/0-face/pricing.js +46 -0
- package/dist/0-face/theme.d.ts.map +1 -0
- package/dist/2-core/ds-banner.d.ts +1 -0
- package/dist/2-core/ds-banner.d.ts.map +1 -0
- package/dist/2-core/ds-banner.js +2 -0
- package/dist/2-core/ds-button.d.ts +2 -7
- package/dist/2-core/ds-button.d.ts.map +1 -1
- package/dist/2-core/ds-button.js +12 -14
- package/dist/2-core/ds-cycle.d.ts +2 -0
- package/dist/2-core/ds-cycle.d.ts.map +1 -1
- package/dist/2-core/ds-cycle.js +80 -34
- package/dist/2-core/{ds-year.d.ts → ds-date.d.ts} +4 -4
- package/dist/2-core/ds-date.d.ts.map +1 -0
- package/dist/2-core/{ds-year.js → ds-date.js} +5 -5
- package/dist/2-core/ds-icon.js +4 -4
- package/dist/2-core/ds-input.d.ts +1 -0
- package/dist/2-core/ds-input.d.ts.map +1 -0
- package/dist/2-core/ds-input.js +1 -0
- package/dist/2-core/ds-text.d.ts +2 -0
- package/dist/2-core/ds-text.d.ts.map +1 -1
- package/dist/2-core/ds-text.js +26 -3
- package/dist/2-core/ds-tooltip.d.ts +1 -1
- package/dist/2-core/ds-tooltip.d.ts.map +1 -1
- package/dist/2-core/ds-tooltip.js +9 -13
- package/dist/3-unit/ds-list.d.ts.map +1 -1
- package/dist/3-unit/ds-list.js +3 -0
- package/dist/3-unit/{ds-doublenav.d.ts → ds-portfolio-doublenav.d.ts} +4 -4
- package/dist/3-unit/ds-portfolio-doublenav.d.ts.map +1 -0
- package/dist/3-unit/{ds-doublenav.js → ds-portfolio-doublenav.js} +4 -4
- package/dist/3-unit/{ds-panel.d.ts → ds-portfolio-panel.d.ts} +3 -3
- package/dist/3-unit/ds-portfolio-panel.d.ts.map +1 -0
- package/dist/3-unit/{ds-panel.js → ds-portfolio-panel.js} +3 -3
- package/dist/3-unit/{ds-singlenav.d.ts → ds-portfolio-singlenav.d.ts} +4 -4
- package/dist/3-unit/ds-portfolio-singlenav.d.ts.map +1 -0
- package/dist/3-unit/{ds-singlenav.js → ds-portfolio-singlenav.js} +7 -7
- package/dist/3-unit/ds-row.js +4 -4
- package/dist/3-unit/ds-table.d.ts.map +1 -1
- package/dist/3-unit/ds-table.js +5 -6
- package/dist/4-page/ds-grid.d.ts +0 -7
- package/dist/4-page/ds-grid.d.ts.map +1 -1
- package/dist/4-page/ds-grid.js +9 -54
- package/dist/4-page/ds-layout.d.ts +1 -1
- package/dist/4-page/ds-layout.d.ts.map +1 -1
- package/dist/4-page/ds-layout.js +126 -17
- package/dist/ds-one.bundle.js +2515 -4132
- package/dist/ds-one.bundle.js.map +4 -4
- package/dist/ds-one.bundle.min.js +245 -787
- package/dist/ds-one.bundle.min.js.map +4 -4
- package/dist/index.d.ts +16 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -34
- package/package.json +8 -9
- package/DS1/0-face/2025-04-23-language.ts +0 -4
- package/DS1/2-core/ds-article.ts +0 -454
- package/DS1/2-core/ds-attributes.ts +0 -155
- package/DS1/2-core/ds-downloadcv.ts +0 -146
- package/DS1/2-core/ds-header.ts +0 -82
- package/DS1/2-core/ds-home.ts +0 -168
- package/DS1/2-core/ds-link.ts +0 -121
- package/DS1/2-core/ds-markdown.ts +0 -252
- package/DS1/2-core/ds-price.ts +0 -108
- package/DS1/2-core/ds-squarecircle.ts +0 -155
- package/DS1/2-core/ds-title.ts +0 -139
- package/DS1/2-core/ds-viewtoggle.ts +0 -83
- package/DS1/3-unit/ds-doublenav.ts +0 -106
- package/DS1/3-unit/ds-panel.ts +0 -27
- package/DS1/3-unit/ds-singlenav.ts +0 -79
- package/DS1/utils/cdn-loader.ts +0 -208
- package/DS1/utils/keys.json +0 -41
- package/DS1/utils/pricing.ts +0 -24
- package/DS1/utils/scroll.ts +0 -184
- package/DS1/utils/settings.ts +0 -23
- package/DS1/utils/viewMode.ts +0 -55
- package/dist/0-face/2025-04-23-device.d.ts.map +0 -1
- package/dist/0-face/2025-04-23-language.d.ts +0 -1
- package/dist/0-face/2025-04-23-language.d.ts.map +0 -1
- package/dist/0-face/2025-04-23-language.js +0 -3
- package/dist/2-core/article-v1.d.ts +0 -129
- package/dist/2-core/article-v1.d.ts.map +0 -1
- package/dist/2-core/article-v1.js +0 -361
- package/dist/2-core/attributes-v1.d.ts +0 -47
- package/dist/2-core/attributes-v1.d.ts.map +0 -1
- package/dist/2-core/attributes-v1.js +0 -128
- package/dist/2-core/cycle-v1.d.ts +0 -66
- package/dist/2-core/cycle-v1.d.ts.map +0 -1
- package/dist/2-core/cycle-v1.js +0 -586
- package/dist/2-core/downloadcv-v1.d.ts +0 -58
- package/dist/2-core/downloadcv-v1.d.ts.map +0 -1
- package/dist/2-core/downloadcv-v1.js +0 -119
- package/dist/2-core/ds-article.d.ts +0 -129
- package/dist/2-core/ds-article.d.ts.map +0 -1
- package/dist/2-core/ds-article.js +0 -361
- package/dist/2-core/ds-attributes.d.ts +0 -47
- package/dist/2-core/ds-attributes.d.ts.map +0 -1
- package/dist/2-core/ds-attributes.js +0 -128
- package/dist/2-core/ds-button.figma.d.ts +0 -2
- package/dist/2-core/ds-button.figma.d.ts.map +0 -1
- package/dist/2-core/ds-button.figma.js +0 -6
- package/dist/2-core/ds-downloadcv.d.ts +0 -58
- package/dist/2-core/ds-downloadcv.d.ts.map +0 -1
- package/dist/2-core/ds-downloadcv.js +0 -119
- package/dist/2-core/ds-header.d.ts +0 -28
- package/dist/2-core/ds-header.d.ts.map +0 -1
- package/dist/2-core/ds-header.js +0 -66
- package/dist/2-core/ds-home.d.ts +0 -26
- package/dist/2-core/ds-home.d.ts.map +0 -1
- package/dist/2-core/ds-home.js +0 -148
- package/dist/2-core/ds-link.d.ts +0 -35
- package/dist/2-core/ds-link.d.ts.map +0 -1
- package/dist/2-core/ds-link.js +0 -85
- package/dist/2-core/ds-markdown.d.ts +0 -7
- package/dist/2-core/ds-markdown.d.ts.map +0 -1
- package/dist/2-core/ds-markdown.js +0 -240
- package/dist/2-core/ds-price.d.ts +0 -46
- package/dist/2-core/ds-price.d.ts.map +0 -1
- package/dist/2-core/ds-price.js +0 -72
- package/dist/2-core/ds-squarecircle.d.ts +0 -50
- package/dist/2-core/ds-squarecircle.d.ts.map +0 -1
- package/dist/2-core/ds-squarecircle.js +0 -133
- package/dist/2-core/ds-title.d.ts +0 -50
- package/dist/2-core/ds-title.d.ts.map +0 -1
- package/dist/2-core/ds-title.js +0 -103
- package/dist/2-core/ds-viewtoggle.d.ts +0 -27
- package/dist/2-core/ds-viewtoggle.d.ts.map +0 -1
- package/dist/2-core/ds-viewtoggle.js +0 -49
- package/dist/2-core/ds-year.d.ts.map +0 -1
- package/dist/2-core/header-v1.d.ts +0 -28
- package/dist/2-core/header-v1.d.ts.map +0 -1
- package/dist/2-core/header-v1.js +0 -66
- package/dist/2-core/home-v1.d.ts +0 -26
- package/dist/2-core/home-v1.d.ts.map +0 -1
- package/dist/2-core/home-v1.js +0 -148
- package/dist/2-core/icon-v1.d.ts +0 -28
- package/dist/2-core/icon-v1.d.ts.map +0 -1
- package/dist/2-core/icon-v1.js +0 -297
- package/dist/2-core/link-v1.d.ts +0 -35
- package/dist/2-core/link-v1.d.ts.map +0 -1
- package/dist/2-core/link-v1.js +0 -85
- package/dist/2-core/markdown-v1.d.ts +0 -7
- package/dist/2-core/markdown-v1.d.ts.map +0 -1
- package/dist/2-core/markdown-v1.js +0 -240
- package/dist/2-core/price-v1.d.ts +0 -46
- package/dist/2-core/price-v1.d.ts.map +0 -1
- package/dist/2-core/price-v1.js +0 -72
- package/dist/2-core/squarecircle-v1.d.ts +0 -50
- package/dist/2-core/squarecircle-v1.d.ts.map +0 -1
- package/dist/2-core/squarecircle-v1.js +0 -133
- package/dist/2-core/text-v1.d.ts +0 -48
- package/dist/2-core/text-v1.d.ts.map +0 -1
- package/dist/2-core/text-v1.js +0 -83
- package/dist/2-core/title-v1.d.ts +0 -50
- package/dist/2-core/title-v1.d.ts.map +0 -1
- package/dist/2-core/title-v1.js +0 -103
- package/dist/2-core/tooltip-v1.d.ts +0 -39
- package/dist/2-core/tooltip-v1.d.ts.map +0 -1
- package/dist/2-core/tooltip-v1.js +0 -145
- package/dist/2-core/viewtoggle-v1.d.ts +0 -27
- package/dist/2-core/viewtoggle-v1.d.ts.map +0 -1
- package/dist/2-core/viewtoggle-v1.js +0 -49
- package/dist/2-core/year-v1.d.ts +0 -16
- package/dist/2-core/year-v1.d.ts.map +0 -1
- package/dist/2-core/year-v1.js +0 -21
- package/dist/3-unit/ds-doublenav.d.ts.map +0 -1
- package/dist/3-unit/ds-panel.d.ts.map +0 -1
- package/dist/3-unit/ds-singlenav.d.ts.map +0 -1
- package/dist/utils/cdn-loader.d.ts +0 -19
- package/dist/utils/cdn-loader.d.ts.map +0 -1
- package/dist/utils/cdn-loader.js +0 -142
- package/dist/utils/keys.json +0 -41
- package/dist/utils/language.d.ts.map +0 -1
- package/dist/utils/pricing.d.ts +0 -8
- package/dist/utils/pricing.d.ts.map +0 -1
- package/dist/utils/pricing.js +0 -14
- package/dist/utils/scroll.d.ts +0 -34
- package/dist/utils/scroll.d.ts.map +0 -1
- package/dist/utils/scroll.js +0 -140
- package/dist/utils/settings.d.ts +0 -9
- package/dist/utils/settings.d.ts.map +0 -1
- package/dist/utils/settings.js +0 -14
- package/dist/utils/theme.d.ts.map +0 -1
- package/dist/utils/viewMode.d.ts +0 -14
- package/dist/utils/viewMode.d.ts.map +0 -1
- package/dist/utils/viewMode.js +0 -46
- /package/DS1/{utils → 0-face}/theme.ts +0 -0
- /package/DS1/{x Icon → x-icon}/1x.svg +0 -0
- /package/DS1/{x Icon → x-icon}/1xdots.svg +0 -0
- /package/DS1/{x Icon → x-icon}/1xgrid.svg +0 -0
- /package/DS1/{x Icon → x-icon}/1xlines.svg +0 -0
- /package/DS1/{x Icon → x-icon}/2x.svg +0 -0
- /package/DS1/{x Icon → x-icon}/2xdots.svg +0 -0
- /package/DS1/{x Icon → x-icon}/2xgrid.svg +0 -0
- /package/DS1/{x Icon → x-icon}/2xlines.svg +0 -0
- /package/DS1/{x Icon → x-icon}/big.svg +0 -0
- /package/DS1/{x Icon → x-icon}/blank.svg +0 -0
- /package/DS1/{x Icon → x-icon}/check.svg +0 -0
- /package/DS1/{x Icon → x-icon}/close.svg +0 -0
- /package/DS1/{x Icon → x-icon}/collapse.svg +0 -0
- /package/DS1/{x Icon → x-icon}/color.svg +0 -0
- /package/DS1/{x Icon → x-icon}/column.svg +0 -0
- /package/DS1/{x Icon → x-icon}/default.svg +0 -0
- /package/DS1/{x Icon → x-icon}/delete.svg +0 -0
- /package/DS1/{x Icon → x-icon}/do.svg +0 -0
- /package/DS1/{x Icon → x-icon}/down.svg +0 -0
- /package/DS1/{x Icon → x-icon}/duplicate.svg +0 -0
- /package/DS1/{x Icon → x-icon}/email.svg +0 -0
- /package/DS1/{x Icon → x-icon}/expand.svg +0 -0
- /package/DS1/{x Icon → x-icon}/gallery.svg +0 -0
- /package/DS1/{x Icon → x-icon}/group.svg +0 -0
- /package/DS1/{x Icon → x-icon}/head.svg +0 -0
- /package/DS1/{x Icon → x-icon}/icon.svg +0 -0
- /package/DS1/{x Icon → x-icon}/left.svg +0 -0
- /package/DS1/{x Icon → x-icon}/lock.svg +0 -0
- /package/DS1/{x Icon → x-icon}/mic.svg +0 -0
- /package/DS1/{x Icon → x-icon}/minimize.svg +0 -0
- /package/DS1/{x Icon → x-icon}/more.svg +0 -0
- /package/DS1/{x Icon → x-icon}/note.svg +0 -0
- /package/DS1/{x Icon → x-icon}/open.svg +0 -0
- /package/DS1/{x Icon → x-icon}/page.svg +0 -0
- /package/DS1/{x Icon → x-icon}/plus.svg +0 -0
- /package/DS1/{x Icon → x-icon}/rewind.svg +0 -0
- /package/DS1/{x Icon → x-icon}/right.svg +0 -0
- /package/DS1/{x Icon/row..svg → x-icon/row.svg} +0 -0
- /package/DS1/{x Icon → x-icon}/search.svg +0 -0
- /package/DS1/{x Icon → x-icon}/see.svg +0 -0
- /package/DS1/{x Icon → x-icon}/star.svg +0 -0
- /package/DS1/{x Icon → x-icon}/title.svg +0 -0
- /package/DS1/{x Icon → x-icon}/undo.svg +0 -0
- /package/DS1/{x Icon → x-icon}/ungroup.svg +0 -0
- /package/DS1/{x Icon → x-icon}/unhead.svg +0 -0
- /package/DS1/{x Icon → x-icon}/unicon.svg +0 -0
- /package/DS1/{x Icon → x-icon}/unlock.svg +0 -0
- /package/DS1/{x Icon → x-icon}/unmic.svg +0 -0
- /package/DS1/{x Icon → x-icon}/unsee.svg +0 -0
- /package/DS1/{x Icon → x-icon}/unstar.svg +0 -0
- /package/DS1/{x Icon → x-icon}/untitle.svg +0 -0
- /package/DS1/{x Icon → x-icon}/up.svg +0 -0
- /package/dist/{utils → 0-face}/theme.d.ts +0 -0
- /package/dist/{utils → 0-face}/theme.js +0 -0
|
@@ -93,9 +93,14 @@ export function initDeviceDetection(): DeviceInfo {
|
|
|
93
93
|
const actualWidth = deviceInfo.screenWidth;
|
|
94
94
|
const scalingFactor = actualWidth / designWidth;
|
|
95
95
|
|
|
96
|
-
// Set CSS custom property for scaling
|
|
96
|
+
// Set CSS custom property for scaling on html element
|
|
97
97
|
document.documentElement.style.setProperty(
|
|
98
|
-
"--
|
|
98
|
+
"--sf",
|
|
99
|
+
scalingFactor.toFixed(3)
|
|
100
|
+
);
|
|
101
|
+
// Also set --sf for backwards compatibility
|
|
102
|
+
document.documentElement.style.setProperty(
|
|
103
|
+
"--sf",
|
|
99
104
|
scalingFactor.toFixed(3)
|
|
100
105
|
);
|
|
101
106
|
|
|
@@ -105,10 +110,9 @@ export function initDeviceDetection(): DeviceInfo {
|
|
|
105
110
|
} else {
|
|
106
111
|
// Desktop - no scaling
|
|
107
112
|
if (typeof document !== "undefined") {
|
|
108
|
-
document.documentElement.style.setProperty(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
);
|
|
113
|
+
document.documentElement.style.setProperty("--sf", "1");
|
|
114
|
+
// Also set --sf for backwards compatibility
|
|
115
|
+
document.documentElement.style.setProperty("--sf", "1");
|
|
112
116
|
}
|
|
113
117
|
console.log(
|
|
114
118
|
`[DS one] Desktop device detected (${deviceInfo.screenWidth}x${deviceInfo.screenHeight})`
|
|
@@ -8,21 +8,22 @@ type TranslationData = {
|
|
|
8
8
|
|
|
9
9
|
type TranslationMap = Record<string, TranslationData>;
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
|
|
11
|
+
// Bundled translations (keys.json may not exist - will use external translations if not available)
|
|
12
|
+
// This is a fallback for when external translations aren't loaded
|
|
13
|
+
let translationKeys: TranslationMap = {};
|
|
13
14
|
|
|
14
15
|
// Primary language list – prioritise the 10 requested languages when cycling
|
|
15
16
|
const LANGUAGE_PRIORITY_ORDER = [
|
|
16
17
|
"da",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"pt",
|
|
18
|
+
"de",
|
|
19
|
+
"en",
|
|
20
20
|
"es",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
21
|
+
"fr",
|
|
22
|
+
"it",
|
|
23
23
|
"ja",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
24
|
+
"pt",
|
|
25
|
+
"sv",
|
|
26
|
+
"zh",
|
|
26
27
|
] as const;
|
|
27
28
|
|
|
28
29
|
const LANGUAGE_PRIORITY_LOOKUP = new Map<string, number>(
|
|
@@ -33,27 +34,27 @@ const LANGUAGE_PRIORITY_LOOKUP = new Map<string, number>(
|
|
|
33
34
|
const FALLBACK_LANGUAGE_NAMES: Record<string, string> = {
|
|
34
35
|
da: "Danish",
|
|
35
36
|
"da-dk": "Danish",
|
|
36
|
-
nb: "Norwegian",
|
|
37
|
-
"nb-no": "Norwegian",
|
|
38
|
-
sv: "Swedish",
|
|
39
|
-
"sv-se": "Swedish",
|
|
40
37
|
de: "German",
|
|
41
38
|
"de-de": "German",
|
|
42
39
|
en: "English",
|
|
43
40
|
"en-us": "English",
|
|
44
|
-
pt: "Portuguese",
|
|
45
|
-
"pt-pt": "Portuguese",
|
|
46
|
-
"pt-br": "Portuguese (Brazil)",
|
|
47
41
|
es: "Spanish",
|
|
48
42
|
"es-es": "Spanish",
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
43
|
+
fr: "French",
|
|
44
|
+
"fr-fr": "French",
|
|
45
|
+
it: "Italian",
|
|
46
|
+
"it-it": "Italian",
|
|
53
47
|
ja: "Japanese",
|
|
54
48
|
"ja-jp": "Japanese",
|
|
55
|
-
|
|
56
|
-
"
|
|
49
|
+
pt: "Portuguese",
|
|
50
|
+
"pt-pt": "Portuguese",
|
|
51
|
+
sv: "Swedish",
|
|
52
|
+
"sv-se": "Swedish",
|
|
53
|
+
zh: "Chinese",
|
|
54
|
+
"zh-cn": "Chinese",
|
|
55
|
+
"zh-tw": "Chinese",
|
|
56
|
+
"zh-hans": "Chinese",
|
|
57
|
+
"zh-hant": "Chinese",
|
|
57
58
|
};
|
|
58
59
|
|
|
59
60
|
const DISPLAY_NAME_CACHE = new Map<string, Intl.DisplayNames>();
|
|
@@ -67,6 +68,187 @@ declare global {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
// CDN Loader: Automatically detects and loads translation JSON files
|
|
72
|
+
// for CDN users who want to use external translations
|
|
73
|
+
|
|
74
|
+
const DEFAULT_TRANSLATION_FILE = "./translations.json";
|
|
75
|
+
let loadAttempted = false;
|
|
76
|
+
|
|
77
|
+
function normalizeCandidate(path: string): string | null {
|
|
78
|
+
if (!path) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const trimmed = path.trim();
|
|
82
|
+
if (!trimmed) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
trimmed.startsWith("./") ||
|
|
88
|
+
trimmed.startsWith("../") ||
|
|
89
|
+
trimmed.startsWith("/") ||
|
|
90
|
+
/^https?:\/\//i.test(trimmed)
|
|
91
|
+
) {
|
|
92
|
+
return trimmed;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return `./${trimmed}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function findAttributeCandidate(): string | null {
|
|
99
|
+
if (typeof document === "undefined") {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const scriptWithAttribute = document.querySelector(
|
|
104
|
+
"script[data-ds-one-translations]"
|
|
105
|
+
);
|
|
106
|
+
const scriptCandidate = scriptWithAttribute?.getAttribute(
|
|
107
|
+
"data-ds-one-translations"
|
|
108
|
+
);
|
|
109
|
+
if (scriptCandidate) {
|
|
110
|
+
return scriptCandidate;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const metaCandidate = document
|
|
114
|
+
.querySelector('meta[name="ds-one:translations"]')
|
|
115
|
+
?.getAttribute("content");
|
|
116
|
+
if (metaCandidate) {
|
|
117
|
+
return metaCandidate;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const linkCandidate = document
|
|
121
|
+
.querySelector('link[rel="ds-one-translations"]')
|
|
122
|
+
?.getAttribute("href");
|
|
123
|
+
if (linkCandidate) {
|
|
124
|
+
return linkCandidate;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function resolveTranslationSources(): string[] {
|
|
131
|
+
const candidates: string[] = [];
|
|
132
|
+
|
|
133
|
+
const windowCandidate =
|
|
134
|
+
typeof window !== "undefined" ? window.DS_ONE_TRANSLATIONS_FILE : null;
|
|
135
|
+
const attributeCandidate = findAttributeCandidate();
|
|
136
|
+
|
|
137
|
+
// Only use explicitly configured paths, or the single default
|
|
138
|
+
const windowNormalized = normalizeCandidate(windowCandidate ?? "");
|
|
139
|
+
if (windowNormalized) {
|
|
140
|
+
candidates.push(windowNormalized);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const attrNormalized = normalizeCandidate(attributeCandidate ?? "");
|
|
144
|
+
if (attrNormalized && !candidates.includes(attrNormalized)) {
|
|
145
|
+
candidates.push(attrNormalized);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Only try default if no explicit path was configured
|
|
149
|
+
if (candidates.length === 0) {
|
|
150
|
+
candidates.push(DEFAULT_TRANSLATION_FILE);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return candidates;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function validateTranslationMap(
|
|
157
|
+
candidate: unknown
|
|
158
|
+
): candidate is TranslationMap {
|
|
159
|
+
if (!candidate || typeof candidate !== "object") {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return Object.values(candidate).every(
|
|
164
|
+
(entry) => entry && typeof entry === "object"
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function fetchTranslationFile(
|
|
169
|
+
source: string
|
|
170
|
+
): Promise<TranslationMap | null> {
|
|
171
|
+
try {
|
|
172
|
+
const response = await fetch(source);
|
|
173
|
+
|
|
174
|
+
if (!response.ok) {
|
|
175
|
+
// 404 is expected if no translations file exists - don't log as error
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const translations = await response.json();
|
|
180
|
+
|
|
181
|
+
if (!validateTranslationMap(translations)) {
|
|
182
|
+
console.warn(
|
|
183
|
+
`[DS one] Invalid translation format in ${source}. Expected object with language codes as keys.`
|
|
184
|
+
);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const languages = Object.keys(translations);
|
|
189
|
+
if (languages.length === 0) {
|
|
190
|
+
console.warn(`[DS one] No languages found in ${source}`);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return translations;
|
|
195
|
+
} catch {
|
|
196
|
+
// Silently fail - file likely doesn't exist or isn't valid JSON
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Attempts to load translations from a JSON file in the same directory
|
|
203
|
+
*/
|
|
204
|
+
async function loadExternalTranslations(): Promise<boolean> {
|
|
205
|
+
// Only attempt once
|
|
206
|
+
if (loadAttempted) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
loadAttempted = true;
|
|
210
|
+
|
|
211
|
+
if (typeof window === "undefined") {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Check if translations are already loaded (e.g., by the application)
|
|
216
|
+
if (
|
|
217
|
+
window.DS_ONE_TRANSLATIONS &&
|
|
218
|
+
Object.keys(window.DS_ONE_TRANSLATIONS).length > 0
|
|
219
|
+
) {
|
|
220
|
+
console.log(
|
|
221
|
+
`[DS one] Translations already loaded (${Object.keys(window.DS_ONE_TRANSLATIONS).length} languages), skipping auto-load`
|
|
222
|
+
);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const sources = resolveTranslationSources();
|
|
227
|
+
|
|
228
|
+
for (const source of sources) {
|
|
229
|
+
const translations = await fetchTranslationFile(source);
|
|
230
|
+
if (!translations) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
window.DS_ONE_TRANSLATIONS = translations;
|
|
235
|
+
|
|
236
|
+
const languages = Object.keys(translations);
|
|
237
|
+
console.log(
|
|
238
|
+
`[DS one] External translations loaded from ${source}: ${languages.length} language(s) – ${languages.join(", ")}`
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
window.dispatchEvent(new CustomEvent("translations-ready"));
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.info(
|
|
246
|
+
`[DS one] No external translations found at ${sources[0] ?? DEFAULT_TRANSLATION_FILE}. Using bundled translations.`
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
70
252
|
// Get translation data - prioritize external, fall back to bundled
|
|
71
253
|
function getTranslationData(): TranslationMap {
|
|
72
254
|
// Check for externally loaded translations first (CDN usage)
|
|
@@ -81,9 +263,6 @@ function getTranslationData(): TranslationMap {
|
|
|
81
263
|
// Cached translation data - use getter to always get fresh data
|
|
82
264
|
let translationData = getTranslationData();
|
|
83
265
|
|
|
84
|
-
type NotionCache = Map<string, string>;
|
|
85
|
-
|
|
86
|
-
const notionStore = new Map<LanguageCode, NotionCache>();
|
|
87
266
|
const defaultLanguage: LanguageCode = "en";
|
|
88
267
|
|
|
89
268
|
function extractPrimarySubtag(code: LanguageCode): string {
|
|
@@ -227,33 +406,30 @@ export function getLanguageDisplayName(
|
|
|
227
406
|
const BROWSER_LANGUAGE_PREFERENCES: Record<string, LanguageCode> = {
|
|
228
407
|
da: "da",
|
|
229
408
|
"da-dk": "da",
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
"
|
|
235
|
-
sv: "sv",
|
|
236
|
-
"sv-se": "sv",
|
|
237
|
-
pt: "pt",
|
|
238
|
-
"pt-pt": "pt",
|
|
239
|
-
"pt-br": "pt",
|
|
409
|
+
de: "de",
|
|
410
|
+
"de-de": "de",
|
|
411
|
+
en: "en",
|
|
412
|
+
"en-us": "en",
|
|
413
|
+
"en-gb": "en",
|
|
240
414
|
es: "es",
|
|
241
415
|
"es-es": "es",
|
|
242
416
|
"es-mx": "es",
|
|
417
|
+
fr: "fr",
|
|
418
|
+
"fr-fr": "fr",
|
|
419
|
+
it: "it",
|
|
420
|
+
"it-it": "it",
|
|
421
|
+
ja: "ja",
|
|
422
|
+
"ja-jp": "ja",
|
|
423
|
+
pt: "pt",
|
|
424
|
+
"pt-pt": "pt",
|
|
425
|
+
"pt-br": "pt",
|
|
426
|
+
sv: "sv",
|
|
427
|
+
"sv-se": "sv",
|
|
243
428
|
zh: "zh",
|
|
244
429
|
"zh-cn": "zh",
|
|
245
430
|
"zh-hans": "zh",
|
|
246
431
|
"zh-tw": "zh",
|
|
247
432
|
"zh-hant": "zh",
|
|
248
|
-
ko: "ko",
|
|
249
|
-
"ko-kr": "ko",
|
|
250
|
-
ja: "ja",
|
|
251
|
-
"ja-jp": "ja",
|
|
252
|
-
en: "en",
|
|
253
|
-
"en-us": "en",
|
|
254
|
-
"en-gb": "en",
|
|
255
|
-
de: "de",
|
|
256
|
-
"de-de": "de",
|
|
257
433
|
};
|
|
258
434
|
|
|
259
435
|
function resolvePreferredLanguage(languageTag: string): LanguageCode | null {
|
|
@@ -329,6 +505,19 @@ export const currentLanguage = {
|
|
|
329
505
|
},
|
|
330
506
|
};
|
|
331
507
|
|
|
508
|
+
// Auto-load translations when this module is imported (for CDN bundle)
|
|
509
|
+
if (typeof window !== "undefined") {
|
|
510
|
+
// Wait a bit to ensure the DOM is ready
|
|
511
|
+
if (document.readyState === "loading") {
|
|
512
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
513
|
+
loadExternalTranslations();
|
|
514
|
+
});
|
|
515
|
+
} else {
|
|
516
|
+
// DOM is already ready
|
|
517
|
+
loadExternalTranslations();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
332
521
|
// Listen for external translations being loaded
|
|
333
522
|
if (typeof window !== "undefined") {
|
|
334
523
|
window.addEventListener("translations-ready", () => {
|
|
@@ -337,7 +526,6 @@ if (typeof window !== "undefined") {
|
|
|
337
526
|
|
|
338
527
|
// Dispatch that translations are loaded
|
|
339
528
|
window.dispatchEvent(new CustomEvent("translations-loaded"));
|
|
340
|
-
(window as any).notionDataLoaded = true;
|
|
341
529
|
|
|
342
530
|
// Dispatch language-changed to update all components
|
|
343
531
|
const currentLang = currentLanguage.value;
|
|
@@ -356,7 +544,6 @@ if (typeof window !== "undefined") {
|
|
|
356
544
|
setTimeout(() => {
|
|
357
545
|
// Since we directly imported the data, just dispatch the events
|
|
358
546
|
window.dispatchEvent(new CustomEvent("translations-loaded"));
|
|
359
|
-
(window as any).notionDataLoaded = true;
|
|
360
547
|
|
|
361
548
|
// Also dispatch language-changed with the current language
|
|
362
549
|
const currentLang = currentLanguage.value;
|
|
@@ -384,7 +571,9 @@ export function translate(key: string): string {
|
|
|
384
571
|
return translationData[defaultLanguage][key];
|
|
385
572
|
}
|
|
386
573
|
|
|
387
|
-
console.warn(
|
|
574
|
+
console.warn(
|
|
575
|
+
`[DS one (Internationalization)] No translation found for key "${key}"`
|
|
576
|
+
);
|
|
388
577
|
return key;
|
|
389
578
|
}
|
|
390
579
|
|
|
@@ -417,51 +606,6 @@ export function getText(key: string): string {
|
|
|
417
606
|
return translate(key);
|
|
418
607
|
}
|
|
419
608
|
|
|
420
|
-
// Get text from translation data (async for compatibility)
|
|
421
|
-
export async function getNotionText(
|
|
422
|
-
key: string,
|
|
423
|
-
language: LanguageCode = currentLanguage.value
|
|
424
|
-
): Promise<string | null> {
|
|
425
|
-
if (!key) {
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
if (!translationData || !translationData[language]) {
|
|
430
|
-
return null;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const text = translationData[language][key];
|
|
434
|
-
if (text) {
|
|
435
|
-
return text;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Fallback to English
|
|
439
|
-
if (language !== defaultLanguage && translationData[defaultLanguage]?.[key]) {
|
|
440
|
-
return translationData[defaultLanguage][key];
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Store Notion text (for dynamic updates)
|
|
447
|
-
export function setNotionText(
|
|
448
|
-
key: string,
|
|
449
|
-
value: string,
|
|
450
|
-
language: LanguageCode = currentLanguage.value
|
|
451
|
-
): void {
|
|
452
|
-
if (!key) return;
|
|
453
|
-
|
|
454
|
-
const bucket = getLanguageBucket(language);
|
|
455
|
-
bucket.set(key, value);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function getLanguageBucket(language: LanguageCode): NotionCache {
|
|
459
|
-
if (!notionStore.has(language)) {
|
|
460
|
-
notionStore.set(language, new Map());
|
|
461
|
-
}
|
|
462
|
-
return notionStore.get(language)!;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
609
|
// Get available languages - dynamically detect from loaded data
|
|
466
610
|
export function getAvailableLanguages(): Promise<LanguageCode[]> {
|
|
467
611
|
// Always get fresh translation data
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LanguageCode } from "./i18n";
|
|
2
|
+
import type { ThemeType } from "./theme";
|
|
3
|
+
|
|
4
|
+
export type Preferences = {
|
|
5
|
+
language?: LanguageCode;
|
|
6
|
+
theme?: ThemeType;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function savePreferences(preferences: Preferences): void {
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const raw = window.localStorage?.getItem("ds-one:preferences");
|
|
17
|
+
const existing = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
|
|
18
|
+
const next = { ...existing, ...preferences };
|
|
19
|
+
window.localStorage?.setItem("ds-one:preferences", JSON.stringify(next));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.warn("ds-one: unable to persist preferences", error);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Currency label utilities for regional price display
|
|
3
|
+
*
|
|
4
|
+
* Note: This module provides currency symbols/labels based on language and region.
|
|
5
|
+
* Consider moving this functionality into i18n.ts as it's region/locale-related.
|
|
6
|
+
* Actual price values will be stored in a database or managed via Stripe.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { LanguageCode } from "./i18n";
|
|
10
|
+
|
|
11
|
+
type PriceLabelOptions = {
|
|
12
|
+
language: LanguageCode;
|
|
13
|
+
country?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Simple price label mapping based on language/country
|
|
17
|
+
const PRICE_LABELS: Record<string, string> = {
|
|
18
|
+
da: "kr.",
|
|
19
|
+
nb: "kr.",
|
|
20
|
+
sv: "kr.",
|
|
21
|
+
de: "€",
|
|
22
|
+
en: "$",
|
|
23
|
+
pt: "€",
|
|
24
|
+
es: "€",
|
|
25
|
+
zh: "¥",
|
|
26
|
+
ja: "¥",
|
|
27
|
+
ko: "₩",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function getPriceLabel(options: PriceLabelOptions): string {
|
|
31
|
+
const { language, country } = options;
|
|
32
|
+
|
|
33
|
+
// If country is provided, try to map it to a currency
|
|
34
|
+
if (country) {
|
|
35
|
+
const countryUpper = country.toUpperCase();
|
|
36
|
+
// Add country-specific mappings if needed
|
|
37
|
+
if (countryUpper === "US" || countryUpper === "USA") {
|
|
38
|
+
return "$";
|
|
39
|
+
}
|
|
40
|
+
if (countryUpper === "GB" || countryUpper === "UK") {
|
|
41
|
+
return "£";
|
|
42
|
+
}
|
|
43
|
+
if (countryUpper === "JP" || countryUpper === "JPN") {
|
|
44
|
+
return "¥";
|
|
45
|
+
}
|
|
46
|
+
if (countryUpper === "CN" || countryUpper === "CHN") {
|
|
47
|
+
return "¥";
|
|
48
|
+
}
|
|
49
|
+
if (countryUpper === "KR" || countryUpper === "KOR") {
|
|
50
|
+
return "₩";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fall back to language-based mapping
|
|
55
|
+
const primaryLang = language.toLowerCase().split(/[-_]/)[0];
|
|
56
|
+
return PRICE_LABELS[primaryLang] || "$";
|
|
57
|
+
}
|
|
Binary file
|