@sveltekit-i18n/base 1.3.7 → 3.0.0-next.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/README.md +357 -82
- package/dist/I18n.svelte.d.ts +80 -0
- package/dist/I18n.svelte.js +486 -0
- package/dist/exports/utils.d.ts +2 -0
- package/dist/exports/utils.js +1 -0
- package/dist/index.d.ts +2 -227
- package/dist/index.js +1 -1
- package/dist/logger.d.ts +5 -0
- package/dist/logger.js +35 -0
- package/dist/types.d.ts +461 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +25 -0
- package/dist/utils.js +237 -0
- package/package.json +40 -25
- package/dist/index.cjs +0 -1
package/dist/utils.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { logError, logger } from './logger.js';
|
|
2
|
+
// Safe own-property read. Translation keys like `toString`, `constructor` or
|
|
3
|
+
// `__proto__` would otherwise resolve to inherited `Object.prototype` members
|
|
4
|
+
// instead of being treated as missing translations.
|
|
5
|
+
export const hasOwn = (obj, key) => obj != null && Object.prototype.hasOwnProperty.call(obj, key);
|
|
6
|
+
// Own-property read: returns the value only when `key` is the object's own
|
|
7
|
+
// property, otherwise undefined. Centralizes the prototype-safe table lookup.
|
|
8
|
+
export const read = (obj, key) => (hasOwn(obj, key) ? obj[key] : undefined);
|
|
9
|
+
// The fail-soft paths return placeholder strings (`''`, the key itself) even
|
|
10
|
+
// for a parser with a non-string output — hence the `as unknown as O` casts.
|
|
11
|
+
export const translate = ({ parser, key, params, translations, locale, fallbackLocale, ...rest }) => {
|
|
12
|
+
if (!key) {
|
|
13
|
+
logger.warn(`No translation key provided ('${locale}' locale). Skipping translation...`);
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
if (!locale) {
|
|
17
|
+
logger.warn(`No locale provided for '${key}' key. Skipping translation...`);
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
const localeTranslations = read(translations, locale);
|
|
21
|
+
let text = read(localeTranslations, key);
|
|
22
|
+
if (fallbackLocale && text === undefined) {
|
|
23
|
+
logger.debug(`No translation provided for '${key}' key in locale '${locale}'. Trying fallback '${fallbackLocale}'`);
|
|
24
|
+
const fallbackTranslations = read(translations, fallbackLocale);
|
|
25
|
+
text = read(fallbackTranslations, key);
|
|
26
|
+
}
|
|
27
|
+
if (text === undefined) {
|
|
28
|
+
logger.debug(`No translation provided for '${key}' key in fallback '${fallbackLocale}'.`);
|
|
29
|
+
if (hasOwn(rest, 'fallbackValue')) {
|
|
30
|
+
return rest.fallbackValue;
|
|
31
|
+
}
|
|
32
|
+
logger.warn(`No translation nor fallback found for '${key}' .`);
|
|
33
|
+
}
|
|
34
|
+
if (!parser || typeof parser.parse !== 'function') {
|
|
35
|
+
// Reached on every call while no parser is set (e.g. before config loads),
|
|
36
|
+
// so keep it at debug to avoid flooding logs on the render path.
|
|
37
|
+
logger.debug(`No parser configured. Returning raw value for '${key}' key.`);
|
|
38
|
+
// Mirror the missing-translation contract: fall back to the key itself.
|
|
39
|
+
if (text === undefined)
|
|
40
|
+
return key;
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
// A key schema narrows the rest params to one key's payload — still a `P`,
|
|
44
|
+
// but no longer provably so once the tuple has been rebuilt.
|
|
45
|
+
return parser.parse(text, params, locale, key);
|
|
46
|
+
};
|
|
47
|
+
// `Intl.Collator.supportedLocalesOf` is comparatively expensive and locales
|
|
48
|
+
// repeat constantly — per loader on every load trigger, per lookup.
|
|
49
|
+
const LOCALE_CACHE_LIMIT = 1000;
|
|
50
|
+
const sanitizedLocaleCache = new Map();
|
|
51
|
+
// Insertion order is the eviction order, so reinserting on a hit makes it
|
|
52
|
+
// least-recently-used: a flood of visitor-supplied locales evicts itself
|
|
53
|
+
// rather than the app's own.
|
|
54
|
+
const recallSanitizedLocale = (locale) => {
|
|
55
|
+
const cached = sanitizedLocaleCache.get(locale);
|
|
56
|
+
if (cached === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
sanitizedLocaleCache.delete(locale);
|
|
59
|
+
sanitizedLocaleCache.set(locale, cached);
|
|
60
|
+
return cached;
|
|
61
|
+
};
|
|
62
|
+
const rememberSanitizedLocale = (locale, sanitized) => {
|
|
63
|
+
if (sanitizedLocaleCache.size >= LOCALE_CACHE_LIMIT) {
|
|
64
|
+
sanitizedLocaleCache.delete(sanitizedLocaleCache.keys().next().value);
|
|
65
|
+
}
|
|
66
|
+
sanitizedLocaleCache.set(locale, sanitized);
|
|
67
|
+
};
|
|
68
|
+
const mapLocales = (transform) => (...locales) => {
|
|
69
|
+
if (!locales.length)
|
|
70
|
+
return [];
|
|
71
|
+
return locales.filter((locale) => !!locale).map(transform);
|
|
72
|
+
};
|
|
73
|
+
export const sanitizeLocales = mapLocales((locale) => {
|
|
74
|
+
// Only a string is a faithful key for itself.
|
|
75
|
+
const cacheable = typeof locale === 'string';
|
|
76
|
+
if (cacheable) {
|
|
77
|
+
const cached = recallSanitizedLocale(locale);
|
|
78
|
+
if (cached !== undefined)
|
|
79
|
+
return cached;
|
|
80
|
+
}
|
|
81
|
+
let current = `${locale}`.toLowerCase();
|
|
82
|
+
try {
|
|
83
|
+
const [sanitized] = Intl.Collator.supportedLocalesOf(locale);
|
|
84
|
+
if (!sanitized)
|
|
85
|
+
throw new Error();
|
|
86
|
+
current = sanitized;
|
|
87
|
+
if (cacheable)
|
|
88
|
+
rememberSanitizedLocale(locale, current);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Deliberately not remembered: a locale Intl does not know yet can
|
|
92
|
+
// recover, and the warning stays tied to the call rather than to
|
|
93
|
+
// whichever logger was installed first.
|
|
94
|
+
logger.warn(`'${locale}' locale is non-standard.`);
|
|
95
|
+
}
|
|
96
|
+
return current;
|
|
97
|
+
});
|
|
98
|
+
// The normalization `config.sanitizeLocales` asks for. A custom transform is
|
|
99
|
+
// consumer code and every table is keyed by what it returns, so a throwing or
|
|
100
|
+
// empty-handed one degrades to the locale as authored.
|
|
101
|
+
export const sanitizerFactory = (sanitize = true) => {
|
|
102
|
+
if (typeof sanitize === 'function') {
|
|
103
|
+
return mapLocales((locale) => {
|
|
104
|
+
const input = `${locale}`;
|
|
105
|
+
try {
|
|
106
|
+
const transformed = sanitize(input);
|
|
107
|
+
if (transformed)
|
|
108
|
+
return `${transformed}`;
|
|
109
|
+
logger.warn(`'sanitizeLocales' returned no locale for '${input}'.`);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
logError(`'sanitizeLocales' failed for '${input}' locale.`, error);
|
|
113
|
+
}
|
|
114
|
+
return input;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (!sanitize)
|
|
118
|
+
return mapLocales((locale) => `${locale}`);
|
|
119
|
+
return sanitizeLocales;
|
|
120
|
+
};
|
|
121
|
+
// Every other locale-keyed surface (`locale`, `fallbackLocale`, loader data,
|
|
122
|
+
// the loaded-key bookkeeping) is sanitized, so a table handed in under a raw
|
|
123
|
+
// locale would be unreachable. Merged rather than replaced: two spellings of
|
|
124
|
+
// one locale are one entry.
|
|
125
|
+
export const sanitizeTranslationLocales = (input, sanitize) => (Object.keys(input).reduce((acc, locale) => {
|
|
126
|
+
const [sanitized = locale] = sanitize(locale);
|
|
127
|
+
return { ...acc, [sanitized]: { ...read(acc, sanitized), ...read(input, locale) } };
|
|
128
|
+
}, {}));
|
|
129
|
+
export const toDotNotation = (input, preserveArrays, parentKey) => {
|
|
130
|
+
if (preserveArrays && Array.isArray(input)) {
|
|
131
|
+
return input.map((v) => toDotNotation(v, preserveArrays));
|
|
132
|
+
}
|
|
133
|
+
if (input && typeof input === 'object') {
|
|
134
|
+
// Mutated in place (rebuilding per key is quadratic) into a null-prototype
|
|
135
|
+
// object, then spread once on the way out — a literal '__proto__' key stays
|
|
136
|
+
// an own property instead of reaching the prototype setter.
|
|
137
|
+
const output = Object.create(null);
|
|
138
|
+
let hasEntries = false;
|
|
139
|
+
const walk = (node, prefix) => {
|
|
140
|
+
Object.keys(node).forEach((key) => {
|
|
141
|
+
const value = node[key];
|
|
142
|
+
const outputKey = prefix ? `${prefix}.${key}` : `${key}`;
|
|
143
|
+
if (value && typeof value === 'object' && !(preserveArrays && Array.isArray(value))) {
|
|
144
|
+
walk(value, outputKey);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
output[outputKey] = toDotNotation(value, preserveArrays);
|
|
148
|
+
hasEntries = true;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
walk(input, parentKey);
|
|
153
|
+
if (hasEntries) {
|
|
154
|
+
return { ...output };
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return input;
|
|
159
|
+
};
|
|
160
|
+
// Loader properties are consumer code — an accessor may throw. Materialized
|
|
161
|
+
// once at the config boundary, so a single unreadable loader costs only itself
|
|
162
|
+
// instead of taking down every locale-keyed read downstream.
|
|
163
|
+
export const resolveLoaders = (input = []) => (input.reduce((acc, descriptor) => {
|
|
164
|
+
try {
|
|
165
|
+
const { key, locale, loader, routes } = descriptor;
|
|
166
|
+
return [...acc, { key, locale, loader, routes }];
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
logError('Skipping a loader that cannot be read.', error);
|
|
170
|
+
return acc;
|
|
171
|
+
}
|
|
172
|
+
}, []));
|
|
173
|
+
const isMergeable = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
174
|
+
// Data reaching a namespace that already holds some — route-scoped chunks of
|
|
175
|
+
// one namespace, a later load, a second `addTranslations` — contributes to it
|
|
176
|
+
// instead of replacing it. Plain objects merge branch by branch; anything else
|
|
177
|
+
// is a leaf, and a leaf collision has no merge to perform, so the incoming
|
|
178
|
+
// value is kept. Only callers for which a collision means an authoring mistake
|
|
179
|
+
// pass `onConflict`.
|
|
180
|
+
export const mergeTranslations = (target, source, path, onConflict) => {
|
|
181
|
+
if (!isMergeable(target) || !isMergeable(source)) {
|
|
182
|
+
onConflict?.(path);
|
|
183
|
+
return source;
|
|
184
|
+
}
|
|
185
|
+
return Object.keys(source).reduce((acc, key) => ({
|
|
186
|
+
...acc,
|
|
187
|
+
[key]: hasOwn(acc, key) ? mergeTranslations(read(acc, key), read(source, key), `${path}.${key}`, onConflict) : read(source, key),
|
|
188
|
+
}), target);
|
|
189
|
+
};
|
|
190
|
+
const reportLoaderConflict = (path) => {
|
|
191
|
+
logger.warn(`Conflicting translations for '${path}'. Keeping the value of the last loader.`);
|
|
192
|
+
};
|
|
193
|
+
export const serialize = (input) => {
|
|
194
|
+
return input.reduce((acc, { key, data, locale }) => {
|
|
195
|
+
if (!data)
|
|
196
|
+
return acc;
|
|
197
|
+
// The locale is already sanitized — loaders are normalized before the fetch.
|
|
198
|
+
const namespaces = read(acc, locale);
|
|
199
|
+
return ({
|
|
200
|
+
...acc,
|
|
201
|
+
[locale]: {
|
|
202
|
+
...namespaces,
|
|
203
|
+
[key]: hasOwn(namespaces, key) ? mergeTranslations(read(namespaces, key), data, `${key}`, reportLoaderConflict) : data,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}, {});
|
|
207
|
+
};
|
|
208
|
+
export const fetchTranslations = async (loaders, route) => {
|
|
209
|
+
const response = await Promise.all(loaders.map(async ({ loader, ...rest }) => {
|
|
210
|
+
let data;
|
|
211
|
+
try {
|
|
212
|
+
data = await loader({ locale: rest.locale, route });
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
logError(`Failed to load translation. Verify your '${rest.locale}' > '${rest.key}' Loader.`, error);
|
|
216
|
+
}
|
|
217
|
+
return { loader, ...rest, data };
|
|
218
|
+
}));
|
|
219
|
+
return serialize(response);
|
|
220
|
+
};
|
|
221
|
+
// `test` advances `lastIndex` on a `g`/`y` pattern, so a route object reused
|
|
222
|
+
// across navigations would match only every other time — and writing to the
|
|
223
|
+
// consumer's own pattern is not ours to do, least of all when it is frozen.
|
|
224
|
+
const withoutMatchState = (input) => (input instanceof RegExp && (input.global || input.sticky)
|
|
225
|
+
? new RegExp(input.source, input.flags)
|
|
226
|
+
: input);
|
|
227
|
+
export const testRoute = (route) => (input) => {
|
|
228
|
+
try {
|
|
229
|
+
if (typeof input === 'string')
|
|
230
|
+
return input === route;
|
|
231
|
+
return withoutMatchState(input).test(route);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
logError('Invalid route config!', error);
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
};
|
package/package.json
CHANGED
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltekit-i18n/base",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-next.0",
|
|
4
4
|
"description": "Base functionality of sveltekit-i18n library with a support for external message parsers.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.cjs",
|
|
7
|
-
"module": "./dist/index.js",
|
|
8
6
|
"types": "./dist/index.d.ts",
|
|
9
7
|
"exports": {
|
|
10
8
|
".": {
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"svelte": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./utils": {
|
|
14
|
+
"types": "./dist/exports/utils.d.ts",
|
|
15
|
+
"svelte": "./dist/exports/utils.js",
|
|
16
|
+
"default": "./dist/exports/utils.js"
|
|
14
17
|
},
|
|
15
18
|
"./package.json": "./package.json"
|
|
16
19
|
},
|
|
17
20
|
"scripts": {
|
|
18
|
-
"dev": "
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
+
"dev": "svelte-package -i src -o dist -w",
|
|
22
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
23
|
+
"pretest": "npm run build && npm run typecheck",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"pretest:dist": "npm run build",
|
|
26
|
+
"test:dist": "vitest run --config vitest.dist.config.ts",
|
|
27
|
+
"build": "svelte-package -i src -o dist",
|
|
21
28
|
"prepublishOnly": "npm run build",
|
|
22
|
-
"lint": "eslint --fix
|
|
29
|
+
"lint": "eslint --fix .",
|
|
30
|
+
"prepare": "simple-git-hooks"
|
|
23
31
|
},
|
|
24
32
|
"files": [
|
|
25
33
|
"dist"
|
|
26
34
|
],
|
|
27
|
-
"pre-commit": [
|
|
28
|
-
"lint"
|
|
29
|
-
],
|
|
30
35
|
"repository": {
|
|
31
36
|
"type": "git",
|
|
32
37
|
"url": "git+ssh://git@github.com/sveltekit-i18n/base.git"
|
|
@@ -46,20 +51,30 @@
|
|
|
46
51
|
"url": "https://github.com/sveltekit-i18n/lib/issues"
|
|
47
52
|
},
|
|
48
53
|
"homepage": "https://github.com/sveltekit-i18n/base#readme",
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=22"
|
|
56
|
+
},
|
|
49
57
|
"peerDependencies": {
|
|
50
|
-
"svelte": ">=
|
|
58
|
+
"svelte": ">=5"
|
|
51
59
|
},
|
|
52
60
|
"devDependencies": {
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
61
|
+
"@eslint/js": "^10.0.1",
|
|
62
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
63
|
+
"@sveltejs/package": "^2.5.8",
|
|
64
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
65
|
+
"@types/node": "^26.2.0",
|
|
66
|
+
"esbuild": "^0.28.2",
|
|
67
|
+
"eslint": "^10.8.1",
|
|
68
|
+
"eslint-plugin-import-x": "^4.17.1",
|
|
69
|
+
"globals": "^17.11.0",
|
|
70
|
+
"simple-git-hooks": "^2.13.1",
|
|
71
|
+
"svelte": "^5.56.9",
|
|
72
|
+
"typescript": "^5.1.6",
|
|
73
|
+
"typescript-eslint": "^8.67.0",
|
|
74
|
+
"vitest": "^4.1.10"
|
|
75
|
+
},
|
|
76
|
+
"svelte": "./dist/index.js",
|
|
77
|
+
"simple-git-hooks": {
|
|
78
|
+
"pre-commit": "npm run lint"
|
|
64
79
|
}
|
|
65
80
|
}
|
package/dist/index.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var H=Object.defineProperty,q=Object.defineProperties;var B=Object.getOwnPropertyDescriptors;var x=Object.getOwnPropertySymbols;var K=Object.prototype.hasOwnProperty,A=Object.prototype.propertyIsEnumerable;var N=(s,t,e)=>t in s?H(s,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):s[t]=e,l=(s,t)=>{for(var e in t||(t={}))K.call(t,e)&&N(s,e,t[e]);if(x)for(var e of x(t))A.call(t,e)&&N(s,e,t[e]);return s},f=(s,t)=>q(s,B(t));var L=(s,t)=>{var e={};for(var a in s)K.call(s,a)&&t.indexOf(a)<0&&(e[a]=s[a]);if(s!=null&&x)for(var a of x(s))t.indexOf(a)<0&&A.call(s,a)&&(e[a]=s[a]);return e};var _store = require('svelte/store');var C=["error","warn","debug"],$=({logger:s=console,level:t=C[1],prefix:e="[i18n]: "})=>C.reduce((a,r,i)=>f(l({},a),{[r]:o=>C.indexOf(t)>=i&&s[r](`${e}${o}`)}),{}),c=$({}),V=s=>{c=s};var z=n=>{var d=n,{parser:s,key:t,params:e,translations:a,locale:r,fallbackLocale:i}=d,o=L(d,["parser","key","params","translations","locale","fallbackLocale"]);if(!t)return c.warn(`No translation key provided ('${r}' locale). Skipping translation...`),"";if(!r)return c.warn(`No locale provided for '${t}' key. Skipping translation...`),"";let u=(a[r]||{})[t];if(i&&u===void 0&&(c.debug(`No translation provided for '${t}' key in locale '${r}'. Trying fallback '${i}'`),u=(a[i]||{})[t]),u===void 0){if(c.debug(`No translation provided for '${t}' key in fallback '${i}'.`),o.hasOwnProperty("fallbackValue"))return o.fallbackValue;c.warn(`No translation nor fallback found for '${t}' .`)}return s.parse(u,e,r,t)},h=(...s)=>s.length?s.filter(t=>!!t).map(t=>{let e=`${t}`.toLowerCase();try{let[a]=Intl.Collator.supportedLocalesOf(t);if(!a)throw new Error;e=a}catch(a){c.warn(`'${t}' locale is non-standard.`)}return e}):[],w=(s,t,e)=>{if(t&&Array.isArray(s))return s.map(a=>w(a,t));if(s&&typeof s=="object"){let a=Object.keys(s).reduce((r,i)=>{let o=s[i],n=e?`${e}.${i}`:`${i}`;return o&&typeof o=="object"&&!(t&&Array.isArray(o))?l(l({},r),w(o,t,n)):f(l({},r),{[n]:w(o,t)})},{});return Object.keys(a).length?a:null}return s},G=s=>s.reduce((t,{key:e,data:a,locale:r})=>{if(!a)return t;let[i]=h(r),o=f(l({},t[i]||{}),{[e]:a});return f(l({},t),{[i]:o})},{}),E=async s=>{try{let t=await Promise.all(s.map(r=>{var i=r,{loader:e}=i,a=L(i,["loader"]);return new Promise(async o=>{let n;try{n=await e()}catch(d){c.error(`Failed to load translation. Verify your '${a.locale}' > '${a.key}' Loader.`),c.error(d)}o(f(l({loader:e},a),{data:n}))})}));return G(t)}catch(t){c.error(t)}return{}},W=s=>t=>{try{if(typeof t=="string")return t===s;if(typeof t=="object")return t.test(s)}catch(e){c.error("Invalid route config!")}return!1},F=(s,t)=>{let e=!0;try{e=Object.keys(s).filter(a=>s[a]!==void 0).every(a=>s[a]===t[a])}catch(a){}return e};var D=1e3*60*60*24,O= exports.default =class{constructor(t){this.cachedAt=0;this.loadedKeys={};this.currentRoute=_store.writable.call(void 0, );this.config=_store.writable.call(void 0, );this.isLoading=_store.writable.call(void 0, !1);this.promises=new Set;this.loading={subscribe:this.isLoading.subscribe,toPromise:(t,e)=>{let{fallbackLocale:a}=_store.get.call(void 0, this.config),r=Array.from(this.promises).filter(i=>{let o=F({locale:h(t)[0],route:e},i);return a&&(o=o||F({locale:h(a)[0],route:e},i)),o}).map(({promise:i})=>i);return Promise.all(r)},get:()=>_store.get.call(void 0, this.isLoading)};this.privateRawTranslations=_store.writable.call(void 0, {});this.rawTranslations={subscribe:this.privateRawTranslations.subscribe,get:()=>_store.get.call(void 0, this.rawTranslations)};this.privateTranslations=_store.writable.call(void 0, {});this.translations={subscribe:this.privateTranslations.subscribe,get:()=>_store.get.call(void 0, this.translations)};this.locales=f(l({},_store.derived.call(void 0, [this.config,this.privateTranslations],([t,e])=>{if(!t)return[];let{loaders:a=[]}=t,r=a.map(({locale:o})=>o),i=Object.keys(e).map(o=>o);return Array.from(new Set([...h(...r),...h(...i)]))},[])),{get:()=>_store.get.call(void 0, this.locales)});this.internalLocale=_store.writable.call(void 0, );this.loaderTrigger=_store.derived.call(void 0, [this.internalLocale,this.currentRoute],([t,e],a)=>{var r,i;t!==void 0&&e!==void 0&&!(t===((r=_store.get.call(void 0, this.loaderTrigger))==null?void 0:r[0])&&e===((i=_store.get.call(void 0, this.loaderTrigger))==null?void 0:i[1]))&&(c.debug("Triggering translation load..."),a([t,e]))},[]);this.localeHelper=_store.writable.call(void 0, );this.locale={subscribe:this.localeHelper.subscribe,forceSet:this.localeHelper.set,set:this.internalLocale.set,update:this.internalLocale.update,get:()=>_store.get.call(void 0, this.locale)};this.initialized=_store.derived.call(void 0, [this.locale,this.currentRoute,this.privateTranslations],([t,e,a],r)=>{_store.get.call(void 0, this.initialized)||r(t!==void 0&&e!==void 0&&!!Object.keys(a).length)});this.translation=_store.derived.call(void 0, [this.privateTranslations,this.locale,this.isLoading],([t,e,a],r)=>{let i=t[e];i&&Object.keys(i).length&&!a&&r(i)},{});this.t=f(l({},_store.derived.call(void 0, [this.config,this.translation],r=>{var[i]=r,o=i,{parser:t,fallbackLocale:e}=o,a=L(o,["parser","fallbackLocale"]);return(n,...d)=>z(l({parser:t,key:n,params:d,translations:this.translations.get(),locale:this.locale.get(),fallbackLocale:e},a.hasOwnProperty("fallbackValue")?{fallbackValue:a.fallbackValue}:{}))})),{get:(t,...e)=>_store.get.call(void 0, this.t)(t,...e)});this.l=f(l({},_store.derived.call(void 0, [this.config,this.translations],i=>{var[o,...n]=i,d=o,{parser:t,fallbackLocale:e}=d,a=L(d,["parser","fallbackLocale"]),[r]=n;return(u,b,...k)=>z(l({parser:t,key:b,params:k,translations:r,locale:u,fallbackLocale:e},a.hasOwnProperty("fallbackValue")?{fallbackValue:a.fallbackValue}:{}))})),{get:(t,e,...a)=>_store.get.call(void 0, this.l)(t,e,...a)});this.getLocale=t=>{let{fallbackLocale:e}=_store.get.call(void 0, this.config)||{},a=t||e;if(!a)return;let r=this.locales.get();return r.find(o=>h(a).includes(o))||r.find(o=>h(e).includes(o))};this.setLocale=t=>{if(t&&t!==_store.get.call(void 0, this.internalLocale))return c.debug(`Setting '${t}' locale.`),this.internalLocale.set(t),this.loading.toPromise(t,_store.get.call(void 0, this.currentRoute))};this.setRoute=t=>{if(t!==_store.get.call(void 0, this.currentRoute)){c.debug(`Setting '${t}' route.`),this.currentRoute.set(t);let e=_store.get.call(void 0, this.internalLocale);return this.loading.toPromise(e,t)}};this.loadConfig=async t=>{await this.configLoader(t)};this.getTranslationProps=async(t=this.locale.get(),e=_store.get.call(void 0, this.currentRoute))=>{let a=_store.get.call(void 0, this.config);if(!a||!t)return[];let r=this.translations.get(),{loaders:i,fallbackLocale:o="",cache:n=D}=a||{},d=Number.isNaN(+n)?D:+n;this.cachedAt?Date.now()>d+this.cachedAt&&(c.debug("Refreshing cache."),this.loadedKeys={},this.cachedAt=0):(c.debug("Setting cache timestamp."),this.cachedAt=Date.now());let[u,b]=h(t,o),k=r[u],I=r[b],R=(i||[]).map(j=>{var T=j,{locale:p}=T,y=L(T,["locale"]);return f(l({},y),{locale:h(p)[0]})}).filter(({routes:p})=>!p||(p||[]).some(W(e))).filter(({key:p,locale:y})=>y===u&&(!k||!(this.loadedKeys[u]||[]).includes(p))||o&&y===b&&(!I||!(this.loadedKeys[b]||[]).includes(p)));if(R.length){this.isLoading.set(!0),c.debug("Fetching translations...");let p=await E(R);this.isLoading.set(!1);let y=Object.keys(p).reduce((T,P)=>f(l({},T),{[P]:Object.keys(p[P])}),{}),j=R.filter(({key:T,locale:P})=>(y[P]||[]).some(S=>`${S}`.startsWith(T))).reduce((T,{key:P,locale:S})=>f(l({},T),{[S]:[...T[S]||[],P]}),{});return[p,j]}return[]};this.addTranslations=(t,e)=>{if(!t)return;let a=_store.get.call(void 0, this.config),{preprocess:r}=a||{};c.debug("Adding translations...");let i=Object.keys(t||{});this.privateRawTranslations.update(o=>i.reduce((n,d)=>f(l({},n),{[d]:l(l({},n[d]||{}),t[d])}),o)),this.privateTranslations.update(o=>i.reduce((n,d)=>{let u=!0,b=t[d];return typeof r=="function"&&(b=r(b)),(typeof r=="function"||r==="none")&&(u=!1),f(l({},n),{[d]:l(l({},n[d]||{}),u?w(b,r==="preserveArrays"):b)})},o)),i.forEach(o=>{let n=Object.keys(t[o]).map(d=>`${d}`.split(".")[0]);e&&(n=e[o]),this.loadedKeys[o]=Array.from(new Set([...this.loadedKeys[o]||[],...n||[]]))})};this.loader=async([t,e])=>{let a=this.getLocale(t)||void 0;c.debug(`Adding loader promise for '${a}' locale and '${e}' route.`);let r=(async()=>{let i=await this.getTranslationProps(a,e);i.length&&this.addTranslations(...i)})();this.promises.add({locale:a,route:e,promise:r}),r.then(()=>{a&&this.locale.get()!==a&&this.locale.forceSet(a)})};this.loadTranslations=(t,e=_store.get.call(void 0, this.currentRoute)||"")=>{let a=this.getLocale(t);if(a)return this.setRoute(e),this.setLocale(a),this.loading.toPromise(a,e)};this.loaderTrigger.subscribe(this.loader),this.isLoading.subscribe(async e=>{e&&this.promises.size&&(await this.loading.toPromise(),this.promises.clear(),c.debug("Loader promises have been purged."))}),t&&this.loadConfig(t)}async configLoader(t){if(!t)return c.error("No config provided!");let n=t,{initLocale:e,fallbackLocale:a,translations:r,log:i}=n,o=L(n,["initLocale","fallbackLocale","translations","log"]);i&&V($(i)),[e]=h(e),[a]=h(a),c.debug("Setting config."),this.config.set(l({initLocale:e,fallbackLocale:a,translations:r},o)),r&&this.addTranslations(r),e&&await this.loadTranslations(e)}};exports.default = O;
|