@verbumia/react-i18next 0.7.1 → 0.9.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 +110 -5
- package/dist/index.cjs +89 -199
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -35
- package/dist/index.d.ts +66 -35
- package/dist/index.js +88 -199
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -50,111 +50,6 @@ var logTransport = (batch) => {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
// src/live.ts
|
|
54
|
-
var LiveClient = class {
|
|
55
|
-
constructor(cfg) {
|
|
56
|
-
this.cfg = cfg;
|
|
57
|
-
}
|
|
58
|
-
cfg;
|
|
59
|
-
_ws = null;
|
|
60
|
-
_id = 0;
|
|
61
|
-
_backoffMs = 1e3;
|
|
62
|
-
_disposed = false;
|
|
63
|
-
_connectAcked = false;
|
|
64
|
-
/** Open the socket and try to subscribe. Idempotent — calling twice is a no-op. */
|
|
65
|
-
async connect() {
|
|
66
|
-
if (this._ws) return;
|
|
67
|
-
if (this._disposed) return;
|
|
68
|
-
this.cfg.onStatus?.("connecting");
|
|
69
|
-
const token = this.cfg.refreshToken ? await this.cfg.refreshToken() : this.cfg.token;
|
|
70
|
-
let url = this.cfg.url;
|
|
71
|
-
if (!url.includes("/connection/websocket")) {
|
|
72
|
-
url = url.replace(/\/+$/, "") + "/connection/websocket";
|
|
73
|
-
}
|
|
74
|
-
const ws = new WebSocket(url);
|
|
75
|
-
this._ws = ws;
|
|
76
|
-
this._connectAcked = false;
|
|
77
|
-
ws.onopen = () => {
|
|
78
|
-
this._send({ id: ++this._id, connect: { token } });
|
|
79
|
-
};
|
|
80
|
-
ws.onmessage = (evt) => this._onFrame(evt.data);
|
|
81
|
-
ws.onclose = () => this._onClose();
|
|
82
|
-
ws.onerror = () => {
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
dispose() {
|
|
86
|
-
this._disposed = true;
|
|
87
|
-
if (this._ws) {
|
|
88
|
-
try {
|
|
89
|
-
this._ws.close();
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
this._ws = null;
|
|
93
|
-
}
|
|
94
|
-
this.cfg.onStatus?.("disconnected");
|
|
95
|
-
}
|
|
96
|
-
// ---- internals ----
|
|
97
|
-
_send(msg) {
|
|
98
|
-
if (!this._ws || this._ws.readyState !== WebSocket.OPEN) return;
|
|
99
|
-
try {
|
|
100
|
-
this._ws.send(JSON.stringify(msg));
|
|
101
|
-
} catch {
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
_onFrame(raw) {
|
|
105
|
-
let parsed;
|
|
106
|
-
try {
|
|
107
|
-
parsed = JSON.parse(raw);
|
|
108
|
-
} catch {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (!parsed || typeof parsed !== "object") return;
|
|
112
|
-
if (Object.keys(parsed).length === 0) {
|
|
113
|
-
this._send({});
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (parsed.connect && !this._connectAcked) {
|
|
117
|
-
this._connectAcked = true;
|
|
118
|
-
this.cfg.onStatus?.("connected");
|
|
119
|
-
this._backoffMs = 1e3;
|
|
120
|
-
this._send({
|
|
121
|
-
id: ++this._id,
|
|
122
|
-
subscribe: { channel: this.cfg.channel }
|
|
123
|
-
});
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const push = parsed.push;
|
|
127
|
-
if (push && push.channel === this.cfg.channel && push.pub) {
|
|
128
|
-
this.cfg.onMessage(push.pub.data);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
_onClose() {
|
|
132
|
-
this._ws = null;
|
|
133
|
-
this._connectAcked = false;
|
|
134
|
-
this.cfg.onStatus?.("disconnected");
|
|
135
|
-
if (this._disposed) return;
|
|
136
|
-
const delay = this._backoffMs;
|
|
137
|
-
this._backoffMs = Math.min(this._backoffMs * 2, 3e4);
|
|
138
|
-
setTimeout(() => {
|
|
139
|
-
if (!this._disposed) void this.connect();
|
|
140
|
-
}, delay);
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
async function fetchCentrifugoToken(endpoint, projectUuid, authToken, fetchImpl = fetch) {
|
|
144
|
-
const r = await fetchImpl(endpoint, {
|
|
145
|
-
method: "POST",
|
|
146
|
-
headers: {
|
|
147
|
-
"Content-Type": "application/json",
|
|
148
|
-
Authorization: `ApiKey ${authToken}`
|
|
149
|
-
},
|
|
150
|
-
body: JSON.stringify({ project_uuid: projectUuid })
|
|
151
|
-
});
|
|
152
|
-
if (!r.ok) {
|
|
153
|
-
throw new Error(`centrifugo-token endpoint ${r.status}: ${await r.text()}`);
|
|
154
|
-
}
|
|
155
|
-
return await r.json();
|
|
156
|
-
}
|
|
157
|
-
|
|
158
53
|
// src/key-registry.ts
|
|
159
54
|
var GLOBAL = "__verbumia_key_registry__";
|
|
160
55
|
var SEP = "\0";
|
|
@@ -292,10 +187,10 @@ var VerbumiaI18n = class {
|
|
|
292
187
|
fallbackLng;
|
|
293
188
|
missingEvents = [];
|
|
294
189
|
_bundles = /* @__PURE__ */ new Map();
|
|
295
|
-
// `${locale}/${ns}` -> tree
|
|
190
|
+
// `${version}/${locale}/${ns}` -> tree
|
|
296
191
|
_attempted = /* @__PURE__ */ new Set();
|
|
297
|
-
// `${locale}/${ns}` keys we've fetched
|
|
298
|
-
// Tighter gate than `_attempted`: this set only contains (locale, ns)
|
|
192
|
+
// `${version}/${locale}/${ns}` keys we've fetched
|
|
193
|
+
// Tighter gate than `_attempted`: this set only contains (version, locale, ns)
|
|
299
194
|
// pairs whose CDN response was 200 with at least one top-level key. An
|
|
300
195
|
// empty bundle (404 → {} OR 200 → {}) is treated as "no data yet";
|
|
301
196
|
// calling t() against a key in such a bundle does NOT fire reportMissing.
|
|
@@ -310,13 +205,20 @@ var VerbumiaI18n = class {
|
|
|
310
205
|
// dedup `${locale}/${ns}/${key}` per-flush
|
|
311
206
|
_timer = null;
|
|
312
207
|
_listeners = /* @__PURE__ */ new Set();
|
|
313
|
-
_live = null;
|
|
314
208
|
// Stable snapshot reference for useSyncExternalStore. Returning a fresh
|
|
315
209
|
// object on each getSnapshot call would loop React forever — we rebuild
|
|
316
210
|
// it ONLY in _notify (when state actually changed) and return the cached
|
|
317
211
|
// reference between notifications.
|
|
318
212
|
_snapshot;
|
|
319
213
|
constructor(config) {
|
|
214
|
+
const removedRealtimeKeys = Object.keys(config).filter(
|
|
215
|
+
(k) => k === "liveUpdates" || k.startsWith("centrifugo")
|
|
216
|
+
);
|
|
217
|
+
if (removedRealtimeKeys.length > 0) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`@verbumia/react-i18next: ${removedRealtimeKeys.join(", ")} ${removedRealtimeKeys.length > 1 ? "were" : "was"} removed in 0.9.0 \u2014 realtime is now the @verbumia/realtime plugin. Remove them and pass \`plugins: [verbumiaRealtime({ wsUrl })]\` to <VerbumiaProvider> instead.`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
320
222
|
this.locale = config.defaultLocale;
|
|
321
223
|
this.fallbackLng = config.fallbackLng;
|
|
322
224
|
this._config = {
|
|
@@ -325,14 +227,11 @@ var VerbumiaI18n = class {
|
|
|
325
227
|
missingHandler: config.missingHandler ?? "send",
|
|
326
228
|
token: config.token,
|
|
327
229
|
projectUuid: config.projectUuid,
|
|
328
|
-
namespaces: config.namespaces?.length ? config.namespaces : ["common"],
|
|
230
|
+
namespaces: config.namespaces?.length ? config.namespaces : config.defaultNS ? [config.defaultNS] : ["common"],
|
|
329
231
|
flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_MS,
|
|
330
232
|
flushBatchSize: config.flushBatchSize ?? DEFAULT_BATCH,
|
|
331
233
|
missingEventsBufferSize: config.missingEventsBufferSize ?? DEFAULT_BUFFER,
|
|
332
|
-
|
|
333
|
-
liveUpdates: !!config.liveUpdates,
|
|
334
|
-
centrifugoTokenEndpoint: config.centrifugoTokenEndpoint ?? `${(config.apiBase ?? DEFAULT_API_BASE).replace(/\/+$/, "")}/v1/auth/centrifugo-token`,
|
|
335
|
-
centrifugoWsUrl: config.centrifugoWsUrl ?? "",
|
|
234
|
+
version: config.version ?? config.versionSlug ?? DEFAULT_VERSION_SLUG,
|
|
336
235
|
env: config.env ?? "prod"
|
|
337
236
|
};
|
|
338
237
|
this._transport = config.transport ?? (this._config.missingHandler === "log" ? logTransport : defaultTransport({
|
|
@@ -354,11 +253,21 @@ var VerbumiaI18n = class {
|
|
|
354
253
|
return {
|
|
355
254
|
ready: this.ready,
|
|
356
255
|
locale: this.locale,
|
|
256
|
+
language: this.locale,
|
|
357
257
|
setLocale: this.setLocale,
|
|
258
|
+
changeLanguage: this.changeLanguage,
|
|
259
|
+
t: this.t,
|
|
358
260
|
missingEvents: this.missingEvents,
|
|
359
|
-
flushMissing: this.flushMissing
|
|
261
|
+
flushMissing: this.flushMissing,
|
|
262
|
+
reload: this.reload
|
|
360
263
|
};
|
|
361
264
|
}
|
|
265
|
+
/** Bundle cache-key builder. Includes `version` so providers with
|
|
266
|
+
* different `version` values never share cached bundles. The segments
|
|
267
|
+
* (version/locale/ns) are slugs and never contain '/'. */
|
|
268
|
+
_bundleKey(locale, ns) {
|
|
269
|
+
return `${this._config.version}/${locale}/${ns}`;
|
|
270
|
+
}
|
|
362
271
|
_notify() {
|
|
363
272
|
this._snapshot = this._buildSnapshot();
|
|
364
273
|
for (const l of this._listeners) l();
|
|
@@ -381,9 +290,6 @@ var VerbumiaI18n = class {
|
|
|
381
290
|
);
|
|
382
291
|
this.ready = true;
|
|
383
292
|
this._startTimer();
|
|
384
|
-
if (this._config.liveUpdates && this._config.env === "dev") {
|
|
385
|
-
this._startLive(fetchImpl);
|
|
386
|
-
}
|
|
387
293
|
this._notify();
|
|
388
294
|
}
|
|
389
295
|
setLocale = async (next) => {
|
|
@@ -397,102 +303,64 @@ var VerbumiaI18n = class {
|
|
|
397
303
|
this.ready = true;
|
|
398
304
|
this._notify();
|
|
399
305
|
};
|
|
306
|
+
/** Alias of {@link setLocale} for react-i18next compatibility. */
|
|
307
|
+
changeLanguage = (next) => this.setLocale(next);
|
|
308
|
+
/** Alias of {@link locale} for react-i18next compatibility. */
|
|
309
|
+
get language() {
|
|
310
|
+
return this.locale;
|
|
311
|
+
}
|
|
400
312
|
stop() {
|
|
401
313
|
keyRegistry.detach();
|
|
402
314
|
if (this._timer) {
|
|
403
315
|
clearInterval(this._timer);
|
|
404
316
|
this._timer = null;
|
|
405
317
|
}
|
|
406
|
-
if (this._live) {
|
|
407
|
-
this._live.dispose();
|
|
408
|
-
this._live = null;
|
|
409
|
-
}
|
|
410
318
|
}
|
|
411
319
|
/**
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
320
|
+
* Bust-refetch already-loaded bundles and re-render once. Generic
|
|
321
|
+
* replacement for the old realtime-only refetch: iterate the
|
|
322
|
+
* `_attempted` cache keys (`${version}/${locale}/${ns}`), optionally
|
|
323
|
+
* filtered by `opts.locale` / `opts.namespace`, and re-pull each one
|
|
324
|
+
* with `{ bust: true }` so the mutable CDN `latest/` alias bypasses the
|
|
325
|
+
* HTTP cache. After all settle, `_notify()` once so React re-renders.
|
|
326
|
+
*
|
|
327
|
+
* Used by `@verbumia/realtime` on a `translations_published` push and as
|
|
328
|
+
* a manual refresh hook. If nothing matches, returns without notifying.
|
|
416
329
|
*/
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
330
|
+
reload = async (opts = {}) => {
|
|
331
|
+
const targets = [];
|
|
332
|
+
for (const key of this._attempted) {
|
|
333
|
+
const parts = key.split("/");
|
|
334
|
+
const locale = parts[1];
|
|
335
|
+
const ns = parts[2];
|
|
336
|
+
if (!locale || !ns) continue;
|
|
337
|
+
if (opts.locale && opts.locale !== locale) continue;
|
|
338
|
+
if (opts.namespace && opts.namespace !== ns) continue;
|
|
339
|
+
targets.push({ locale, ns });
|
|
426
340
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
projectUuid,
|
|
434
|
-
apiToken,
|
|
435
|
-
fetchImpl
|
|
436
|
-
);
|
|
437
|
-
return token;
|
|
438
|
-
};
|
|
439
|
-
void (async () => {
|
|
440
|
-
let channel;
|
|
441
|
-
let token;
|
|
442
|
-
try {
|
|
443
|
-
const minted = await fetchCentrifugoToken(
|
|
444
|
-
tokenEndpoint,
|
|
445
|
-
projectUuid,
|
|
446
|
-
apiToken,
|
|
447
|
-
fetchImpl
|
|
448
|
-
);
|
|
449
|
-
channel = minted.channel;
|
|
450
|
-
token = minted.token;
|
|
451
|
-
} catch (err) {
|
|
452
|
-
if (typeof console !== "undefined") {
|
|
453
|
-
console.warn("@verbumia/react-i18next: live token mint failed", err);
|
|
454
|
-
}
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
this._live = new LiveClient({
|
|
458
|
-
url: wsUrl,
|
|
459
|
-
token,
|
|
460
|
-
channel,
|
|
461
|
-
refreshToken,
|
|
462
|
-
onMessage: (data) => this._onLiveMessage(data, fetchImpl)
|
|
463
|
-
});
|
|
464
|
-
void this._live.connect();
|
|
465
|
-
})();
|
|
466
|
-
}
|
|
467
|
-
_onLiveMessage(data, fetchImpl) {
|
|
468
|
-
if (!data || typeof data !== "object") return;
|
|
469
|
-
const d = data;
|
|
470
|
-
if (d.event !== "translations_published") return;
|
|
471
|
-
const lang = d.language_code;
|
|
472
|
-
const ns = d.namespace_slug;
|
|
473
|
-
if (!lang || !ns) return;
|
|
474
|
-
const cacheKey = `${lang}/${ns}`;
|
|
475
|
-
if (!this._attempted.has(cacheKey)) return;
|
|
476
|
-
void this._loadBundle(lang, ns, fetchImpl, { bust: true }).then(() => {
|
|
477
|
-
this._notify();
|
|
478
|
-
});
|
|
479
|
-
}
|
|
341
|
+
if (targets.length === 0) return;
|
|
342
|
+
await Promise.all(
|
|
343
|
+
targets.map((t) => this._loadBundle(t.locale, t.ns, fetch, { bust: true }))
|
|
344
|
+
);
|
|
345
|
+
this._notify();
|
|
346
|
+
};
|
|
480
347
|
// ---- Translation ----
|
|
481
|
-
t = (key,
|
|
348
|
+
t = (key, optionsOrDefault, maybeOptions) => {
|
|
349
|
+
const options = typeof optionsOrDefault === "string" ? { ...maybeOptions ?? {}, defaultValue: optionsOrDefault } : optionsOrDefault;
|
|
482
350
|
const namespace = this._splitNamespace(key);
|
|
483
351
|
const bareKey = namespace.bareKey;
|
|
484
352
|
const ns = namespace.ns;
|
|
485
|
-
const fromActive = resolve(this._bundles.get(
|
|
353
|
+
const fromActive = resolve(this._bundles.get(this._bundleKey(this.locale, ns)), bareKey);
|
|
486
354
|
if (fromActive != null) {
|
|
487
355
|
return this._render(fromActive, this.locale, options);
|
|
488
356
|
}
|
|
489
357
|
if (this.fallbackLng && this.fallbackLng !== this.locale) {
|
|
490
|
-
const fb = resolve(this._bundles.get(
|
|
358
|
+
const fb = resolve(this._bundles.get(this._bundleKey(this.fallbackLng, ns)), bareKey);
|
|
491
359
|
if (fb != null) {
|
|
492
360
|
return this._render(fb, this.fallbackLng, options);
|
|
493
361
|
}
|
|
494
362
|
}
|
|
495
|
-
if (this.ready && this._attempted.has(
|
|
363
|
+
if (this.ready && this._attempted.has(this._bundleKey(this.locale, ns)) && this._hasContent.has(this._bundleKey(this.locale, ns))) {
|
|
496
364
|
this._reportMissing({
|
|
497
365
|
key: bareKey,
|
|
498
366
|
namespace: ns,
|
|
@@ -539,13 +407,13 @@ var VerbumiaI18n = class {
|
|
|
539
407
|
return { ns: this._config.namespaces[0], bareKey: key };
|
|
540
408
|
}
|
|
541
409
|
async _loadBundle(locale, ns, fetchImpl = fetch, opts = {}) {
|
|
542
|
-
const cacheKey =
|
|
410
|
+
const cacheKey = this._bundleKey(locale, ns);
|
|
543
411
|
let url;
|
|
544
412
|
let init;
|
|
545
413
|
if (this._config.env === "dev") {
|
|
546
414
|
const params = new URLSearchParams({ language: locale, namespace: ns });
|
|
547
|
-
if (this._config.
|
|
548
|
-
params.set("version_slug", this._config.
|
|
415
|
+
if (this._config.version && this._config.version !== "main") {
|
|
416
|
+
params.set("version_slug", this._config.version);
|
|
549
417
|
}
|
|
550
418
|
url = `${this._config.apiBase.replace(/\/+$/, "")}/v1/projects/${this._config.projectUuid}/translations/runtime?${params.toString()}`;
|
|
551
419
|
init = {
|
|
@@ -554,7 +422,7 @@ var VerbumiaI18n = class {
|
|
|
554
422
|
credentials: "omit"
|
|
555
423
|
};
|
|
556
424
|
} else {
|
|
557
|
-
url = `${this._config.cdnBase.replace(/\/+$/, "")}/p/${this._config.projectUuid}/${this._config.
|
|
425
|
+
url = `${this._config.cdnBase.replace(/\/+$/, "")}/p/${this._config.projectUuid}/${this._config.version}/latest/${locale}/${ns}.json`;
|
|
558
426
|
init = { method: "GET", credentials: "omit" };
|
|
559
427
|
}
|
|
560
428
|
if (opts.bust) {
|
|
@@ -609,7 +477,7 @@ var VerbumiaI18n = class {
|
|
|
609
477
|
return options.defaultValue;
|
|
610
478
|
}
|
|
611
479
|
if (this.fallbackLng && this.fallbackLng !== this.locale) {
|
|
612
|
-
const fb = resolve(this._bundles.get(
|
|
480
|
+
const fb = resolve(this._bundles.get(this._bundleKey(this.fallbackLng, ns)), bareKey);
|
|
613
481
|
if (typeof fb === "string") {
|
|
614
482
|
return fb;
|
|
615
483
|
}
|
|
@@ -633,6 +501,23 @@ var VerbumiaI18n = class {
|
|
|
633
501
|
}
|
|
634
502
|
};
|
|
635
503
|
|
|
504
|
+
// src/singleton.ts
|
|
505
|
+
var _active = null;
|
|
506
|
+
function _setActiveInstance(instance) {
|
|
507
|
+
_active = instance;
|
|
508
|
+
}
|
|
509
|
+
function _clearActiveInstance(instance) {
|
|
510
|
+
if (_active === instance) _active = null;
|
|
511
|
+
}
|
|
512
|
+
function getI18n() {
|
|
513
|
+
if (!_active) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
"@verbumia/react-i18next: getI18n() was called before <VerbumiaProvider> mounted (no active i18n instance)."
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
return _active;
|
|
519
|
+
}
|
|
520
|
+
|
|
636
521
|
// src/provider.tsx
|
|
637
522
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
638
523
|
var VerbumiaContext = createContext(null);
|
|
@@ -642,11 +527,13 @@ function VerbumiaProvider({
|
|
|
642
527
|
}) {
|
|
643
528
|
const i18n = useMemo(() => new VerbumiaI18n(config), []);
|
|
644
529
|
useEffect(() => {
|
|
530
|
+
_setActiveInstance(i18n);
|
|
645
531
|
void i18n.start();
|
|
646
532
|
const teardowns = (config.plugins ?? []).map((p) => p.setup?.({ i18n, config })).filter((t) => typeof t === "function");
|
|
647
533
|
return () => {
|
|
648
534
|
teardowns.forEach((t) => t());
|
|
649
535
|
i18n.stop();
|
|
536
|
+
_clearActiveInstance(i18n);
|
|
650
537
|
};
|
|
651
538
|
}, [i18n]);
|
|
652
539
|
const value = useMemo(() => ({ i18n }), [i18n]);
|
|
@@ -678,13 +565,14 @@ function useTranslation(defaultNamespace) {
|
|
|
678
565
|
renderedRef.current = /* @__PURE__ */ new Set();
|
|
679
566
|
const tokenRef = useRef(/* @__PURE__ */ Symbol("verbumia.t"));
|
|
680
567
|
const t = useMemo2(() => {
|
|
681
|
-
|
|
568
|
+
const fn = (key, optionsOrDefault, maybeOptions) => {
|
|
682
569
|
const fullKey = defaultNamespace && !key.includes(":") ? `${defaultNamespace}:${key}` : key;
|
|
683
570
|
renderedRef.current.add(
|
|
684
571
|
keyRegistry.encode(fullKey, i18n.defaultNamespace)
|
|
685
572
|
);
|
|
686
|
-
return i18n.t(fullKey,
|
|
573
|
+
return i18n.t(fullKey, optionsOrDefault, maybeOptions);
|
|
687
574
|
};
|
|
575
|
+
return fn;
|
|
688
576
|
}, [i18n, defaultNamespace]);
|
|
689
577
|
useEffect2(() => {
|
|
690
578
|
keyRegistry._set(tokenRef.current, renderedRef.current);
|
|
@@ -739,6 +627,7 @@ export {
|
|
|
739
627
|
Trans,
|
|
740
628
|
VerbumiaProvider,
|
|
741
629
|
defaultTransport,
|
|
630
|
+
getI18n,
|
|
742
631
|
keyRegistry,
|
|
743
632
|
logTransport,
|
|
744
633
|
useTranslation
|