@owlmeans/i18n 0.1.18-rc.8 → 0.1.18
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 +22 -12
- package/agent-meta/manifest.json +3 -3
- package/agent-meta/skills/i18n/SKILL.md +98 -33
- package/agent-meta/skills/localization/SKILL.md +15 -7
- package/build/consts.d.ts +0 -1
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js.map +1 -1
- package/build/helper/types.d.ts +46 -0
- package/build/helper/types.d.ts.map +1 -0
- package/build/helper/types.js +2 -0
- package/build/helper/types.js.map +1 -0
- package/build/helper.d.ts +15 -1
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +117 -40
- package/build/helper.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/types.d.ts +20 -1
- package/build/types.d.ts.map +1 -1
- package/build/utils/consts.d.ts +7 -0
- package/build/utils/consts.d.ts.map +1 -1
- package/build/utils/consts.js +10 -0
- package/build/utils/consts.js.map +1 -1
- package/build/utils/index.d.ts +0 -1
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +0 -1
- package/build/utils/index.js.map +1 -1
- package/build/utils/storage/types.d.ts +9 -0
- package/build/utils/storage/types.d.ts.map +1 -0
- package/build/utils/storage/types.js +2 -0
- package/build/utils/storage/types.js.map +1 -0
- package/build/utils/storage.d.ts +3 -6
- package/build/utils/storage.d.ts.map +1 -1
- package/build/utils/storage.js +26 -18
- package/build/utils/storage.js.map +1 -1
- package/package.json +2 -2
- package/src/consts.ts +0 -2
- package/src/helper/types.ts +46 -0
- package/src/helper.ts +151 -39
- package/src/index.ts +1 -0
- package/src/types.ts +23 -1
- package/src/utils/consts.ts +13 -0
- package/src/utils/index.ts +0 -1
- package/src/utils/storage/types.ts +9 -0
- package/src/utils/storage.ts +28 -16
- package/tests/context.ts +3 -2
- package/tests/loader.spec.ts +153 -0
- package/tests/registration.spec.ts +24 -25
- package/tests/resolve.spec.ts +64 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Multi-level translation resource registration for OwlMeans applications.
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
bun add @owlmeans/i18n@^0.1.18
|
|
14
|
+
bun add @owlmeans/i18n@^0.1.18
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
@@ -19,32 +19,42 @@ bun add @owlmeans/i18n@^0.1.18-rc.7
|
|
|
19
19
|
Register translations for a package at startup:
|
|
20
20
|
|
|
21
21
|
```typescript
|
|
22
|
-
import {
|
|
22
|
+
import { i18nHelper } from '@owlmeans/i18n'
|
|
23
23
|
|
|
24
24
|
// Register app-level translations (highest priority)
|
|
25
|
-
addI18nApp('en', 'manager-web', {
|
|
25
|
+
i18nHelper.addI18nApp('en', 'manager-web', {
|
|
26
26
|
'project.create.title': 'Create Project',
|
|
27
27
|
'project.create.submit': 'Create',
|
|
28
28
|
})
|
|
29
29
|
|
|
30
30
|
// Register library-level translations (lower priority — overridable by apps)
|
|
31
|
-
|
|
32
|
-
addI18n('en', 'client-panel', { 'form.submit': 'Submit' })
|
|
31
|
+
i18nHelper.addI18nLib('en', 'client-panel', { 'form.submit': 'Submit' })
|
|
33
32
|
```
|
|
34
33
|
|
|
35
34
|
## API
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
All registration and loading goes through the `i18nHelper` object (`createI18nHelper()` builds one
|
|
37
|
+
over the same process-wide storage).
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
### `i18nHelper.addI18nApp(lng, resource, data, opts?)`
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Register translations at app tier (highest). Typically called in `src/i18n.ts`.
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
### `i18nHelper.addI18nLib(lng, resource, data, opts?)`
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Register translations at library tier (namespace `lib` by default). App-tier bundles override them.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
### `i18nHelper.initI18nResource(lng, resource, ns?)` / `i18nHelper.resolveI18nResource(lng, resource, ns?)`
|
|
48
|
+
|
|
49
|
+
Drain the bundles of one slot in merge order, or read their merged result without draining.
|
|
50
|
+
|
|
51
|
+
### `i18nHelper.addI18nLoader(lng, loader)` / `i18nHelper.loadI18nLanguage(lng)` / `i18nHelper.isI18nLanguageLoaded(lng)`
|
|
52
|
+
|
|
53
|
+
Register lazy language loaders, await them, and check whether they completed.
|
|
54
|
+
|
|
55
|
+
### `I18nTier`
|
|
56
|
+
|
|
57
|
+
Priority tiers: `Library` < `App`. App-tier translations win over library translations for the same key.
|
|
48
58
|
|
|
49
59
|
### `i18nStorage`
|
|
50
60
|
|
|
@@ -62,7 +72,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
62
72
|
your project's skill store (`.agents/skills/`):
|
|
63
73
|
|
|
64
74
|
```sh
|
|
65
|
-
npx @owlmeans/agent-skills@^0.1.18
|
|
75
|
+
npx @owlmeans/agent-skills@^0.1.18
|
|
66
76
|
```
|
|
67
77
|
|
|
68
78
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion":
|
|
2
|
+
"schemaVersion": 3,
|
|
3
3
|
"package": "@owlmeans/i18n",
|
|
4
|
-
"version": "0.1.18
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18",
|
|
5
|
+
"generatedAt": "2026-10-28T00:31:34.701Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: i18n
|
|
3
|
-
description: How to use @owlmeans/i18n — the core localization registry (no runtime deps). Auto-invoked when adding translatable strings to a library package, importing from this package,
|
|
3
|
+
description: How to use @owlmeans/i18n — the core localization registry (no runtime deps). Auto-invoked when adding translatable strings to a library package, importing from this package, working with the tier/priority system, or deferring a language pack with i18nHelper.addI18nLoader/loadI18nLanguage.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -8,19 +8,23 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/i18n
|
|
9
9
|
|
|
10
10
|
**Layer:** Core (no runtime deps)
|
|
11
|
-
**Install:** `"@owlmeans/i18n": "^0.1.18
|
|
11
|
+
**Install:** `"@owlmeans/i18n": "^0.1.18"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Purpose
|
|
14
14
|
|
|
15
|
-
Global registration store that packages write into at import time. React clients drain it lazily via `@owlmeans/client-i18n`. The store is addressed by **(ns, resource, language)**.
|
|
15
|
+
Global registration store that packages write into at import time — or, for an application's deferred language packs, from an async loader. React clients drain it lazily via `@owlmeans/client-i18n`. The store is addressed by **(ns, resource, language)**.
|
|
16
16
|
|
|
17
17
|
## Key Exports
|
|
18
18
|
|
|
19
19
|
| Export | Description |
|
|
20
20
|
|--------|-------------|
|
|
21
|
-
| `addI18nLib(lng, resource, data, opts?)` | Register library-owned strings (ns defaults to `'lib'`) |
|
|
22
|
-
| `addI18nApp(lng, resource, data, opts?)` | Register app-owned strings (ns defaults to resource name) |
|
|
23
|
-
| `initI18nResource(lng, resource, ns?)` | Drain a registered bundle for a language (called by client-i18n) |
|
|
21
|
+
| `i18nHelper.addI18nLib(lng, resource, data, opts?)` | Register library-owned strings (ns defaults to `'lib'`) |
|
|
22
|
+
| `i18nHelper.addI18nApp(lng, resource, data, opts?)` | Register app-owned strings (ns defaults to resource name) |
|
|
23
|
+
| `i18nHelper.initI18nResource(lng, resource, ns?)` | Drain a registered bundle for a language (called by client-i18n) |
|
|
24
|
+
| `i18nHelper.resolveI18nResource(lng, resource, ns = DEFAULT_NAMESPACE)` | The merged bundle of ANY language, read WITHOUT draining it — `null` when nothing is registered (see § Reading without draining) |
|
|
25
|
+
| `i18nHelper.addI18nLoader(lng, loader)` | Register an async `I18nLoader` (`() => Promise<unknown>`, typically a dynamic `import()` of a module that calls `i18nHelper.addI18nLib` / `addI18nApp`) for a language — see Deferred language packs |
|
|
26
|
+
| `i18nHelper.loadI18nLanguage(lng)` | Await every loader registered for `lng`; idempotent, retries a failed loader |
|
|
27
|
+
| `i18nHelper.isI18nLanguageLoaded(lng)` | Whether every loader of `lng` has completed; `true` for a language with none |
|
|
24
28
|
| `SUPPORTED_LNGS` / `SupportedLng` | `['en','pl','ru','be','uk','es','de']` — the canonical language set, and its union type |
|
|
25
29
|
| `DEFAULT_LNG` | `'en'` |
|
|
26
30
|
| `LIB_NAMESPACE` | `'lib'` |
|
|
@@ -30,35 +34,37 @@ Global registration store that packages write into at import time. React clients
|
|
|
30
34
|
| `I18nResourceOptions` | `{ ns?, priority? }` — the `opts` every `add*` takes |
|
|
31
35
|
| `MAX_PRIORITY` | `Number.MAX_SAFE_INTEGER` — the value substituted for an unset `priority`, which is what makes an unset one sort last |
|
|
32
36
|
|
|
33
|
-
`opts` also accepts a bare string, which is read as `ns`:
|
|
37
|
+
`opts` also accepts a bare string, which is read as `ns`:
|
|
38
|
+
`i18nHelper.addI18nLib('en', 'wallet', walletEn, 'did')`.
|
|
34
39
|
|
|
35
40
|
## Subpath Exports
|
|
36
41
|
|
|
37
|
-
- `./utils` — the store itself: `_OwlMeansI18nStorage` (`{ data }`, keyed ns → resource → language)
|
|
38
|
-
`
|
|
39
|
-
`
|
|
42
|
+
- `./utils` — the store itself: `_OwlMeansI18nStorage` (`{ data }`, keyed ns → resource → language)
|
|
43
|
+
and `tierCost`, the `I18nTier` → number map the sort is written against; plus the separate loader
|
|
44
|
+
store `_OwlMeansI18nLoaders` (`{ data }`, keyed by language).
|
|
40
45
|
|
|
41
46
|
Reach for `./utils` only to work around the drain-once rule below: assigning
|
|
42
47
|
`_OwlMeansI18nStorage.data = {}` empties every slot including its `lngInitialized` marks, so a
|
|
43
|
-
suite can register and drain the same resource repeatedly.
|
|
44
|
-
|
|
48
|
+
suite can register and drain the same resource repeatedly; `_OwlMeansI18nLoaders.data = {}`
|
|
49
|
+
forgets every loader and request mark. This package's own tests reset both between cases.
|
|
50
|
+
Application and library code registers through `i18nHelper.addI18nLib` / `addI18nApp`.
|
|
45
51
|
|
|
46
52
|
## Tiers
|
|
47
53
|
|
|
48
54
|
| Tier | Helper | Default ns | When to use |
|
|
49
55
|
|------|--------|-----------|-------------|
|
|
50
|
-
| Library | `addI18nLib` | `'lib'` | Any `@owlmeans/*` package |
|
|
51
|
-
| App | `addI18nApp` | resource name | Project-specific app / shared project package |
|
|
56
|
+
| Library | `i18nHelper.addI18nLib` | `'lib'` | Any `@owlmeans/*` package |
|
|
57
|
+
| App | `i18nHelper.addI18nApp` | resource name | Project-specific app / shared project package |
|
|
52
58
|
|
|
53
59
|
App-tier strings deep-merge **over** Library-tier strings at resolution time — but only for the
|
|
54
60
|
same **(ns, resource, language)** slot, because that triple is the address the store is keyed on
|
|
55
|
-
and the only thing `initI18nResource` drains. The default namespaces differ (`'lib'` for
|
|
61
|
+
and the only thing `i18nHelper.initI18nResource` drains. The default namespaces differ (`'lib'` for
|
|
56
62
|
`addI18nLib`, the resource name for `addI18nApp`), so overriding a library bundle means saying so:
|
|
57
63
|
|
|
58
64
|
```typescript
|
|
59
|
-
import {
|
|
65
|
+
import { i18nHelper, LIB_NAMESPACE } from '@owlmeans/i18n'
|
|
60
66
|
|
|
61
|
-
addI18nApp('en', 'errors', myErrors, { ns: LIB_NAMESPACE })
|
|
67
|
+
i18nHelper.addI18nApp('en', 'errors', myErrors, { ns: LIB_NAMESPACE })
|
|
62
68
|
```
|
|
63
69
|
|
|
64
70
|
Without `{ ns: LIB_NAMESPACE }` the app bundle lands in namespace `errors` while the library's sits
|
|
@@ -69,11 +75,35 @@ it — so the **last** applied wins. A registration that states no `priority` so
|
|
|
69
75
|
therefore beats every one that states a number: `priority` lowers a bundle in the stack rather than
|
|
70
76
|
raising it. Leave it unset unless one library must lose to another.
|
|
71
77
|
|
|
72
|
-
**A bundle only reaches i18next if it was
|
|
73
|
-
`initI18nResource`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
**A bundle only reaches i18next if it was REGISTERED before its slot is first drained.**
|
|
79
|
+
`i18nHelper.initI18nResource` reads a (ns, resource, language) slot exactly once — it marks the slot
|
|
80
|
+
drained and answers `null` for every later call — so a bundle registered through `addI18nLib` /
|
|
81
|
+
`addI18nApp` after a screen has drawn that language adds nothing a component can read. Registering
|
|
82
|
+
synchronously at import time is the simplest way to guarantee it — a side-effect import at the top
|
|
83
|
+
of the entry file, which is what re-exporting `./i18n.js` from `src/index.ts` achieves. The loader
|
|
84
|
+
API (`i18nHelper.addI18nLoader` / `loadI18nLanguage`) lets an application defer registration to an
|
|
85
|
+
async load instead, AS LONG AS it awaits `i18nHelper.loadI18nLanguage` — or
|
|
86
|
+
`@owlmeans/client-i18n`'s `i18nInstanceHelper.prepareI18n` — before the first render that would
|
|
87
|
+
drain that language.
|
|
88
|
+
|
|
89
|
+
## Reading without draining
|
|
90
|
+
|
|
91
|
+
`i18nHelper.resolveI18nResource(lng, resource, ns)` deep-merges every bundle registered for the slot
|
|
92
|
+
in the exact order `initI18nResource` hands them out — library tier, then app tier, `priority`
|
|
93
|
+
ascending, an unset priority last — so an app override wins as it does in i18next. Plain objects
|
|
94
|
+
merge key by key; any other value replaces (arrays are copied). It never marks the slot drained and
|
|
95
|
+
never creates an empty slot, so a later `initI18nResource` still returns the same bundles, and a
|
|
96
|
+
drained slot still resolves. The result never aliases a registered bundle.
|
|
97
|
+
|
|
98
|
+
Use it where there is no i18next instance or the language is not the active one: a server
|
|
99
|
+
rendering an e-mail or a paygate text, a legal text shown in the billing country's language while
|
|
100
|
+
the interface speaks another (`@owlmeans/payment`'s `consumerCopyHelper.consumerRightsCopy` is built
|
|
101
|
+
on it). Pass the namespace the bundle lives in — `LIB_NAMESPACE` for an `i18nHelper.addI18nLib`
|
|
102
|
+
bundle; the default is i18next's own `'translation'`.
|
|
103
|
+
|
|
104
|
+
It reads only what is REGISTERED: a language an application defers to a loader (Deferred language
|
|
105
|
+
packs) resolves `null` — or without its app-tier overrides — until
|
|
106
|
+
`await i18nHelper.loadI18nLanguage(lng)` has run for it.
|
|
77
107
|
|
|
78
108
|
## Per-package pattern
|
|
79
109
|
|
|
@@ -81,7 +111,7 @@ Every package that ships translatable strings exports a side-effect `i18n.ts`:
|
|
|
81
111
|
|
|
82
112
|
```typescript
|
|
83
113
|
// src/i18n.ts
|
|
84
|
-
import {
|
|
114
|
+
import { i18nHelper } from '@owlmeans/i18n'
|
|
85
115
|
import en from './i18n/en.json' with { type: 'json' }
|
|
86
116
|
import pl from './i18n/pl.json' with { type: 'json' }
|
|
87
117
|
import ru from './i18n/ru.json' with { type: 'json' }
|
|
@@ -89,14 +119,16 @@ import be from './i18n/be.json' with { type: 'json' }
|
|
|
89
119
|
import uk from './i18n/uk.json' with { type: 'json' }
|
|
90
120
|
import es from './i18n/es.json' with { type: 'json' }
|
|
91
121
|
import de from './i18n/de.json' with { type: 'json' }
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
addI18nLib('
|
|
95
|
-
addI18nLib('
|
|
96
|
-
addI18nLib('
|
|
97
|
-
addI18nLib('
|
|
98
|
-
addI18nLib('
|
|
99
|
-
addI18nLib('
|
|
122
|
+
import fr from './i18n/fr.json' with { type: 'json' }
|
|
123
|
+
|
|
124
|
+
i18nHelper.addI18nLib('en', 'my-package', en)
|
|
125
|
+
i18nHelper.addI18nLib('pl', 'my-package', pl)
|
|
126
|
+
i18nHelper.addI18nLib('ru', 'my-package', ru)
|
|
127
|
+
i18nHelper.addI18nLib('be', 'my-package', be)
|
|
128
|
+
i18nHelper.addI18nLib('uk', 'my-package', uk)
|
|
129
|
+
i18nHelper.addI18nLib('es', 'my-package', es)
|
|
130
|
+
i18nHelper.addI18nLib('de', 'my-package', de)
|
|
131
|
+
i18nHelper.addI18nLib('fr', 'my-package', fr)
|
|
100
132
|
```
|
|
101
133
|
|
|
102
134
|
Then re-export from `src/index.ts`:
|
|
@@ -123,12 +155,45 @@ Consumers use `useI18nLib('my-package', 'mySection')` → `t('title')` → resol
|
|
|
123
155
|
|
|
124
156
|
Use the optional `opts.ns` when keys must live in a namespace other than `'lib'`:
|
|
125
157
|
```typescript
|
|
126
|
-
addI18nLib('en', 'wallet', walletEn, { ns: 'did' })
|
|
158
|
+
i18nHelper.addI18nLib('en', 'wallet', walletEn, { ns: 'did' })
|
|
127
159
|
```
|
|
128
160
|
|
|
129
161
|
## Languages
|
|
130
162
|
|
|
131
|
-
All packages **must** ship
|
|
163
|
+
All packages **must** ship the 7 languages from `SUPPORTED_LNGS`. Adding a new key → add it to all
|
|
164
|
+
7 files in the same commit. An application may opt into an additional language without changing the
|
|
165
|
+
global constant; a reusable package used by that application then ships and synchronously registers
|
|
166
|
+
the same extra bundle, while other applications retain their existing selectable languages.
|
|
167
|
+
|
|
168
|
+
### Deferred language packs (applications only)
|
|
169
|
+
|
|
170
|
+
An application may keep a language's bundles out of its initial chunk and register them from a
|
|
171
|
+
loader. The loader store is separate from the resource store: a loader only REGISTERS (it calls
|
|
172
|
+
`i18nHelper.addI18nLib` / `addI18nApp` once its module evaluates); draining stays
|
|
173
|
+
`initI18nResource`'s, once per slot, unchanged.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// app entry, module scope — each module calls i18nHelper.addI18nApp('<lng>', …) when it evaluates
|
|
177
|
+
i18nHelper.addI18nLoader('de', () => import('./i18n/de.js'))
|
|
178
|
+
i18nHelper.addI18nLoader('pl', () => import('./i18n/pl.js'))
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- A loader does not run until its language is requested by `i18nHelper.loadI18nLanguage(lng)`; one
|
|
182
|
+
registered AFTER the language was requested starts at once, so a module evaluated late is never
|
|
183
|
+
skipped.
|
|
184
|
+
- `i18nHelper.loadI18nLanguage(lng)` resolves once every loader of `lng` has completed — including
|
|
185
|
+
loaders added while the call is in flight; one added after a call resolved is awaited by the next
|
|
186
|
+
call. Concurrent and repeated calls share in-flight runs and never re-run a completed loader.
|
|
187
|
+
- A failure rejects the call once the pass has settled; the failed loader alone is retried by the
|
|
188
|
+
next call, never cached as failed.
|
|
189
|
+
- The app awaits the fallback and the initial language before its first render —
|
|
190
|
+
`@owlmeans/client-i18n`'s `i18nInstanceHelper.prepareI18n` does both — and its
|
|
191
|
+
`i18nInstanceHelper.setLanguage` / `useLanguage` load a language before switching to it.
|
|
192
|
+
`@owlmeans/web-panel` re-exports `i18nHelper` (the loader API included) but not
|
|
193
|
+
`i18nInstanceHelper` — import that from `@owlmeans/client-i18n`.
|
|
194
|
+
|
|
195
|
+
A library package never defers: it registers every language synchronously at import (Per-package
|
|
196
|
+
pattern), because it cannot know the application it lands in awaits a boot step.
|
|
132
197
|
|
|
133
198
|
## Depends On
|
|
134
199
|
|
|
@@ -21,8 +21,8 @@ Keys are addressed as: `(namespace, resource.prefix.key)`.
|
|
|
21
21
|
|
|
22
22
|
| Who registers | Helper | namespace | key path |
|
|
23
23
|
|---|---|---|---|
|
|
24
|
-
| `@owlmeans/*` library package | `addI18nLib` | `'lib'` | `resource.prefix.key` |
|
|
25
|
-
| App or shared project package | `addI18nApp` | resource name | `resource.prefix.key` |
|
|
24
|
+
| `@owlmeans/*` library package | `i18nHelper.addI18nLib` | `'lib'` | `resource.prefix.key` |
|
|
25
|
+
| App or shared project package | `i18nHelper.addI18nApp` | resource name | `resource.prefix.key` |
|
|
26
26
|
|
|
27
27
|
The **resource name** is a stable identifier for the registering package, e.g. `'errors'`, `'payment'`, `'viable-manager-web'`.
|
|
28
28
|
|
|
@@ -32,7 +32,7 @@ A key like `t('title')` resolves to `namespace : resource.prefix.key`. The prefi
|
|
|
32
32
|
|
|
33
33
|
Example chain:
|
|
34
34
|
```
|
|
35
|
-
addI18nApp('en', 'viable-manager-web', data)
|
|
35
|
+
i18nHelper.addI18nApp('en', 'viable-manager-web', data)
|
|
36
36
|
// data = { "home-screen": { "title": "Welcome" } }
|
|
37
37
|
|
|
38
38
|
const t = useI18nApp(undefined, 'home-screen')
|
|
@@ -84,14 +84,14 @@ fallbacks; a surrounding `PanelContext` is what redirects it.
|
|
|
84
84
|
## App-level override of library strings
|
|
85
85
|
|
|
86
86
|
App-tier registrations deep-merge over Library-tier for the same `(ns, resource)` — and the
|
|
87
|
-
namespace has to be said out loud, because `addI18nApp` defaults it to the **resource**
|
|
88
|
-
the library's bundle sits in `lib`:
|
|
87
|
+
namespace has to be said out loud, because `i18nHelper.addI18nApp` defaults it to the **resource**
|
|
88
|
+
name while the library's bundle sits in `lib`:
|
|
89
89
|
```typescript
|
|
90
90
|
import '@owlmeans/error' // Library tier: lib:errors.*
|
|
91
|
-
import {
|
|
91
|
+
import { i18nHelper, LIB_NAMESPACE } from '@owlmeans/i18n'
|
|
92
92
|
import myErrors from './i18n/en.json'
|
|
93
93
|
|
|
94
|
-
addI18nApp('en', 'errors', myErrors, { ns: LIB_NAMESPACE }) // App tier wins for matching keys
|
|
94
|
+
i18nHelper.addI18nApp('en', 'errors', myErrors, { ns: LIB_NAMESPACE }) // App tier wins for matching keys
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Drop `{ ns: LIB_NAMESPACE }` and the app bundle lands in namespace `errors` while `useI18nLib('errors')`
|
|
@@ -106,9 +106,17 @@ When adding translatable strings to any package:
|
|
|
106
106
|
3. Re-export from `src/index.ts`: `export * from './i18n.js'`
|
|
107
107
|
4. In components, use `useI18nLib` (library package) or `useI18nApp` (app package) — never hardcode UI strings
|
|
108
108
|
|
|
109
|
+
A library package (`@owlmeans/*`) always registers every language synchronously at import: it has
|
|
110
|
+
no boot step that could await a loader, and a bundle registered after its slot was first drained
|
|
111
|
+
never renders. An APPLICATION may instead defer a language's pack with `i18nHelper.addI18nLoader`,
|
|
112
|
+
provided it `await`s `i18nInstanceHelper.prepareI18n(config)` before its first render (`i18n`,
|
|
113
|
+
`client-i18n` skills).
|
|
114
|
+
|
|
109
115
|
## Language switcher
|
|
110
116
|
|
|
111
117
|
Use `useLanguage()` from `@owlmeans/client-i18n`. The selection is persisted to `localStorage`.
|
|
118
|
+
The setter is async — it loads a deferred pack before switching — and the returned language is
|
|
119
|
+
i18next's own, so it changes only once the switch has happened.
|
|
112
120
|
|
|
113
121
|
```tsx
|
|
114
122
|
import { useLanguage } from '@owlmeans/client-i18n'
|
package/build/consts.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export declare const DEFAULT_NAMESPACE = "translation";
|
|
|
2
2
|
export declare const LIB_NAMESPACE = "lib";
|
|
3
3
|
export declare const DEFAULT_LNG = "en";
|
|
4
4
|
export declare const SUPPORTED_LNGS: readonly ['en', 'pl', 'ru', 'be', 'uk', 'es', 'de'];
|
|
5
|
-
export type SupportedLng = (typeof SUPPORTED_LNGS)[number];
|
|
6
5
|
export declare const MAX_PRIORITY: number;
|
|
7
6
|
export declare enum I18nTier {
|
|
8
7
|
Library = "library",
|
package/build/consts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,iBAAiB,gBAAgB,CAAA;AAE9C,eAAO,MAAM,aAAa,QAAQ,CAAA;AAElC,eAAO,MAAM,WAAW,OAAO,CAAA;AAE/B,eAAO,MAAM,cAAc,YAAI,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAEjF,
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,iBAAiB,gBAAgB,CAAA;AAE9C,eAAO,MAAM,aAAa,QAAQ,CAAA;AAElC,eAAO,MAAM,WAAW,OAAO,CAAA;AAE/B,eAAO,MAAM,cAAc,YAAI,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAEjF,eAAO,MAAM,YAAY,QAA0B,CAAA;AAEnD,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,GAAG,QAAQ;CACZ;AAED,+BAA+B;AAC/B,eAAO,MAAM,SAAS,iBAAW,CAAA"}
|
package/build/consts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAA;AAE9C,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAA;AAElC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAA;AAE/B,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAA;AAE9C,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAA;AAElC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAA;AAE/B,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAEjF,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAA;AAEnD,MAAM,CAAN,IAAY,QAGX;AAHD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,uBAAW,CAAA;AACb,CAAC,EAHW,QAAQ,KAAR,QAAQ,QAGnB;AAED,+BAA+B;AAC/B,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { I18nLeveledResourceSignature, I18nLoader, I18nResource } from '../types.js';
|
|
2
|
+
/** The process-wide i18n registry: resource bundles per (ns, resource, language) and language loaders. */
|
|
3
|
+
export interface I18nHelper {
|
|
4
|
+
/** Registers a library-tier bundle; its namespace defaults to `lib`. */
|
|
5
|
+
addI18nLib: I18nLeveledResourceSignature;
|
|
6
|
+
/** Registers an app-tier bundle; its namespace defaults to the resource name. */
|
|
7
|
+
addI18nApp: I18nLeveledResourceSignature;
|
|
8
|
+
/**
|
|
9
|
+
* Drains the bundles of one (ns, resource, language) slot in merge order — library tier before app
|
|
10
|
+
* tier, then `priority` ascending. `null` once the slot was drained for the language.
|
|
11
|
+
*/
|
|
12
|
+
initI18nResource: (lng: string, resource: string, ns?: string) => null | I18nResource[];
|
|
13
|
+
/**
|
|
14
|
+
* The merged bundle of one (ns, resource, language) slot, read WITHOUT draining it.
|
|
15
|
+
*
|
|
16
|
+
* Every registered bundle is deep-merged in the order `initI18nResource` hands them out — library
|
|
17
|
+
* tier before app tier, then `priority` ascending, a bundle with no priority last — so an app
|
|
18
|
+
* override wins exactly as it does in i18next. The slot is never marked drained and no empty slot
|
|
19
|
+
* is created, so a later `initI18nResource` still returns the same bundles. This is the read for
|
|
20
|
+
* code with no i18next instance (a server rendering an e-mail) and for a language other than the
|
|
21
|
+
* active one (a legal text shown in the billing country's language). `null` when nothing is
|
|
22
|
+
* registered for the slot.
|
|
23
|
+
*/
|
|
24
|
+
resolveI18nResource: (lng: string, resource: string, ns?: string) => Record<string, unknown> | null;
|
|
25
|
+
/**
|
|
26
|
+
* Register a loader for a language's resources — typically a dynamic `import()` of a module that
|
|
27
|
+
* calls `addI18nLib` / `addI18nApp`. If the language was already requested (`loadI18nLanguage` was
|
|
28
|
+
* called for it), the loader starts at once: one registered late, by a module that only evaluates
|
|
29
|
+
* after the app started, is never silently skipped. Its failure then surfaces through the next
|
|
30
|
+
* `loadI18nLanguage` call, which retries it.
|
|
31
|
+
*/
|
|
32
|
+
addI18nLoader: (lng: string, loader: I18nLoader) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Await every loader registered for `lng` — including those registered while this call is in
|
|
35
|
+
* flight — then resolve. Concurrent and repeated calls share in-flight runs and never re-run a
|
|
36
|
+
* completed loader; a loader registered after an earlier call resolved is awaited by the next one.
|
|
37
|
+
* A language with no loaders resolves at once.
|
|
38
|
+
*
|
|
39
|
+
* Rejects with the first failure once every loader of the pass has settled; the failed loader
|
|
40
|
+
* alone is re-run by the next call.
|
|
41
|
+
*/
|
|
42
|
+
loadI18nLanguage: (lng: string) => Promise<void>;
|
|
43
|
+
/** Whether every loader registered for `lng` has completed; true for a language with none. */
|
|
44
|
+
isI18nLanguageLoaded: (lng: string) => boolean;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/helper/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEzF,0GAA0G;AAC1G,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,UAAU,EAAE,4BAA4B,CAAA;IACxC,iFAAiF;IACjF,UAAU,EAAE,4BAA4B,CAAA;IACxC;;;OAGG;IACH,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,EAAE,CAAA;IACvF;;;;;;;;;;OAUG;IACH,mBAAmB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACnG;;;;;;OAMG;IACH,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IACxD;;;;;;;;OAQG;IACH,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD,8FAA8F;IAC9F,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAC/C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/helper/types.ts"],"names":[],"mappings":""}
|
package/build/helper.d.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import type { I18nLeveledResourceSignature, I18nResource } from './types.js';
|
|
1
|
+
import type { I18nLeveledResourceSignature, I18nLoader, I18nResource } from './types.js';
|
|
2
|
+
import type { I18nHelper } from './helper/types.js';
|
|
3
|
+
export declare const createI18nHelper: () => I18nHelper;
|
|
4
|
+
export declare const i18nHelper: I18nHelper;
|
|
5
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.addI18nLib(…)` */
|
|
2
6
|
export declare const addI18nLib: I18nLeveledResourceSignature;
|
|
7
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.addI18nApp(…)` */
|
|
3
8
|
export declare const addI18nApp: I18nLeveledResourceSignature;
|
|
9
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.initI18nResource(…)` */
|
|
4
10
|
export declare const initI18nResource: (lng: string, resource: string, ns?: string) => null | I18nResource[];
|
|
11
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.resolveI18nResource(…)` */
|
|
12
|
+
export declare const resolveI18nResource: (lng: string, resource: string, ns?: string) => Record<string, unknown> | null;
|
|
13
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.addI18nLoader(…)` */
|
|
14
|
+
export declare const addI18nLoader: (lng: string, loader: I18nLoader) => void;
|
|
15
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.loadI18nLanguage(…)` */
|
|
16
|
+
export declare const loadI18nLanguage: (lng: string) => Promise<void>;
|
|
17
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.isI18nLanguageLoaded(…)` */
|
|
18
|
+
export declare const isI18nLanguageLoaded: (lng: string) => boolean;
|
|
5
19
|
//# sourceMappingURL=helper.d.ts.map
|
package/build/helper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,4BAA4B,EAAE,UAAU,EAAmB,YAAY,EACxE,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAInD,eAAO,MAAM,gBAAgB,QAAO,UA6InC,CAAA;AAED,eAAO,MAAM,UAAU,YAAqB,CAAA;AAE5C,2EAA2E;AAC3E,eAAO,MAAM,UAAU,EAAE,4BAA8G,CAAA;AAEvI,2EAA2E;AAC3E,eAAO,MAAM,UAAU,EAAE,4BAA8G,CAAA;AAEvI,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,QAAS,MAAM,YAAY,MAAM,OAAO,MAAM,KAAG,IAAI,GAAG,YAAY,EAAoD,CAAA;AAErJ,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,QAAS,MAAM,YAAY,MAAM,OAAO,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAyD,CAAA;AAEpK,8EAA8E;AAC9E,eAAO,MAAM,aAAa,QAAS,MAAM,UAAU,UAAU,KAAG,IAA6C,CAAA;AAE7G,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,QAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAAqC,CAAA;AAEhG,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,QAAS,MAAM,KAAG,OAA+C,CAAA"}
|
package/build/helper.js
CHANGED
|
@@ -1,42 +1,39 @@
|
|
|
1
1
|
import { DEFAULT_NAMESPACE, I18nTier, LIB_NAMESPACE, MAX_PRIORITY } from './consts.js';
|
|
2
|
-
import { tierCost } from './utils/consts.js';
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ns
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
const result = [...translation.resources].sort((a, b) => {
|
|
2
|
+
import { tierCost, _OwlMeansI18nLoaders, _OwlMeansI18nStorage } from './utils/consts.js';
|
|
3
|
+
import { i18nStorageUtils } from './utils/storage.js';
|
|
4
|
+
export const createI18nHelper = () => {
|
|
5
|
+
const { ensureLoaders, ensureStructure } = i18nStorageUtils;
|
|
6
|
+
const prepareOptions = (opts, ns) => {
|
|
7
|
+
if (typeof opts === 'string') {
|
|
8
|
+
opts = { ns: opts };
|
|
9
|
+
}
|
|
10
|
+
else if (typeof opts === 'undefined') {
|
|
11
|
+
opts = {};
|
|
12
|
+
}
|
|
13
|
+
if (opts.ns == null && ns != null) {
|
|
14
|
+
opts.ns = ns;
|
|
15
|
+
}
|
|
16
|
+
return opts;
|
|
17
|
+
};
|
|
18
|
+
const _addI18n = (tier, lng, resource, data, opts) => {
|
|
19
|
+
opts = prepareOptions(opts);
|
|
20
|
+
const storage = ensureStructure(lng, resource, opts.ns);
|
|
21
|
+
const ns = opts.ns ?? DEFAULT_NAMESPACE;
|
|
22
|
+
const entry = {
|
|
23
|
+
ns, lng, tier, resource, data, priority: opts.priority
|
|
24
|
+
};
|
|
25
|
+
storage.resources.push(entry);
|
|
26
|
+
};
|
|
27
|
+
const addI18nLib = (lng, resource, data, opts) => {
|
|
28
|
+
opts = prepareOptions(opts, LIB_NAMESPACE);
|
|
29
|
+
_addI18n(I18nTier.Library, lng, resource, data, opts);
|
|
30
|
+
};
|
|
31
|
+
const addI18nApp = (lng, resource, data, opts) => {
|
|
32
|
+
opts = prepareOptions(opts, resource);
|
|
33
|
+
_addI18n(I18nTier.App, lng, resource, data, opts);
|
|
34
|
+
};
|
|
35
|
+
/** Library tier before app tier, then `priority` ascending — the order bundles are merged in. */
|
|
36
|
+
const ordered = (resources) => [...resources].sort((a, b) => {
|
|
40
37
|
const aTier = tierCost[a.tier];
|
|
41
38
|
const bTier = tierCost[b.tier];
|
|
42
39
|
if (aTier !== bTier) {
|
|
@@ -44,7 +41,87 @@ export const initI18nResource = (lng, resource, ns) => {
|
|
|
44
41
|
}
|
|
45
42
|
return (a.priority ?? MAX_PRIORITY) - (b.priority ?? MAX_PRIORITY);
|
|
46
43
|
});
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
const initI18nResource = (lng, resource, ns) => {
|
|
45
|
+
ns = ns ?? DEFAULT_NAMESPACE;
|
|
46
|
+
const translation = ensureStructure(lng, resource, ns);
|
|
47
|
+
if (translation.lngInitialized.includes(lng)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const result = ordered(translation.resources);
|
|
51
|
+
translation.lngInitialized.push(lng);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
const isPlainObject = (value) => value != null && typeof value === 'object' && !Array.isArray(value);
|
|
55
|
+
/** Merge `source` over `target` in place; nested objects are copied, never shared with a bundle. */
|
|
56
|
+
const mergeInto = (target, source) => {
|
|
57
|
+
for (const [key, value] of Object.entries(source)) {
|
|
58
|
+
const current = target[key];
|
|
59
|
+
target[key] = isPlainObject(value)
|
|
60
|
+
? mergeInto(isPlainObject(current) ? current : {}, value)
|
|
61
|
+
: Array.isArray(value) ? structuredClone(value) : value;
|
|
62
|
+
}
|
|
63
|
+
return target;
|
|
64
|
+
};
|
|
65
|
+
const resolveI18nResource = (lng, resource, ns = DEFAULT_NAMESPACE) => {
|
|
66
|
+
const translation = _OwlMeansI18nStorage.data[ns]?.[resource]?.[lng];
|
|
67
|
+
if (translation == null || translation.resources.length === 0) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return ordered(translation.resources).reduce((merged, bundle) => mergeInto(merged, bundle.data), {});
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Start `entry`'s loader, or join the run already in flight. A completed entry never runs again;
|
|
74
|
+
* a failed run clears `running`, so the next caller starts it afresh.
|
|
75
|
+
*/
|
|
76
|
+
const runLoader = (entry) => {
|
|
77
|
+
if (entry.done) {
|
|
78
|
+
return Promise.resolve();
|
|
79
|
+
}
|
|
80
|
+
return entry.running ??= Promise.resolve().then(() => entry.loader()).then(() => { entry.done = true; }, (error) => {
|
|
81
|
+
entry.running = undefined;
|
|
82
|
+
throw error;
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const addI18nLoader = (lng, loader) => {
|
|
86
|
+
const bucket = ensureLoaders(lng);
|
|
87
|
+
const entry = { loader, done: false };
|
|
88
|
+
bucket.entries.push(entry);
|
|
89
|
+
if (bucket.requested) {
|
|
90
|
+
runLoader(entry).catch(() => { });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const loadI18nLanguage = async (lng) => {
|
|
94
|
+
const bucket = ensureLoaders(lng);
|
|
95
|
+
bucket.requested = true;
|
|
96
|
+
// Each pass settles what was pending when it began; a loader added meanwhile is left for the
|
|
97
|
+
// next pass, so the call resolves only once nothing is pending.
|
|
98
|
+
for (let pending = bucket.entries.filter(entry => !entry.done); pending.length > 0; pending = bucket.entries.filter(entry => !entry.done)) {
|
|
99
|
+
const results = await Promise.allSettled(pending.map(runLoader));
|
|
100
|
+
const failure = results.find(result => result.status === 'rejected');
|
|
101
|
+
if (failure != null) {
|
|
102
|
+
throw failure.reason;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const isI18nLanguageLoaded = (lng) => _OwlMeansI18nLoaders.data[lng]?.entries.every(entry => entry.done) ?? true;
|
|
107
|
+
return {
|
|
108
|
+
addI18nLib, addI18nApp, initI18nResource, resolveI18nResource,
|
|
109
|
+
addI18nLoader, loadI18nLanguage, isI18nLanguageLoaded
|
|
110
|
+
};
|
|
49
111
|
};
|
|
112
|
+
export const i18nHelper = createI18nHelper();
|
|
113
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.addI18nLib(…)` */
|
|
114
|
+
export const addI18nLib = (lng, resource, data, opts) => i18nHelper.addI18nLib(lng, resource, data, opts);
|
|
115
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.addI18nApp(…)` */
|
|
116
|
+
export const addI18nApp = (lng, resource, data, opts) => i18nHelper.addI18nApp(lng, resource, data, opts);
|
|
117
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.initI18nResource(…)` */
|
|
118
|
+
export const initI18nResource = (lng, resource, ns) => i18nHelper.initI18nResource(lng, resource, ns);
|
|
119
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.resolveI18nResource(…)` */
|
|
120
|
+
export const resolveI18nResource = (lng, resource, ns) => i18nHelper.resolveI18nResource(lng, resource, ns);
|
|
121
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.addI18nLoader(…)` */
|
|
122
|
+
export const addI18nLoader = (lng, loader) => i18nHelper.addI18nLoader(lng, loader);
|
|
123
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.loadI18nLanguage(…)` */
|
|
124
|
+
export const loadI18nLanguage = (lng) => i18nHelper.loadI18nLanguage(lng);
|
|
125
|
+
/** @deprecated compat:factory-refactor — use `i18nHelper.isI18nLanguageLoaded(…)` */
|
|
126
|
+
export const isI18nLanguageLoaded = (lng) => i18nHelper.isI18nLanguageLoaded(lng);
|
|
50
127
|
//# sourceMappingURL=helper.js.map
|