@sveltekit-i18n/base 3.0.1 → 3.1.0-next.1
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 +156 -57
- package/dist/I18n.svelte.d.ts +101 -28
- package/dist/I18n.svelte.js +1126 -245
- package/dist/exports/kit.d.ts +2 -0
- package/dist/exports/kit.js +1 -0
- package/dist/exports/utils.d.ts +1 -1
- package/dist/exports/utils.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/kit/define.svelte.d.ts +9 -0
- package/dist/kit/define.svelte.js +186 -0
- package/dist/kit/env.browser.d.ts +1 -0
- package/dist/kit/env.browser.js +1 -0
- package/dist/kit/env.d.ts +1 -0
- package/dist/kit/env.js +1 -0
- package/dist/kit/internal.d.ts +21 -0
- package/dist/kit/internal.js +1 -0
- package/dist/kit/server.browser.d.ts +2 -0
- package/dist/kit/server.browser.js +6 -0
- package/dist/kit/server.d.ts +2 -0
- package/dist/kit/server.js +72 -0
- package/dist/kit/types.d.ts +93 -0
- package/dist/kit/types.js +1 -0
- package/dist/types.d.ts +176 -30
- package/dist/utils.d.ts +81 -3
- package/dist/utils.js +431 -31
- package/package.json +23 -3
package/README.md
CHANGED
|
@@ -30,10 +30,20 @@ Core i18n functionality for SvelteKit with support for custom message parsers. T
|
|
|
30
30
|
✅ **TypeScript** – Locales inferred from your config, keys and payloads from a [`schema`](#schema)
|
|
31
31
|
✅ **Zero dependencies** – Lightweight and fast
|
|
32
32
|
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
Svelte 5 or newer, and one of Node 22+, Bun 1.2+ or Deno 2+. The package is
|
|
36
|
+
ESM-only and imports no `node:` module, so every runtime that runs your
|
|
37
|
+
SvelteKit build runs it. The [`/kit`](#sveltekit) subpath needs SvelteKit 2;
|
|
38
|
+
the SvelteKit behaviour the docs describe is checked against 2.70 and the 3.0
|
|
39
|
+
prerelease.
|
|
40
|
+
|
|
33
41
|
## Installation
|
|
34
42
|
|
|
35
43
|
```bash
|
|
36
44
|
npm install @sveltekit-i18n/base
|
|
45
|
+
# bun add @sveltekit-i18n/base
|
|
46
|
+
# deno add npm:@sveltekit-i18n/base
|
|
37
47
|
```
|
|
38
48
|
|
|
39
49
|
You'll also need a parser:
|
|
@@ -42,7 +52,8 @@ You'll also need a parser:
|
|
|
42
52
|
# Choose one:
|
|
43
53
|
npm install @sveltekit-i18n/parser-curly
|
|
44
54
|
npm install @sveltekit-i18n/parser-icu
|
|
45
|
-
|
|
55
|
+
npm install @sveltekit-i18n/parser-mf2
|
|
56
|
+
npm install @sveltekit-i18n/parser-i18next
|
|
46
57
|
```
|
|
47
58
|
|
|
48
59
|
## Quick Start
|
|
@@ -60,70 +71,80 @@ npm install @sveltekit-i18n/parser-icu
|
|
|
60
71
|
### 2. Setup with a parser
|
|
61
72
|
|
|
62
73
|
```javascript
|
|
63
|
-
// src/lib/
|
|
64
|
-
import {
|
|
74
|
+
// src/lib/i18n.js
|
|
75
|
+
import { defineI18n } from '@sveltekit-i18n/base/kit';
|
|
65
76
|
import parser from '@sveltekit-i18n/parser-curly';
|
|
66
77
|
|
|
67
|
-
|
|
68
|
-
const config = {
|
|
78
|
+
export const config = {
|
|
69
79
|
parser: parser({ onReport: null, /* other parser options */ }),
|
|
70
80
|
loaders: [
|
|
71
81
|
{
|
|
72
|
-
locale: 'en',
|
|
73
|
-
|
|
74
|
-
loader: async () => (await import(
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
locale: 'cs',
|
|
78
|
-
key: 'common',
|
|
79
|
-
loader: async () => (await import('./cs/common.json')).default,
|
|
82
|
+
locale: ['en', 'cs'],
|
|
83
|
+
namespace: 'common',
|
|
84
|
+
loader: async ({ locale, namespace }) => (await import(`./translations/${locale}/${namespace}.json`)).default,
|
|
80
85
|
},
|
|
81
86
|
],
|
|
82
87
|
};
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
// them off the instance is what makes templates reactive. (`t`/`l` are
|
|
86
|
-
// functions and stay reactive even when destructured, since the tracked reads
|
|
87
|
-
// happen at call time. In a component, `const { loading } = $derived(i18n)`
|
|
88
|
-
// destructures value reads without losing reactivity.)
|
|
89
|
-
export const i18n = new I18n(config);
|
|
89
|
+
export const { handle, load, use, get } = defineI18n(config);
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
### 3.
|
|
92
|
+
### 3. Wire it into SvelteKit
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// src/hooks.server.js
|
|
96
|
+
export { handle } from '$lib/i18n';
|
|
97
|
+
```
|
|
93
98
|
|
|
94
99
|
```javascript
|
|
95
|
-
// src/routes/+layout.js
|
|
96
|
-
|
|
100
|
+
// src/routes/+layout.server.js and src/routes/+layout.js — the same line in both
|
|
101
|
+
export { load } from '$lib/i18n';
|
|
102
|
+
```
|
|
97
103
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
```svelte
|
|
105
|
+
<!-- src/routes/+layout.svelte -->
|
|
106
|
+
<script>
|
|
107
|
+
import { use } from '$lib/i18n';
|
|
102
108
|
|
|
103
|
-
|
|
109
|
+
let { data, children } = $props();
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
use(() => data);
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
{@render children()}
|
|
107
115
|
```
|
|
108
116
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
```html
|
|
118
|
+
<!-- src/app.html -->
|
|
119
|
+
<html lang="%lang%" dir="%dir%">
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The server picks the visitor's locale from the `Accept-Language` header (or
|
|
123
|
+
from a cookie, with `preferredLocale`), loads it per request and hands it to
|
|
124
|
+
the browser, so nothing loads twice and no visitor sees another's locale. See
|
|
125
|
+
[SvelteKit](./docs/README.md#sveltekit) for the details and
|
|
126
|
+
[Server-Side Rendering](./docs/README.md#server-side-rendering) for wiring it
|
|
127
|
+
by hand.
|
|
114
128
|
|
|
115
129
|
### 4. Use in components
|
|
116
130
|
|
|
117
131
|
```svelte
|
|
118
132
|
<script>
|
|
119
|
-
import {
|
|
133
|
+
import { get } from '$lib/i18n';
|
|
134
|
+
|
|
135
|
+
const i18n = get();
|
|
120
136
|
</script>
|
|
121
137
|
|
|
122
138
|
<p>{i18n.t('common.greeting', { name: 'World' })}</p>
|
|
123
139
|
```
|
|
124
140
|
|
|
125
141
|
The call reads the reactive translation table and locale, so the text updates
|
|
126
|
-
automatically when either changes — no stores, no `$` prefix.
|
|
142
|
+
automatically when either changes — no stores, no `$` prefix. Do NOT
|
|
143
|
+
destructure the instance's value properties: reading them off the instance is
|
|
144
|
+
what makes templates reactive. (`t`/`l` are functions and stay reactive even
|
|
145
|
+
when destructured, since the tracked reads happen at call time. In a component,
|
|
146
|
+
`const { loading } = $derived(i18n)` destructures value reads without losing
|
|
147
|
+
reactivity.)
|
|
127
148
|
|
|
128
149
|
## Using Different Parsers
|
|
129
150
|
|
|
@@ -152,8 +173,10 @@ import i18n from '@sveltekit-i18n/base';
|
|
|
152
173
|
|
|
153
174
|
const customParser = () => ({
|
|
154
175
|
parse: (value, params) => {
|
|
155
|
-
// Your custom interpolation logic
|
|
156
|
-
return value
|
|
176
|
+
// Your custom interpolation logic; `parse` must not throw on a non-string value
|
|
177
|
+
return typeof value === 'string'
|
|
178
|
+
? value.replace(/\{(\w+)\}/g, (_, key) => params[0]?.[key] ?? key)
|
|
179
|
+
: value;
|
|
157
180
|
},
|
|
158
181
|
});
|
|
159
182
|
|
|
@@ -179,18 +202,60 @@ Array of loader configurations:
|
|
|
179
202
|
loaders: [
|
|
180
203
|
{
|
|
181
204
|
locale: 'en', // Required: locale identifier
|
|
182
|
-
|
|
205
|
+
namespace: 'common', // Required: translation namespace
|
|
183
206
|
loader: async () => {}, // Required: async function returning translations
|
|
184
207
|
routes: ['/about'], // Optional: load only for specific routes
|
|
185
208
|
},
|
|
186
209
|
]
|
|
187
210
|
```
|
|
188
211
|
|
|
212
|
+
`locale` and `namespace` each take a list as well. Such a descriptor stands for one loader per locale and namespace pair, and the loader receives the pair it is loading, so one computed loader can replace a descriptor per file:
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
loaders: [
|
|
216
|
+
{
|
|
217
|
+
locale: ['en', 'cs'],
|
|
218
|
+
namespace: ['common', 'nav'],
|
|
219
|
+
loader: async ({ locale, namespace }) => (await import(`./${locale}/${namespace}.json`)).default,
|
|
220
|
+
},
|
|
221
|
+
]
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
A named capture group in a `RegExp` route is a load parameter: its match reaches the loader as `params`, and the loader runs again when it changes, its new data replacing the old:
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
{
|
|
228
|
+
locale: 'en',
|
|
229
|
+
namespace: 'article',
|
|
230
|
+
routes: [/^\/article\/(?<articleId>[^/]+)/],
|
|
231
|
+
loader: async ({ locale, params }) => (await fetch(`/api/articles/${params.articleId}/i18n/${locale}`)).json(),
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
See [route params](./docs/README.md#route-params) for the rules.
|
|
236
|
+
|
|
237
|
+
A loader whose source does the caching itself — a SvelteKit remote `query`, an SWR layer, an HTTP cache — sets `cache: false`. It then runs on every load trigger that selects it, and `config.cache` does not apply to it; only a hydrated snapshot holds it back, for the locale and route it was rendered for. See [the loader's `cache`](./docs/README.md#cache-optional).
|
|
238
|
+
|
|
239
|
+
A loader that throws is logged, and the rest of the load lands without its data; it runs again on the next load trigger. SvelteKit's `redirect()` and an `error()` below 500 (told by their shape: an own `status` with a `location` or a `body`, on a value that is not an `Error`) are logged too, but they also reject the load, so a SvelteKit `load` awaiting the call hands them to SvelteKit; the rejected call is undone — what it replaced goes back — unless a later call that has not failed came in the meantime. See [the loader](./docs/README.md#loader-required).
|
|
240
|
+
|
|
189
241
|
Both `loaders` and a loader's `routes` accept readonly arrays, so a whole-config `as const` is fine.
|
|
190
242
|
|
|
243
|
+
### `basePath`
|
|
244
|
+
|
|
245
|
+
The path the app is served under — SvelteKit's `kit.paths.base`. Every route handed in loses it on the way in, on a segment boundary only (under `/repo`, `/repo/about` is `/about`), so loader `routes` name the app's own paths. Set both from one environment variable:
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
// svelte.config.js: kit: { paths: { base: process.env.PUBLIC_BASE_PATH ?? '' } }
|
|
249
|
+
import { PUBLIC_BASE_PATH } from '$env/static/public';
|
|
250
|
+
|
|
251
|
+
basePath: PUBLIC_BASE_PATH
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
See [`basePath`](./docs/README.md#basepath).
|
|
255
|
+
|
|
191
256
|
### `translations`
|
|
192
257
|
|
|
193
|
-
Synchronous translations
|
|
258
|
+
Synchronous translations, available immediately. They seed the tables: the loaders of a namespace they name still run and merge into it. Hand a server's state over with [`hydrate()`](./docs/README.md#hydrateenvelope) instead:
|
|
194
259
|
|
|
195
260
|
```javascript
|
|
196
261
|
translations: {
|
|
@@ -208,6 +273,8 @@ Initialize with a specific locale immediately:
|
|
|
208
273
|
initLocale: 'en'
|
|
209
274
|
```
|
|
210
275
|
|
|
276
|
+
With [`defineI18n()`](#sveltekit) it loads nothing: it is a negotiation candidate. Leave it out of a config whose instance you [`hydrate()`](./docs/README.md#hydrateenvelope) by hand — its load starts in the constructor, before the hand-off can be applied.
|
|
277
|
+
|
|
211
278
|
### `fallbackLocale`
|
|
212
279
|
|
|
213
280
|
Fallback when translation is missing:
|
|
@@ -226,6 +293,10 @@ Default return value when translation key is not found:
|
|
|
226
293
|
fallbackValue: '...' // Default: returns the key itself
|
|
227
294
|
```
|
|
228
295
|
|
|
296
|
+
### `sanitizeLocales`
|
|
297
|
+
|
|
298
|
+
How locale identifiers are normalized before they key anything: `true` (default) resolves them to their ISO form through `Intl` (`'en-us'` is `'en-US'`), `false` keeps them as authored, and a function normalizes them your way. See [`sanitizeLocales`](./docs/README.md#sanitizelocales).
|
|
299
|
+
|
|
229
300
|
### `preprocess`
|
|
230
301
|
|
|
231
302
|
Transform translations after loading:
|
|
@@ -252,19 +323,19 @@ type TranslationSchema = {
|
|
|
252
323
|
const i18n = new I18n({ ...config, schema: {} as TranslationSchema });
|
|
253
324
|
```
|
|
254
325
|
|
|
255
|
-
Hand-write it for a small set of messages, or point the slot at a generated artifact. A schema whose keys are not a closed set is ignored, and keys stay plain strings. See [`schema`](./docs/README.md#schema) for the full rules.
|
|
326
|
+
Hand-write it for a small set of messages, or point the slot at a generated artifact — [@sveltekit-i18n/typegen](https://github.com/sveltekit-i18n/typegen), a separate package, generates one. A schema whose keys are not a closed set is ignored, and keys stay plain strings. See [`schema`](./docs/README.md#schema) for the full rules.
|
|
256
327
|
|
|
257
328
|
### `cache`
|
|
258
329
|
|
|
259
|
-
Time in milliseconds the loaded translations stay fresh for. By default, loaded translations never expire —
|
|
330
|
+
Time in milliseconds the loaded translations stay fresh for. By default, loaded translations never expire — each loader (but one with [`cache: false`](./docs/README.md#cache-optional)) runs once per locale and [route params](./docs/README.md#route-params) (a loader's `routes` decide whether a load trigger considers it, and the params their named groups capture decide when it runs again).
|
|
260
331
|
|
|
261
332
|
Set a finite value when your loaders fetch from a source that can change at runtime (e.g. a CMS):
|
|
262
333
|
|
|
263
334
|
```javascript
|
|
264
|
-
cache: 3600000 // Translations older than 1 hour refetch on the next load
|
|
335
|
+
cache: 3600000 // Translations older than 1 hour refetch on the next activating load
|
|
265
336
|
```
|
|
266
337
|
|
|
267
|
-
Set to `0` to treat translations as always stale (refetch on every load trigger). You can also drop the loaded state manually at any time with [`invalidate()`](#methods).
|
|
338
|
+
Expiry is evaluated by the next activating load trigger (`setLocale`, `setRoute`, `loadTranslations`); a warm load — `loadTranslations(…, { activate: false })` or `loadNamespace()` — fills the tables without evaluating it. Set to `0` to treat translations as always stale (refetch on every activating load trigger). A loader with `cache: false` is outside the window. You can also drop the loaded state manually at any time with [`invalidate()`](#methods).
|
|
268
339
|
|
|
269
340
|
### `extensions`
|
|
270
341
|
|
|
@@ -315,38 +386,57 @@ log: {
|
|
|
315
386
|
- `l(locale, key, ...params)` – translate for an explicit locale
|
|
316
387
|
- `locale` – the ACTIVE locale; assignment is a fire-and-forget `setLocale()`
|
|
317
388
|
- `locales` – available locales
|
|
318
|
-
- `loading` – `true` while any load is in flight
|
|
389
|
+
- `loading` – `true` while any activating load is in flight; a `{ activate: false }` load counts only once an activating trigger joins it
|
|
319
390
|
- `initialized` – locale and route set, translations present
|
|
320
391
|
- `translations` / `rawTranslations` – the (pre/post-preprocess) tables
|
|
321
392
|
|
|
322
393
|
### Methods
|
|
323
394
|
|
|
324
|
-
Load-triggering methods return the promise of the matching load — concurrent duplicate triggers share one in-flight load (and its promise) instead of fetching twice.
|
|
395
|
+
Load-triggering methods return the promise of the matching load — concurrent duplicate triggers from one route share one in-flight load (and its promise) instead of fetching twice.
|
|
325
396
|
|
|
326
|
-
- `loadTranslations(locale, route?)` – load translations for locale and route; `route` defaults to the current one
|
|
397
|
+
- `loadTranslations(locale, route?, options?)` – load translations for locale and route; `route` defaults to the current one, and `{ activate: false }` only fills the tables without switching to them
|
|
398
|
+
- `loadNamespace(namespace, locale?)` – load one namespace on demand, whatever its loaders' routes, without switching to it; it stays loaded across routes
|
|
327
399
|
- `setLocale(locale)` – request a locale; loads once a route is known
|
|
328
400
|
- `setRoute(route)` – update the current route
|
|
329
401
|
- `loadConfig(config)` – (re)configure the instance
|
|
330
|
-
- `addTranslations(translations)` –
|
|
331
|
-
- `snapshot()` – serialize the active locale (and the fallback)
|
|
332
|
-
- `
|
|
402
|
+
- `addTranslations(translations)` – seed synchronous translations; the loaders of their namespaces still run and merge into them
|
|
403
|
+
- `snapshot(options?)` – serialize what the active locale (and the fallback) holds; `{ records: true }` returns the envelope `hydrate()` restores, with the loaders that delivered, the active locale and the route, and no argument returns the data alone, shaped like `config.translations`, for a plain `hydrate({ translations })`
|
|
404
|
+
- `hydrate(envelope?)` – restore a server's snapshot: its data, its load records (so those loaders do not run again — one with `cache: false` only for the locale and route it was rendered for), its locale and its route; an envelope without records keeps the loaders of the namespaces its data names from running
|
|
405
|
+
- `invalidate(locale?, namespace?)` – mark loaded translations stale (one locale or all, one namespace or all); loaders run again on the next load trigger, and a loader still in flight for what was invalidated settles with whatever it returns or throws discarded — an activating trigger fetches it again before it activates, unless another loader of its load threw SvelteKit's control flow
|
|
333
406
|
- `destroy()` – detach a per-request or per-component instance: in-flight loads settle discarded, further load and mutation calls are ignored, reads keep working
|
|
334
407
|
|
|
335
408
|
### Utilities
|
|
336
409
|
|
|
337
|
-
|
|
410
|
+
Pure helpers ship from a separate subpath, for the code around the instance that has to match the library's own behavior or decide which locale to ask for:
|
|
338
411
|
|
|
339
412
|
```javascript
|
|
340
|
-
import { sanitizeLocales, toDotNotation } from '@sveltekit-i18n/base/utils';
|
|
413
|
+
import { matchLocale, resolveLoaders, sanitizeLocales, textDirection, toDotNotation } from '@sveltekit-i18n/base/utils';
|
|
341
414
|
```
|
|
342
415
|
|
|
343
416
|
- `toDotNotation(input, preserveArrays?)` – the flattening behind [`preprocess`](#preprocess), for a custom `preprocess` that still wants dot notation
|
|
417
|
+
- `resolveLoaders(loaders, sanitizeLocales?)` – normalizes `config.loaders` the way the instance does, into one loader per locale and namespace pair, for code that reads a config from outside the instance
|
|
344
418
|
- `sanitizeLocales(...locales)` – normalizes a locale from a URL, cookie or `Accept-Language` header the way the instance does, so it can be compared against `locale`
|
|
419
|
+
- `matchLocale(requested, available)` – picks the configured locale a visitor asked for, from an `Accept-Language` header or `navigator.languages`, falling back from `en-GB` to `en` and answering `undefined` when nothing matches
|
|
420
|
+
- `textDirection(locale)` – `'ltr'` or `'rtl'` for a `dir` attribute, from the script the tag spells or the one `Intl.Locale#maximize()` adds, with no list of languages to keep
|
|
421
|
+
|
|
422
|
+
### SvelteKit
|
|
423
|
+
|
|
424
|
+
```javascript
|
|
425
|
+
import { defineI18n } from '@sveltekit-i18n/base/kit';
|
|
426
|
+
|
|
427
|
+
export const { handle, load, use, get } = defineI18n(config, { preferredLocale });
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
- `handle` – the `hooks.server.js` hook; fills `%lang%` and `%dir%` in `app.html`'s `<html>` tag
|
|
431
|
+
- `load` – the root layout's `load`, exported from `+layout.server.js` and `+layout.js` alike: negotiates the locale, loads it on the server per request and hands it to the one instance a browser tab keeps
|
|
432
|
+
- `use(() => data)` – called once in the root `+layout.svelte`; provides the instance, follows every navigation and keeps `<html lang>` and `<html dir>` in sync
|
|
433
|
+
- `get()` – the instance, in any component below the root layout
|
|
345
434
|
|
|
346
435
|
Full API documentation: [docs/README.md](./docs/README.md)
|
|
347
436
|
|
|
348
437
|
## Documentation
|
|
349
438
|
|
|
439
|
+
- 🌐 [sveltekit-i18n.github.io](https://sveltekit-i18n.github.io) – The documentation site, with a live playground
|
|
350
440
|
- 📖 [Full API Documentation](./docs/README.md) – Complete reference
|
|
351
441
|
- 📚 [Main Library Docs](https://github.com/sveltekit-i18n/lib/tree/master/docs/INDEX.md) – Guides, tutorials, and best practices
|
|
352
442
|
- 🎨 [Parsers](https://github.com/sveltekit-i18n/parsers) – Available parsers and how to create your own
|
|
@@ -356,11 +446,12 @@ Full API documentation: [docs/README.md](./docs/README.md)
|
|
|
356
446
|
|
|
357
447
|
```typescript
|
|
358
448
|
import { I18n, type Config } from '@sveltekit-i18n/base';
|
|
359
|
-
import parser from '@sveltekit-i18n/parser-curly';
|
|
449
|
+
import parser, { type Parser } from '@sveltekit-i18n/parser-curly';
|
|
360
450
|
|
|
361
451
|
// The parser's params – the rest parameters of `t`/`l`. Annotate only when the
|
|
362
|
-
// config lives on its own; `new I18n({ ... })` infers them.
|
|
363
|
-
|
|
452
|
+
// config lives on its own; `new I18n({ ... })` infers them. Take the tuple from
|
|
453
|
+
// the parser rather than spelling it by hand.
|
|
454
|
+
type Params = Parser.Params;
|
|
364
455
|
|
|
365
456
|
const config: Config.T<Params> = {
|
|
366
457
|
parser: parser({ onReport: null }),
|
|
@@ -368,7 +459,7 @@ const config: Config.T<Params> = {
|
|
|
368
459
|
};
|
|
369
460
|
```
|
|
370
461
|
|
|
371
|
-
Two more things are inferred from the config itself. [`schema`](#schema) types the keys and payloads of `t`/`l`, and every locale the config names — loader locales, `initLocale`, `fallbackLocale` and the keys of `translations` — completes the locale arguments and reads (`setLocale`, `loadTranslations`, `invalidate`, `l`, `locale`, `locales`):
|
|
462
|
+
Two more things are inferred from the config itself. [`schema`](#schema) types the keys and payloads of `t`/`l`, and every locale the config names — loader locales, `initLocale`, `fallbackLocale` and the keys of `translations` — completes the locale arguments and reads (`setLocale`, `loadTranslations`, `loadNamespace`, `invalidate`, `l`, `locale`, `locales`):
|
|
372
463
|
|
|
373
464
|
```typescript
|
|
374
465
|
const i18n = new I18n({ parser: parser({ onReport: null }), initLocale: 'en', fallbackLocale: 'de' });
|
|
@@ -382,9 +473,12 @@ The locales survive only when the config reaches the constructor as a literal
|
|
|
382
473
|
## Related Packages
|
|
383
474
|
|
|
384
475
|
- [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) – Complete solution, with the Curly Message Format parser included
|
|
385
|
-
- [@sveltekit-i18n/parser-curly](https://github.com/sveltekit-i18n/parsers/tree/master/parser-curly) – [Curly Message Format](https://
|
|
476
|
+
- [@sveltekit-i18n/parser-curly](https://github.com/sveltekit-i18n/parsers/tree/master/parser-curly) – [Curly Message Format](https://curlymessage.dev) parser
|
|
386
477
|
- [@sveltekit-i18n/parser-icu](https://github.com/sveltekit-i18n/parsers/tree/master/parser-icu) – ICU message format parser
|
|
478
|
+
- [@sveltekit-i18n/parser-mf2](https://github.com/sveltekit-i18n/parsers/tree/master/parser-mf2) – [Unicode MessageFormat 2](https://unicode.org/reports/tr35/tr35-messageFormat.html) parser
|
|
479
|
+
- [@sveltekit-i18n/parser-i18next](https://github.com/sveltekit-i18n/parsers/tree/master/parser-i18next) – [i18next](https://www.i18next.com) interpolation and formatting syntax parser
|
|
387
480
|
- [Extensions](https://github.com/sveltekit-i18n/extensions) – Official extensions for the `config.extensions` pipe
|
|
481
|
+
- [@sveltekit-i18n/typegen](https://github.com/sveltekit-i18n/typegen) – Generates the [`schema`](#schema) type from your translation files
|
|
388
482
|
|
|
389
483
|
## Contributing
|
|
390
484
|
|
|
@@ -396,6 +490,11 @@ For issues specific to base functionality, create a ticket [here](https://github
|
|
|
396
490
|
|
|
397
491
|
See [Releases](https://github.com/sveltekit-i18n/base/releases) for version history.
|
|
398
492
|
|
|
493
|
+
## Sponsor
|
|
494
|
+
|
|
495
|
+
You can support the maintenance of this package through
|
|
496
|
+
[GitHub Sponsors](https://github.com/sponsors/sveltekit-i18n).
|
|
497
|
+
|
|
399
498
|
## License
|
|
400
499
|
|
|
401
500
|
MIT
|
package/dist/I18n.svelte.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type { Config, Extension, Parser, Schema, Translations } from './types.js';
|
|
1
|
+
import type { Config, Extension, Loader, Parser, Schema, Snapshot, Translations } from './types.js';
|
|
2
2
|
declare class I18nCore<ParserParams extends Parser.Params = any, ParserOutput = string, TranslationSchema = never, LocaleUnion extends string = string> {
|
|
3
3
|
#private;
|
|
4
4
|
constructor(config?: Config.T<ParserParams, ParserOutput>);
|
|
5
5
|
/**
|
|
6
6
|
* The active locale. Reading it is reactive; assigning it is a shorthand for
|
|
7
7
|
* a fire-and-forget `setLocale()` — the value therefore updates once the
|
|
8
|
-
* locale's translations resolved, not synchronously on assignment.
|
|
8
|
+
* locale's translations resolved, not synchronously on assignment. It does
|
|
9
|
+
* not advance when a loader throws SvelteKit's `redirect()` or an `error()`
|
|
10
|
+
* below 500, which an assignment only logs.
|
|
9
11
|
*/
|
|
10
12
|
get locale(): Config.LocaleInput<LocaleUnion> | undefined;
|
|
11
13
|
set locale(value: Config.LocaleInput<LocaleUnion> | undefined);
|
|
@@ -25,43 +27,114 @@ declare class I18nCore<ParserParams extends Parser.Params = any, ParserOutput =
|
|
|
25
27
|
/** Like `t`, for an explicit locale. */
|
|
26
28
|
l: Translations.LocalTranslationFunction<ParserParams, ParserOutput, TranslationSchema, LocaleUnion>;
|
|
27
29
|
/**
|
|
28
|
-
* Public entry for (re)configuration.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
30
|
+
* Public entry for (re)configuration. It returns the promise of the
|
|
31
|
+
* `initLocale` load, which reports its own failure; a config that fails to
|
|
32
|
+
* apply is reported here. Either way the promise is marked handled, so a
|
|
33
|
+
* fire-and-forget call cannot become an unhandled rejection; an awaiting
|
|
34
|
+
* caller still receives it.
|
|
31
35
|
*/
|
|
32
36
|
loadConfig: (config: Config.T<ParserParams, ParserOutput>) => Promise<void>;
|
|
33
37
|
setLocale: (locale?: Config.LocaleInput<LocaleUnion>) => Promise<void>;
|
|
34
|
-
setRoute: (
|
|
35
|
-
loadTranslations: (locale: Config.LocaleInput<LocaleUnion>, route?: string) => Promise<void>;
|
|
38
|
+
setRoute: (input: string) => Promise<void>;
|
|
36
39
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* `{ activate: false }` only fills the tables: it leaves the requested
|
|
41
|
+
* locale, the route and `locale` untouched and does not count towards
|
|
42
|
+
* `loading` — what is rendered does not change: data of a loader whose route
|
|
43
|
+
* params differ from the ones the current route asks for is kept aside, the
|
|
44
|
+
* latest per loader, and the activating trigger that asks for them applies
|
|
45
|
+
* it. It
|
|
46
|
+
* leaves `cache` expiry to the next activating trigger. A loader's
|
|
47
|
+
* `redirect()` or `error()` below 500 rejects it all the same.
|
|
42
48
|
*/
|
|
43
|
-
|
|
49
|
+
loadTranslations: (locale: Config.LocaleInput<LocaleUnion>, route?: string, { activate }?: {
|
|
50
|
+
activate?: boolean;
|
|
51
|
+
}) => Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Loads one namespace for the active locale (or `locale`), whatever the
|
|
54
|
+
* routes of its loaders say — for what an interaction needs rather than a
|
|
55
|
+
* route: a modal, a panel, an editor. Warm, like `{ activate: false }`: it
|
|
56
|
+
* changes neither the locale nor `loading`, and it leaves `cache` expiry to
|
|
57
|
+
* the next activating trigger. It honours the load records, so calling it
|
|
58
|
+
* on every interaction fetches once — a loader with `cache: false` runs each
|
|
59
|
+
* time on its routes — and what it loads stays loaded across routes. A
|
|
60
|
+
* loader's `redirect()` or `error()` below 500 rejects it.
|
|
61
|
+
*/
|
|
62
|
+
loadNamespace: (namespace: Loader.Key, locale?: Config.LocaleInput<LocaleUnion>) => Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Marks loaded translations stale — for one locale or all of them, and for
|
|
65
|
+
* one namespace or all of them. Loaders run again on the NEXT load trigger;
|
|
66
|
+
* the call itself starts no load and keeps the currently displayed
|
|
67
|
+
* translations in place. A loader still in flight for what was invalidated
|
|
68
|
+
* is severed: its load settles, but what it returns or throws is discarded —
|
|
69
|
+
* it predates the invalidation — and an activating trigger fetches it again,
|
|
70
|
+
* once, before its locale activates. It leaves it to the next trigger when
|
|
71
|
+
* another loader of its load threw SvelteKit's control flow that still
|
|
72
|
+
* counts, which rejects the trigger, and when the refetch is severed too. A
|
|
73
|
+
* namespace invalidation leaves the locale's `cache` window where it was.
|
|
74
|
+
*/
|
|
75
|
+
invalidate: (locale?: Config.LocaleInput<LocaleUnion>, namespace?: Loader.Key) => void;
|
|
44
76
|
addTranslations: (translations?: Translations.SerializedTranslations) => void;
|
|
77
|
+
/**
|
|
78
|
+
* Restores the state `snapshot({ records: true })` captured on another
|
|
79
|
+
* instance: its data, its load records, the active locale and the route.
|
|
80
|
+
* A loader named by a record does not run again for the same params — one
|
|
81
|
+
* with `cache: false` only for the pass the envelope arrived with; data no
|
|
82
|
+
* record names is displayed but keeps no loader from running. An envelope
|
|
83
|
+
* without `records` is a plain hand-off instead: every namespace its data
|
|
84
|
+
* names keeps its loaders without params from running, and holds one with
|
|
85
|
+
* `cache: false` back for that pass. Nothing
|
|
86
|
+
* happens for `undefined`, so a load whose server half sent nothing can call
|
|
87
|
+
* it unconditionally.
|
|
88
|
+
*/
|
|
89
|
+
hydrate: (envelope?: Snapshot.Envelope) => void;
|
|
45
90
|
/**
|
|
46
91
|
* Serializes what this instance holds for the active locale and the fallback
|
|
47
|
-
* locale
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* data
|
|
92
|
+
* locale. The result is shaped like `config.translations`, and a client
|
|
93
|
+
* hands it over with `hydrate({ translations })`: a plain hand-off, whose
|
|
94
|
+
* namespace records keep the matching loaders without params from fetching
|
|
95
|
+
* it again and hold one with `cache: false` back for that pass. Passed to
|
|
96
|
+
* `addTranslations()` or assigned to `config.translations` it only seeds,
|
|
97
|
+
* and every loader runs again.
|
|
98
|
+
* A namespace plain data cannot hand over is left out, for the client to
|
|
99
|
+
* load: one fed by several loaders, whose record would suppress a part the
|
|
100
|
+
* payload lacks; one whose loader's routes can capture params, whose data a
|
|
101
|
+
* plain hand-off keeps as data no loader delivered, so the next params could
|
|
102
|
+
* not replace it; and one none of whose loaders delivered here and no
|
|
103
|
+
* hand-off named, whose seeded data would keep the client's loaders from
|
|
104
|
+
* ever running.
|
|
105
|
+
* A literal `__proto__` key is left out too, and with records its
|
|
106
|
+
* namespace's loaders are, and a namespace whose loader captures params,
|
|
107
|
+
* without them a namespace a loader serves, so the client loads it whole: the serializer SvelteKit hands load data to refuses an
|
|
108
|
+
* object that carries one. A locale
|
|
109
|
+
* named `__proto__` is left out altogether.
|
|
110
|
+
*
|
|
111
|
+
* `{ records: true }` returns an envelope for `hydrate()` instead: the same
|
|
112
|
+
* data, the loaders that delivered it, the active locale and the route. The
|
|
113
|
+
* records name each loader, so a namespace fed by several loaders is handed
|
|
114
|
+
* over too, and so is one a loader delivered for route params while its
|
|
115
|
+
* record says so — not a namespace with both, whose data the client could
|
|
116
|
+
* not split between them. What was seeded into the namespace of a loader
|
|
117
|
+
* whose routes capture params travels apart as `seeds`, so it outlives new
|
|
118
|
+
* params there, and reaches the client where the namespace is left out.
|
|
57
119
|
*/
|
|
58
|
-
snapshot:
|
|
120
|
+
snapshot: {
|
|
121
|
+
(options?: {
|
|
122
|
+
records?: false;
|
|
123
|
+
}): Translations.SerializedTranslations;
|
|
124
|
+
(options: {
|
|
125
|
+
records: true;
|
|
126
|
+
}): Snapshot.Envelope;
|
|
127
|
+
(options?: {
|
|
128
|
+
records?: boolean;
|
|
129
|
+
}): Translations.SerializedTranslations | Snapshot.Envelope;
|
|
130
|
+
};
|
|
59
131
|
/**
|
|
60
132
|
* Detaches the instance from its loading lifecycle: in-flight loads settle
|
|
61
|
-
* with their
|
|
62
|
-
* load or mutation call is ignored with a warning.
|
|
63
|
-
* `translations`, `snapshot`) keep working, so a
|
|
64
|
-
* renders its last state instead of breaking.
|
|
133
|
+
* with whatever their loaders return or throw discarded, `loading` drops to
|
|
134
|
+
* `false`, and every further load or mutation call is ignored with a warning.
|
|
135
|
+
* Reads (`t`, `l`, `locale`, `translations`, `snapshot`) keep working, so a
|
|
136
|
+
* component still tearing down renders its last state instead of breaking.
|
|
137
|
+
* Idempotent.
|
|
65
138
|
*/
|
|
66
139
|
destroy: () => void;
|
|
67
140
|
}
|