@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/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": "1.3.7",
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
- "require": "./dist/index.cjs",
12
- "import": "./dist/index.js",
13
- "types": "./dist/index.d.ts"
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": "tsup --watch",
19
- "test": "npx cross-env NODE_OPTIONS=--experimental-vm-modules jest",
20
- "build": "tsup",
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 --ext .ts,.js --ignore-path .gitignore ."
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": ">=3.49.0"
58
+ "svelte": ">=5"
51
59
  },
52
60
  "devDependencies": {
53
- "@types/jest": "^29.5.2",
54
- "@typescript-eslint/eslint-plugin": "^6.0.0",
55
- "@typescript-eslint/parser": "^6.0.0",
56
- "eslint": "^8.4.1",
57
- "eslint-config-airbnb-typescript": "^17.1.0",
58
- "eslint-plugin-import": "^2.25.3",
59
- "jest": "^29.6.0",
60
- "pre-commit": "^1.2.2",
61
- "ts-jest": "^29.1.1",
62
- "tsup": "^7.1.0",
63
- "typescript": "^5.1.6"
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;