@sveltekit-i18n/base 1.3.6 → 1.3.8
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 +257 -80
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,126 +1,303 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
2
|
[](https://badge.fury.io/js/@sveltekit-i18n%2Fbase) 
|
|
4
3
|
|
|
5
4
|
# @sveltekit-i18n/base
|
|
6
|
-
This repository contains the base functionality of [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) and provides support for external message [parsers](https://github.com/sveltekit-i18n/parsers).
|
|
7
5
|
|
|
6
|
+
Core i18n functionality for SvelteKit with support for custom message parsers. This package provides the foundation for [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) and can be used standalone when you need maximum flexibility with custom parsers.
|
|
7
|
+
|
|
8
|
+
## When to use @sveltekit-i18n/base
|
|
9
|
+
|
|
10
|
+
**Use this package if you:**
|
|
11
|
+
- Need a custom message parser (like ICU, Fluent, or your own format)
|
|
12
|
+
- Want full control over message interpolation
|
|
13
|
+
- Are building a custom i18n solution
|
|
14
|
+
|
|
15
|
+
**Use [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) if you:**
|
|
16
|
+
- Want the quickest setup with sensible defaults
|
|
17
|
+
- Are happy with the default placeholder/modifier syntax
|
|
18
|
+
- Don't need custom parsers
|
|
8
19
|
|
|
9
|
-
## Key
|
|
20
|
+
## Key Features
|
|
21
|
+
|
|
22
|
+
✅ **SvelteKit ready** – Full SSR and CSR support
|
|
23
|
+
✅ **Parser-agnostic** – Use any message syntax you need
|
|
24
|
+
✅ **Custom data sources** – Load translations from anywhere (files, APIs, databases)
|
|
25
|
+
✅ **Module-based** – Translations load only for visited pages
|
|
26
|
+
✅ **Route-aware** – Automatic loading based on SvelteKit routes
|
|
27
|
+
✅ **Component-scoped** – Multiple translation instances with custom definitions
|
|
28
|
+
✅ **TypeScript** – Full type support
|
|
29
|
+
✅ **Zero dependencies** – Lightweight and fast
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @sveltekit-i18n/base
|
|
35
|
+
```
|
|
10
36
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
You'll also need a parser:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Choose one:
|
|
41
|
+
npm install @sveltekit-i18n/parser-default
|
|
42
|
+
npm install @sveltekit-i18n/parser-icu
|
|
43
|
+
# or create your own
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
### 1. Create translation files
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
// src/lib/translations/en/common.json
|
|
52
|
+
{
|
|
53
|
+
"greeting": "Hello, {{name}}!",
|
|
54
|
+
"farewell": "Goodbye!"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
19
57
|
|
|
20
|
-
|
|
58
|
+
### 2. Setup with a parser
|
|
21
59
|
|
|
22
|
-
Setup `translations.js` in your lib folder...
|
|
23
60
|
```javascript
|
|
61
|
+
// src/lib/translations/index.js
|
|
24
62
|
import i18n from '@sveltekit-i18n/base';
|
|
25
|
-
// use your preferred parser (or create your own)
|
|
26
63
|
import parser from '@sveltekit-i18n/parser-default';
|
|
27
|
-
// import parser from '@sveltekit-i18n/parser-icu';
|
|
28
64
|
|
|
29
|
-
/** @type {import('@sveltekit-i18n/
|
|
30
|
-
const config =
|
|
31
|
-
parser: parser({/*
|
|
65
|
+
/** @type {import('@sveltekit-i18n/base').Config} */
|
|
66
|
+
const config = {
|
|
67
|
+
parser: parser({ /* parser options */ }),
|
|
32
68
|
loaders: [
|
|
33
69
|
{
|
|
34
70
|
locale: 'en',
|
|
35
71
|
key: 'common',
|
|
36
|
-
loader: async () => (
|
|
37
|
-
await import('./en/common.json')
|
|
38
|
-
).default,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
locale: 'en',
|
|
42
|
-
key: 'home',
|
|
43
|
-
routes: ['/'], // you can use regexes as well!
|
|
44
|
-
loader: async () => (
|
|
45
|
-
await import('./en/home.json')
|
|
46
|
-
).default,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
locale: 'en',
|
|
50
|
-
key: 'about',
|
|
51
|
-
routes: ['/about'],
|
|
52
|
-
loader: async () => (
|
|
53
|
-
await import('./en/about.json')
|
|
54
|
-
).default,
|
|
72
|
+
loader: async () => (await import('./en/common.json')).default,
|
|
55
73
|
},
|
|
56
74
|
{
|
|
57
75
|
locale: 'cs',
|
|
58
76
|
key: 'common',
|
|
59
|
-
loader: async () => (
|
|
60
|
-
await import('./cs/common.json')
|
|
61
|
-
).default,
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
locale: 'cs',
|
|
65
|
-
key: 'home',
|
|
66
|
-
routes: ['/'],
|
|
67
|
-
loader: async () => (
|
|
68
|
-
await import('./cs/home.json')
|
|
69
|
-
).default,
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
locale: 'cs',
|
|
73
|
-
key: 'about',
|
|
74
|
-
routes: ['/about'],
|
|
75
|
-
loader: async () => (
|
|
76
|
-
await import('./cs/about.json')
|
|
77
|
-
).default,
|
|
77
|
+
loader: async () => (await import('./cs/common.json')).default,
|
|
78
78
|
},
|
|
79
79
|
],
|
|
80
|
-
}
|
|
80
|
+
};
|
|
81
81
|
|
|
82
82
|
export const { t, locale, locales, loading, loadTranslations } = new i18n(config);
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
### 3. Load translations in your layout
|
|
86
86
|
|
|
87
|
-
```
|
|
87
|
+
```javascript
|
|
88
|
+
// src/routes/+layout.js
|
|
88
89
|
import { loadTranslations } from '$lib/translations';
|
|
89
90
|
|
|
90
|
-
/** @type {import('
|
|
91
|
+
/** @type {import('./$types').LayoutLoad} */
|
|
91
92
|
export const load = async ({ url }) => {
|
|
92
93
|
const { pathname } = url;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
const initLocale = 'en';
|
|
95
|
+
|
|
96
|
+
await loadTranslations(initLocale, pathname);
|
|
97
|
+
|
|
98
98
|
return {};
|
|
99
|
-
}
|
|
99
|
+
};
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
### 4. Use in components
|
|
103
103
|
|
|
104
104
|
```svelte
|
|
105
105
|
<script>
|
|
106
106
|
import { t } from '$lib/translations';
|
|
107
|
-
|
|
108
|
-
const pageName = 'This page is Home page!';
|
|
109
107
|
</script>
|
|
110
108
|
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
<p>{$t('common.greeting', { name: 'World' })}</p>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Using Different Parsers
|
|
113
|
+
|
|
114
|
+
### ICU Message Format
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
import i18n from '@sveltekit-i18n/base';
|
|
118
|
+
import parser from '@sveltekit-i18n/parser-icu';
|
|
119
|
+
|
|
120
|
+
const config = {
|
|
121
|
+
parser: parser(),
|
|
122
|
+
loaders: [/* ... */],
|
|
123
|
+
};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"items": "You have {count, plural, =0 {no items} one {# item} other {# items}}."
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Custom Parser
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
import i18n from '@sveltekit-i18n/base';
|
|
136
|
+
|
|
137
|
+
const customParser = () => ({
|
|
138
|
+
parse: (value, params) => {
|
|
139
|
+
// Your custom interpolation logic
|
|
140
|
+
return value.replace(/\{(\w+)\}/g, (_, key) => params[0]?.[key] ?? key);
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const config = {
|
|
145
|
+
parser: customParser(),
|
|
146
|
+
loaders: [/* ... */],
|
|
147
|
+
};
|
|
115
148
|
```
|
|
116
149
|
|
|
150
|
+
Learn more about [creating custom parsers](https://github.com/sveltekit-i18n/parsers#creating-custom-parsers).
|
|
151
|
+
|
|
152
|
+
## Configuration Options
|
|
153
|
+
|
|
154
|
+
### `parser` (required)
|
|
155
|
+
|
|
156
|
+
Message parser instance. See [Parsers](https://github.com/sveltekit-i18n/parsers).
|
|
157
|
+
|
|
158
|
+
### `loaders`
|
|
159
|
+
|
|
160
|
+
Array of loader configurations:
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
loaders: [
|
|
164
|
+
{
|
|
165
|
+
locale: 'en', // Required: locale identifier
|
|
166
|
+
key: 'common', // Required: translation namespace
|
|
167
|
+
loader: async () => {}, // Required: async function returning translations
|
|
168
|
+
routes: ['/about'], // Optional: load only for specific routes
|
|
169
|
+
},
|
|
170
|
+
]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `translations`
|
|
174
|
+
|
|
175
|
+
Synchronous translations loaded immediately:
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
translations: {
|
|
179
|
+
en: {
|
|
180
|
+
'app.name': 'My App',
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `initLocale`
|
|
186
|
+
|
|
187
|
+
Initialize with a specific locale immediately:
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
initLocale: 'en'
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### `fallbackLocale`
|
|
194
|
+
|
|
195
|
+
Fallback when translation is missing:
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
fallbackLocale: 'en'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Note:** This loads translations for both current locale and fallback locale, which may impact performance.
|
|
202
|
+
|
|
203
|
+
### `fallbackValue`
|
|
204
|
+
|
|
205
|
+
Default return value when translation key is not found:
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
fallbackValue: '...' // Default: returns the key itself
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `preprocess`
|
|
212
|
+
|
|
213
|
+
Transform translations after loading:
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
preprocess: 'full' // 'full' | 'preserveArrays' | 'none' | custom function
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- `'full'` (default): Flattens all nested objects to dot notation
|
|
220
|
+
- `'preserveArrays'`: Flattens objects but preserves arrays
|
|
221
|
+
- `'none'`: No preprocessing
|
|
222
|
+
- Custom function: `(input) => transformedOutput`
|
|
223
|
+
|
|
224
|
+
### `cache`
|
|
225
|
+
|
|
226
|
+
Server-side cache duration in milliseconds:
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
cache: 86400000 // Default: 24 hours
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Set to `Number.POSITIVE_INFINITY` to disable cache refresh.
|
|
233
|
+
|
|
234
|
+
### `log`
|
|
235
|
+
|
|
236
|
+
Logging configuration:
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
log: {
|
|
240
|
+
level: 'warn', // 'error' | 'warn' | 'debug'
|
|
241
|
+
prefix: '[i18n]: ', // Log prefix
|
|
242
|
+
logger: console, // Custom logger
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## API Reference
|
|
247
|
+
|
|
248
|
+
### Stores
|
|
249
|
+
|
|
250
|
+
- `t` – Translation function store
|
|
251
|
+
- `locale` – Current locale (writable)
|
|
252
|
+
- `locales` – Available locales (readable)
|
|
253
|
+
- `loading` – Loading state (readable)
|
|
254
|
+
- `initialized` – Initialization state (readable)
|
|
255
|
+
- `translations` – All loaded translations (readable)
|
|
256
|
+
|
|
257
|
+
### Methods
|
|
258
|
+
|
|
259
|
+
- `loadTranslations(locale, route)` – Load translations for locale and route
|
|
260
|
+
- `setLocale(locale)` – Change current locale
|
|
261
|
+
- `setRoute(route)` – Update current route
|
|
262
|
+
|
|
263
|
+
Full API documentation: [docs/README.md](./docs/README.md)
|
|
264
|
+
|
|
265
|
+
## Documentation
|
|
266
|
+
|
|
267
|
+
- 📖 [Full API Documentation](./docs/README.md) – Complete reference
|
|
268
|
+
- 📚 [Main Library Docs](https://github.com/sveltekit-i18n/lib/tree/master/docs/INDEX.md) – Guides, tutorials, and best practices
|
|
269
|
+
- 🎨 [Parsers](https://github.com/sveltekit-i18n/parsers) – Available parsers and how to create your own
|
|
270
|
+
- 💡 [Examples](https://github.com/sveltekit-i18n/lib/tree/master/examples) – Real-world usage examples
|
|
271
|
+
|
|
272
|
+
## TypeScript Support
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import i18n, { type Config } from '@sveltekit-i18n/base';
|
|
276
|
+
import parser from '@sveltekit-i18n/parser-default';
|
|
277
|
+
import type { Config as ParserConfig } from '@sveltekit-i18n/parser-default';
|
|
278
|
+
|
|
279
|
+
const config: Config<ParserConfig> = {
|
|
280
|
+
parser: parser(),
|
|
281
|
+
loaders: [/* ... */],
|
|
282
|
+
};
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Related Packages
|
|
286
|
+
|
|
287
|
+
- [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) – Complete solution with default parser
|
|
288
|
+
- [@sveltekit-i18n/parser-default](https://github.com/sveltekit-i18n/parsers/tree/master/parser-default) – Default message parser
|
|
289
|
+
- [@sveltekit-i18n/parser-icu](https://github.com/sveltekit-i18n/parsers/tree/master/parser-icu) – ICU message format parser
|
|
290
|
+
|
|
291
|
+
## Contributing
|
|
292
|
+
|
|
293
|
+
For general contribution guidelines, see the [Contributing Guide](https://github.com/sveltekit-i18n/lib/blob/master/CONTRIBUTING.md) in the main library repository.
|
|
294
|
+
|
|
295
|
+
For issues specific to base functionality, create a ticket [here](https://github.com/sveltekit-i18n/lib/issues).
|
|
296
|
+
|
|
297
|
+
## Changelog
|
|
117
298
|
|
|
118
|
-
|
|
119
|
-
[Parsers](https://github.com/sveltekit-i18n/parsers)\
|
|
120
|
-
[Docs](https://github.com/sveltekit-i18n/base/tree/master/docs/README.md)\
|
|
121
|
-
[Examples](https://github.com/sveltekit-i18n/lib/tree/master/examples#parsers)\
|
|
122
|
-
[Changelog](https://github.com/sveltekit-i18n/base/releases)
|
|
299
|
+
See [Releases](https://github.com/sveltekit-i18n/base/releases) for version history.
|
|
123
300
|
|
|
301
|
+
## License
|
|
124
302
|
|
|
125
|
-
|
|
126
|
-
If you are facing some issues related to the base functionality, create a ticket [here](https://github.com/sveltekit-i18n/lib/issues).
|
|
303
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var B=Object.defineProperty,G=Object.defineProperties;var J=Object.getOwnPropertyDescriptors;var $=Object.getOwnPropertySymbols;var V=Object.prototype.hasOwnProperty,E=Object.prototype.propertyIsEnumerable;var A=(r,t,e)=>t in r?B(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,l=(r,t)=>{for(var e in t||(t={}))V.call(t,e)&&A(r,e,t[e]);if($)for(var e of $(t))E.call(t,e)&&A(r,e,t[e]);return r},g=(r,t)=>G(r,J(t));var L=(r,t)=>{var e={};for(var a in r)V.call(r,a)&&t.indexOf(a)<0&&(e[a]=r[a]);if(r!=null&&$)for(var a of $(r))t.indexOf(a)<0&&E.call(r,a)&&(e[a]=r[a]);return e};var _store = require('svelte/store');var S=["error","warn","debug"],O=({logger:r=console,level:t=S[1],prefix:e="[i18n]: "})=>{let a=S.includes(t)?S.indexOf(t):S.indexOf("warn");return S.reduce((n,s,i)=>g(l({},n),{[s]:o=>{if(!(a<i)&&typeof r[s]=="function")return r[s](`${e}${o}`)}}),{})},c=O({}),W=r=>{c=r};var C=(r,t)=>r!=null&&Object.prototype.hasOwnProperty.call(r,t),h=(r,t)=>C(r,t)?r[t]:void 0,F=o=>{var f=o,{parser:r,key:t,params:e,translations:a,locale:n,fallbackLocale:s}=f,i=L(f,["parser","key","params","translations","locale","fallbackLocale"]);if(!t)return c.warn(`No translation key provided ('${n}' locale). Skipping translation...`),"";if(!n)return c.warn(`No locale provided for '${t}' key. Skipping translation...`),"";let T=h(a,n),u=h(T,t);if(s&&u===void 0){c.debug(`No translation provided for '${t}' key in locale '${n}'. Trying fallback '${s}'`);let x=h(a,s);u=h(x,t)}if(u===void 0){if(c.debug(`No translation provided for '${t}' key in fallback '${s}'.`),C(i,"fallbackValue"))return i.fallbackValue;c.warn(`No translation nor fallback found for '${t}' .`)}return!r||typeof r.parse!="function"?(c.debug(`No parser configured. Returning raw value for '${t}' key.`),u===void 0?t:u):r.parse(u,e,n,t)},b=(...r)=>r.length?r.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}):[],R=(r,t,e)=>{if(t&&Array.isArray(r))return r.map(a=>R(a,t));if(r&&typeof r=="object"){let a=Object.keys(r).reduce((n,s)=>{let i=r[s],o=e?`${e}.${s}`:`${s}`;return i&&typeof i=="object"&&!(t&&Array.isArray(i))?l(l({},n),R(i,t,o)):g(l({},n),{[o]:R(i,t)})},{});return Object.keys(a).length?a:null}return r},M=r=>r.reduce((t,{key:e,data:a,locale:n})=>{if(!a)return t;let[s]=b(n),i=g(l({},t[s]||{}),{[e]:a});return g(l({},t),{[s]:i})},{}),D=async r=>{let t=await Promise.all(r.map(async n=>{var s=n,{loader:e}=s,a=L(s,["loader"]);let i;try{i=await e()}catch(o){c.error(`Failed to load translation. Verify your '${a.locale}' > '${a.key}' Loader.`),c.error(o)}return g(l({loader:e},a),{data:i})}));return M(t)},I=r=>t=>{try{if(typeof t=="string")return t===r;if(typeof t=="object")return t.test(r)}catch(e){c.error("Invalid route config!")}return!1},K=(r,t)=>{let e=!0;try{e=Object.keys(r).filter(a=>r[a]!==void 0).every(a=>r[a]===t[a])}catch(a){}return e};var H=1e3*60*60*24,N= 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),n=Array.from(this.promises).filter(s=>{let i=K({locale:b(t)[0],route:e},s);return a&&(i=i||K({locale:b(a)[0],route:e},s)),i}).map(({promise:s})=>s);return Promise.all(n)},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=g(l({},_store.derived.call(void 0, [this.config,this.privateTranslations],([t,e])=>{if(!t)return[];let{loaders:a=[]}=t,n=a.map(({locale:i})=>i),s=Object.keys(e).map(i=>i);return Array.from(new Set([...b(...n),...b(...s)]))},[])),{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 n,s;t!==void 0&&e!==void 0&&!(t===((n=_store.get.call(void 0, this.loaderTrigger))==null?void 0:n[0])&&e===((s=_store.get.call(void 0, this.loaderTrigger))==null?void 0:s[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],n)=>{_store.get.call(void 0, this.initialized)||n(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],n)=>{let s=h(t,e);s&&Object.keys(s).length&&!a&&n(s)},{});this.t=g(l({},_store.derived.call(void 0, [this.config,this.translation],n=>{var[s={}]=n,i=s,{parser:t,fallbackLocale:e}=i,a=L(i,["parser","fallbackLocale"]);return(o,...f)=>F(l({parser:t,key:o,params:f,translations:this.translations.get(),locale:this.locale.get(),fallbackLocale:e},C(a,"fallbackValue")?{fallbackValue:a.fallbackValue}:{}))})),{get:(t,...e)=>_store.get.call(void 0, this.t)(t,...e)});this.l=g(l({},_store.derived.call(void 0, [this.config,this.translations],s=>{var[i={},...o]=s,f=i,{parser:t,fallbackLocale:e}=f,a=L(f,["parser","fallbackLocale"]),[n]=o;return(T,u,...x)=>F(l({parser:t,key:u,params:x,translations:n,locale:T,fallbackLocale:e},C(a,"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 n=this.locales.get();return n.find(i=>b(a).includes(i))||n.find(i=>b(e).includes(i))};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 n=this.translations.get(),{loaders:s,fallbackLocale:i="",cache:o=H}=a||{},f=Number.isNaN(+o)?H:+o;this.cachedAt?Date.now()>f+this.cachedAt&&(c.debug("Refreshing cache."),this.loadedKeys={},this.cachedAt=0):(c.debug("Setting cache timestamp."),this.cachedAt=Date.now());let[T,u]=b(t,i),x=h(n,T),q=h(n,u),z=(s||[]).map(j=>{var y=j,{locale:p}=y,P=L(y,["locale"]);return g(l({},P),{locale:b(p)[0]})}).filter(({routes:p})=>!p||(p||[]).some(I(e))).filter(({key:p,locale:P})=>P===T&&(!x||!(h(this.loadedKeys,T)||[]).includes(p))||i&&P===u&&(!q||!(h(this.loadedKeys,u)||[]).includes(p)));if(z.length){this.isLoading.set(!0),c.debug("Fetching translations...");let p;try{p=await D(z)}finally{this.isLoading.set(!1)}let P=Object.keys(p).reduce((y,v)=>g(l({},y),{[v]:Object.keys(p[v])}),{}),j=z.filter(({key:y,locale:v})=>(h(P,v)||[]).some(k=>`${k}`.startsWith(y))).reduce((y,{key:v,locale:k})=>g(l({},y),{[k]:[...y[k]||[],v]}),{});return[p,j]}return[]};this.addTranslations=(t,e)=>{if(!t)return;let a=_store.get.call(void 0, this.config),{preprocess:n}=a||{};c.debug("Adding translations...");let s=Object.keys(t||{});this.privateRawTranslations.update(i=>s.reduce((o,f)=>g(l({},o),{[f]:l(l({},o[f]||{}),t[f])}),i)),this.privateTranslations.update(i=>s.reduce((o,f)=>{let T=!0,u=t[f];return typeof n=="function"&&(u=n(u)),(typeof n=="function"||n==="none")&&(T=!1),g(l({},o),{[f]:l(l({},o[f]||{}),T?R(u,n==="preserveArrays"):u)})},i)),s.forEach(i=>{let o=Object.keys(t[i]).map(f=>`${f}`.split(".")[0]);e&&(o=h(e,i)),this.loadedKeys[i]=Array.from(new Set([...h(this.loadedKeys,i)||[],...o||[]]))})};this.loader=async([t,e])=>{let a=this.getLocale(t)||void 0;c.debug(`Adding loader promise for '${a}' locale and '${e}' route.`);let n=(async()=>{let s=await this.getTranslationProps(a,e);s.length&&this.addTranslations(...s)})();this.promises.add({locale:a,route:e,promise:n}),n.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 o=t,{initLocale:e,fallbackLocale:a,translations:n,log:s}=o,i=L(o,["initLocale","fallbackLocale","translations","log"]);s&&W(O(s)),[e]=b(e),[a]=b(a),c.debug("Setting config."),this.config.set(l({initLocale:e,fallbackLocale:a,translations:n},i)),n&&this.addTranslations(n),e&&await this.loadTranslations(e)}};exports.default = N;
|
package/dist/index.d.ts
CHANGED
|
@@ -217,8 +217,7 @@ declare class I18n<ParserParams extends Parser.Params = any> {
|
|
|
217
217
|
setRoute: (route: string) => Promise<void | void[]> | undefined;
|
|
218
218
|
configLoader(config: Config.T<ParserParams>): Promise<void>;
|
|
219
219
|
loadConfig: (config: Config.T<ParserParams>) => Promise<void>;
|
|
220
|
-
getTranslationProps: ($locale?: string, $route?: string) => Promise<[Translations.SerializedTranslations, Loader.IndexedKeys] | [
|
|
221
|
-
]>;
|
|
220
|
+
getTranslationProps: ($locale?: string, $route?: string) => Promise<[Translations.SerializedTranslations, Loader.IndexedKeys] | []>;
|
|
222
221
|
addTranslations: (translations?: Translations.SerializedTranslations, keys?: Loader.IndexedKeys) => void;
|
|
223
222
|
private loader;
|
|
224
223
|
loadTranslations: (locale: Config.Locale, route?: string) => Promise<void | void[]> | undefined;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var B=Object.defineProperty,G=Object.defineProperties;var J=Object.getOwnPropertyDescriptors;var $=Object.getOwnPropertySymbols;var V=Object.prototype.hasOwnProperty,E=Object.prototype.propertyIsEnumerable;var A=(r,t,e)=>t in r?B(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,l=(r,t)=>{for(var e in t||(t={}))V.call(t,e)&&A(r,e,t[e]);if($)for(var e of $(t))E.call(t,e)&&A(r,e,t[e]);return r},g=(r,t)=>G(r,J(t));var L=(r,t)=>{var e={};for(var a in r)V.call(r,a)&&t.indexOf(a)<0&&(e[a]=r[a]);if(r!=null&&$)for(var a of $(r))t.indexOf(a)<0&&E.call(r,a)&&(e[a]=r[a]);return e};import{derived as w,get as d,writable as m}from"svelte/store";var S=["error","warn","debug"],O=({logger:r=console,level:t=S[1],prefix:e="[i18n]: "})=>{let a=S.includes(t)?S.indexOf(t):S.indexOf("warn");return S.reduce((n,s,i)=>g(l({},n),{[s]:o=>{if(!(a<i)&&typeof r[s]=="function")return r[s](`${e}${o}`)}}),{})},c=O({}),W=r=>{c=r};var C=(r,t)=>r!=null&&Object.prototype.hasOwnProperty.call(r,t),h=(r,t)=>C(r,t)?r[t]:void 0,F=o=>{var f=o,{parser:r,key:t,params:e,translations:a,locale:n,fallbackLocale:s}=f,i=L(f,["parser","key","params","translations","locale","fallbackLocale"]);if(!t)return c.warn(`No translation key provided ('${n}' locale). Skipping translation...`),"";if(!n)return c.warn(`No locale provided for '${t}' key. Skipping translation...`),"";let T=h(a,n),u=h(T,t);if(s&&u===void 0){c.debug(`No translation provided for '${t}' key in locale '${n}'. Trying fallback '${s}'`);let x=h(a,s);u=h(x,t)}if(u===void 0){if(c.debug(`No translation provided for '${t}' key in fallback '${s}'.`),C(i,"fallbackValue"))return i.fallbackValue;c.warn(`No translation nor fallback found for '${t}' .`)}return!r||typeof r.parse!="function"?(c.debug(`No parser configured. Returning raw value for '${t}' key.`),u===void 0?t:u):r.parse(u,e,n,t)},b=(...r)=>r.length?r.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}):[],R=(r,t,e)=>{if(t&&Array.isArray(r))return r.map(a=>R(a,t));if(r&&typeof r=="object"){let a=Object.keys(r).reduce((n,s)=>{let i=r[s],o=e?`${e}.${s}`:`${s}`;return i&&typeof i=="object"&&!(t&&Array.isArray(i))?l(l({},n),R(i,t,o)):g(l({},n),{[o]:R(i,t)})},{});return Object.keys(a).length?a:null}return r},M=r=>r.reduce((t,{key:e,data:a,locale:n})=>{if(!a)return t;let[s]=b(n),i=g(l({},t[s]||{}),{[e]:a});return g(l({},t),{[s]:i})},{}),D=async r=>{let t=await Promise.all(r.map(async n=>{var s=n,{loader:e}=s,a=L(s,["loader"]);let i;try{i=await e()}catch(o){c.error(`Failed to load translation. Verify your '${a.locale}' > '${a.key}' Loader.`),c.error(o)}return g(l({loader:e},a),{data:i})}));return M(t)},I=r=>t=>{try{if(typeof t=="string")return t===r;if(typeof t=="object")return t.test(r)}catch(e){c.error("Invalid route config!")}return!1},K=(r,t)=>{let e=!0;try{e=Object.keys(r).filter(a=>r[a]!==void 0).every(a=>r[a]===t[a])}catch(a){}return e};var H=1e3*60*60*24,N=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}=d(this.config),n=Array.from(this.promises).filter(s=>{let i=K({locale:b(t)[0],route:e},s);return a&&(i=i||K({locale:b(a)[0],route:e},s)),i}).map(({promise:s})=>s);return Promise.all(n)},get:()=>d(this.isLoading)};this.privateRawTranslations=m({});this.rawTranslations={subscribe:this.privateRawTranslations.subscribe,get:()=>d(this.rawTranslations)};this.privateTranslations=m({});this.translations={subscribe:this.privateTranslations.subscribe,get:()=>d(this.translations)};this.locales=g(l({},w([this.config,this.privateTranslations],([t,e])=>{if(!t)return[];let{loaders:a=[]}=t,n=a.map(({locale:i})=>i),s=Object.keys(e).map(i=>i);return Array.from(new Set([...b(...n),...b(...s)]))},[])),{get:()=>d(this.locales)});this.internalLocale=m();this.loaderTrigger=w([this.internalLocale,this.currentRoute],([t,e],a)=>{var n,s;t!==void 0&&e!==void 0&&!(t===((n=d(this.loaderTrigger))==null?void 0:n[0])&&e===((s=d(this.loaderTrigger))==null?void 0:s[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:()=>d(this.locale)};this.initialized=w([this.locale,this.currentRoute,this.privateTranslations],([t,e,a],n)=>{d(this.initialized)||n(t!==void 0&&e!==void 0&&!!Object.keys(a).length)});this.translation=w([this.privateTranslations,this.locale,this.isLoading],([t,e,a],n)=>{let s=h(t,e);s&&Object.keys(s).length&&!a&&n(s)},{});this.t=g(l({},w([this.config,this.translation],n=>{var[s={}]=n,i=s,{parser:t,fallbackLocale:e}=i,a=L(i,["parser","fallbackLocale"]);return(o,...f)=>F(l({parser:t,key:o,params:f,translations:this.translations.get(),locale:this.locale.get(),fallbackLocale:e},C(a,"fallbackValue")?{fallbackValue:a.fallbackValue}:{}))})),{get:(t,...e)=>d(this.t)(t,...e)});this.l=g(l({},w([this.config,this.translations],s=>{var[i={},...o]=s,f=i,{parser:t,fallbackLocale:e}=f,a=L(f,["parser","fallbackLocale"]),[n]=o;return(T,u,...x)=>F(l({parser:t,key:u,params:x,translations:n,locale:T,fallbackLocale:e},C(a,"fallbackValue")?{fallbackValue:a.fallbackValue}:{}))})),{get:(t,e,...a)=>d(this.l)(t,e,...a)});this.getLocale=t=>{let{fallbackLocale:e}=d(this.config)||{},a=t||e;if(!a)return;let n=this.locales.get();return n.find(i=>b(a).includes(i))||n.find(i=>b(e).includes(i))};this.setLocale=t=>{if(t&&t!==d(this.internalLocale))return c.debug(`Setting '${t}' locale.`),this.internalLocale.set(t),this.loading.toPromise(t,d(this.currentRoute))};this.setRoute=t=>{if(t!==d(this.currentRoute)){c.debug(`Setting '${t}' route.`),this.currentRoute.set(t);let e=d(this.internalLocale);return this.loading.toPromise(e,t)}};this.loadConfig=async t=>{await this.configLoader(t)};this.getTranslationProps=async(t=this.locale.get(),e=d(this.currentRoute))=>{let a=d(this.config);if(!a||!t)return[];let n=this.translations.get(),{loaders:s,fallbackLocale:i="",cache:o=H}=a||{},f=Number.isNaN(+o)?H:+o;this.cachedAt?Date.now()>f+this.cachedAt&&(c.debug("Refreshing cache."),this.loadedKeys={},this.cachedAt=0):(c.debug("Setting cache timestamp."),this.cachedAt=Date.now());let[T,u]=b(t,i),x=h(n,T),q=h(n,u),z=(s||[]).map(j=>{var y=j,{locale:p}=y,P=L(y,["locale"]);return g(l({},P),{locale:b(p)[0]})}).filter(({routes:p})=>!p||(p||[]).some(I(e))).filter(({key:p,locale:P})=>P===T&&(!x||!(h(this.loadedKeys,T)||[]).includes(p))||i&&P===u&&(!q||!(h(this.loadedKeys,u)||[]).includes(p)));if(z.length){this.isLoading.set(!0),c.debug("Fetching translations...");let p;try{p=await D(z)}finally{this.isLoading.set(!1)}let P=Object.keys(p).reduce((y,v)=>g(l({},y),{[v]:Object.keys(p[v])}),{}),j=z.filter(({key:y,locale:v})=>(h(P,v)||[]).some(k=>`${k}`.startsWith(y))).reduce((y,{key:v,locale:k})=>g(l({},y),{[k]:[...y[k]||[],v]}),{});return[p,j]}return[]};this.addTranslations=(t,e)=>{if(!t)return;let a=d(this.config),{preprocess:n}=a||{};c.debug("Adding translations...");let s=Object.keys(t||{});this.privateRawTranslations.update(i=>s.reduce((o,f)=>g(l({},o),{[f]:l(l({},o[f]||{}),t[f])}),i)),this.privateTranslations.update(i=>s.reduce((o,f)=>{let T=!0,u=t[f];return typeof n=="function"&&(u=n(u)),(typeof n=="function"||n==="none")&&(T=!1),g(l({},o),{[f]:l(l({},o[f]||{}),T?R(u,n==="preserveArrays"):u)})},i)),s.forEach(i=>{let o=Object.keys(t[i]).map(f=>`${f}`.split(".")[0]);e&&(o=h(e,i)),this.loadedKeys[i]=Array.from(new Set([...h(this.loadedKeys,i)||[],...o||[]]))})};this.loader=async([t,e])=>{let a=this.getLocale(t)||void 0;c.debug(`Adding loader promise for '${a}' locale and '${e}' route.`);let n=(async()=>{let s=await this.getTranslationProps(a,e);s.length&&this.addTranslations(...s)})();this.promises.add({locale:a,route:e,promise:n}),n.then(()=>{a&&this.locale.get()!==a&&this.locale.forceSet(a)})};this.loadTranslations=(t,e=d(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 o=t,{initLocale:e,fallbackLocale:a,translations:n,log:s}=o,i=L(o,["initLocale","fallbackLocale","translations","log"]);s&&W(O(s)),[e]=b(e),[a]=b(a),c.debug("Setting config."),this.config.set(l({initLocale:e,fallbackLocale:a,translations:n},i)),n&&this.addTranslations(n),e&&await this.loadTranslations(e)}};export{N as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltekit-i18n/base",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.8",
|
|
4
4
|
"description": "Base functionality of sveltekit-i18n library with a support for external message parsers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"jest": "^29.6.0",
|
|
60
60
|
"pre-commit": "^1.2.2",
|
|
61
61
|
"ts-jest": "^29.1.1",
|
|
62
|
-
"tsup": "^
|
|
62
|
+
"tsup": "^8.0.1",
|
|
63
63
|
"typescript": "^5.1.6"
|
|
64
64
|
}
|
|
65
65
|
}
|