@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/index.d.ts CHANGED
@@ -1,227 +1,2 @@
1
- import { Readable, Writable } from 'svelte/store';
2
-
3
- type ExtendedStore<T, Get = () => T, Store = Readable<T>> = Store & {
4
- get: Get;
5
- };
6
- type LoadingStore = Readable<boolean> & {
7
- toPromise: (locale?: Config.Locale, route?: string) => Promise<void[] | void>;
8
- get: () => boolean;
9
- };
10
- declare namespace DotNotation {
11
- type Input = any;
12
- type Output<V = any, K extends keyof V = keyof V> = {
13
- [P in K]?: V[K];
14
- } | null | V;
15
- type T = <I = Input>(input: I, preserveArrays?: boolean, parentKey?: string) => Output<I>;
16
- }
17
- declare namespace Logger {
18
- type Level = 'error' | 'warn' | 'debug';
19
- type Prefix = string;
20
- type T = {
21
- [key in Logger.Level]: (value: any) => void;
22
- };
23
- type FactoryProps = {
24
- /**
25
- * You can setup your custom logger using this property.
26
- *
27
- * @default console
28
- */
29
- logger?: Logger.T;
30
- /**
31
- * You can manage log level using this property.
32
- *
33
- * @default 'warn'
34
- */
35
- level?: Logger.Level;
36
- /**
37
- * You can prefix output logs using this property.
38
- *
39
- * @default '[i18n]: '
40
- */
41
- prefix?: Logger.Prefix;
42
- };
43
- }
44
- declare namespace Config {
45
- type Loader = Loader.LoaderModule;
46
- type Translations = Translations.T;
47
- type Locale = Translations.Locales[number];
48
- type InitLocale = Locale | undefined;
49
- type FallbackLocale = Locale | undefined;
50
- type FallbackValue = any;
51
- type T<P extends Parser.Params = Parser.Params> = {
52
- /**
53
- * You can use loaders to define your asyncronous translation load. All loaded data are stored so loader is triggered only once – in case there is no previous version of the translation. It can get refreshed according to `config.cache`.
54
- */
55
- loaders?: Loader[];
56
- /**
57
- * Locale-indexed translations, which should be in place before loaders will trigger. It's useful for static pages and synchronous translations – for example locally defined language names which are the same for all of the language mutations.
58
- *
59
- * @example {
60
- * "en": {"lang": {"en": "English", "cs": "Česky"}}
61
- * "cs": {"lang": {"en": "English", "cs": "Česky"}}
62
- * }
63
- */
64
- translations?: Translations.T;
65
- /**
66
- * If you set this property, translations will be initialized immediately using this locale.
67
- */
68
- initLocale?: InitLocale;
69
- /**
70
- * If you set this property, translations are automatically loaded not for current `$locale` only, but for this locale as well. In case there is no translation for current `$locale`, fallback locale translation is used instead of translation key placeholder. This is also used as a fallback when unknown locale is set.
71
- */
72
- fallbackLocale?: FallbackLocale;
73
- /**
74
- * By default, translation key is returned in case no translation is found for given translation key. For example, `$t('unknown.key')` will result in `'unknown.key'` output. You can set this output value using this config prop.
75
- */
76
- fallbackValue?: FallbackValue;
77
- /**
78
- * Preprocessor strategy or a custom function. Defines, how to transform the translation data immediately after the load.
79
- * @default 'full'
80
- *
81
- * @example 'full'
82
- * {a: {b: [{c: {d: 1}}, {c: {d: 2}}]}} => {"a.b.0.c.d": 1, "a.b.1.c.d": 2}
83
- *
84
- * @example 'preserveArrays'
85
- * {a: {b: [{c: {d: 1}}, {c: {d: 2}}]}} => {"a.b": [{"c.d": 1}, {"c.d": 2}]}
86
- *
87
- * @example 'none'
88
- * {a: {b: [{c: {d: 1}}, {c: {d: 2}}]}} => {a: {b: [{c: {d: 1}}, {c: {d: 2}}]}}
89
- */
90
- preprocess?: 'full' | 'preserveArrays' | 'none' | ((input: Translations.Input) => any);
91
- /**
92
- * This property defines translation syntax you want to use.
93
- */
94
- parser: Parser.T<P>;
95
- /**
96
- * When you are running your app on Node.js server, translations are loaded only once during the SSR. This property allows you to setup a refresh period in milliseconds when your translations are refetched on the server.
97
- *
98
- * @default 86400000 // 24 hours
99
- *
100
- * @tip You can set to `Number.POSITIVE_INFINITY` to disable server-side refreshing.
101
- */
102
- cache?: number;
103
- /**
104
- * Custom logger configuration.
105
- */
106
- log?: Logger.FactoryProps;
107
- };
108
- }
109
- declare namespace Loader {
110
- type Key = string;
111
- type Locale = Config.Locale;
112
- type Route = string | RegExp;
113
- type IndexedKeys = Translations.LocaleIndexed<Key[]>;
114
- type LoaderModule = {
115
- /**
116
- * Represents the translation namespace. This key is used as a translation prefix so it should be module-unique. You can access your translation later using `$t('key.yourTranslation')`. It shouldn't include `.` (dot) character.
117
- */
118
- key: Key;
119
- /**
120
- * Locale (e.g. `en`, `de`) which is this loader for.
121
- */
122
- locale: Locale;
123
- /**
124
- * Function returning a `Promise` with translation data. You can use it to load files locally, fetch it from your API etc...
125
- */
126
- loader: T;
127
- /**
128
- * Define routes this loader should be triggered for. You can use Regular expressions too. For example `[/\/.ome/]` will be triggered for `/home` and `/rome` route as well (but still only once). Leave this `undefined` in case you want to load this module with any route (useful for common translations).
129
- */
130
- routes?: Route[];
131
- };
132
- type T = () => Promise<Translations.Input>;
133
- }
134
- declare namespace Parser {
135
- type Value = any;
136
- type Params = Array<unknown>;
137
- type Locale = Config.Locale;
138
- type Key = Loader.Key;
139
- type Output = any;
140
- type Parse<P extends Parser.Params = Parser.Params> = (
141
- /**
142
- * Translation value from the definitions.
143
- */
144
- value: Value,
145
- /**
146
- * Array of rest parameters given by user (e.g. payload variables etc...)
147
- */
148
- params: P,
149
- /**
150
- * Locale of translated message.
151
- */
152
- locale: Locale,
153
- /**
154
- * This key is serialized path to translation (e.g., `home.content.title`)
155
- */
156
- key: Key) => Output;
157
- type T<P extends Parser.Params = Parser.Params> = {
158
- /**
159
- * Parse function deals with interpolation of user payload and returns interpolated message.
160
- */
161
- parse: Parse<P>;
162
- };
163
- }
164
- declare namespace Translations {
165
- type Locales<T = string> = T[];
166
- type SerializedTranslations = LocaleIndexed<DotNotation.Input>;
167
- type TranslationData<T = any> = Loader.LoaderModule & {
168
- data: T;
169
- };
170
- type FetchTranslations = (loaders: Loader.LoaderModule[]) => Promise<SerializedTranslations>;
171
- type TranslationFunction<P extends Parser.Params = Parser.Params> = (key: string, ...restParams: P) => any;
172
- type LocalTranslationFunction<P extends Parser.Params = Parser.Params> = (locale: Config.Locale, key: string, ...restParams: P) => any;
173
- type Translate = <P extends Parser.Params = Parser.Params>(props: {
174
- parser: Parser.T<P>;
175
- key: string;
176
- params: P;
177
- translations: SerializedTranslations;
178
- locale: Locales[number];
179
- fallbackLocale?: Config.FallbackLocale;
180
- fallbackValue?: Config.FallbackValue;
181
- }) => string;
182
- type Input<V = any> = {
183
- [K in any]: Input<V> | V;
184
- };
185
- type LocaleIndexed<V, L extends string = string> = {
186
- [locale in Locales<L>[number]]: V;
187
- };
188
- type T<V = any, L extends string = string> = LocaleIndexed<Input<V>, L>;
189
- }
190
-
191
- declare class I18n<ParserParams extends Parser.Params = any> {
192
- constructor(config?: Config.T<ParserParams>);
193
- private cachedAt;
194
- private loadedKeys;
195
- private currentRoute;
196
- private config;
197
- private isLoading;
198
- private promises;
199
- loading: LoadingStore;
200
- private privateRawTranslations;
201
- rawTranslations: ExtendedStore<Translations.SerializedTranslations>;
202
- private privateTranslations;
203
- translations: ExtendedStore<Translations.SerializedTranslations>;
204
- locales: ExtendedStore<Config.Locale[]>;
205
- private internalLocale;
206
- private loaderTrigger;
207
- private localeHelper;
208
- locale: ExtendedStore<Config.Locale, () => Config.Locale, Writable<string>> & {
209
- forceSet: any;
210
- };
211
- initialized: Readable<boolean>;
212
- private translation;
213
- t: ExtendedStore<Translations.TranslationFunction<ParserParams>, Translations.TranslationFunction<ParserParams>>;
214
- l: ExtendedStore<Translations.LocalTranslationFunction<ParserParams>, Translations.LocalTranslationFunction<ParserParams>>;
215
- private getLocale;
216
- setLocale: (locale?: string) => Promise<void | void[]> | undefined;
217
- setRoute: (route: string) => Promise<void | void[]> | undefined;
218
- configLoader(config: Config.T<ParserParams>): Promise<void>;
219
- loadConfig: (config: Config.T<ParserParams>) => Promise<void>;
220
- getTranslationProps: ($locale?: string, $route?: string) => Promise<[Translations.SerializedTranslations, Loader.IndexedKeys] | [
221
- ]>;
222
- addTranslations: (translations?: Translations.SerializedTranslations, keys?: Loader.IndexedKeys) => void;
223
- private loader;
224
- loadTranslations: (locale: Config.Locale, route?: string) => Promise<void | void[]> | undefined;
225
- }
226
-
227
- export { Config, Loader, Logger, Parser, Translations, I18n as default };
1
+ export { default, I18n } from './I18n.svelte.js';
2
+ export type { Config, Extension, Loader, Logger, Parser, Schema, Translations } from './types.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 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};import{derived as v,get as g,writable as m}from"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=class{constructor(t){this.cachedAt=0;this.loadedKeys={};this.currentRoute=m();this.config=m();this.isLoading=m(!1);this.promises=new Set;this.loading={subscribe:this.isLoading.subscribe,toPromise:(t,e)=>{let{fallbackLocale:a}=g(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:()=>g(this.isLoading)};this.privateRawTranslations=m({});this.rawTranslations={subscribe:this.privateRawTranslations.subscribe,get:()=>g(this.rawTranslations)};this.privateTranslations=m({});this.translations={subscribe:this.privateTranslations.subscribe,get:()=>g(this.translations)};this.locales=f(l({},v([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:()=>g(this.locales)});this.internalLocale=m();this.loaderTrigger=v([this.internalLocale,this.currentRoute],([t,e],a)=>{var r,i;t!==void 0&&e!==void 0&&!(t===((r=g(this.loaderTrigger))==null?void 0:r[0])&&e===((i=g(this.loaderTrigger))==null?void 0:i[1]))&&(c.debug("Triggering translation load..."),a([t,e]))},[]);this.localeHelper=m();this.locale={subscribe:this.localeHelper.subscribe,forceSet:this.localeHelper.set,set:this.internalLocale.set,update:this.internalLocale.update,get:()=>g(this.locale)};this.initialized=v([this.locale,this.currentRoute,this.privateTranslations],([t,e,a],r)=>{g(this.initialized)||r(t!==void 0&&e!==void 0&&!!Object.keys(a).length)});this.translation=v([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({},v([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)=>g(this.t)(t,...e)});this.l=f(l({},v([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)=>g(this.l)(t,e,...a)});this.getLocale=t=>{let{fallbackLocale:e}=g(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!==g(this.internalLocale))return c.debug(`Setting '${t}' locale.`),this.internalLocale.set(t),this.loading.toPromise(t,g(this.currentRoute))};this.setRoute=t=>{if(t!==g(this.currentRoute)){c.debug(`Setting '${t}' route.`),this.currentRoute.set(t);let e=g(this.internalLocale);return this.loading.toPromise(e,t)}};this.loadConfig=async t=>{await this.configLoader(t)};this.getTranslationProps=async(t=this.locale.get(),e=g(this.currentRoute))=>{let a=g(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=g(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=g(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)}};export{O as default};
1
+ export { default, I18n } from './I18n.svelte.js';
@@ -0,0 +1,5 @@
1
+ import type { Logger } from './types.js';
2
+ export declare const loggerFactory: ({ logger, level, prefix }: Logger.FactoryProps) => Logger.T;
3
+ export declare let logger: Logger.T;
4
+ export declare const setLogger: (l: Logger.T) => void;
5
+ export declare const logError: (message: string, error?: unknown) => void;
package/dist/logger.js ADDED
@@ -0,0 +1,35 @@
1
+ const loggerLevels = ['error', 'warn', 'debug'];
2
+ export const loggerFactory = ({ logger = console, level = loggerLevels[1], prefix = '[i18n]: ' }) => {
3
+ // An unknown level would otherwise yield indexOf === -1 and silence everything.
4
+ const levelIndex = loggerLevels.includes(level) ? loggerLevels.indexOf(level) : loggerLevels.indexOf('warn');
5
+ return loggerLevels.reduce((acc, key, i) => ({
6
+ ...acc,
7
+ [key]: (message, error) => {
8
+ if (levelIndex < i)
9
+ return undefined;
10
+ try {
11
+ // Inside the `try`: the logger is consumer code — it can be null, omit
12
+ // a level, or throw. Several call sites log from promise handlers that
13
+ // nothing awaits, where a throw would become an unhandled rejection.
14
+ if (typeof logger[key] !== 'function')
15
+ return undefined;
16
+ // The prefix applies to the message only; the error passes through raw
17
+ // so the logger formats its stack (or serializes it) itself. Forwarded
18
+ // only when present — `console` would otherwise print `undefined`.
19
+ if (error === undefined)
20
+ return logger[key](`${prefix}${message}`);
21
+ return logger[key](`${prefix}${message}`, error);
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ },
27
+ }), {});
28
+ };
29
+ export let logger = loggerFactory({});
30
+ export const setLogger = (l) => { logger = l; };
31
+ // Single shape for every reported failure: the context message first, the raw
32
+ // error alongside it, in one call — so a consumer's logger sees them together.
33
+ export const logError = (message, error) => {
34
+ logger.error(message, error);
35
+ };