i18n-keyless-react 1.17.7 → 2.0.0
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/dist/I18nKeylessProvider.d.ts +48 -0
- package/dist/I18nKeylessProvider.js +38 -0
- package/dist/I18nKeylessText.d.ts +1 -1
- package/dist/I18nKeylessText.js +16 -8
- package/dist/index.d.ts +9 -5
- package/dist/index.js +6 -3
- package/dist/server.d.ts +18 -0
- package/dist/server.js +49 -0
- package/dist/store.d.ts +1 -1
- package/dist/store.js +28 -5
- package/dist/types.d.ts +12 -0
- package/dist/utils.d.ts +14 -1
- package/dist/utils.js +27 -0
- package/package.json +2 -2
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type Lang, type Translations } from "i18n-keyless-core";
|
|
3
|
+
export interface I18nKeylessContextValue {
|
|
4
|
+
/**
|
|
5
|
+
* The language this subtree renders in (typically derived from the URL / Accept-Language).
|
|
6
|
+
*/
|
|
7
|
+
lang: Lang;
|
|
8
|
+
/**
|
|
9
|
+
* The translations map for `lang`, typically produced by `getServerTranslations(lang)`.
|
|
10
|
+
*/
|
|
11
|
+
translations: Translations;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns the nearest `I18nKeylessProvider` value, or `null` when none is present.
|
|
15
|
+
* When `null`, `<I18nKeylessText>` falls back to the global zustand store (SPA mode).
|
|
16
|
+
*/
|
|
17
|
+
export declare function useI18nKeylessContext(): I18nKeylessContextValue | null;
|
|
18
|
+
export interface I18nKeylessProviderProps {
|
|
19
|
+
/**
|
|
20
|
+
* The language to render in for this subtree (typically from the URL: `/{lang}/...`
|
|
21
|
+
* or `?lang={lang}`, or from `Accept-Language`).
|
|
22
|
+
*/
|
|
23
|
+
lang: Lang;
|
|
24
|
+
/**
|
|
25
|
+
* The translations map for `lang`. On the server, produce it with
|
|
26
|
+
* `getServerTranslations(lang)`; serialize it into the HTML and pass the same map
|
|
27
|
+
* here on the client so the first client render matches the server output.
|
|
28
|
+
*/
|
|
29
|
+
translations: Translations;
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Per-request language provider for SSR.
|
|
34
|
+
*
|
|
35
|
+
* When present, `<I18nKeylessText>` ("`<T>`") reads `lang` and `translations` from
|
|
36
|
+
* this context instead of the module-scope store. This is what lets a single server
|
|
37
|
+
* render produce HTML in a chosen non-primary language without leaking language state
|
|
38
|
+
* across concurrent requests (the store is a process-wide singleton; the context is
|
|
39
|
+
* per-render).
|
|
40
|
+
*
|
|
41
|
+
* On the client it additionally seeds the global store once on mount, so store-based
|
|
42
|
+
* consumers (e.g. `useCurrentLanguage`) stay consistent and there is no flash after
|
|
43
|
+
* hydration.
|
|
44
|
+
*
|
|
45
|
+
* In provider mode the language is controlled by the `lang` prop (drive it from the
|
|
46
|
+
* URL). `setCurrentLanguage` is for non-provider SPA mode. See docs/SSR.md.
|
|
47
|
+
*/
|
|
48
|
+
export declare const I18nKeylessProvider: React.FC<I18nKeylessProviderProps>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect } from "react";
|
|
3
|
+
import { useI18nKeyless } from "./store.js";
|
|
4
|
+
const I18nKeylessContext = createContext(null);
|
|
5
|
+
/**
|
|
6
|
+
* Returns the nearest `I18nKeylessProvider` value, or `null` when none is present.
|
|
7
|
+
* When `null`, `<I18nKeylessText>` falls back to the global zustand store (SPA mode).
|
|
8
|
+
*/
|
|
9
|
+
export function useI18nKeylessContext() {
|
|
10
|
+
return useContext(I18nKeylessContext);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Per-request language provider for SSR.
|
|
14
|
+
*
|
|
15
|
+
* When present, `<I18nKeylessText>` ("`<T>`") reads `lang` and `translations` from
|
|
16
|
+
* this context instead of the module-scope store. This is what lets a single server
|
|
17
|
+
* render produce HTML in a chosen non-primary language without leaking language state
|
|
18
|
+
* across concurrent requests (the store is a process-wide singleton; the context is
|
|
19
|
+
* per-render).
|
|
20
|
+
*
|
|
21
|
+
* On the client it additionally seeds the global store once on mount, so store-based
|
|
22
|
+
* consumers (e.g. `useCurrentLanguage`) stay consistent and there is no flash after
|
|
23
|
+
* hydration.
|
|
24
|
+
*
|
|
25
|
+
* In provider mode the language is controlled by the `lang` prop (drive it from the
|
|
26
|
+
* URL). `setCurrentLanguage` is for non-provider SPA mode. See docs/SSR.md.
|
|
27
|
+
*/
|
|
28
|
+
export const I18nKeylessProvider = ({ lang, translations, children }) => {
|
|
29
|
+
// Client-only (effects don't run during SSR): seed the global store so reads after
|
|
30
|
+
// hydration match the server-rendered, context-driven output.
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
useI18nKeyless.setState((state) => ({
|
|
33
|
+
currentLanguage: lang,
|
|
34
|
+
translations: { ...state.translations, ...translations },
|
|
35
|
+
}));
|
|
36
|
+
}, [lang, translations]);
|
|
37
|
+
return _jsx(I18nKeylessContext.Provider, { value: { lang, translations }, children: children });
|
|
38
|
+
};
|
|
@@ -5,7 +5,7 @@ export interface I18nKeylessTextProps {
|
|
|
5
5
|
* The `children` prop must be a string.
|
|
6
6
|
* It's the text to translate from your primary language.
|
|
7
7
|
*/
|
|
8
|
-
children: string;
|
|
8
|
+
children: string | React.ReactNode;
|
|
9
9
|
/**
|
|
10
10
|
* The keys to replace in the text.
|
|
11
11
|
* It's an object where the key is the placeholder and the value is the replacement.
|
package/dist/I18nKeylessText.js
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useEffect, useMemo } from "react";
|
|
3
|
-
import { useI18nKeyless, getTranslation } from "./store";
|
|
3
|
+
import { useI18nKeyless, getTranslation } from "./store.js";
|
|
4
|
+
import { useI18nKeylessContext } from "./I18nKeylessProvider.js";
|
|
4
5
|
const warnAboutWhitespace = (text) => {
|
|
5
6
|
if (process.env.NODE_ENV === "development" && text !== text.trim()) {
|
|
6
7
|
console.warn(`I18nKeylessText received text with leading/trailing whitespace: "${text}". ` +
|
|
7
8
|
"This may cause inconsistencies in translations. Consider trimming the text.");
|
|
8
9
|
}
|
|
9
10
|
};
|
|
10
|
-
export const I18nKeylessText = ({ children, replace, context, debug = false, forceTemporary
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
export const I18nKeylessText = ({ children, replace, context, debug = false, forceTemporary }) => {
|
|
12
|
+
const storeTranslations = useI18nKeyless((store) => store.translations);
|
|
13
|
+
const storeCurrentLanguage = useI18nKeyless((store) => store.currentLanguage);
|
|
13
14
|
const config = useI18nKeyless((store) => store.config);
|
|
15
|
+
// In SSR/provider mode, the language and translations come from the per-request
|
|
16
|
+
// context (so concurrent requests don't share state). Otherwise (SPA mode) they come
|
|
17
|
+
// from the global store. See docs/SSR.md.
|
|
18
|
+
const providerContext = useI18nKeylessContext();
|
|
19
|
+
const translations = providerContext?.translations ?? storeTranslations;
|
|
20
|
+
const currentLanguage = providerContext?.lang ?? storeCurrentLanguage;
|
|
14
21
|
// Trim the source text immediately
|
|
15
|
-
const
|
|
22
|
+
const rawText = Array.isArray(children) ? children.join("") : String(children ?? "");
|
|
23
|
+
const sourceText = rawText.trim();
|
|
16
24
|
useEffect(() => {
|
|
17
|
-
warnAboutWhitespace(
|
|
18
|
-
}, [
|
|
25
|
+
warnAboutWhitespace(rawText);
|
|
26
|
+
}, [rawText]);
|
|
19
27
|
useEffect(() => {
|
|
20
28
|
getTranslation(sourceText, { context, debug, forceTemporary });
|
|
21
29
|
}, [sourceText, currentLanguage, context, debug, forceTemporary]);
|
|
@@ -44,7 +52,7 @@ export const I18nKeylessText = ({ children, replace, context, debug = false, for
|
|
|
44
52
|
finalText,
|
|
45
53
|
replace,
|
|
46
54
|
context,
|
|
47
|
-
forceTemporary
|
|
55
|
+
forceTemporary
|
|
48
56
|
});
|
|
49
57
|
}
|
|
50
58
|
return _jsx(React.Fragment, { children: finalText }, currentLanguage);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
export { I18nKeylessText, I18nKeylessText as T } from "./I18nKeylessText";
|
|
2
|
-
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation, getSupportedLanguages, useI18nKeyless } from "./store";
|
|
3
|
-
export { clearI18nKeylessStorage, validateLanguage } from "./utils";
|
|
4
|
-
export
|
|
5
|
-
export
|
|
1
|
+
export { I18nKeylessText, I18nKeylessText as T } from "./I18nKeylessText.tsx";
|
|
2
|
+
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation, getSupportedLanguages, useI18nKeyless } from "./store.ts";
|
|
3
|
+
export { clearI18nKeylessStorage, validateLanguage, createMemoryStorage } from "./utils.ts";
|
|
4
|
+
export { I18nKeylessProvider, useI18nKeylessContext } from "./I18nKeylessProvider.tsx";
|
|
5
|
+
export type { I18nKeylessProviderProps } from "./I18nKeylessProvider.tsx";
|
|
6
|
+
export { getServerTranslations, clearServerTranslationsCache } from "./server.ts";
|
|
7
|
+
export { clearI18nKeylessStorageAndStore } from "./store.ts";
|
|
8
|
+
export type { I18nKeylessTextProps } from "./I18nKeylessText.tsx";
|
|
9
|
+
export { type I18nConfig, type TranslationStoreState, type TranslationOptions, type TranslationStore } from "./types.ts";
|
|
6
10
|
export { AVAILABLE_LANGS, type Lang, type PrimaryLang, type Translations, type I18nKeylessRequestBody, type I18nKeylessResponse, getAllTranslationsFromLanguage, queue } from "i18n-keyless-core";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export { I18nKeylessText, I18nKeylessText as T } from "./I18nKeylessText";
|
|
2
|
-
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation, getSupportedLanguages, useI18nKeyless } from "./store";
|
|
3
|
-
export { clearI18nKeylessStorage, validateLanguage } from "./utils";
|
|
1
|
+
export { I18nKeylessText, I18nKeylessText as T } from "./I18nKeylessText.js";
|
|
2
|
+
export { init, setCurrentLanguage, useCurrentLanguage, getTranslation, getSupportedLanguages, useI18nKeyless } from "./store.js";
|
|
3
|
+
export { clearI18nKeylessStorage, validateLanguage, createMemoryStorage } from "./utils.js";
|
|
4
|
+
export { I18nKeylessProvider, useI18nKeylessContext } from "./I18nKeylessProvider.js";
|
|
5
|
+
export { getServerTranslations, clearServerTranslationsCache } from "./server.js";
|
|
6
|
+
export { clearI18nKeylessStorageAndStore } from "./store.js";
|
|
4
7
|
export { AVAILABLE_LANGS, getAllTranslationsFromLanguage, queue } from "i18n-keyless-core";
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Lang, type Translations } from "i18n-keyless-core";
|
|
2
|
+
/**
|
|
3
|
+
* Fetches the translations map for `lang` on the server, cached per process.
|
|
4
|
+
*
|
|
5
|
+
* Pass the result to `<I18nKeylessProvider lang={lang} translations={...}>` and
|
|
6
|
+
* serialize it into the HTML so the client can hydrate without a flash.
|
|
7
|
+
*
|
|
8
|
+
* Requires `init()` to have been called first (so the store holds API config). Returns
|
|
9
|
+
* an empty map for the primary language (no translation needed) and on fetch failure.
|
|
10
|
+
*
|
|
11
|
+
* See docs/SSR.md.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getServerTranslations(lang: Lang): Promise<Translations>;
|
|
14
|
+
/**
|
|
15
|
+
* Clears the per-process server translations cache. Pass a `lang` to evict a single
|
|
16
|
+
* language, or omit it to clear everything (e.g. after publishing new translations).
|
|
17
|
+
*/
|
|
18
|
+
export declare function clearServerTranslationsCache(lang?: Lang): void;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getAllTranslationsFromLanguage } from "i18n-keyless-core";
|
|
2
|
+
import { useI18nKeyless } from "./store.js";
|
|
3
|
+
/**
|
|
4
|
+
* Process-wide cache of translations per language.
|
|
5
|
+
*
|
|
6
|
+
* Translations for a given language are identical for every request, so they are
|
|
7
|
+
* global, cacheable data — only the *choice* of language is per-request. Caching here
|
|
8
|
+
* means each language is fetched at most once per server process. A process restart /
|
|
9
|
+
* redeploy naturally picks up new translations.
|
|
10
|
+
*/
|
|
11
|
+
const cache = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Fetches the translations map for `lang` on the server, cached per process.
|
|
14
|
+
*
|
|
15
|
+
* Pass the result to `<I18nKeylessProvider lang={lang} translations={...}>` and
|
|
16
|
+
* serialize it into the HTML so the client can hydrate without a flash.
|
|
17
|
+
*
|
|
18
|
+
* Requires `init()` to have been called first (so the store holds API config). Returns
|
|
19
|
+
* an empty map for the primary language (no translation needed) and on fetch failure.
|
|
20
|
+
*
|
|
21
|
+
* See docs/SSR.md.
|
|
22
|
+
*/
|
|
23
|
+
export async function getServerTranslations(lang) {
|
|
24
|
+
const store = useI18nKeyless.getState();
|
|
25
|
+
if (lang === store.config.languages.primary) {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
const cached = cache.get(lang);
|
|
29
|
+
if (cached) {
|
|
30
|
+
return cached;
|
|
31
|
+
}
|
|
32
|
+
// lastRefresh: null forces a full fetch of the language.
|
|
33
|
+
const response = await getAllTranslationsFromLanguage(lang, { ...store, lastRefresh: null });
|
|
34
|
+
const translations = response?.ok ? response.data.translations : {};
|
|
35
|
+
cache.set(lang, translations);
|
|
36
|
+
return translations;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Clears the per-process server translations cache. Pass a `lang` to evict a single
|
|
40
|
+
* language, or omit it to clear everything (e.g. after publishing new translations).
|
|
41
|
+
*/
|
|
42
|
+
export function clearServerTranslationsCache(lang) {
|
|
43
|
+
if (lang) {
|
|
44
|
+
cache.delete(lang);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
cache.clear();
|
|
48
|
+
}
|
|
49
|
+
}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Lang, type TranslationOptions } from "i18n-keyless-core";
|
|
2
|
-
import { type I18nConfig, type TranslationStore } from "./types";
|
|
2
|
+
import { type I18nConfig, type TranslationStore } from "./types.ts";
|
|
3
3
|
export declare const useI18nKeyless: import("zustand").UseBoundStore<import("zustand").StoreApi<TranslationStore>>;
|
|
4
4
|
/**
|
|
5
5
|
* Initializes the i18n configuration with defaults and validation
|
package/dist/store.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { queue, getAllTranslationsFromLanguage, getTranslationCore, sendTranslationsUsageToI18nKeyless, } from "i18n-keyless-core";
|
|
2
2
|
import { create } from "zustand";
|
|
3
|
-
import { storeKeys, setItem, getItem, clearI18nKeylessStorage, validateLanguage } from "./utils";
|
|
3
|
+
import { storeKeys, setItem, getItem, clearI18nKeylessStorage, validateLanguage, createMemoryStorage } from "./utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* True when running without a DOM (server-side rendering). On the server the lib is
|
|
6
|
+
* read-only: usage analytics are neither recorded nor sent. Evaluated at call time so
|
|
7
|
+
* the environment can be detected (and stubbed in tests) per call. See docs/SSR.md.
|
|
8
|
+
*/
|
|
9
|
+
function isServerEnv() {
|
|
10
|
+
return typeof window === "undefined";
|
|
11
|
+
}
|
|
4
12
|
queue.on("empty", () => {
|
|
5
13
|
// when each word is translated, fetch the translations for the current language
|
|
6
14
|
const store = useI18nKeyless.getState();
|
|
@@ -190,8 +198,16 @@ export async function init(newConfig) {
|
|
|
190
198
|
newConfig.languages.supported.push(newConfig.languages.initWithDefault);
|
|
191
199
|
}
|
|
192
200
|
if (!newConfig.storage) {
|
|
193
|
-
|
|
194
|
-
|
|
201
|
+
if (typeof window === "undefined") {
|
|
202
|
+
// Server-side (SSR): no DOM storage exists. Default to an in-memory adapter so
|
|
203
|
+
// the server can init and cache translations for the process lifetime, instead
|
|
204
|
+
// of throwing. See docs/SSR.md.
|
|
205
|
+
newConfig.storage = createMemoryStorage();
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
console.log("storage is required", newConfig.storage);
|
|
209
|
+
throw new Error("i18n-keyless: storage is required. You can use react-native-mmkv, @react-native-async-storage/async-storage, or window.localStorage, or any storage that has a getItem, setItem, removeItem, or get, set, and remove method");
|
|
210
|
+
}
|
|
195
211
|
}
|
|
196
212
|
if (!newConfig.getAllTranslations || !newConfig.handleTranslate) {
|
|
197
213
|
if (!newConfig.API_KEY) {
|
|
@@ -213,7 +229,11 @@ export async function init(newConfig) {
|
|
|
213
229
|
newConfig.onInit?.(currentLanguage);
|
|
214
230
|
// initialize the language to fetch all the translations
|
|
215
231
|
useI18nKeyless.getState().setLanguage(currentLanguage);
|
|
216
|
-
|
|
232
|
+
// Read-only on the server: don't POST usage stats on boot (crawler-triggered renders
|
|
233
|
+
// and serverless per-request inits would otherwise pollute/spam the prune signal).
|
|
234
|
+
if (!isServerEnv() && !newConfig.ssr) {
|
|
235
|
+
useI18nKeyless.getState().sendTranslationsUsage();
|
|
236
|
+
}
|
|
217
237
|
}
|
|
218
238
|
export function useCurrentLanguage() {
|
|
219
239
|
const currentLanguage = useI18nKeyless((state) => state.currentLanguage);
|
|
@@ -224,7 +244,10 @@ export function getSupportedLanguages() {
|
|
|
224
244
|
}
|
|
225
245
|
export function getTranslation(key, options) {
|
|
226
246
|
const store = useI18nKeyless.getState();
|
|
227
|
-
|
|
247
|
+
// Read-only on the server: don't record usage (a render may be a crawler hit).
|
|
248
|
+
if (!isServerEnv() && !store.config.ssr) {
|
|
249
|
+
store.setTranslationUsage(key, options?.context);
|
|
250
|
+
}
|
|
228
251
|
return getTranslationCore(key, store, options);
|
|
229
252
|
}
|
|
230
253
|
export function setCurrentLanguage(lang) {
|
package/dist/types.d.ts
CHANGED
|
@@ -48,6 +48,18 @@ export interface I18nConfig {
|
|
|
48
48
|
* if true, all the logs will be displayed in the console
|
|
49
49
|
*/
|
|
50
50
|
debug?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Read-only mode for server-side rendering.
|
|
53
|
+
*
|
|
54
|
+
* When the lib runs on a server (no `window`) it is automatically treated as
|
|
55
|
+
* read-only: usage analytics are not sent and not recorded (a server render can be
|
|
56
|
+
* triggered by a crawler, which would pollute the prune signal, and serverless
|
|
57
|
+
* per-request inits would POST on every request). Set `ssr: true` to force this
|
|
58
|
+
* read-only behavior explicitly even in an environment where `window` exists.
|
|
59
|
+
*
|
|
60
|
+
* This does NOT affect translate-on-miss — missing keys are still requested. See docs/SSR.md.
|
|
61
|
+
*/
|
|
62
|
+
ssr?: boolean;
|
|
51
63
|
/**
|
|
52
64
|
* called everytime the language is set, maybe to set also the locale to dayjs or whatever
|
|
53
65
|
*/
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { I18nConfig } from "./types";
|
|
1
|
+
import type { I18nConfig } from "./types.ts";
|
|
2
2
|
/**
|
|
3
3
|
* The keys used to store i18n-keyless data in storage
|
|
4
4
|
*/
|
|
@@ -37,6 +37,19 @@ export declare function deleteItem(key: string, storage: I18nConfig["storage"]):
|
|
|
37
37
|
* @param storage - The storage implementation to clear
|
|
38
38
|
*/
|
|
39
39
|
export declare function clearI18nKeylessStorage(storage: I18nConfig["storage"]): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Creates an in-memory storage adapter backed by a Map.
|
|
42
|
+
*
|
|
43
|
+
* Used as the default storage on the server (when `typeof window === "undefined"`
|
|
44
|
+
* and no `storage` is provided to `init`). It satisfies the storage interface and
|
|
45
|
+
* keeps translations cached for the lifetime of the process, so a long-lived server
|
|
46
|
+
* fetches each language at most once per boot. It does NOT persist across restarts —
|
|
47
|
+
* which is the correct, expected behavior server-side.
|
|
48
|
+
*
|
|
49
|
+
* On the browser, a missing `storage` remains a loud error (a real misconfiguration),
|
|
50
|
+
* so this is intentionally not used as a client default.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createMemoryStorage(): NonNullable<I18nConfig["storage"]>;
|
|
40
53
|
/**
|
|
41
54
|
* Validates the language against the supported languages
|
|
42
55
|
* @param lang - The language to validate
|
package/dist/utils.js
CHANGED
|
@@ -100,6 +100,33 @@ export async function clearI18nKeylessStorage(storage) {
|
|
|
100
100
|
deleteItem(key, storage);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Creates an in-memory storage adapter backed by a Map.
|
|
105
|
+
*
|
|
106
|
+
* Used as the default storage on the server (when `typeof window === "undefined"`
|
|
107
|
+
* and no `storage` is provided to `init`). It satisfies the storage interface and
|
|
108
|
+
* keeps translations cached for the lifetime of the process, so a long-lived server
|
|
109
|
+
* fetches each language at most once per boot. It does NOT persist across restarts —
|
|
110
|
+
* which is the correct, expected behavior server-side.
|
|
111
|
+
*
|
|
112
|
+
* On the browser, a missing `storage` remains a loud error (a real misconfiguration),
|
|
113
|
+
* so this is intentionally not used as a client default.
|
|
114
|
+
*/
|
|
115
|
+
export function createMemoryStorage() {
|
|
116
|
+
const map = new Map();
|
|
117
|
+
return {
|
|
118
|
+
getItem: (key) => map.get(key) ?? null,
|
|
119
|
+
setItem: (key, value) => {
|
|
120
|
+
map.set(key, value);
|
|
121
|
+
},
|
|
122
|
+
removeItem: (key) => {
|
|
123
|
+
map.delete(key);
|
|
124
|
+
},
|
|
125
|
+
clear: () => {
|
|
126
|
+
map.clear();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
103
130
|
/**
|
|
104
131
|
* Validates the language against the supported languages
|
|
105
132
|
* @param lang - The language to validate
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18n-keyless-react",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"postpublish": "rm -rf ./dist && rm *.tgz"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"i18n-keyless-core": "
|
|
27
|
+
"i18n-keyless-core": "2.0.0",
|
|
28
28
|
"zustand": "^5.0.3"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|