@better-i18n/expo 0.2.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/backend.d.ts +37 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +131 -0
- package/dist/backend.js.map +1 -0
- package/dist/helpers.d.ts +39 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +37 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/locale.d.ts +28 -0
- package/dist/locale.d.ts.map +1 -0
- package/dist/locale.js +51 -0
- package/dist/locale.js.map +1 -0
- package/dist/storage.d.ts +37 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +105 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { BackendModule, InitOptions, ReadCallback, Services } from "i18next";
|
|
2
|
+
import type { BetterI18nBackendOptions } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* i18next backend plugin for better-i18n CDN with offline caching.
|
|
5
|
+
*
|
|
6
|
+
* Implements a network-first strategy:
|
|
7
|
+
* 1. Check in-memory cache (TtlCache) — avoids redundant fetches within a session
|
|
8
|
+
* 2. Fetch from CDN — always get fresh translations
|
|
9
|
+
* 3. On CDN failure, fall back to persistent cache (AsyncStorage)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import i18n from 'i18next';
|
|
14
|
+
* import { BetterI18nBackend } from '@better-i18n/expo';
|
|
15
|
+
*
|
|
16
|
+
* i18n.use(BetterI18nBackend).init({
|
|
17
|
+
* backend: { project: 'acme/my-app' },
|
|
18
|
+
* lng: 'en',
|
|
19
|
+
* fallbackLng: 'en',
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class BetterI18nBackend implements BackendModule<BetterI18nBackendOptions> {
|
|
24
|
+
static type: "backend";
|
|
25
|
+
type: "backend";
|
|
26
|
+
private core;
|
|
27
|
+
private storage;
|
|
28
|
+
private memoryCache;
|
|
29
|
+
private cacheExpiration;
|
|
30
|
+
private project;
|
|
31
|
+
private debug;
|
|
32
|
+
init(_services: Services, backendOptions: BetterI18nBackendOptions, _i18nextOptions: InitOptions): void;
|
|
33
|
+
read(language: string, namespace: string, callback: ReadCallback): void;
|
|
34
|
+
private loadTranslations;
|
|
35
|
+
private log;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EAEb,WAAW,EACX,YAAY,EACZ,QAAQ,EACT,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,wBAAwB,EAAsB,MAAM,SAAS,CAAC;AA8C5E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,iBAAkB,YAAW,aAAa,CAAC,wBAAwB,CAAC;IAC/E,MAAM,CAAC,IAAI,EAAG,SAAS,CAAU;IACjC,IAAI,EAAG,SAAS,CAAU;IAE1B,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,KAAK,CAAS;IAEtB,IAAI,CACF,SAAS,EAAE,QAAQ,EACnB,cAAc,EAAE,wBAAwB,EACxC,eAAe,EAAE,WAAW,GAC3B,IAAI;IAwBP,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;YAgBzD,gBAAgB;IA2C9B,OAAO,CAAC,GAAG;CAKZ"}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { createI18nCore, TtlCache } from "@better-i18n/core";
|
|
2
|
+
import { readCache, resolveStorage, writeCache } from "./storage";
|
|
3
|
+
const DEFAULT_CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
4
|
+
/**
|
|
5
|
+
* Extract keys for a specific namespace from the flat CDN response.
|
|
6
|
+
*
|
|
7
|
+
* CDN returns: `{ "common.hero.title": "Welcome", "common.nav.home": "Home" }`
|
|
8
|
+
* i18next expects for namespace "common": `{ "hero.title": "Welcome", "nav.home": "Home" }`
|
|
9
|
+
*
|
|
10
|
+
* If keys are already nested (no namespace prefix), returns the namespace
|
|
11
|
+
* subtree or the full object if namespace doesn't exist as a top-level key.
|
|
12
|
+
*/
|
|
13
|
+
function extractNamespace(data, namespace) {
|
|
14
|
+
// Case 1: Data is already nested with namespace as a top-level key
|
|
15
|
+
// e.g. { "common": { "hero": { "title": "..." } } }
|
|
16
|
+
if (namespace in data &&
|
|
17
|
+
typeof data[namespace] === "object" &&
|
|
18
|
+
data[namespace] !== null) {
|
|
19
|
+
return data[namespace];
|
|
20
|
+
}
|
|
21
|
+
// Case 2: Flat keys with namespace prefix ("common.hero.title")
|
|
22
|
+
const prefix = `${namespace}.`;
|
|
23
|
+
const result = {};
|
|
24
|
+
let foundAny = false;
|
|
25
|
+
for (const key in data) {
|
|
26
|
+
if (key.startsWith(prefix)) {
|
|
27
|
+
result[key.slice(prefix.length)] = data[key];
|
|
28
|
+
foundAny = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (foundAny)
|
|
32
|
+
return result;
|
|
33
|
+
// Case 3: No namespace structure found — return everything as-is
|
|
34
|
+
// (single-namespace projects or default namespace)
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* i18next backend plugin for better-i18n CDN with offline caching.
|
|
39
|
+
*
|
|
40
|
+
* Implements a network-first strategy:
|
|
41
|
+
* 1. Check in-memory cache (TtlCache) — avoids redundant fetches within a session
|
|
42
|
+
* 2. Fetch from CDN — always get fresh translations
|
|
43
|
+
* 3. On CDN failure, fall back to persistent cache (AsyncStorage)
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import i18n from 'i18next';
|
|
48
|
+
* import { BetterI18nBackend } from '@better-i18n/expo';
|
|
49
|
+
*
|
|
50
|
+
* i18n.use(BetterI18nBackend).init({
|
|
51
|
+
* backend: { project: 'acme/my-app' },
|
|
52
|
+
* lng: 'en',
|
|
53
|
+
* fallbackLng: 'en',
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export class BetterI18nBackend {
|
|
58
|
+
static type = "backend";
|
|
59
|
+
type = "backend";
|
|
60
|
+
core;
|
|
61
|
+
storage;
|
|
62
|
+
memoryCache = new TtlCache();
|
|
63
|
+
cacheExpiration = DEFAULT_CACHE_EXPIRATION_MS;
|
|
64
|
+
project = "";
|
|
65
|
+
debug = false;
|
|
66
|
+
init(_services, backendOptions, _i18nextOptions) {
|
|
67
|
+
if (!backendOptions.project) {
|
|
68
|
+
throw new Error("[better-i18n/expo] `project` is required in backend options");
|
|
69
|
+
}
|
|
70
|
+
this.project = backendOptions.project;
|
|
71
|
+
this.debug = backendOptions.debug ?? false;
|
|
72
|
+
this.cacheExpiration =
|
|
73
|
+
backendOptions.cacheExpiration ?? DEFAULT_CACHE_EXPIRATION_MS;
|
|
74
|
+
this.storage = resolveStorage(backendOptions.storage);
|
|
75
|
+
this.core = createI18nCore({
|
|
76
|
+
project: backendOptions.project,
|
|
77
|
+
defaultLocale: backendOptions.defaultLocale ?? "en",
|
|
78
|
+
cdnBaseUrl: backendOptions.cdnBaseUrl,
|
|
79
|
+
debug: this.debug,
|
|
80
|
+
fetch: backendOptions.fetch,
|
|
81
|
+
...backendOptions.coreOptions,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
read(language, namespace, callback) {
|
|
85
|
+
this.loadTranslations(language)
|
|
86
|
+
.then((allData) => {
|
|
87
|
+
const nsData = extractNamespace(allData, namespace);
|
|
88
|
+
this.log("namespace", namespace, "→", Object.keys(nsData).length, "keys");
|
|
89
|
+
callback(null, nsData);
|
|
90
|
+
})
|
|
91
|
+
.catch((err) => callback(err, null));
|
|
92
|
+
}
|
|
93
|
+
async loadTranslations(locale) {
|
|
94
|
+
const memoryCacheKey = `${this.project}:${locale}`;
|
|
95
|
+
// 1. In-memory cache — skip redundant fetches within the same session
|
|
96
|
+
const memoryHit = this.memoryCache.get(memoryCacheKey);
|
|
97
|
+
if (memoryHit) {
|
|
98
|
+
this.log("memory cache hit", locale);
|
|
99
|
+
return memoryHit;
|
|
100
|
+
}
|
|
101
|
+
// 2. Network-first: always try CDN, fall back to persistent cache
|
|
102
|
+
try {
|
|
103
|
+
this.log("fetching from CDN", locale);
|
|
104
|
+
const data = (await this.core.getMessages(locale));
|
|
105
|
+
// Persist to both caches
|
|
106
|
+
this.memoryCache.set(memoryCacheKey, data, this.cacheExpiration);
|
|
107
|
+
writeCache(this.storage, this.project, locale, data).catch((err) => {
|
|
108
|
+
this.log("failed to write persistent cache", err);
|
|
109
|
+
});
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
this.log("CDN fetch failed, checking persistent cache", locale, err);
|
|
114
|
+
// 3. CDN down — fall back to persistent cache
|
|
115
|
+
const cached = await readCache(this.storage, this.project, locale);
|
|
116
|
+
if (cached) {
|
|
117
|
+
this.log("serving from persistent cache (offline fallback)", locale);
|
|
118
|
+
this.memoryCache.set(memoryCacheKey, cached.data, this.cacheExpiration);
|
|
119
|
+
return cached.data;
|
|
120
|
+
}
|
|
121
|
+
// 4. No cache, no CDN — nothing we can do
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
log(...args) {
|
|
126
|
+
if (this.debug) {
|
|
127
|
+
console.debug("[better-i18n/expo]", ...args);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { i18n } from "i18next";
|
|
2
|
+
import type { TranslationStorage } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Options for the `initBetterI18n` convenience helper
|
|
5
|
+
*/
|
|
6
|
+
export interface InitBetterI18nOptions {
|
|
7
|
+
/** Project identifier in "org/project" format */
|
|
8
|
+
project: string;
|
|
9
|
+
/** i18next instance to configure */
|
|
10
|
+
i18n: i18n;
|
|
11
|
+
/** Default/fallback locale @default "en" */
|
|
12
|
+
defaultLocale?: string;
|
|
13
|
+
/** Cache TTL in ms @default 86400000 (24h) */
|
|
14
|
+
cacheExpiration?: number;
|
|
15
|
+
/** Custom storage adapter */
|
|
16
|
+
storage?: TranslationStorage;
|
|
17
|
+
/** Auto-detect device locale via expo-localization @default false */
|
|
18
|
+
useDeviceLocale?: boolean;
|
|
19
|
+
/** Enable debug logging @default false */
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* One-liner setup helper that configures i18next with the BetterI18nBackend.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import i18n from 'i18next';
|
|
28
|
+
* import { initReactI18next } from 'react-i18next';
|
|
29
|
+
* import { initBetterI18n } from '@better-i18n/expo';
|
|
30
|
+
*
|
|
31
|
+
* await initBetterI18n({
|
|
32
|
+
* project: 'acme/my-app',
|
|
33
|
+
* i18n: i18n.use(initReactI18next),
|
|
34
|
+
* useDeviceLocale: true,
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare const initBetterI18n: (options: InitBetterI18nOptions) => Promise<void>;
|
|
39
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAGpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAEhB,oCAAoC;IACpC,IAAI,EAAE,IAAI,CAAC;IAEX,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,8CAA8C;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAE7B,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,GACzB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,IAAI,CA2Bd,CAAC"}
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BetterI18nBackend } from "./backend";
|
|
2
|
+
import { getDeviceLocale } from "./locale";
|
|
3
|
+
/**
|
|
4
|
+
* One-liner setup helper that configures i18next with the BetterI18nBackend.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import i18n from 'i18next';
|
|
9
|
+
* import { initReactI18next } from 'react-i18next';
|
|
10
|
+
* import { initBetterI18n } from '@better-i18n/expo';
|
|
11
|
+
*
|
|
12
|
+
* await initBetterI18n({
|
|
13
|
+
* project: 'acme/my-app',
|
|
14
|
+
* i18n: i18n.use(initReactI18next),
|
|
15
|
+
* useDeviceLocale: true,
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export const initBetterI18n = async (options) => {
|
|
20
|
+
const { project, i18n: i18nInstance, defaultLocale = "en", cacheExpiration, storage, useDeviceLocale = false, debug = false, } = options;
|
|
21
|
+
const lng = useDeviceLocale
|
|
22
|
+
? getDeviceLocale({ fallback: defaultLocale })
|
|
23
|
+
: defaultLocale;
|
|
24
|
+
await i18nInstance.use(BetterI18nBackend).init({
|
|
25
|
+
backend: {
|
|
26
|
+
project,
|
|
27
|
+
defaultLocale,
|
|
28
|
+
cacheExpiration,
|
|
29
|
+
storage,
|
|
30
|
+
debug,
|
|
31
|
+
},
|
|
32
|
+
lng,
|
|
33
|
+
fallbackLng: defaultLocale,
|
|
34
|
+
interpolation: { escapeValue: false },
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AA6B3C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,OAA8B,EACf,EAAE;IACjB,MAAM,EACJ,OAAO,EACP,IAAI,EAAE,YAAY,EAClB,aAAa,GAAG,IAAI,EACpB,eAAe,EACf,OAAO,EACP,eAAe,GAAG,KAAK,EACvB,KAAK,GAAG,KAAK,GACd,GAAG,OAAO,CAAC;IAEZ,MAAM,GAAG,GAAG,eAAe;QACzB,CAAC,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;QAC9C,CAAC,CAAC,aAAa,CAAC;IAElB,MAAM,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC;QAC7C,OAAO,EAAE;YACP,OAAO;YACP,aAAa;YACb,eAAe;YACf,OAAO;YACP,KAAK;SACN;QACD,GAAG;QACH,WAAW,EAAE,aAAa;QAC1B,aAAa,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;KACtC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { BetterI18nBackend } from "./backend";
|
|
2
|
+
export { createMemoryStorage, resolveStorage } from "./storage";
|
|
3
|
+
export { getDeviceLocale, getDeviceLocales } from "./locale";
|
|
4
|
+
export { initBetterI18n } from "./helpers";
|
|
5
|
+
export { createI18nCore } from "@better-i18n/core";
|
|
6
|
+
export type { LanguageOption, ManifestResponse } from "@better-i18n/core";
|
|
7
|
+
export type { BetterI18nBackendOptions, TranslationStorage, CacheMeta, } from "./types";
|
|
8
|
+
export type { InitBetterI18nOptions } from "./helpers";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAG9C,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG7D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1E,YAAY,EACV,wBAAwB,EACxB,kBAAkB,EAClB,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Backend plugin (main API)
|
|
2
|
+
export { BetterI18nBackend } from "./backend";
|
|
3
|
+
// Storage
|
|
4
|
+
export { createMemoryStorage, resolveStorage } from "./storage";
|
|
5
|
+
// Locale detection
|
|
6
|
+
export { getDeviceLocale, getDeviceLocales } from "./locale";
|
|
7
|
+
// Convenience helper
|
|
8
|
+
export { initBetterI18n } from "./helpers";
|
|
9
|
+
// Re-export core utilities for manifest/language fetching
|
|
10
|
+
export { createI18nCore } from "@better-i18n/core";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,UAAU;AACV,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhE,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE7D,qBAAqB;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,0DAA0D;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/locale.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the device's primary locale using expo-localization.
|
|
3
|
+
*
|
|
4
|
+
* Falls back to the provided default if expo-localization is not installed
|
|
5
|
+
* or if no locale can be detected.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const locale = getDeviceLocale({ fallback: 'en' });
|
|
10
|
+
* // "tr" (on a Turkish device)
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare const getDeviceLocale: (options?: {
|
|
14
|
+
fallback?: string;
|
|
15
|
+
}) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Get all device locales as language codes.
|
|
18
|
+
*
|
|
19
|
+
* Returns an empty array if expo-localization is not installed.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const locales = getDeviceLocales();
|
|
24
|
+
* // ["tr", "en", "de"]
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare const getDeviceLocales: () => string[];
|
|
28
|
+
//# sourceMappingURL=locale.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../src/locale.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC1B,UAAS;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,KAClC,MAaF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,QAAO,MAAM,EAazC,CAAC"}
|
package/dist/locale.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the device's primary locale using expo-localization.
|
|
3
|
+
*
|
|
4
|
+
* Falls back to the provided default if expo-localization is not installed
|
|
5
|
+
* or if no locale can be detected.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const locale = getDeviceLocale({ fallback: 'en' });
|
|
10
|
+
* // "tr" (on a Turkish device)
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export const getDeviceLocale = (options = {}) => {
|
|
14
|
+
const { fallback = "en" } = options;
|
|
15
|
+
try {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
17
|
+
const localization = require("expo-localization");
|
|
18
|
+
const getLocales = localization.getLocales;
|
|
19
|
+
const locales = getLocales();
|
|
20
|
+
return locales[0]?.languageCode ?? fallback;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Get all device locales as language codes.
|
|
28
|
+
*
|
|
29
|
+
* Returns an empty array if expo-localization is not installed.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const locales = getDeviceLocales();
|
|
34
|
+
* // ["tr", "en", "de"]
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export const getDeviceLocales = () => {
|
|
38
|
+
try {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
40
|
+
const localization = require("expo-localization");
|
|
41
|
+
const getLocales = localization.getLocales;
|
|
42
|
+
const locales = getLocales();
|
|
43
|
+
return locales
|
|
44
|
+
.map((l) => l.languageCode)
|
|
45
|
+
.filter((code) => code != null);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=locale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locale.js","sourceRoot":"","sources":["../src/locale.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,UAAiC,EAAE,EAC3B,EAAE;IACV,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAEpC,IAAI,CAAC;QACH,iEAAiE;QACjE,MAAM,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAClD,MAAM,UAAU,GACd,YAAY,CAAC,UAAU,CAAC;QAC1B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,QAAQ,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAa,EAAE;IAC7C,IAAI,CAAC;QACH,iEAAiE;QACjE,MAAM,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAClD,MAAM,UAAU,GACd,YAAY,CAAC,UAAU,CAAC;QAC1B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,OAAO,OAAO;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CacheMeta, TranslationStorage } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Build the storage key for cached translations
|
|
4
|
+
*/
|
|
5
|
+
export declare const buildStorageKey: (project: string, locale: string) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Build the storage key for cache metadata
|
|
8
|
+
*/
|
|
9
|
+
export declare const buildMetaKey: (project: string, locale: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Create an in-memory storage adapter.
|
|
12
|
+
* Used as fallback when no persistent storage is available.
|
|
13
|
+
*/
|
|
14
|
+
export declare const createMemoryStorage: () => TranslationStorage;
|
|
15
|
+
/**
|
|
16
|
+
* Auto-detect the best available storage. Zero config.
|
|
17
|
+
*
|
|
18
|
+
* Priority:
|
|
19
|
+
* 1. User-provided custom storage
|
|
20
|
+
* 2. react-native-mmkv (fastest, sync I/O)
|
|
21
|
+
* 3. @react-native-async-storage/async-storage (most common)
|
|
22
|
+
* 4. In-memory Map (no persistence, works everywhere)
|
|
23
|
+
*/
|
|
24
|
+
export declare const resolveStorage: (userStorage?: TranslationStorage) => TranslationStorage;
|
|
25
|
+
/**
|
|
26
|
+
* Read cached translations from storage.
|
|
27
|
+
* Meta is optional — if only translations exist, they are still returned.
|
|
28
|
+
*/
|
|
29
|
+
export declare const readCache: (storage: TranslationStorage, project: string, locale: string) => Promise<{
|
|
30
|
+
data: Record<string, unknown>;
|
|
31
|
+
meta: CacheMeta;
|
|
32
|
+
} | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Write translations to persistent storage with metadata
|
|
35
|
+
*/
|
|
36
|
+
export declare const writeCache: (storage: TranslationStorage, project: string, locale: string, data: Record<string, unknown>) => Promise<void>;
|
|
37
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAI7D;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,KAAG,MACb,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,KAAG,MAClB,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAO,kBAWtC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,cAAc,kBAAkB,KAC/B,kBAiCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,GACpB,SAAS,kBAAkB,EAC3B,SAAS,MAAM,EACf,QAAQ,MAAM,KACb,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG,IAAI,CAoBnE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,SAAS,kBAAkB,EAC3B,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,IAAI,CAMd,CAAC"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const CACHE_PREFIX = "@better-i18n";
|
|
2
|
+
/**
|
|
3
|
+
* Build the storage key for cached translations
|
|
4
|
+
*/
|
|
5
|
+
export const buildStorageKey = (project, locale) => `${CACHE_PREFIX}:${project}:${locale}:translations`;
|
|
6
|
+
/**
|
|
7
|
+
* Build the storage key for cache metadata
|
|
8
|
+
*/
|
|
9
|
+
export const buildMetaKey = (project, locale) => `${CACHE_PREFIX}:${project}:${locale}:meta`;
|
|
10
|
+
/**
|
|
11
|
+
* Create an in-memory storage adapter.
|
|
12
|
+
* Used as fallback when no persistent storage is available.
|
|
13
|
+
*/
|
|
14
|
+
export const createMemoryStorage = () => {
|
|
15
|
+
const store = new Map();
|
|
16
|
+
return {
|
|
17
|
+
getItem: async (key) => store.get(key) ?? null,
|
|
18
|
+
setItem: async (key, value) => {
|
|
19
|
+
store.set(key, value);
|
|
20
|
+
},
|
|
21
|
+
removeItem: async (key) => {
|
|
22
|
+
store.delete(key);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Auto-detect the best available storage. Zero config.
|
|
28
|
+
*
|
|
29
|
+
* Priority:
|
|
30
|
+
* 1. User-provided custom storage
|
|
31
|
+
* 2. react-native-mmkv (fastest, sync I/O)
|
|
32
|
+
* 3. @react-native-async-storage/async-storage (most common)
|
|
33
|
+
* 4. In-memory Map (no persistence, works everywhere)
|
|
34
|
+
*/
|
|
35
|
+
export const resolveStorage = (userStorage) => {
|
|
36
|
+
if (userStorage)
|
|
37
|
+
return userStorage;
|
|
38
|
+
// 1. Try MMKV — fastest persistent storage for RN
|
|
39
|
+
try {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
41
|
+
const { MMKV } = require("react-native-mmkv");
|
|
42
|
+
const mmkv = new MMKV({ id: "better-i18n" });
|
|
43
|
+
return {
|
|
44
|
+
getItem: async (key) => mmkv.getString(key) ?? null,
|
|
45
|
+
setItem: async (key, value) => { mmkv.set(key, value); },
|
|
46
|
+
removeItem: async (key) => { mmkv.delete(key); },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// not installed
|
|
51
|
+
}
|
|
52
|
+
// 2. Try AsyncStorage
|
|
53
|
+
try {
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
55
|
+
const asyncStorage = require("@react-native-async-storage/async-storage");
|
|
56
|
+
const mod = asyncStorage.default ?? asyncStorage;
|
|
57
|
+
return {
|
|
58
|
+
getItem: (key) => mod.getItem(key),
|
|
59
|
+
setItem: (key, value) => mod.setItem(key, value),
|
|
60
|
+
removeItem: (key) => mod.removeItem(key),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// not installed
|
|
65
|
+
}
|
|
66
|
+
// 3. Fallback — in-memory (no persistence across restarts)
|
|
67
|
+
return createMemoryStorage();
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Read cached translations from storage.
|
|
71
|
+
* Meta is optional — if only translations exist, they are still returned.
|
|
72
|
+
*/
|
|
73
|
+
export const readCache = async (storage, project, locale) => {
|
|
74
|
+
try {
|
|
75
|
+
const raw = await storage.getItem(buildStorageKey(project, locale));
|
|
76
|
+
if (!raw)
|
|
77
|
+
return null;
|
|
78
|
+
const data = JSON.parse(raw);
|
|
79
|
+
// Meta is best-effort — default to epoch if missing/corrupt
|
|
80
|
+
let meta = { cachedAt: 0 };
|
|
81
|
+
try {
|
|
82
|
+
const rawMeta = await storage.getItem(buildMetaKey(project, locale));
|
|
83
|
+
if (rawMeta)
|
|
84
|
+
meta = JSON.parse(rawMeta);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// meta is non-critical
|
|
88
|
+
}
|
|
89
|
+
return { data, meta };
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Write translations to persistent storage with metadata
|
|
97
|
+
*/
|
|
98
|
+
export const writeCache = async (storage, project, locale, data) => {
|
|
99
|
+
const meta = { cachedAt: Date.now() };
|
|
100
|
+
await Promise.all([
|
|
101
|
+
storage.setItem(buildStorageKey(project, locale), JSON.stringify(data)),
|
|
102
|
+
storage.setItem(buildMetaKey(project, locale), JSON.stringify(meta)),
|
|
103
|
+
]);
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { I18nCoreConfig } from "@better-i18n/core";
|
|
2
|
+
/**
|
|
3
|
+
* Pluggable storage interface for persistent translation caching.
|
|
4
|
+
*
|
|
5
|
+
* Compatible with AsyncStorage, MMKV, or any custom key-value store.
|
|
6
|
+
*/
|
|
7
|
+
export interface TranslationStorage {
|
|
8
|
+
getItem(key: string): Promise<string | null>;
|
|
9
|
+
setItem(key: string, value: string): Promise<void>;
|
|
10
|
+
removeItem(key: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Metadata stored alongside cached translations
|
|
14
|
+
*/
|
|
15
|
+
export interface CacheMeta {
|
|
16
|
+
/** Timestamp when translations were cached */
|
|
17
|
+
cachedAt: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Configuration options for the BetterI18nBackend plugin.
|
|
21
|
+
*
|
|
22
|
+
* Passed via i18next's `backend` option:
|
|
23
|
+
* ```ts
|
|
24
|
+
* i18n.init({ backend: { project: 'acme/my-app' } })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export interface BetterI18nBackendOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Project identifier in "org/project" format (e.g., "acme/dashboard")
|
|
30
|
+
*/
|
|
31
|
+
project: string;
|
|
32
|
+
/**
|
|
33
|
+
* Default/fallback locale
|
|
34
|
+
* @default "en"
|
|
35
|
+
*/
|
|
36
|
+
defaultLocale?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Custom CDN base URL
|
|
39
|
+
* @default "https://cdn.better-i18n.com"
|
|
40
|
+
*/
|
|
41
|
+
cdnBaseUrl?: string;
|
|
42
|
+
/**
|
|
43
|
+
* In-memory cache TTL in milliseconds. Controls how long translations
|
|
44
|
+
* are kept in memory before the next CDN fetch within the same session.
|
|
45
|
+
* @default 86400000 (24 hours)
|
|
46
|
+
*/
|
|
47
|
+
cacheExpiration?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Pluggable storage adapter for persistent caching.
|
|
50
|
+
* Defaults to AsyncStorage if installed, otherwise in-memory.
|
|
51
|
+
*/
|
|
52
|
+
storage?: TranslationStorage;
|
|
53
|
+
/**
|
|
54
|
+
* Enable debug logging
|
|
55
|
+
* @default false
|
|
56
|
+
*/
|
|
57
|
+
debug?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Custom fetch function (useful for testing)
|
|
60
|
+
*/
|
|
61
|
+
fetch?: typeof fetch;
|
|
62
|
+
/**
|
|
63
|
+
* Additional options forwarded to `@better-i18n/core`
|
|
64
|
+
*/
|
|
65
|
+
coreOptions?: Partial<Omit<I18nCoreConfig, "project" | "defaultLocale">>;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAE7B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,eAAe,CAAC,CAAC,CAAC;CAC1E"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@better-i18n/expo",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Better i18n backend plugin for i18next in Expo/React Native",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/better-i18n/better-i18n.git",
|
|
9
|
+
"directory": "packages/expo"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/better-i18n/better-i18n/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/better-i18n/better-i18n/tree/main/packages/expo",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./src/index.ts",
|
|
17
|
+
"types": "./src/index.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./src/index.ts",
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"package.json",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"i18n",
|
|
41
|
+
"localization",
|
|
42
|
+
"internationalization",
|
|
43
|
+
"expo",
|
|
44
|
+
"react-native",
|
|
45
|
+
"i18next",
|
|
46
|
+
"cdn",
|
|
47
|
+
"translations",
|
|
48
|
+
"offline"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"clean": "rm -rf dist",
|
|
54
|
+
"prepublishOnly": "bun run build"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@better-i18n/core": "0.1.7"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"i18next": ">=23.0.0",
|
|
61
|
+
"react-i18next": ">=14.0.0",
|
|
62
|
+
"expo-localization": ">=15.0.0",
|
|
63
|
+
"react-native-mmkv": ">=3.0.0",
|
|
64
|
+
"@react-native-async-storage/async-storage": ">=1.19.0"
|
|
65
|
+
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"react-i18next": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"expo-localization": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"react-native-mmkv": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"@react-native-async-storage/async-storage": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@types/node": "^20.0.0",
|
|
82
|
+
"i18next": "^23.0.0",
|
|
83
|
+
"typescript": "~5.9.2"
|
|
84
|
+
}
|
|
85
|
+
}
|