@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 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.41
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.39
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
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/client-i18n",
4
- "version": "0.1.18-rc.41",
5
- "generatedAt": "2026-09-25T15:32:39.584Z",
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.41"` in `dependencies`
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 (if the persistence guard allows) and switch — what `useLanguage`'s setter calls |
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`, `setLanguage` and the three persistence functions above.
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 the persistence guard.
108
-
109
- **Persistence is guarded, not assumed.** With no guard installed every explicit choice is written, as
110
- always. An application that asks its visitors before remembering anything installs one
111
- (`setLanguagePersistence(() => allowed)`; `@owlmeans/web-panel/consent`'s `installConsentLanguage()` is
112
- the ready-made one for the cookie dialog's `functional` category — see the `consent` skill). While it
113
- says no: `setLanguage` still switches the UI but writes nothing and remembers the refused choice
114
- (`persistLanguage()` writes it when storage becomes allowed), and a value already in storage counts
115
- as ABSENT at start-up (`resolveInitialLanguage`), so a language nobody was allowed to remember is not
116
- used. Installing a guard also resets what this page has chosen so far — install it before anything
117
- calls `setLanguage`.
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, setLanguagePersistence, persistLanguage, getExplicitLanguage, } from './utils/instance.js';
3
+ export { prepareI18n, resolveInitialLanguage, setLanguage } from './utils/instance.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -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,EACL,WAAW,EAAE,sBAAsB,EAAE,WAAW,EAAE,sBAAsB,EAAE,eAAe,EAAE,mBAAmB,GAC/G,MAAM,qBAAqB,CAAA"}
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, setLanguagePersistence, persistLanguage, getExplicitLanguage, } from './utils/instance.js';
3
+ export { prepareI18n, resolveInitialLanguage, setLanguage } from './utils/instance.js';
4
4
  //# sourceMappingURL=index.js.map
@@ -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,EACL,WAAW,EAAE,sBAAsB,EAAE,WAAW,EAAE,sBAAsB,EAAE,eAAe,EAAE,mBAAmB,GAC/G,MAAM,qBAAqB,CAAA"}
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;AA4B5D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAO,MAAM,GAAG,IAAsB,CAAA;AAItE;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,UAAW,CAAC,MAAM,OAAO,CAAC,GAAG,IAAI,KAAG,IAKtE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAAO,OAYlC,CAAA;AAED,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,IAItD,CAAA;AAED,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,IAC3B,CAAA;AAE5B;;;;GAIG;AACH,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAA8B,CAAA;AA+BpF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,WAAkB,YAAY,KAAG,OAAO,CAAC,MAAM,CAatE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,WAAY,YAAY,KAAG,MAO7D,CAAA;AAgBD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,kBACf,SAAS,MAAM,EAAE,aAAa,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAClF,MAAM,GAAG,IAUX,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"}
@@ -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
- explicitChoice = lng;
82
- if (persistenceAllowed()) {
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":"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"}
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.41",
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.42",
41
- "@owlmeans/client-context": "^0.1.18-rc.39",
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'
@@ -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
- explicitChoice = lng
102
- if (persistenceAllowed()) {
103
- refusedChoice = null
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 (_) {
@@ -1,8 +1,6 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
- import { afterEach, beforeEach } from 'bun:test'
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('language persistence guard — a choice is remembered only while storage is allowed', () => {
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('with the guard saying no, an explicit choice is not written', async () => {
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 refused choice is the one that is written', async () => {
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('with the guard saying yes, a choice is written at once, as before', async () => {
76
- allowed = true
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(store.get('owlmeans-lng')).toBe('fr')
59
+ expect(resolveInitialLanguage(config)).toBe('es')
80
60
  })
81
61
 
82
- test('a stored language counts as absent while the guard says no, and is used once it says yes', () => {
83
- store.set('owlmeans-lng', 'de')
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
- allowed = true
88
- expect(resolveInitialLanguage(config)).toBe('de')
65
+ expect(resolveInitialLanguage(config)).not.toBe('ja')
89
66
  })
90
67
 
91
- test('with no guard installed nothing changes: choices are written and read', async () => {
92
- setLanguagePersistence(null)
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
- expect(store.get('owlmeans-lng')).toBe('es')
96
- expect(resolveInitialLanguage(config)).toBe('es')
71
+ await setLanguage('fr')
72
+
73
+ expect(store.has('owlmeans-lng')).toBe(false)
97
74
  })
98
75
  })
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=instance.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instance.spec.d.ts","sourceRoot":"","sources":["instance.spec.ts"],"names":[],"mappings":""}
@@ -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"}