react-native-i18njs 0.0.3 → 1.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/README.md +38 -3
- package/dist/index.d.mts +54 -90
- package/dist/index.d.ts +54 -90
- package/dist/index.js +1 -588
- package/dist/index.mjs +1 -543
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,543 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as RNLocalize from 'react-native-localize';
|
|
3
|
-
import { I18nManager, AppState, Text } from 'react-native';
|
|
4
|
-
import React, { createContext, useState, useEffect, useMemo, useContext, isValidElement, Fragment, cloneElement } from 'react';
|
|
5
|
-
|
|
6
|
-
var __defProp = Object.defineProperty;
|
|
7
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
9
|
-
var RTL_SCRIPTS = /* @__PURE__ */ new Set(["Arab", "Hebr", "Thaa", "Syrc", "Nkoo", "Adlm"]);
|
|
10
|
-
var RTL_LANGUAGE_CODES = /* @__PURE__ */ new Set([
|
|
11
|
-
"ar",
|
|
12
|
-
// Arabic
|
|
13
|
-
"fa",
|
|
14
|
-
// Persian
|
|
15
|
-
"he",
|
|
16
|
-
// Hebrew
|
|
17
|
-
"iw",
|
|
18
|
-
// Hebrew (legacy)
|
|
19
|
-
"ur",
|
|
20
|
-
// Urdu
|
|
21
|
-
"ps",
|
|
22
|
-
// Pashto
|
|
23
|
-
"dv",
|
|
24
|
-
// Divehi
|
|
25
|
-
"ku",
|
|
26
|
-
// Kurdish
|
|
27
|
-
"ug",
|
|
28
|
-
// Uyghur
|
|
29
|
-
"yi",
|
|
30
|
-
// Yiddish
|
|
31
|
-
"sd"
|
|
32
|
-
// Sindhi
|
|
33
|
-
]);
|
|
34
|
-
var DefaultI18nEngine = class {
|
|
35
|
-
constructor() {
|
|
36
|
-
__publicField(this, "i18n");
|
|
37
|
-
__publicField(this, "listeners");
|
|
38
|
-
__publicField(this, "onLocaleChange");
|
|
39
|
-
__publicField(this, "fallbackLocales");
|
|
40
|
-
__publicField(this, "missingBehavior");
|
|
41
|
-
__publicField(this, "onMissingKey");
|
|
42
|
-
__publicField(this, "enableFallback");
|
|
43
|
-
__publicField(this, "followSystem");
|
|
44
|
-
__publicField(this, "localeSource");
|
|
45
|
-
__publicField(this, "initialized");
|
|
46
|
-
__publicField(this, "version");
|
|
47
|
-
__publicField(this, "readyPromise");
|
|
48
|
-
__publicField(this, "resolveReady");
|
|
49
|
-
// --- 性能缓存 ---
|
|
50
|
-
/** Intl 格式化器缓存,key = locale + JSON(options) */
|
|
51
|
-
__publicField(this, "numberFormatCache", /* @__PURE__ */ new Map());
|
|
52
|
-
__publicField(this, "dateFormatCache", /* @__PURE__ */ new Map());
|
|
53
|
-
/** locale chain 缓存,locale 或 fallbackLocales 变化时失效 */
|
|
54
|
-
__publicField(this, "localeChainCache", /* @__PURE__ */ new Map());
|
|
55
|
-
this.i18n = new I18n();
|
|
56
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
57
|
-
this.i18n.enableFallback = false;
|
|
58
|
-
this.missingBehavior = "key";
|
|
59
|
-
this.enableFallback = true;
|
|
60
|
-
this.followSystem = true;
|
|
61
|
-
this.localeSource = "system";
|
|
62
|
-
this.initialized = false;
|
|
63
|
-
this.version = 0;
|
|
64
|
-
this.readyPromise = new Promise((resolve) => {
|
|
65
|
-
this.resolveReady = resolve;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
init(translations, options = {}) {
|
|
69
|
-
var _a;
|
|
70
|
-
const {
|
|
71
|
-
defaultLocale = "en",
|
|
72
|
-
enableFallback = true,
|
|
73
|
-
onLocaleChange,
|
|
74
|
-
fallbackLocales,
|
|
75
|
-
followSystem = true,
|
|
76
|
-
missingBehavior = "key",
|
|
77
|
-
onMissingKey
|
|
78
|
-
} = options;
|
|
79
|
-
this.i18n.store(this.normalizeTranslations(translations));
|
|
80
|
-
this.i18n.defaultLocale = this.normalizeLocaleTag(defaultLocale);
|
|
81
|
-
this.i18n.locale = this.normalizeLocaleTag(defaultLocale);
|
|
82
|
-
this.enableFallback = enableFallback;
|
|
83
|
-
this.onLocaleChange = onLocaleChange;
|
|
84
|
-
this.fallbackLocales = fallbackLocales;
|
|
85
|
-
this.followSystem = followSystem;
|
|
86
|
-
this.localeSource = "system";
|
|
87
|
-
this.missingBehavior = missingBehavior;
|
|
88
|
-
this.onMissingKey = onMissingKey;
|
|
89
|
-
this.invalidateCaches();
|
|
90
|
-
if (this.followSystem) this.updateLocale();
|
|
91
|
-
this.version += 1;
|
|
92
|
-
this.notifyListeners({ type: "translations", version: this.version });
|
|
93
|
-
if (!this.initialized) {
|
|
94
|
-
this.initialized = true;
|
|
95
|
-
(_a = this.resolveReady) == null ? void 0 : _a.call(this);
|
|
96
|
-
this.resolveReady = void 0;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
loadTranslations(translations) {
|
|
100
|
-
this.i18n.store(this.normalizeTranslations(translations));
|
|
101
|
-
this.version += 1;
|
|
102
|
-
this.notifyListeners({ type: "translations", version: this.version });
|
|
103
|
-
}
|
|
104
|
-
updateLocale() {
|
|
105
|
-
if (!this.followSystem) return;
|
|
106
|
-
if (this.localeSource === "user") return;
|
|
107
|
-
let locales = [];
|
|
108
|
-
try {
|
|
109
|
-
locales = RNLocalize.getLocales();
|
|
110
|
-
} catch {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
if (!locales || locales.length === 0) return;
|
|
114
|
-
const systemLocale = this.normalizeLocaleTag(locales[0].languageTag);
|
|
115
|
-
if (!systemLocale) return;
|
|
116
|
-
this.setLocaleFromSystem(systemLocale);
|
|
117
|
-
}
|
|
118
|
-
setLocale(locale) {
|
|
119
|
-
this.localeSource = "user";
|
|
120
|
-
this.applyLocale(locale);
|
|
121
|
-
}
|
|
122
|
-
getLocale() {
|
|
123
|
-
return this.i18n.locale;
|
|
124
|
-
}
|
|
125
|
-
resetToSystem() {
|
|
126
|
-
this.localeSource = "system";
|
|
127
|
-
this.updateLocale();
|
|
128
|
-
}
|
|
129
|
-
t(scope, options) {
|
|
130
|
-
var _a, _b, _c;
|
|
131
|
-
if (typeof scope !== "string") {
|
|
132
|
-
return (_a = this.normalizeTranslateResult(this.i18n.t(scope, options))) != null ? _a : "";
|
|
133
|
-
}
|
|
134
|
-
const key = scope;
|
|
135
|
-
const currentLocale = this.i18n.locale;
|
|
136
|
-
const chain = this.enableFallback ? this.getLocaleChain(currentLocale) : [currentLocale];
|
|
137
|
-
for (const locale of chain) {
|
|
138
|
-
if (this.hasTranslation(locale, key)) {
|
|
139
|
-
const translated = this.normalizeTranslateResult(
|
|
140
|
-
this.translateAtLocale(locale, scope, options)
|
|
141
|
-
);
|
|
142
|
-
if (translated !== void 0) return translated;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
(_b = this.onMissingKey) == null ? void 0 : _b.call(this, key, currentLocale);
|
|
146
|
-
if (options && Object.prototype.hasOwnProperty.call(options, "defaultValue")) {
|
|
147
|
-
return (_c = this.normalizeTranslateResult(
|
|
148
|
-
this.translateAtLocale(currentLocale, scope, options)
|
|
149
|
-
)) != null ? _c : "";
|
|
150
|
-
}
|
|
151
|
-
if (this.missingBehavior === "empty") return "";
|
|
152
|
-
if (this.missingBehavior === "throw") {
|
|
153
|
-
throw new Error(`Missing translation: ${currentLocale}.${key}`);
|
|
154
|
-
}
|
|
155
|
-
return key;
|
|
156
|
-
}
|
|
157
|
-
formatNumber(n, options) {
|
|
158
|
-
if (typeof Intl === "undefined" || typeof Intl.NumberFormat !== "function") {
|
|
159
|
-
return String(n);
|
|
160
|
-
}
|
|
161
|
-
try {
|
|
162
|
-
return this.getCachedNumberFormat(this.i18n.locale, options).format(n);
|
|
163
|
-
} catch {
|
|
164
|
-
return String(n);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
formatCurrency(n, currency, options) {
|
|
168
|
-
if (typeof Intl === "undefined" || typeof Intl.NumberFormat !== "function") {
|
|
169
|
-
return `${n} ${currency}`;
|
|
170
|
-
}
|
|
171
|
-
try {
|
|
172
|
-
return this.getCachedNumberFormat(this.i18n.locale, {
|
|
173
|
-
...options,
|
|
174
|
-
style: "currency",
|
|
175
|
-
currency
|
|
176
|
-
}).format(n);
|
|
177
|
-
} catch {
|
|
178
|
-
return `${n} ${currency}`;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
formatDate(date, options) {
|
|
182
|
-
const d = typeof date === "number" ? new Date(date) : date;
|
|
183
|
-
if (Number.isNaN(d.getTime())) return "";
|
|
184
|
-
if (typeof Intl === "undefined" || typeof Intl.DateTimeFormat !== "function") {
|
|
185
|
-
return d.toISOString();
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
return this.getCachedDateFormat(this.i18n.locale, options).format(d);
|
|
189
|
-
} catch {
|
|
190
|
-
return d.toISOString();
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
subscribe(listener) {
|
|
194
|
-
this.listeners.add(listener);
|
|
195
|
-
return () => {
|
|
196
|
-
this.listeners.delete(listener);
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
isRTL() {
|
|
200
|
-
return I18nManager.isRTL;
|
|
201
|
-
}
|
|
202
|
-
ready() {
|
|
203
|
-
if (this.initialized) return Promise.resolve();
|
|
204
|
-
return this.readyPromise;
|
|
205
|
-
}
|
|
206
|
-
isReady() {
|
|
207
|
-
return this.initialized;
|
|
208
|
-
}
|
|
209
|
-
// ─── 缓存管理 ────────────────────────────────────────────
|
|
210
|
-
invalidateCaches() {
|
|
211
|
-
this.numberFormatCache.clear();
|
|
212
|
-
this.dateFormatCache.clear();
|
|
213
|
-
this.localeChainCache.clear();
|
|
214
|
-
}
|
|
215
|
-
getCachedNumberFormat(locale, options) {
|
|
216
|
-
const cacheKey = locale + (options ? JSON.stringify(options) : "");
|
|
217
|
-
let fmt = this.numberFormatCache.get(cacheKey);
|
|
218
|
-
if (!fmt) {
|
|
219
|
-
fmt = new Intl.NumberFormat(locale, options);
|
|
220
|
-
this.numberFormatCache.set(cacheKey, fmt);
|
|
221
|
-
}
|
|
222
|
-
return fmt;
|
|
223
|
-
}
|
|
224
|
-
getCachedDateFormat(locale, options) {
|
|
225
|
-
const cacheKey = locale + (options ? JSON.stringify(options) : "");
|
|
226
|
-
let fmt = this.dateFormatCache.get(cacheKey);
|
|
227
|
-
if (!fmt) {
|
|
228
|
-
fmt = new Intl.DateTimeFormat(locale, options);
|
|
229
|
-
this.dateFormatCache.set(cacheKey, fmt);
|
|
230
|
-
}
|
|
231
|
-
return fmt;
|
|
232
|
-
}
|
|
233
|
-
// ─── 私有方法 ────────────────────────────────────────────
|
|
234
|
-
handleRTL(locale) {
|
|
235
|
-
var _a, _b;
|
|
236
|
-
const isRTL2 = this.isRTLLocale(locale);
|
|
237
|
-
if (typeof ((_a = I18nManager) == null ? void 0 : _a.allowRTL) !== "function" || typeof ((_b = I18nManager) == null ? void 0 : _b.forceRTL) !== "function") {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
if (I18nManager.isRTL !== isRTL2) {
|
|
241
|
-
I18nManager.allowRTL(isRTL2);
|
|
242
|
-
I18nManager.forceRTL(isRTL2);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
notifyListeners(change) {
|
|
246
|
-
this.listeners.forEach((listener) => listener(this.i18n.locale, change));
|
|
247
|
-
}
|
|
248
|
-
setLocaleFromSystem(locale) {
|
|
249
|
-
this.localeSource = "system";
|
|
250
|
-
this.applyLocale(locale);
|
|
251
|
-
}
|
|
252
|
-
applyLocale(locale) {
|
|
253
|
-
var _a;
|
|
254
|
-
const normalizedLocale = this.normalizeLocaleTag(locale);
|
|
255
|
-
if (this.i18n.locale !== normalizedLocale) {
|
|
256
|
-
this.i18n.locale = normalizedLocale;
|
|
257
|
-
this.invalidateCaches();
|
|
258
|
-
this.handleRTL(normalizedLocale);
|
|
259
|
-
this.version += 1;
|
|
260
|
-
this.notifyListeners({ type: "locale", version: this.version });
|
|
261
|
-
(_a = this.onLocaleChange) == null ? void 0 : _a.call(this, normalizedLocale);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
translateAtLocale(locale, scope, options) {
|
|
265
|
-
const prevLocale = this.i18n.locale;
|
|
266
|
-
this.i18n.locale = locale;
|
|
267
|
-
try {
|
|
268
|
-
return this.i18n.t(scope, options);
|
|
269
|
-
} finally {
|
|
270
|
-
this.i18n.locale = prevLocale;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/** 将 locale 及其自动降级(如 en-US → en)追加到 chain 中 */
|
|
274
|
-
pushWithDegradation(chain, locale) {
|
|
275
|
-
const normalized = this.normalizeLocaleTag(locale);
|
|
276
|
-
chain.push(normalized);
|
|
277
|
-
const parts = normalized.split("-").filter(Boolean);
|
|
278
|
-
for (let i = parts.length - 1; i >= 1; i -= 1) {
|
|
279
|
-
chain.push(parts.slice(0, i).join("-"));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
getLocaleChain(locale) {
|
|
283
|
-
const cached = this.localeChainCache.get(locale);
|
|
284
|
-
if (cached) return cached;
|
|
285
|
-
const chain = [];
|
|
286
|
-
this.pushWithDegradation(chain, locale);
|
|
287
|
-
const normalizedLocale = this.normalizeLocaleTag(locale);
|
|
288
|
-
const extra = this.fallbackLocales ? typeof this.fallbackLocales === "function" ? this.fallbackLocales(normalizedLocale) : this.fallbackLocales : [];
|
|
289
|
-
for (const l of extra) {
|
|
290
|
-
this.pushWithDegradation(chain, l);
|
|
291
|
-
}
|
|
292
|
-
if (this.i18n.defaultLocale) {
|
|
293
|
-
this.pushWithDegradation(chain, this.i18n.defaultLocale);
|
|
294
|
-
}
|
|
295
|
-
const seen = /* @__PURE__ */ new Set();
|
|
296
|
-
const result = chain.filter((l) => {
|
|
297
|
-
if (!l || seen.has(l)) return false;
|
|
298
|
-
seen.add(l);
|
|
299
|
-
return true;
|
|
300
|
-
});
|
|
301
|
-
this.localeChainCache.set(locale, result);
|
|
302
|
-
return result;
|
|
303
|
-
}
|
|
304
|
-
hasTranslation(locale, key) {
|
|
305
|
-
var _a;
|
|
306
|
-
const table = (_a = this.i18n.translations) == null ? void 0 : _a[locale];
|
|
307
|
-
if (!table || typeof table !== "object") return false;
|
|
308
|
-
if (!key.includes(".") && Object.prototype.hasOwnProperty.call(table, key)) {
|
|
309
|
-
const direct = table[key];
|
|
310
|
-
return direct !== null && direct !== void 0;
|
|
311
|
-
}
|
|
312
|
-
const parts = key.split(".").filter(Boolean);
|
|
313
|
-
let node = table;
|
|
314
|
-
for (const part of parts) {
|
|
315
|
-
if (!node || typeof node !== "object" || !(part in node)) return false;
|
|
316
|
-
node = node[part];
|
|
317
|
-
}
|
|
318
|
-
return node !== null && node !== void 0;
|
|
319
|
-
}
|
|
320
|
-
normalizeTranslateResult(value) {
|
|
321
|
-
if (typeof value === "string") return value;
|
|
322
|
-
if (typeof value === "number") return String(value);
|
|
323
|
-
return void 0;
|
|
324
|
-
}
|
|
325
|
-
normalizeLocaleTag(tag) {
|
|
326
|
-
return tag.replace(/_/g, "-");
|
|
327
|
-
}
|
|
328
|
-
normalizeTranslations(translations) {
|
|
329
|
-
const normalized = {};
|
|
330
|
-
for (const key of Object.keys(translations)) {
|
|
331
|
-
normalized[this.normalizeLocaleTag(key)] = translations[key];
|
|
332
|
-
}
|
|
333
|
-
return normalized;
|
|
334
|
-
}
|
|
335
|
-
isRTLLocale(locale) {
|
|
336
|
-
var _a;
|
|
337
|
-
const normalized = this.normalizeLocaleTag(locale);
|
|
338
|
-
const parts = normalized.split("-").filter(Boolean);
|
|
339
|
-
const languageCode = (_a = parts[0]) == null ? void 0 : _a.toLowerCase();
|
|
340
|
-
if (!languageCode) return false;
|
|
341
|
-
const scriptSubtag = parts.find((part) => /^[A-Za-z]{4}$/.test(part));
|
|
342
|
-
if (scriptSubtag) {
|
|
343
|
-
const script = scriptSubtag[0].toUpperCase() + scriptSubtag.slice(1).toLowerCase();
|
|
344
|
-
return RTL_SCRIPTS.has(script);
|
|
345
|
-
}
|
|
346
|
-
return RTL_LANGUAGE_CODES.has(languageCode);
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
var i18nService = new DefaultI18nEngine();
|
|
350
|
-
var DEFAULT_I18N_CONTEXT = {
|
|
351
|
-
locale: i18nService.getLocale(),
|
|
352
|
-
setLocale: (locale) => i18nService.setLocale(locale),
|
|
353
|
-
t: (scope, options) => i18nService.t(scope, options),
|
|
354
|
-
formatNumber: (n, o) => i18nService.formatNumber(n, o),
|
|
355
|
-
formatCurrency: (n, c, o) => i18nService.formatCurrency(n, c, o),
|
|
356
|
-
formatDate: (d, o) => i18nService.formatDate(d, o)
|
|
357
|
-
};
|
|
358
|
-
var I18nContext = createContext(DEFAULT_I18N_CONTEXT);
|
|
359
|
-
var I18nProvider = ({ children, readyGate, fallback }) => {
|
|
360
|
-
const [locale, setLocaleState] = useState(i18nService.getLocale());
|
|
361
|
-
const [version, setVersion] = useState(0);
|
|
362
|
-
const [ready, setReady] = useState(i18nService.isReady());
|
|
363
|
-
useEffect(() => {
|
|
364
|
-
const unsubscribe = i18nService.subscribe((newLocale, change) => {
|
|
365
|
-
setLocaleState(newLocale);
|
|
366
|
-
setVersion((prev) => {
|
|
367
|
-
var _a;
|
|
368
|
-
return (_a = change == null ? void 0 : change.version) != null ? _a : prev + 1;
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
return () => unsubscribe();
|
|
372
|
-
}, []);
|
|
373
|
-
useEffect(() => {
|
|
374
|
-
var _a, _b;
|
|
375
|
-
const handler = (state) => {
|
|
376
|
-
if (state === "active") {
|
|
377
|
-
i18nService.updateLocale();
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
const subscription = (_b = (_a = AppState) == null ? void 0 : _a.addEventListener) == null ? void 0 : _b.call(_a, "change", handler);
|
|
381
|
-
return () => {
|
|
382
|
-
var _a2, _b2;
|
|
383
|
-
if (typeof (subscription == null ? void 0 : subscription.remove) === "function") {
|
|
384
|
-
subscription.remove();
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
(_b2 = (_a2 = AppState) == null ? void 0 : _a2.removeEventListener) == null ? void 0 : _b2.call(_a2, "change", handler);
|
|
388
|
-
};
|
|
389
|
-
}, []);
|
|
390
|
-
useEffect(() => {
|
|
391
|
-
if (!readyGate) return;
|
|
392
|
-
let cancelled = false;
|
|
393
|
-
i18nService.ready().then(() => {
|
|
394
|
-
if (!cancelled) setReady(true);
|
|
395
|
-
});
|
|
396
|
-
return () => {
|
|
397
|
-
cancelled = true;
|
|
398
|
-
};
|
|
399
|
-
}, [readyGate]);
|
|
400
|
-
const value = useMemo(() => ({
|
|
401
|
-
locale,
|
|
402
|
-
setLocale: (l) => i18nService.setLocale(l),
|
|
403
|
-
t: (scope, options) => i18nService.t(scope, options),
|
|
404
|
-
formatNumber: (n, o) => i18nService.formatNumber(n, o),
|
|
405
|
-
formatCurrency: (n, c, o) => i18nService.formatCurrency(n, c, o),
|
|
406
|
-
formatDate: (d, o) => i18nService.formatDate(d, o)
|
|
407
|
-
}), [locale, version]);
|
|
408
|
-
if (readyGate && !ready) return /* @__PURE__ */ React.createElement(React.Fragment, null, fallback != null ? fallback : null);
|
|
409
|
-
return /* @__PURE__ */ React.createElement(I18nContext.Provider, { value }, children);
|
|
410
|
-
};
|
|
411
|
-
function withI18n(Component) {
|
|
412
|
-
return function WithI18n(props) {
|
|
413
|
-
return /* @__PURE__ */ React.createElement(I18nContext.Consumer, null, (context) => {
|
|
414
|
-
if (context === DEFAULT_I18N_CONTEXT) {
|
|
415
|
-
throw new Error("withI18n must be used within an I18nProvider");
|
|
416
|
-
}
|
|
417
|
-
return /* @__PURE__ */ React.createElement(Component, { ...props, ...context });
|
|
418
|
-
});
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
function useI18n() {
|
|
422
|
-
const context = useContext(I18nContext);
|
|
423
|
-
if (context === DEFAULT_I18N_CONTEXT) {
|
|
424
|
-
throw new Error("useI18n must be used within an I18nProvider");
|
|
425
|
-
}
|
|
426
|
-
return useMemo(() => {
|
|
427
|
-
const t2 = (scope, options) => {
|
|
428
|
-
return context.t(scope, options);
|
|
429
|
-
};
|
|
430
|
-
return {
|
|
431
|
-
...context,
|
|
432
|
-
t: t2
|
|
433
|
-
};
|
|
434
|
-
}, [context]);
|
|
435
|
-
}
|
|
436
|
-
var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
437
|
-
var TAG_REGEX = /<(\/?)([A-Za-z][\w-]*)(?:\s[^>]*?)?(\/?)>/g;
|
|
438
|
-
var parseString = (input) => {
|
|
439
|
-
var _a;
|
|
440
|
-
const root = { type: "tag", name: "root", children: [] };
|
|
441
|
-
const stack = [root];
|
|
442
|
-
let lastIndex = 0;
|
|
443
|
-
for (const match of input.matchAll(TAG_REGEX)) {
|
|
444
|
-
const fullMatch = match[0];
|
|
445
|
-
const isClosing = match[1] === "/";
|
|
446
|
-
const tagName = match[2];
|
|
447
|
-
const isSelfClosing = match[3] === "/" || fullMatch.endsWith("/>");
|
|
448
|
-
const index = (_a = match.index) != null ? _a : 0;
|
|
449
|
-
if (index > lastIndex) {
|
|
450
|
-
const text = input.slice(lastIndex, index);
|
|
451
|
-
if (text) stack[stack.length - 1].children.push({ type: "text", content: text });
|
|
452
|
-
}
|
|
453
|
-
if (isClosing) {
|
|
454
|
-
if (stack.length > 1 && stack[stack.length - 1].name === tagName) {
|
|
455
|
-
stack.pop();
|
|
456
|
-
} else {
|
|
457
|
-
stack[stack.length - 1].children.push({ type: "text", content: fullMatch });
|
|
458
|
-
}
|
|
459
|
-
} else if (isSelfClosing) {
|
|
460
|
-
stack[stack.length - 1].children.push({ type: "tag", name: tagName, children: [] });
|
|
461
|
-
} else {
|
|
462
|
-
const node = { type: "tag", name: tagName, children: [] };
|
|
463
|
-
stack[stack.length - 1].children.push(node);
|
|
464
|
-
stack.push(node);
|
|
465
|
-
}
|
|
466
|
-
lastIndex = index + fullMatch.length;
|
|
467
|
-
}
|
|
468
|
-
if (lastIndex < input.length) {
|
|
469
|
-
stack[stack.length - 1].children.push({ type: "text", content: input.slice(lastIndex) });
|
|
470
|
-
}
|
|
471
|
-
return root.children;
|
|
472
|
-
};
|
|
473
|
-
var renderAST = (nodes, keyPrefix, components, placeholderToElement) => {
|
|
474
|
-
return nodes.map((node, index) => {
|
|
475
|
-
const key = `${keyPrefix}-${index}`;
|
|
476
|
-
if (node.type === "text") {
|
|
477
|
-
const placeholders = Object.keys(placeholderToElement);
|
|
478
|
-
if (placeholders.length === 0) return node.content;
|
|
479
|
-
const pattern = new RegExp(`(${placeholders.map(escapeRegExp).join("|")})`, "g");
|
|
480
|
-
const parts = node.content.split(pattern);
|
|
481
|
-
if (parts.length === 1) return node.content;
|
|
482
|
-
return /* @__PURE__ */ React.createElement(Fragment, { key }, parts.map((part, i) => {
|
|
483
|
-
if (placeholderToElement[part]) {
|
|
484
|
-
return cloneElement(placeholderToElement[part], { key: `${key}-${i}` });
|
|
485
|
-
}
|
|
486
|
-
return part;
|
|
487
|
-
}));
|
|
488
|
-
} else if (node.type === "tag") {
|
|
489
|
-
const Component = components == null ? void 0 : components[node.name];
|
|
490
|
-
const children = renderAST(node.children, key, components, placeholderToElement);
|
|
491
|
-
if (isValidElement(Component)) {
|
|
492
|
-
return cloneElement(Component, { key }, children.length > 0 ? children : void 0);
|
|
493
|
-
}
|
|
494
|
-
return /* @__PURE__ */ React.createElement(Fragment, { key }, children);
|
|
495
|
-
}
|
|
496
|
-
return null;
|
|
497
|
-
});
|
|
498
|
-
};
|
|
499
|
-
var Trans = ({ i18nKey, values, components, ...props }) => {
|
|
500
|
-
const { t: t2 } = useI18n();
|
|
501
|
-
const { stringValues, placeholderToElement } = useMemo(() => {
|
|
502
|
-
const sv = {};
|
|
503
|
-
const pte = {};
|
|
504
|
-
if (values) {
|
|
505
|
-
for (const key of Object.keys(values)) {
|
|
506
|
-
const value = values[key];
|
|
507
|
-
if (isValidElement(value)) {
|
|
508
|
-
const placeholder = `__ELEMENT_${key}__`;
|
|
509
|
-
pte[placeholder] = value;
|
|
510
|
-
sv[key] = placeholder;
|
|
511
|
-
} else {
|
|
512
|
-
sv[key] = value == null ? "" : String(value);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
return { stringValues: sv, placeholderToElement: pte };
|
|
517
|
-
}, [values]);
|
|
518
|
-
const translatedText = t2(i18nKey, stringValues);
|
|
519
|
-
const ast = useMemo(() => parseString(translatedText), [translatedText]);
|
|
520
|
-
const children = useMemo(
|
|
521
|
-
() => renderAST(ast, "trans", components, placeholderToElement),
|
|
522
|
-
[ast, components, placeholderToElement]
|
|
523
|
-
);
|
|
524
|
-
return /* @__PURE__ */ React.createElement(Text, { ...props }, children);
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
// src/index.ts
|
|
528
|
-
var initI18n = (translations, options) => i18nService.init(translations, options);
|
|
529
|
-
var loadTranslations = (translations) => i18nService.loadTranslations(translations);
|
|
530
|
-
var setLocale = (locale) => i18nService.setLocale(locale);
|
|
531
|
-
var getLocale = () => i18nService.getLocale();
|
|
532
|
-
var t = (scope, options) => i18nService.t(scope, options);
|
|
533
|
-
var formatNumber = (n, options) => i18nService.formatNumber(n, options);
|
|
534
|
-
var formatCurrency = (n, currency, options) => i18nService.formatCurrency(n, currency, options);
|
|
535
|
-
var formatDate = (date, options) => i18nService.formatDate(date, options);
|
|
536
|
-
var subscribe = (listener) => i18nService.subscribe(listener);
|
|
537
|
-
var isRTL = () => i18nService.isRTL();
|
|
538
|
-
var resetToSystem = () => i18nService.resetToSystem();
|
|
539
|
-
var readyI18n = () => i18nService.ready();
|
|
540
|
-
var isI18nReady = () => i18nService.isReady();
|
|
541
|
-
var index_default = i18nService;
|
|
542
|
-
|
|
543
|
-
export { I18nContext, I18nProvider, Trans, index_default as default, formatCurrency, formatDate, formatNumber, getLocale, initI18n, isI18nReady, isRTL, loadTranslations, readyI18n, resetToSystem, setLocale, subscribe, t, useI18n, withI18n };
|
|
1
|
+
import {I18n}from'i18n-js';import*as R from'react-native-localize';import {I18nManager,AppState,Text}from'react-native';import T,{createContext,useState,useEffect,useMemo,useContext,isValidElement,Fragment,cloneElement}from'react';var E=Object.defineProperty;var M=(n,t,e)=>t in n?E(n,t,{enumerable:true,configurable:true,writable:true,value:e}):n[t]=e;var h=(n,t,e)=>M(n,typeof t!="symbol"?t+"":t,e);var K=new Set(["ar","fa","he","iw","ur","ps","dv","ku","ug","yi","sd"]),_=new Set(["Arab","Hebr","Thaa","Syrc","Nkoo","Adlm"]),d=n=>n.replace(/_/g,"-"),x=class{constructor(){h(this,"i",new I18n);h(this,"ls",new Set);h(this,"cb");h(this,"fb");h(this,"mb","key");h(this,"mk");h(this,"ef",true);h(this,"fs",true);h(this,"sr","system");h(this,"ok",false);h(this,"v",0);h(this,"rp");h(this,"rr");h(this,"nc",new Map);h(this,"dc",new Map);h(this,"lc",new Map);this.i.enableFallback=false,this.rp=new Promise(t=>{this.rr=t;});}init(t,e={}){var a;let{defaultLocale:r="en",enableFallback:s=true,onLocaleChange:l,fallbackLocales:o,followSystem:p=true,missingBehavior:f="key",onMissingKey:m}=e;this.i.store(this.nt(t)),this.i.defaultLocale=this.i.locale=d(r),this.ef=s,this.cb=l,this.fb=o,this.fs=p,this.sr="system",this.mb=f,this.mk=m,this.cl(),p&&this.updateLocale(),this.em("translations"),this.ok||(this.ok=true,(a=this.rr)==null||a.call(this),this.rr=void 0);}loadTranslations(t){this.i.store(this.nt(t)),this.em("translations");}updateLocale(){var t,e;if(!(!this.fs||this.sr==="user"))try{let r=(e=(t=R.getLocales())==null?void 0:t[0])==null?void 0:e.languageTag;r&&(this.sr="system",this.ap(r));}catch{}}setLocale(t){this.sr="user",this.ap(t);}getLocale(){return this.i.locale}resetToSystem(){this.sr="system",this.updateLocale();}t(t,e){var l,o,p;if(typeof t!="string")return (l=this.nr(this.i.t(t,e)))!=null?l:"";let r=this.i.locale,s=this.ef?this.gc(r):[r];for(let f of s)if(this.ht(f,t)){let m=this.nr(this.ta(f,t,e));if(m!==void 0)return m}if((o=this.mk)==null||o.call(this,t,r),e&&Object.prototype.hasOwnProperty.call(e,"defaultValue"))return (p=this.nr(this.ta(r,t,e)))!=null?p:"";if(this.mb==="empty")return "";if(this.mb==="throw")throw new Error(`Missing translation: ${r}.${t}`);return t}formatNumber(t,e){if(typeof Intl=="undefined"||!Intl.NumberFormat)return String(t);try{return this.gn(this.i.locale,e).format(t)}catch{return String(t)}}formatCurrency(t,e,r){if(typeof Intl=="undefined"||!Intl.NumberFormat)return `${t} ${e}`;try{return this.gn(this.i.locale,{...r,style:"currency",currency:e}).format(t)}catch{return `${t} ${e}`}}formatDate(t,e){let r=typeof t=="number"?new Date(t):t;if(Number.isNaN(r.getTime()))return "";if(typeof Intl=="undefined"||!Intl.DateTimeFormat)return r.toISOString();try{return this.gd(this.i.locale,e).format(r)}catch{return r.toISOString()}}subscribe(t){return this.ls.add(t),()=>{this.ls.delete(t);}}isRTL(){return I18nManager.isRTL}ready(){return this.ok?Promise.resolve():this.rp}isReady(){return this.ok}cl(){this.nc.clear(),this.dc.clear(),this.lc.clear();}gn(t,e){let r=t+(e?JSON.stringify(e):""),s=this.nc.get(r);return s||(s=new Intl.NumberFormat(t,e),this.nc.set(r,s)),s}gd(t,e){let r=t+(e?JSON.stringify(e):""),s=this.dc.get(r);return s||(s=new Intl.DateTimeFormat(t,e),this.dc.set(r,s)),s}em(t){this.v++,this.ls.forEach(e=>e(this.i.locale,{type:t,version:this.v}));}ap(t){var r;let e=d(t);this.i.locale!==e&&(this.i.locale=e,this.cl(),this.hr(e),this.em("locale"),(r=this.cb)==null||r.call(this,e));}hr(t){var p;let e=d(t).split("-").filter(Boolean),r=(p=e[0])==null?void 0:p.toLowerCase();if(!r)return;let s=e.find(f=>/^[A-Za-z]{4}$/.test(f)),l=s?_.has(s[0].toUpperCase()+s.slice(1).toLowerCase()):K.has(r),o=I18nManager;typeof(o==null?void 0:o.allowRTL)=="function"&&typeof(o==null?void 0:o.forceRTL)=="function"&&I18nManager.isRTL!==l&&(o.allowRTL(l),o.forceRTL(l));}ta(t,e,r){let s=this.i.locale;this.i.locale=t;try{return this.i.t(e,r)}finally{this.i.locale=s;}}dg(t,e){let r=d(e);t.push(r);let s=r.split("-").filter(Boolean);for(let l=s.length-1;l>=1;l--)t.push(s.slice(0,l).join("-"));}gc(t){let e=this.lc.get(t);if(e)return e;let r=[];this.dg(r,t);let s=this.fb?typeof this.fb=="function"?this.fb(d(t)):this.fb:[];for(let p of s)this.dg(r,p);this.i.defaultLocale&&this.dg(r,this.i.defaultLocale);let l=new Set,o=r.filter(p=>p&&!l.has(p)&&(l.add(p),true));return this.lc.set(t,o),o}ht(t,e){var l;let r=(l=this.i.translations)==null?void 0:l[t];if(!r||typeof r!="object")return false;if(!e.includes(".")&&Object.prototype.hasOwnProperty.call(r,e))return r[e]!=null;let s=r;for(let o of e.split(".").filter(Boolean)){if(!s||typeof s!="object"||!(o in s))return false;s=s[o];}return s!=null}nr(t){return typeof t=="string"?t:typeof t=="number"?String(t):void 0}nt(t){let e={};for(let r of Object.keys(t))e[d(r)]=t[r];return e}},i=new x;var N={locale:i.getLocale(),setLocale:n=>i.setLocale(n),t:(n,t)=>i.t(n,t),formatNumber:(n,t)=>i.formatNumber(n,t),formatCurrency:(n,t,e)=>i.formatCurrency(n,t,e),formatDate:(n,t)=>i.formatDate(n,t)},b=createContext(N),V=({children:n,readyGate:t,fallback:e})=>{let[r,s]=useState(i.getLocale()),[l,o]=useState(0),[p,f]=useState(i.isReady());useEffect(()=>i.subscribe((a,c)=>{s(a),o(u=>{var g;return (g=c==null?void 0:c.version)!=null?g:u+1});}),[]),useEffect(()=>{var u,g;let a=y=>{y==="active"&&i.updateLocale();},c=(g=(u=AppState)==null?void 0:u.addEventListener)==null?void 0:g.call(u,"change",a);return ()=>{var y,C;typeof(c==null?void 0:c.remove)=="function"?c.remove():(C=(y=AppState)==null?void 0:y.removeEventListener)==null||C.call(y,"change",a);}},[]),useEffect(()=>{if(!t)return;let a=false;return i.ready().then(()=>{a||f(true);}),()=>{a=true;}},[t]);let m=useMemo(()=>({locale:r,setLocale:a=>i.setLocale(a),t:(a,c)=>i.t(a,c),formatNumber:(a,c)=>i.formatNumber(a,c),formatCurrency:(a,c,u)=>i.formatCurrency(a,c,u),formatDate:(a,c)=>i.formatDate(a,c)}),[r,l]);return t&&!p?T.createElement(T.Fragment,null,e!=null?e:null):T.createElement(b.Provider,{value:m},n)};function w(){let n=useContext(b);if(n===N)throw new Error("useI18n must be used within an I18nProvider");return useMemo(()=>({...n,t:((t,e)=>n.t(t,e))}),[n])}function J(n){return t=>T.createElement(b.Consumer,null,e=>{if(e===N)throw new Error("withI18n must be used within an I18nProvider");return T.createElement(n,{...t,...e})})}var H=/<(\/?)([A-Za-z][\w-]*)(?:\s[^>]*?)?(\/?)>/g,U=n=>n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),W=n=>{var s;let t={t:"g",n:"",ch:[]},e=[t],r=0;for(let l of n.matchAll(H)){let[o,p,f,m]=l,a=(s=l.index)!=null?s:0;if(a>r){let c=n.slice(r,a);c&&e[e.length-1].ch.push({t:"x",c});}if(p==="/")e.length>1&&e[e.length-1].n===f?e.pop():e[e.length-1].ch.push({t:"x",c:o});else if(m==="/"||o.endsWith("/>"))e[e.length-1].ch.push({t:"g",n:f,ch:[]});else {let c={t:"g",n:f,ch:[]};e[e.length-1].ch.push(c),e.push(c);}r=a+o.length;}return r<n.length&&e[e.length-1].ch.push({t:"x",c:n.slice(r)}),t.ch},j=(n,t,e,r={})=>n.map((s,l)=>{let o=`${t}-${l}`;if(s.t==="x"){let m=Object.keys(r);if(!m.length)return s.c;let a=s.c.split(new RegExp(`(${m.map(U).join("|")})`,"g"));return a.length===1?s.c:T.createElement(Fragment,{key:o},a.map((c,u)=>r[c]?cloneElement(r[c],{key:`${o}-${u}`}):c))}let p=e==null?void 0:e[s.n],f=j(s.ch,o,e,r);return isValidElement(p)?cloneElement(p,{key:o},f.length?f:void 0):T.createElement(Fragment,{key:o},f)}),q=({i18nKey:n,values:t,components:e,...r})=>{let{t:s}=w(),{sv:l,pe:o}=useMemo(()=>{let a={},c={};if(t)for(let u of Object.keys(t)){let g=t[u];if(isValidElement(g)){let y=`__E_${u}__`;c[y]=g,a[u]=y;}else a[u]=g==null?"":String(g);}return {sv:a,pe:c}},[t]),p=s(n,l),f=useMemo(()=>W(p),[p]),m=useMemo(()=>j(f,"tr",e,o),[f,e,o]);return T.createElement(Text,{...r},m)};var bt=(n,t)=>i.init(n,t),vt=n=>i.loadTranslations(n),xt=n=>i.setLocale(n),It=()=>i.getLocale(),Lt=(n,t)=>i.t(n,t),Nt=(n,t)=>i.formatNumber(n,t),wt=(n,t,e)=>i.formatCurrency(n,t,e),Ot=(n,t)=>i.formatDate(n,t),kt=n=>i.subscribe(n),Ct=()=>i.isRTL(),Rt=()=>i.resetToSystem(),St=()=>i.ready(),Ft=()=>i.isReady(),Pt=i;export{b as I18nContext,V as I18nProvider,q as Trans,Pt as default,wt as formatCurrency,Ot as formatDate,Nt as formatNumber,It as getLocale,bt as initI18n,Ft as isI18nReady,Ct as isRTL,vt as loadTranslations,St as readyI18n,Rt as resetToSystem,xt as setLocale,kt as subscribe,Lt as t,w as useI18n,J as withI18n};
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-i18njs",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "一个轻量级、类型安全、零心智负担的 React Native 国际化解决方案。",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://gitee.com/ws18250840411/react-native-i18njs.git"
|
|
8
8
|
},
|
|
9
9
|
"license": "ISC",
|
|
10
|
-
"author": "
|
|
10
|
+
"author": "wangwenshan",
|
|
11
11
|
"keywords": [
|
|
12
12
|
"react-native",
|
|
13
13
|
"i18n",
|