@owlmeans/client-i18n 0.1.18-rc.41 → 0.1.18-rc.43
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 +2 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/client-i18n/SKILL.md +14 -17
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/utils/instance.d.ts +4 -18
- package/build/utils/instance.d.ts.map +1 -1
- package/build/utils/instance.js +7 -62
- package/build/utils/instance.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -3
- package/src/utils/instance.ts +7 -68
- package/tests/instance.spec.ts +16 -39
- package/tests/instance.spec.d.ts +0 -2
- package/tests/instance.spec.d.ts.map +0 -1
- package/tests/instance.spec.js +0 -23
- package/tests/instance.spec.js.map +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ React i18next adapter that loads translations registered via `@owlmeans/i18n` in
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
bun add @owlmeans/client-i18n@^0.1.18-rc.
|
|
15
|
+
bun add @owlmeans/client-i18n@^0.1.18-rc.43
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
@@ -68,7 +68,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
68
68
|
your project's skill store (`.agents/skills/`):
|
|
69
69
|
|
|
70
70
|
```sh
|
|
71
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
71
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.41
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
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
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/client-i18n",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"version": "0.1.18-rc.43",
|
|
5
|
+
"generatedAt": "2026-09-28T02:06:37.882Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/client-i18n
|
|
9
9
|
|
|
10
10
|
**Layer:** Client (React)
|
|
11
|
-
**Install:** `"@owlmeans/client-i18n": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/client-i18n": "^0.1.18-rc.43"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Purpose
|
|
14
14
|
|
|
@@ -37,16 +37,13 @@ The instance itself, for a host that has to configure i18next before the tree re
|
|
|
37
37
|
|---|---|
|
|
38
38
|
| `useI18nInstance(config)` | The i18next instance for this application, memoised for the mount |
|
|
39
39
|
| `getI18nInstance(config)` | The same instance outside React |
|
|
40
|
-
| `setLanguage(lng)` | Async: load `lng`'s pack (`loadI18nLanguage`), then persist
|
|
41
|
-
| `setLanguagePersistence(guard \| null)` | Install the guard that decides whether an explicit choice may be written to storage; `null` removes it. Call BEFORE `prepareI18n` |
|
|
42
|
-
| `persistLanguage()` | Write the explicit choice storage refused earlier, now that the guard allows; `false` when nothing waits or storage is still refused |
|
|
43
|
-
| `getExplicitLanguage()` | The language a person chose explicitly since this page loaded (stored or not), else `null` — what a host asks before applying a language that arrived some other way |
|
|
40
|
+
| `setLanguage(lng)` | Async: load `lng`'s pack (`loadI18nLanguage`), then persist and switch — what `useLanguage`'s setter calls |
|
|
44
41
|
| `prepareI18n(config)` | Await the fallback language's loaders, then the initial language's; resolves to the language the instance will start in. Call once, before the first render |
|
|
45
42
|
| `resolveInitialLanguage(config)` | The persisted `owlmeans-lng` when it is in `supportedLngs`, else the browser's language (`preferredLanguageOf`), else `fallbackLng ?? defaultLng ?? DEFAULT_LNG` |
|
|
46
43
|
| `preferredLanguageOf(supportedLngs, preferred)` | Pure: the first preferred tag the config supports, exact or by base (`de-DE` → `de`), else `null` |
|
|
47
44
|
|
|
48
45
|
All of these live in `src/utils/instance.ts`; the package root re-exports `prepareI18n`,
|
|
49
|
-
`resolveInitialLanguage
|
|
46
|
+
`resolveInitialLanguage` and `setLanguage`.
|
|
50
47
|
|
|
51
48
|
**There is exactly one instance per document**, created on first request and reused by every later
|
|
52
49
|
call whatever config is passed. So configuration is read once, at the first creation, and a plugin
|
|
@@ -104,17 +101,17 @@ the browser's language, then `fallbackLng`, instead of resolving nothing. Every
|
|
|
104
101
|
`navigator` access is guarded, so a browser that refuses site data (or no browser) still renders.
|
|
105
102
|
|
|
106
103
|
`tests/instance.spec.ts` pins `preferredLanguageOf` (base matching, browser order, exact tag first,
|
|
107
|
-
`null` when nothing matches) and
|
|
108
|
-
|
|
109
|
-
**Persistence is
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
the
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
104
|
+
`null` when nothing matches) and that an explicit choice is always stored and read back.
|
|
105
|
+
|
|
106
|
+
**Persistence is unconditional.** The interface language is strictly necessary storage — the visitor
|
|
107
|
+
asked for it by choosing it — so `setLanguage` writes `owlmeans-lng` on every explicit switch and
|
|
108
|
+
`resolveInitialLanguage` reads it at every start, whatever cookie decision was made (the `consent`
|
|
109
|
+
skill has the reasoning, and how a language carried by a link from another OwlMeans domain lands in
|
|
110
|
+
the same key before the app renders). There is no guard to install and no consent hook: the
|
|
111
|
+
earlier `setLanguagePersistence` / `persistLanguage` / `getExplicitLanguage` are gone, and
|
|
112
|
+
`@owlmeans/web-panel/consent`'s `installConsentLanguage()` is a deprecated no-op kept for
|
|
113
|
+
applications that still call it. A start-up switch to a language nobody chose (the browser's) is never
|
|
114
|
+
written — only an explicit choice is.
|
|
118
115
|
|
|
119
116
|
`setLanguage(lng)` is async: it awaits `loadI18nLanguage(lng)`, then persists and calls i18next's
|
|
120
117
|
`changeLanguage` — before the instance exists it records the language the instance will start in
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export { I18nContext, composePrefix, useI18n, useI18nLib, useI18nApp, useLanguage } from './context.js';
|
|
3
|
-
export { prepareI18n, resolveInitialLanguage, setLanguage
|
|
3
|
+
export { prepareI18n, resolveInitialLanguage, setLanguage } from './utils/instance.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACvG,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACvG,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export { I18nContext, composePrefix, useI18n, useI18nLib, useI18nApp, useLanguage } from './context.js';
|
|
3
|
-
export { prepareI18n, resolveInitialLanguage, setLanguage
|
|
3
|
+
export { prepareI18n, resolveInitialLanguage, setLanguage } from './utils/instance.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACvG,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACvG,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
import type { i18n } from 'i18next';
|
|
2
2
|
import type { ClientConfig } from '@owlmeans/client-context';
|
|
3
|
-
/**
|
|
4
|
-
* The language a person chose explicitly since this page loaded (`setLanguage`), or `null`. What a
|
|
5
|
-
* host asks before it applies a language that arrived some other way — a link — which must never
|
|
6
|
-
* override what the person just picked.
|
|
7
|
-
*/
|
|
8
|
-
export declare const getExplicitLanguage: () => string | null;
|
|
9
|
-
/**
|
|
10
|
-
* Install (or, with `null`, remove) the guard that decides whether a language choice may be
|
|
11
|
-
* remembered. Call it BEFORE `prepareI18n`: the initial language is resolved from storage, and a
|
|
12
|
-
* guard that says no makes a stored value count as absent.
|
|
13
|
-
*/
|
|
14
|
-
export declare const setLanguagePersistence: (guard: (() => boolean) | null) => void;
|
|
15
|
-
/**
|
|
16
|
-
* Write the explicit choice storage refused earlier, now that it may be — call it when the guard's
|
|
17
|
-
* answer turns to yes (the visitor granted functional storage). `false` when there was nothing
|
|
18
|
-
* waiting, storage is still refused, or the write failed.
|
|
19
|
-
*/
|
|
20
|
-
export declare const persistLanguage: () => boolean;
|
|
21
3
|
export declare const useI18nInstance: (config: ClientConfig) => i18n;
|
|
22
4
|
export declare const getI18nInstance: (config: ClientConfig) => i18n;
|
|
23
5
|
/**
|
|
24
6
|
* Load `lng`'s registered loaders, then persist it and switch the instance to it. Race-safe:
|
|
25
7
|
* when several calls overlap, the latest one wins whatever order their loads finish in.
|
|
26
8
|
* Rejects when the language's pack fails to load; nothing is persisted or switched then.
|
|
9
|
+
*
|
|
10
|
+
* The choice is stored unconditionally: the interface language is strictly necessary storage — the
|
|
11
|
+
* visitor asked for it by choosing it — so no cookie decision stands between a switch and
|
|
12
|
+
* `owlmeans-lng`.
|
|
27
13
|
*/
|
|
28
14
|
export declare const setLanguage: (lng: string) => Promise<void>;
|
|
29
15
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/utils/instance.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/utils/instance.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAa5D,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,IAItD,CAAA;AAED,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,IAC3B,CAAA;AAE5B;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAA8B,CAAA;AAwBpF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,WAAkB,YAAY,KAAG,OAAO,CAAC,MAAM,CAatE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,WAAY,YAAY,KAAG,MAO7D,CAAA;AAaD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,kBACf,SAAS,MAAM,EAAE,aAAa,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAClF,MAAM,GAAG,IAUX,CAAA"}
|
package/build/utils/instance.js
CHANGED
|
@@ -8,54 +8,6 @@ let i18nInstance = null;
|
|
|
8
8
|
let preparedLng;
|
|
9
9
|
/** The language of the latest `setLanguage` call — an earlier call that finishes later yields to it. */
|
|
10
10
|
let requestedLng;
|
|
11
|
-
/**
|
|
12
|
-
* Whether an explicit language choice may be written to storage right now. Unset, storage is always
|
|
13
|
-
* allowed — an application with no notion of consent behaves exactly as before. A host that asks its
|
|
14
|
-
* visitors (`@owlmeans/web-panel/consent`'s `installConsentLanguage`) installs one that answers
|
|
15
|
-
* "has the visitor allowed functional storage?", so a language is not remembered on a device nobody
|
|
16
|
-
* asked about.
|
|
17
|
-
*/
|
|
18
|
-
let persistenceGuard = null;
|
|
19
|
-
/** The latest explicit choice that storage refused; written the moment storage is allowed. */
|
|
20
|
-
let refusedChoice = null;
|
|
21
|
-
/** The latest language a person chose explicitly in this page's life, stored or not. */
|
|
22
|
-
let explicitChoice = null;
|
|
23
|
-
/**
|
|
24
|
-
* The language a person chose explicitly since this page loaded (`setLanguage`), or `null`. What a
|
|
25
|
-
* host asks before it applies a language that arrived some other way — a link — which must never
|
|
26
|
-
* override what the person just picked.
|
|
27
|
-
*/
|
|
28
|
-
export const getExplicitLanguage = () => explicitChoice;
|
|
29
|
-
const persistenceAllowed = () => persistenceGuard == null || persistenceGuard();
|
|
30
|
-
/**
|
|
31
|
-
* Install (or, with `null`, remove) the guard that decides whether a language choice may be
|
|
32
|
-
* remembered. Call it BEFORE `prepareI18n`: the initial language is resolved from storage, and a
|
|
33
|
-
* guard that says no makes a stored value count as absent.
|
|
34
|
-
*/
|
|
35
|
-
export const setLanguagePersistence = (guard) => {
|
|
36
|
-
persistenceGuard = guard;
|
|
37
|
-
// A new guard starts a new account of what this page's visitor has chosen and what storage refused.
|
|
38
|
-
refusedChoice = null;
|
|
39
|
-
explicitChoice = null;
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Write the explicit choice storage refused earlier, now that it may be — call it when the guard's
|
|
43
|
-
* answer turns to yes (the visitor granted functional storage). `false` when there was nothing
|
|
44
|
-
* waiting, storage is still refused, or the write failed.
|
|
45
|
-
*/
|
|
46
|
-
export const persistLanguage = () => {
|
|
47
|
-
if (refusedChoice == null || !persistenceAllowed()) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
localStorage.setItem(LNG_STORAGE_KEY, refusedChoice);
|
|
52
|
-
refusedChoice = null;
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
catch (_) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
11
|
export const useI18nInstance = (config) => {
|
|
60
12
|
const instance = useMemo(() => createI18nInstance(config), []);
|
|
61
13
|
return instance;
|
|
@@ -65,6 +17,10 @@ export const getI18nInstance = (config) => createI18nInstance(config);
|
|
|
65
17
|
* Load `lng`'s registered loaders, then persist it and switch the instance to it. Race-safe:
|
|
66
18
|
* when several calls overlap, the latest one wins whatever order their loads finish in.
|
|
67
19
|
* Rejects when the language's pack fails to load; nothing is persisted or switched then.
|
|
20
|
+
*
|
|
21
|
+
* The choice is stored unconditionally: the interface language is strictly necessary storage — the
|
|
22
|
+
* visitor asked for it by choosing it — so no cookie decision stands between a switch and
|
|
23
|
+
* `owlmeans-lng`.
|
|
68
24
|
*/
|
|
69
25
|
export const setLanguage = (lng) => switchLanguage(lng, true);
|
|
70
26
|
/**
|
|
@@ -78,18 +34,10 @@ const switchLanguage = async (lng, persist) => {
|
|
|
78
34
|
return;
|
|
79
35
|
}
|
|
80
36
|
if (persist) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
refusedChoice = null;
|
|
84
|
-
try {
|
|
85
|
-
localStorage.setItem(LNG_STORAGE_KEY, lng);
|
|
86
|
-
}
|
|
87
|
-
catch (_) { /* noop in non-browser environments */ }
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
// Not remembered — but not forgotten either: the visitor may allow it a moment later.
|
|
91
|
-
refusedChoice = lng;
|
|
37
|
+
try {
|
|
38
|
+
localStorage.setItem(LNG_STORAGE_KEY, lng);
|
|
92
39
|
}
|
|
40
|
+
catch (_) { /* noop in non-browser environments */ }
|
|
93
41
|
}
|
|
94
42
|
if (i18nInstance == null) {
|
|
95
43
|
preparedLng = lng;
|
|
@@ -135,9 +83,6 @@ export const resolveInitialLanguage = (config) => {
|
|
|
135
83
|
};
|
|
136
84
|
const resolveFallbackLanguage = (config) => config.i18n?.fallbackLng ?? config.i18n?.defaultLng ?? DEFAULT_LNG;
|
|
137
85
|
const getPersistedLanguage = () => {
|
|
138
|
-
if (!persistenceAllowed()) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
86
|
try {
|
|
142
87
|
return localStorage.getItem(LNG_STORAGE_KEY);
|
|
143
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/utils/instance.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE/B,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEvH,MAAM,eAAe,GAAG,cAAc,CAAA;AAEtC,IAAI,YAAY,GAAgB,IAAI,CAAA;AAEpC,4FAA4F;AAC5F,IAAI,WAA+B,CAAA;AAEnC,wGAAwG;AACxG,IAAI,YAAgC,CAAA;AAEpC
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/utils/instance.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-i18n",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.43",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"typescript": "^7.0.2"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@owlmeans/client": "^0.1.18-rc.
|
|
41
|
-
"@owlmeans/client-context": "^0.1.18-rc.
|
|
40
|
+
"@owlmeans/client": "^0.1.18-rc.44",
|
|
41
|
+
"@owlmeans/client-context": "^0.1.18-rc.41",
|
|
42
42
|
"@owlmeans/i18n": "^0.1.18-rc.37",
|
|
43
43
|
"i18next": "^26.2.0",
|
|
44
44
|
"react-i18next": "^17.0.11"
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
|
|
2
2
|
export * from './types.js'
|
|
3
3
|
export { I18nContext, composePrefix, useI18n, useI18nLib, useI18nApp, useLanguage } from './context.js'
|
|
4
|
-
export {
|
|
5
|
-
prepareI18n, resolveInitialLanguage, setLanguage, setLanguagePersistence, persistLanguage, getExplicitLanguage,
|
|
6
|
-
} from './utils/instance.js'
|
|
4
|
+
export { prepareI18n, resolveInitialLanguage, setLanguage } from './utils/instance.js'
|
package/src/utils/instance.ts
CHANGED
|
@@ -16,61 +16,6 @@ let preparedLng: string | undefined
|
|
|
16
16
|
/** The language of the latest `setLanguage` call — an earlier call that finishes later yields to it. */
|
|
17
17
|
let requestedLng: string | undefined
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* Whether an explicit language choice may be written to storage right now. Unset, storage is always
|
|
21
|
-
* allowed — an application with no notion of consent behaves exactly as before. A host that asks its
|
|
22
|
-
* visitors (`@owlmeans/web-panel/consent`'s `installConsentLanguage`) installs one that answers
|
|
23
|
-
* "has the visitor allowed functional storage?", so a language is not remembered on a device nobody
|
|
24
|
-
* asked about.
|
|
25
|
-
*/
|
|
26
|
-
let persistenceGuard: (() => boolean) | null = null
|
|
27
|
-
|
|
28
|
-
/** The latest explicit choice that storage refused; written the moment storage is allowed. */
|
|
29
|
-
let refusedChoice: string | null = null
|
|
30
|
-
|
|
31
|
-
/** The latest language a person chose explicitly in this page's life, stored or not. */
|
|
32
|
-
let explicitChoice: string | null = null
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* The language a person chose explicitly since this page loaded (`setLanguage`), or `null`. What a
|
|
36
|
-
* host asks before it applies a language that arrived some other way — a link — which must never
|
|
37
|
-
* override what the person just picked.
|
|
38
|
-
*/
|
|
39
|
-
export const getExplicitLanguage = (): string | null => explicitChoice
|
|
40
|
-
|
|
41
|
-
const persistenceAllowed = (): boolean => persistenceGuard == null || persistenceGuard()
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Install (or, with `null`, remove) the guard that decides whether a language choice may be
|
|
45
|
-
* remembered. Call it BEFORE `prepareI18n`: the initial language is resolved from storage, and a
|
|
46
|
-
* guard that says no makes a stored value count as absent.
|
|
47
|
-
*/
|
|
48
|
-
export const setLanguagePersistence = (guard: (() => boolean) | null): void => {
|
|
49
|
-
persistenceGuard = guard
|
|
50
|
-
// A new guard starts a new account of what this page's visitor has chosen and what storage refused.
|
|
51
|
-
refusedChoice = null
|
|
52
|
-
explicitChoice = null
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Write the explicit choice storage refused earlier, now that it may be — call it when the guard's
|
|
57
|
-
* answer turns to yes (the visitor granted functional storage). `false` when there was nothing
|
|
58
|
-
* waiting, storage is still refused, or the write failed.
|
|
59
|
-
*/
|
|
60
|
-
export const persistLanguage = (): boolean => {
|
|
61
|
-
if (refusedChoice == null || !persistenceAllowed()) {
|
|
62
|
-
return false
|
|
63
|
-
}
|
|
64
|
-
try {
|
|
65
|
-
localStorage.setItem(LNG_STORAGE_KEY, refusedChoice)
|
|
66
|
-
refusedChoice = null
|
|
67
|
-
|
|
68
|
-
return true
|
|
69
|
-
} catch (_) {
|
|
70
|
-
return false
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
19
|
export const useI18nInstance = (config: ClientConfig): i18n => {
|
|
75
20
|
const instance = useMemo(() => createI18nInstance(config), [])
|
|
76
21
|
|
|
@@ -84,6 +29,10 @@ export const getI18nInstance = (config: ClientConfig): i18n =>
|
|
|
84
29
|
* Load `lng`'s registered loaders, then persist it and switch the instance to it. Race-safe:
|
|
85
30
|
* when several calls overlap, the latest one wins whatever order their loads finish in.
|
|
86
31
|
* Rejects when the language's pack fails to load; nothing is persisted or switched then.
|
|
32
|
+
*
|
|
33
|
+
* The choice is stored unconditionally: the interface language is strictly necessary storage — the
|
|
34
|
+
* visitor asked for it by choosing it — so no cookie decision stands between a switch and
|
|
35
|
+
* `owlmeans-lng`.
|
|
87
36
|
*/
|
|
88
37
|
export const setLanguage = (lng: string): Promise<void> => switchLanguage(lng, true)
|
|
89
38
|
|
|
@@ -98,16 +47,9 @@ const switchLanguage = async (lng: string, persist: boolean): Promise<void> => {
|
|
|
98
47
|
return
|
|
99
48
|
}
|
|
100
49
|
if (persist) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
localStorage.setItem(LNG_STORAGE_KEY, lng)
|
|
106
|
-
} catch (_) { /* noop in non-browser environments */ }
|
|
107
|
-
} else {
|
|
108
|
-
// Not remembered — but not forgotten either: the visitor may allow it a moment later.
|
|
109
|
-
refusedChoice = lng
|
|
110
|
-
}
|
|
50
|
+
try {
|
|
51
|
+
localStorage.setItem(LNG_STORAGE_KEY, lng)
|
|
52
|
+
} catch (_) { /* noop in non-browser environments */ }
|
|
111
53
|
}
|
|
112
54
|
if (i18nInstance == null) {
|
|
113
55
|
preparedLng = lng
|
|
@@ -159,9 +101,6 @@ const resolveFallbackLanguage = (config: ClientConfig): string =>
|
|
|
159
101
|
config.i18n?.fallbackLng ?? config.i18n?.defaultLng ?? DEFAULT_LNG
|
|
160
102
|
|
|
161
103
|
const getPersistedLanguage = (): string | null => {
|
|
162
|
-
if (!persistenceAllowed()) {
|
|
163
|
-
return null
|
|
164
|
-
}
|
|
165
104
|
try {
|
|
166
105
|
return localStorage.getItem(LNG_STORAGE_KEY)
|
|
167
106
|
} catch (_) {
|
package/tests/instance.spec.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
persistLanguage, preferredLanguageOf, resolveInitialLanguage, setLanguage, setLanguagePersistence,
|
|
5
|
-
} from '../src/utils/instance.js'
|
|
2
|
+
import { beforeEach } from 'bun:test'
|
|
3
|
+
import { preferredLanguageOf, resolveInitialLanguage, setLanguage } from '../src/utils/instance.js'
|
|
6
4
|
|
|
7
5
|
const SUPPORTED = ['en', 'pl', 'ru', 'be', 'uk', 'es', 'de', 'fr']
|
|
8
6
|
|
|
@@ -29,70 +27,49 @@ describe('preferredLanguageOf — the first-visit language', () => {
|
|
|
29
27
|
})
|
|
30
28
|
})
|
|
31
29
|
|
|
32
|
-
describe('
|
|
30
|
+
describe('the interface language is strictly necessary — always stored, always read', () => {
|
|
33
31
|
const store = new Map<string, string>()
|
|
34
32
|
const config = { i18n: { supportedLngs: SUPPORTED, fallbackLng: 'en' } } as never
|
|
35
|
-
let allowed = false
|
|
36
33
|
|
|
37
34
|
beforeEach(() => {
|
|
38
35
|
store.clear()
|
|
39
|
-
allowed = false
|
|
40
36
|
;(globalThis as any).localStorage = {
|
|
41
37
|
getItem: (k: string) => store.get(k) ?? null,
|
|
42
38
|
setItem: (k: string, v: string) => { store.set(k, v) },
|
|
43
39
|
removeItem: (k: string) => { store.delete(k) },
|
|
44
40
|
}
|
|
45
|
-
setLanguagePersistence(() => allowed)
|
|
46
41
|
})
|
|
47
|
-
afterEach(() => { setLanguagePersistence(null) })
|
|
48
42
|
|
|
49
|
-
test('
|
|
43
|
+
test('an explicit choice is written at once, whatever cookie decision the visitor made', async () => {
|
|
50
44
|
await setLanguage('pl')
|
|
51
45
|
|
|
52
|
-
expect(store.has('owlmeans-lng')).toBe(false)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('the refused choice is written the moment storage is allowed', async () => {
|
|
56
|
-
await setLanguage('pl')
|
|
57
|
-
expect(persistLanguage()).toBe(false)
|
|
58
|
-
|
|
59
|
-
allowed = true
|
|
60
|
-
|
|
61
|
-
expect(persistLanguage()).toBe(true)
|
|
62
46
|
expect(store.get('owlmeans-lng')).toBe('pl')
|
|
63
|
-
expect(persistLanguage()).toBe(false)
|
|
64
47
|
})
|
|
65
48
|
|
|
66
|
-
test('the latest
|
|
49
|
+
test('the latest choice is the one that is stored', async () => {
|
|
67
50
|
await setLanguage('pl')
|
|
68
51
|
await setLanguage('de')
|
|
69
|
-
allowed = true
|
|
70
|
-
persistLanguage()
|
|
71
52
|
|
|
72
53
|
expect(store.get('owlmeans-lng')).toBe('de')
|
|
73
54
|
})
|
|
74
55
|
|
|
75
|
-
test('
|
|
76
|
-
|
|
77
|
-
await setLanguage('fr')
|
|
56
|
+
test('a stored language is what the next visit starts in', async () => {
|
|
57
|
+
await setLanguage('es')
|
|
78
58
|
|
|
79
|
-
expect(
|
|
59
|
+
expect(resolveInitialLanguage(config)).toBe('es')
|
|
80
60
|
})
|
|
81
61
|
|
|
82
|
-
test('a stored language
|
|
83
|
-
store.set('owlmeans-lng', '
|
|
84
|
-
|
|
85
|
-
expect(resolveInitialLanguage(config)).not.toBe('de')
|
|
62
|
+
test('a stored language the application no longer supports is ignored', () => {
|
|
63
|
+
store.set('owlmeans-lng', 'ja')
|
|
86
64
|
|
|
87
|
-
|
|
88
|
-
expect(resolveInitialLanguage(config)).toBe('de')
|
|
65
|
+
expect(resolveInitialLanguage(config)).not.toBe('ja')
|
|
89
66
|
})
|
|
90
67
|
|
|
91
|
-
test('
|
|
92
|
-
|
|
93
|
-
await setLanguage('es')
|
|
68
|
+
test('storage that refuses the write is not an error', async () => {
|
|
69
|
+
;(globalThis as any).localStorage.setItem = () => { throw new Error('blocked') }
|
|
94
70
|
|
|
95
|
-
|
|
96
|
-
|
|
71
|
+
await setLanguage('fr')
|
|
72
|
+
|
|
73
|
+
expect(store.has('owlmeans-lng')).toBe(false)
|
|
97
74
|
})
|
|
98
75
|
})
|
package/tests/instance.spec.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"instance.spec.d.ts","sourceRoot":"","sources":["instance.spec.ts"],"names":[],"mappings":""}
|
package/tests/instance.spec.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import { preferredLanguageOf } from '../src/utils/instance.js';
|
|
3
|
-
const SUPPORTED = ['en', 'pl', 'ru', 'be', 'uk', 'es', 'de', 'fr'];
|
|
4
|
-
describe('preferredLanguageOf — the first-visit language', () => {
|
|
5
|
-
test('a regional tag resolves to its supported base language', () => {
|
|
6
|
-
expect(preferredLanguageOf(SUPPORTED, ['de-DE'])).toBe('de');
|
|
7
|
-
expect(preferredLanguageOf(SUPPORTED, ['fr-FR', 'en'])).toBe('fr');
|
|
8
|
-
expect(preferredLanguageOf(SUPPORTED, ['pl_PL'])).toBe('pl');
|
|
9
|
-
});
|
|
10
|
-
test('the browser order decides, and an unsupported language is skipped', () => {
|
|
11
|
-
expect(preferredLanguageOf(SUPPORTED, ['it-IT', 'de-CH', 'en-US'])).toBe('de');
|
|
12
|
-
expect(preferredLanguageOf(SUPPORTED, ['EN-us'])).toBe('en');
|
|
13
|
-
});
|
|
14
|
-
test('an exact supported tag wins over its base', () => {
|
|
15
|
-
expect(preferredLanguageOf(['pt', 'pt-BR'], ['pt-BR'])).toBe('pt-BR');
|
|
16
|
-
});
|
|
17
|
-
test('nothing supported, or nothing known — null (the caller falls back)', () => {
|
|
18
|
-
expect(preferredLanguageOf(SUPPORTED, ['it-IT', 'ja'])).toBeNull();
|
|
19
|
-
expect(preferredLanguageOf(SUPPORTED, [])).toBeNull();
|
|
20
|
-
expect(preferredLanguageOf(SUPPORTED, [null, undefined, ''])).toBeNull();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
//# sourceMappingURL=instance.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"instance.spec.js","sourceRoot":"","sources":["instance.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;AAElE,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5D,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClE,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9E,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,mBAAmB,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACvE,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC9E,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;QAClE,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;QACrD,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC1E,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|