experimental-ciao-react 1.1.9 → 1.1.11
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 +4 -4
- package/dist/index.cjs +439 -285
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +19 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +439 -286
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { cloneElement, isValidElement, useCallback, useEffect, useRef } from "react";
|
|
1
|
+
import React, { cloneElement, isValidElement, useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
2
|
import { create } from "zustand";
|
|
3
3
|
import { persist } from "zustand/middleware";
|
|
4
4
|
|
|
@@ -8,127 +8,32 @@ function CTContextBlock({ children }) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
//#endregion
|
|
11
|
-
//#region src/
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
24
|
-
request.onerror = () => reject(request.error);
|
|
25
|
-
request.onsuccess = () => resolve(request.result);
|
|
26
|
-
request.onupgradeneeded = (event) => {
|
|
27
|
-
const db = event.target.result;
|
|
28
|
-
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
29
|
-
const store = db.createObjectStore(STORE_NAME, { keyPath: "url" });
|
|
30
|
-
store.createIndex("language", "language", { unique: false });
|
|
31
|
-
store.createIndex("projectId", "projectId", { unique: false });
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
return dbPromise;
|
|
36
|
-
}
|
|
37
|
-
async function getCachedTranslation(url) {
|
|
38
|
-
try {
|
|
39
|
-
const db = await openDB();
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
const request = db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(url);
|
|
42
|
-
request.onerror = () => reject(request.error);
|
|
43
|
-
request.onsuccess = () => {
|
|
44
|
-
const entry = request.result;
|
|
45
|
-
resolve(entry?.data ?? null);
|
|
46
|
-
};
|
|
47
|
-
});
|
|
48
|
-
} catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async function cacheTranslation(url, language, projectId, data) {
|
|
53
|
-
try {
|
|
54
|
-
const db = await openDB();
|
|
55
|
-
return new Promise((resolve, reject) => {
|
|
56
|
-
const store = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME);
|
|
57
|
-
const entry = {
|
|
58
|
-
url,
|
|
59
|
-
language,
|
|
60
|
-
projectId,
|
|
61
|
-
data,
|
|
62
|
-
cachedAt: Date.now()
|
|
63
|
-
};
|
|
64
|
-
const request = store.put(entry);
|
|
65
|
-
request.onerror = () => reject(request.error);
|
|
66
|
-
request.onsuccess = () => resolve();
|
|
67
|
-
});
|
|
68
|
-
} catch {}
|
|
69
|
-
}
|
|
70
|
-
async function clearCache(projectId) {
|
|
71
|
-
try {
|
|
72
|
-
const db = await openDB();
|
|
73
|
-
return new Promise((resolve, reject) => {
|
|
74
|
-
const transaction = db.transaction(STORE_NAME, "readwrite");
|
|
75
|
-
const store = transaction.objectStore(STORE_NAME);
|
|
76
|
-
if (projectId) {
|
|
77
|
-
const request = store.index("projectId").openCursor(IDBKeyRange.only(projectId));
|
|
78
|
-
request.onsuccess = (event) => {
|
|
79
|
-
const cursor = event.target.result;
|
|
80
|
-
if (cursor) {
|
|
81
|
-
cursor.delete();
|
|
82
|
-
cursor.continue();
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
transaction.oncomplete = () => resolve();
|
|
86
|
-
transaction.onerror = () => reject(transaction.error);
|
|
87
|
-
} else {
|
|
88
|
-
const request = store.clear();
|
|
89
|
-
request.onerror = () => reject(request.error);
|
|
90
|
-
request.onsuccess = () => resolve();
|
|
11
|
+
//#region src/interpolate.ts
|
|
12
|
+
const MAX_CACHE_SIZE = 100;
|
|
13
|
+
function createBoundedCache(maxSize) {
|
|
14
|
+
const cache = /* @__PURE__ */ new Map();
|
|
15
|
+
return {
|
|
16
|
+
get(key) {
|
|
17
|
+
return cache.get(key);
|
|
18
|
+
},
|
|
19
|
+
set(key, value) {
|
|
20
|
+
if (cache.size >= maxSize) {
|
|
21
|
+
const firstKey = cache.keys().next().value;
|
|
22
|
+
if (firstKey !== void 0) cache.delete(firstKey);
|
|
91
23
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const countRequest = store.count();
|
|
102
|
-
const languages = /* @__PURE__ */ new Set();
|
|
103
|
-
const cursorRequest = store.openCursor();
|
|
104
|
-
cursorRequest.onsuccess = (event) => {
|
|
105
|
-
const cursor = event.target.result;
|
|
106
|
-
if (cursor) {
|
|
107
|
-
languages.add(cursor.value.language);
|
|
108
|
-
cursor.continue();
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
transaction.oncomplete = () => {
|
|
112
|
-
resolve({
|
|
113
|
-
count: countRequest.result,
|
|
114
|
-
languages: Array.from(languages)
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
transaction.onerror = () => reject(transaction.error);
|
|
118
|
-
});
|
|
119
|
-
} catch {
|
|
120
|
-
return {
|
|
121
|
-
count: 0,
|
|
122
|
-
languages: []
|
|
123
|
-
};
|
|
124
|
-
}
|
|
24
|
+
cache.set(key, value);
|
|
25
|
+
},
|
|
26
|
+
has(key) {
|
|
27
|
+
return cache.has(key);
|
|
28
|
+
},
|
|
29
|
+
clear() {
|
|
30
|
+
cache.clear();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
125
33
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const numberFormatCache = /* @__PURE__ */ new Map();
|
|
130
|
-
const dateFormatCache = /* @__PURE__ */ new Map();
|
|
131
|
-
const pluralRulesCache = /* @__PURE__ */ new Map();
|
|
34
|
+
const numberFormatCache = createBoundedCache(MAX_CACHE_SIZE);
|
|
35
|
+
const dateFormatCache = createBoundedCache(MAX_CACHE_SIZE);
|
|
36
|
+
const pluralRulesCache = createBoundedCache(MAX_CACHE_SIZE);
|
|
132
37
|
function clearFormatterCache() {
|
|
133
38
|
numberFormatCache.clear();
|
|
134
39
|
dateFormatCache.clear();
|
|
@@ -251,9 +156,8 @@ const useTranslationStore = create()(persist((set) => ({
|
|
|
251
156
|
isLoading: false,
|
|
252
157
|
isReady: false,
|
|
253
158
|
isHydrated: false,
|
|
254
|
-
|
|
159
|
+
storedManifest: null,
|
|
255
160
|
lastVersionCheck: null,
|
|
256
|
-
cachedLanguages: [],
|
|
257
161
|
setLanguage: (language) => {
|
|
258
162
|
set({
|
|
259
163
|
currentLanguage: language,
|
|
@@ -293,24 +197,24 @@ const useTranslationStore = create()(persist((set) => ({
|
|
|
293
197
|
setReady: (ready) => {
|
|
294
198
|
set({ isReady: ready });
|
|
295
199
|
},
|
|
296
|
-
|
|
200
|
+
setStoredManifest: (manifest) => {
|
|
297
201
|
set({
|
|
298
|
-
|
|
202
|
+
storedManifest: manifest,
|
|
299
203
|
lastVersionCheck: Date.now()
|
|
300
204
|
});
|
|
301
|
-
},
|
|
302
|
-
setCachedLanguages: (languages) => {
|
|
303
|
-
set({ cachedLanguages: languages });
|
|
304
205
|
}
|
|
305
206
|
}), {
|
|
306
207
|
name: "ciao-tools-language",
|
|
307
208
|
partialize: (state) => ({
|
|
308
209
|
currentLanguage: state.currentLanguage,
|
|
309
|
-
|
|
310
|
-
cachedLanguages: state.cachedLanguages
|
|
210
|
+
storedManifest: state.storedManifest
|
|
311
211
|
}),
|
|
312
212
|
onRehydrateStorage: () => (_, error) => {
|
|
313
|
-
if (
|
|
213
|
+
if (error) console.error("[ciao-tools] Storage hydration failed:", error);
|
|
214
|
+
if (hydrationResolver) {
|
|
215
|
+
hydrationResolver();
|
|
216
|
+
hydrationResolver = null;
|
|
217
|
+
}
|
|
314
218
|
}
|
|
315
219
|
}));
|
|
316
220
|
hydrationPromise.then(() => {
|
|
@@ -322,6 +226,133 @@ function getTranslation(translations, language, text, values) {
|
|
|
322
226
|
return interpolate(translated, values, language);
|
|
323
227
|
}
|
|
324
228
|
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/cache.ts
|
|
231
|
+
const DB_NAME = "ciao-tools-translations";
|
|
232
|
+
const DB_VERSION = 1;
|
|
233
|
+
const STORE_NAME = "translations";
|
|
234
|
+
let dbPromise = null;
|
|
235
|
+
function openDB() {
|
|
236
|
+
if (dbPromise) return dbPromise;
|
|
237
|
+
dbPromise = new Promise((resolve, reject) => {
|
|
238
|
+
if (typeof indexedDB === "undefined") {
|
|
239
|
+
dbPromise = null;
|
|
240
|
+
reject(/* @__PURE__ */ new Error("IndexedDB not available"));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
244
|
+
request.onerror = () => {
|
|
245
|
+
dbPromise = null;
|
|
246
|
+
reject(request.error);
|
|
247
|
+
};
|
|
248
|
+
request.onsuccess = () => resolve(request.result);
|
|
249
|
+
request.onupgradeneeded = (event) => {
|
|
250
|
+
const db = event.target.result;
|
|
251
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
252
|
+
const store = db.createObjectStore(STORE_NAME, { keyPath: "url" });
|
|
253
|
+
store.createIndex("language", "language", { unique: false });
|
|
254
|
+
store.createIndex("projectId", "projectId", { unique: false });
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
});
|
|
258
|
+
return dbPromise;
|
|
259
|
+
}
|
|
260
|
+
async function getCachedTranslation(url) {
|
|
261
|
+
try {
|
|
262
|
+
const db = await openDB();
|
|
263
|
+
return new Promise((resolve, reject) => {
|
|
264
|
+
const request = db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(url);
|
|
265
|
+
request.onerror = () => reject(request.error);
|
|
266
|
+
request.onsuccess = () => {
|
|
267
|
+
const entry = request.result;
|
|
268
|
+
resolve(entry?.data ?? null);
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.warn("[ciao-tools] Failed to read from cache:", error);
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async function cacheTranslation(url, language, projectId, data) {
|
|
277
|
+
try {
|
|
278
|
+
const db = await openDB();
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
const store = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME);
|
|
281
|
+
const entry = {
|
|
282
|
+
url,
|
|
283
|
+
language,
|
|
284
|
+
projectId,
|
|
285
|
+
data,
|
|
286
|
+
cachedAt: Date.now()
|
|
287
|
+
};
|
|
288
|
+
const request = store.put(entry);
|
|
289
|
+
request.onerror = () => reject(request.error);
|
|
290
|
+
request.onsuccess = () => resolve();
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.warn("[ciao-tools] Failed to write to cache:", error);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function clearCache(projectId) {
|
|
297
|
+
try {
|
|
298
|
+
const db = await openDB();
|
|
299
|
+
return new Promise((resolve, reject) => {
|
|
300
|
+
const transaction = db.transaction(STORE_NAME, "readwrite");
|
|
301
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
302
|
+
if (projectId) {
|
|
303
|
+
const request = store.index("projectId").openCursor(IDBKeyRange.only(projectId));
|
|
304
|
+
request.onsuccess = (event) => {
|
|
305
|
+
const cursor = event.target.result;
|
|
306
|
+
if (cursor) {
|
|
307
|
+
cursor.delete();
|
|
308
|
+
cursor.continue();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
transaction.oncomplete = () => resolve();
|
|
312
|
+
transaction.onerror = () => reject(transaction.error);
|
|
313
|
+
} else {
|
|
314
|
+
const request = store.clear();
|
|
315
|
+
request.onerror = () => reject(request.error);
|
|
316
|
+
request.onsuccess = () => resolve();
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.warn("[ciao-tools] Failed to clear cache:", error);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async function getCacheStats() {
|
|
324
|
+
try {
|
|
325
|
+
const db = await openDB();
|
|
326
|
+
return new Promise((resolve, reject) => {
|
|
327
|
+
const transaction = db.transaction(STORE_NAME, "readonly");
|
|
328
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
329
|
+
const countRequest = store.count();
|
|
330
|
+
const languages = /* @__PURE__ */ new Set();
|
|
331
|
+
const cursorRequest = store.openCursor();
|
|
332
|
+
cursorRequest.onsuccess = (event) => {
|
|
333
|
+
const cursor = event.target.result;
|
|
334
|
+
if (cursor) {
|
|
335
|
+
languages.add(cursor.value.language);
|
|
336
|
+
cursor.continue();
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
transaction.oncomplete = () => {
|
|
340
|
+
resolve({
|
|
341
|
+
count: countRequest.result,
|
|
342
|
+
languages: Array.from(languages)
|
|
343
|
+
});
|
|
344
|
+
};
|
|
345
|
+
transaction.onerror = () => reject(transaction.error);
|
|
346
|
+
});
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.warn("[ciao-tools] Failed to get cache stats:", error);
|
|
349
|
+
return {
|
|
350
|
+
count: 0,
|
|
351
|
+
languages: []
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
325
356
|
//#endregion
|
|
326
357
|
//#region src/hooks/useHotUpdates.ts
|
|
327
358
|
const CDN_BASE_URL = "https://t1.ciao-tools.com";
|
|
@@ -334,7 +365,8 @@ async function fetchLatestManifest(projectId) {
|
|
|
334
365
|
throw new Error(`Failed to fetch latest manifest: ${response.statusText}`);
|
|
335
366
|
}
|
|
336
367
|
return response.json();
|
|
337
|
-
} catch {
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.warn("[ciao-tools] Failed to fetch latest manifest:", error);
|
|
338
370
|
return null;
|
|
339
371
|
}
|
|
340
372
|
}
|
|
@@ -343,64 +375,81 @@ async function fetchTranslations(url) {
|
|
|
343
375
|
if (!response.ok) throw new Error(`Failed to fetch translations: ${response.statusText}`);
|
|
344
376
|
return response.json();
|
|
345
377
|
}
|
|
346
|
-
function useHotUpdates(config, projectId) {
|
|
378
|
+
function useHotUpdates(config, projectId, sourceLanguage) {
|
|
347
379
|
const isCheckingRef = useRef(false);
|
|
348
380
|
const hasCheckedOnMountRef = useRef(false);
|
|
349
|
-
const
|
|
381
|
+
const configRef = useRef(config);
|
|
382
|
+
const projectIdRef = useRef(projectId);
|
|
383
|
+
const sourceLanguageRef = useRef(sourceLanguage);
|
|
384
|
+
useEffect(() => {
|
|
385
|
+
configRef.current = config;
|
|
386
|
+
projectIdRef.current = projectId;
|
|
387
|
+
sourceLanguageRef.current = sourceLanguage;
|
|
388
|
+
}, [
|
|
389
|
+
config,
|
|
390
|
+
projectId,
|
|
391
|
+
sourceLanguage
|
|
392
|
+
]);
|
|
393
|
+
const { setStoredManifest, addLanguage } = useTranslationStore();
|
|
350
394
|
const checkForUpdates = useCallback(async () => {
|
|
351
|
-
|
|
352
|
-
|
|
395
|
+
const currentConfig = configRef.current;
|
|
396
|
+
const currentProjectId = projectIdRef.current;
|
|
397
|
+
const currentSourceLanguage = sourceLanguageRef.current;
|
|
398
|
+
if (!currentConfig || !currentProjectId || isCheckingRef.current) return;
|
|
399
|
+
if (currentConfig.enabled !== true) return;
|
|
353
400
|
isCheckingRef.current = true;
|
|
354
401
|
try {
|
|
355
402
|
console.log("[ciao-tools] Checking for hot updates...");
|
|
356
|
-
const
|
|
357
|
-
if (!
|
|
403
|
+
const latestManifest = await fetchLatestManifest(currentProjectId);
|
|
404
|
+
if (!latestManifest) {
|
|
358
405
|
console.log("[ciao-tools] No latest.json found (project may not have hot updates yet)");
|
|
359
406
|
return;
|
|
360
407
|
}
|
|
361
|
-
console.log("[ciao-tools] Found latest.json, version:",
|
|
362
|
-
const
|
|
363
|
-
const
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
408
|
+
console.log("[ciao-tools] Found latest.json, version:", latestManifest.version);
|
|
409
|
+
const currentManifest = useTranslationStore.getState().storedManifest;
|
|
410
|
+
const currentVersion = currentManifest?.serverVersion ?? null;
|
|
411
|
+
const isFirstCheck = currentVersion === null;
|
|
412
|
+
const hasNewVersion = currentVersion !== null && latestManifest.version > currentVersion;
|
|
413
|
+
if (isFirstCheck || hasNewVersion) {
|
|
414
|
+
if (Object.keys(latestManifest.urls).length > 0) {
|
|
415
|
+
const reason = hasNewVersion ? "new version" : "first check";
|
|
368
416
|
console.log(`[ciao-tools] Fetching translations (${reason})...`);
|
|
369
|
-
await clearCache(
|
|
417
|
+
if (hasNewVersion) await clearCache(currentProjectId);
|
|
370
418
|
const updatedLanguages = [];
|
|
371
|
-
for (const [langCode, url] of Object.entries(
|
|
419
|
+
for (const [langCode, url] of Object.entries(latestManifest.urls)) try {
|
|
372
420
|
const translations = await fetchTranslations(url);
|
|
373
421
|
addLanguage(langCode, translations);
|
|
374
|
-
await cacheTranslation(url, langCode,
|
|
422
|
+
await cacheTranslation(url, langCode, currentProjectId, translations);
|
|
375
423
|
updatedLanguages.push(langCode);
|
|
376
424
|
} catch (err) {
|
|
377
425
|
console.error(`[ciao-tools] Failed to fetch ${langCode} translations:`, err);
|
|
378
426
|
}
|
|
379
427
|
if (updatedLanguages.length > 0) {
|
|
380
|
-
setCachedLanguages(updatedLanguages);
|
|
381
428
|
console.log("[ciao-tools] Updated translations for:", updatedLanguages);
|
|
382
|
-
if (hasNewVersion &&
|
|
429
|
+
if (hasNewVersion && currentConfig.onTranslationsUpdated) currentConfig.onTranslationsUpdated(updatedLanguages);
|
|
383
430
|
}
|
|
384
431
|
}
|
|
385
|
-
|
|
386
|
-
|
|
432
|
+
setStoredManifest({
|
|
433
|
+
serverVersion: latestManifest.version,
|
|
434
|
+
updatedAt: latestManifest.updatedAt,
|
|
435
|
+
projectId: currentProjectId,
|
|
436
|
+
sourceLanguage: currentManifest?.sourceLanguage ?? currentSourceLanguage ?? "en",
|
|
437
|
+
languages: Object.keys(latestManifest.urls),
|
|
438
|
+
cdnUrls: latestManifest.urls
|
|
439
|
+
});
|
|
440
|
+
} else console.log("[ciao-tools] Already up to date (version " + latestManifest.version + ")");
|
|
387
441
|
} catch (error) {
|
|
388
442
|
console.error("[ciao-tools] Hot update check failed:", error);
|
|
389
443
|
} finally {
|
|
390
444
|
isCheckingRef.current = false;
|
|
391
445
|
}
|
|
392
|
-
}, [
|
|
393
|
-
config,
|
|
394
|
-
projectId,
|
|
395
|
-
serverVersion,
|
|
396
|
-
setServerVersion,
|
|
397
|
-
addLanguage,
|
|
398
|
-
cachedLanguages,
|
|
399
|
-
setCachedLanguages
|
|
400
|
-
]);
|
|
446
|
+
}, [setStoredManifest, addLanguage]);
|
|
401
447
|
useEffect(() => {
|
|
402
|
-
|
|
403
|
-
|
|
448
|
+
const currentConfig = configRef.current;
|
|
449
|
+
const currentProjectId = projectIdRef.current;
|
|
450
|
+
if (!currentConfig || !currentProjectId) return;
|
|
451
|
+
if (currentConfig.enabled !== true) return;
|
|
452
|
+
if (typeof document === "undefined") return;
|
|
404
453
|
if (!hasCheckedOnMountRef.current) {
|
|
405
454
|
hasCheckedOnMountRef.current = true;
|
|
406
455
|
checkForUpdates();
|
|
@@ -412,22 +461,133 @@ function useHotUpdates(config, projectId) {
|
|
|
412
461
|
return () => {
|
|
413
462
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
414
463
|
};
|
|
415
|
-
}, [
|
|
416
|
-
config,
|
|
417
|
-
projectId,
|
|
418
|
-
checkForUpdates
|
|
419
|
-
]);
|
|
464
|
+
}, [checkForUpdates]);
|
|
420
465
|
return { checkForUpdates };
|
|
421
466
|
}
|
|
422
467
|
|
|
423
468
|
//#endregion
|
|
424
|
-
//#region src/
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
469
|
+
//#region src/hooks/useManifest.ts
|
|
470
|
+
function getManifestUrlsHash(manifest) {
|
|
471
|
+
if (!manifest) return "";
|
|
472
|
+
return JSON.stringify(manifest.cdnUrls);
|
|
473
|
+
}
|
|
474
|
+
function useManifest({ manifest }) {
|
|
475
|
+
const storedManifest = useTranslationStore((state) => state.storedManifest);
|
|
476
|
+
const effectiveManifest = useMemo(() => {
|
|
477
|
+
if (storedManifest && storedManifest.projectId === manifest?.projectId) return {
|
|
478
|
+
version: String(storedManifest.serverVersion),
|
|
479
|
+
projectId: storedManifest.projectId,
|
|
480
|
+
sourceLanguage: storedManifest.sourceLanguage,
|
|
481
|
+
languages: storedManifest.languages,
|
|
482
|
+
cdnUrls: storedManifest.cdnUrls,
|
|
483
|
+
generatedAt: storedManifest.updatedAt
|
|
484
|
+
};
|
|
485
|
+
return manifest;
|
|
486
|
+
}, [storedManifest, manifest]);
|
|
487
|
+
const manifestRef = useRef(effectiveManifest);
|
|
488
|
+
const previousManifestHashRef = useRef("");
|
|
489
|
+
const loadedUrlsRef = useRef(/* @__PURE__ */ new Map());
|
|
490
|
+
const hasManifestChangedRef = useRef(false);
|
|
491
|
+
useEffect(() => {
|
|
492
|
+
manifestRef.current = effectiveManifest;
|
|
493
|
+
const newHash = getManifestUrlsHash(effectiveManifest);
|
|
494
|
+
const oldHash = previousManifestHashRef.current;
|
|
495
|
+
if (oldHash && newHash && oldHash !== newHash) {
|
|
496
|
+
loadedUrlsRef.current.clear();
|
|
497
|
+
useTranslationStore.setState({ translations: {} });
|
|
498
|
+
hasManifestChangedRef.current = true;
|
|
499
|
+
} else hasManifestChangedRef.current = false;
|
|
500
|
+
previousManifestHashRef.current = newHash;
|
|
501
|
+
}, [effectiveManifest]);
|
|
502
|
+
return {
|
|
503
|
+
effectiveManifest,
|
|
504
|
+
manifestRef,
|
|
505
|
+
hasManifestChanged: hasManifestChangedRef.current,
|
|
506
|
+
loadedUrlsRef
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
//#endregion
|
|
511
|
+
//#region src/hooks/useTranslationLoader.ts
|
|
512
|
+
async function fetchTranslationsFromCDN(url, signal) {
|
|
513
|
+
const response = await fetch(url, { signal });
|
|
428
514
|
if (!response.ok) throw new Error(`Failed to fetch translations: ${response.statusText}`);
|
|
429
515
|
return response.json();
|
|
430
516
|
}
|
|
517
|
+
function useTranslationLoader({ manifestRef, loadedUrlsRef, hotUpdatesEnabled }) {
|
|
518
|
+
const addLanguage = useTranslationStore((state) => state.addLanguage);
|
|
519
|
+
const setLoading = useTranslationStore((state) => state.setLoading);
|
|
520
|
+
const setReady = useTranslationStore((state) => state.setReady);
|
|
521
|
+
const abortControllerRef = useRef(null);
|
|
522
|
+
return {
|
|
523
|
+
loadLanguage: useCallback(async (language, isPreload = false) => {
|
|
524
|
+
const currentManifest = manifestRef.current;
|
|
525
|
+
if (!currentManifest) {
|
|
526
|
+
if (!isPreload) setReady(true);
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
const cdnUrl = currentManifest.cdnUrls[language];
|
|
530
|
+
if (!cdnUrl) {
|
|
531
|
+
if (!isPreload) setReady(true);
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
if (loadedUrlsRef.current.get(language) === cdnUrl) {
|
|
535
|
+
if (!isPreload) setReady(true);
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
if (!isPreload) {
|
|
539
|
+
if (abortControllerRef.current) abortControllerRef.current.abort();
|
|
540
|
+
abortControllerRef.current = new AbortController();
|
|
541
|
+
}
|
|
542
|
+
const controller = isPreload ? void 0 : abortControllerRef.current;
|
|
543
|
+
if (!isPreload) {
|
|
544
|
+
setLoading(true);
|
|
545
|
+
setReady(false);
|
|
546
|
+
}
|
|
547
|
+
const useCache = hotUpdatesEnabled !== true;
|
|
548
|
+
try {
|
|
549
|
+
if (useCache) {
|
|
550
|
+
const cached = await getCachedTranslation(cdnUrl);
|
|
551
|
+
if (cached) {
|
|
552
|
+
if (controller?.signal.aborted) return false;
|
|
553
|
+
addLanguage(language, cached);
|
|
554
|
+
loadedUrlsRef.current.set(language, cdnUrl);
|
|
555
|
+
if (!isPreload) {
|
|
556
|
+
setReady(true);
|
|
557
|
+
setLoading(false);
|
|
558
|
+
}
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const translationData = await fetchTranslationsFromCDN(cdnUrl, controller?.signal);
|
|
563
|
+
if (controller?.signal.aborted) return false;
|
|
564
|
+
addLanguage(language, translationData);
|
|
565
|
+
loadedUrlsRef.current.set(language, cdnUrl);
|
|
566
|
+
if (useCache) await cacheTranslation(cdnUrl, language, currentManifest.projectId, translationData);
|
|
567
|
+
if (!isPreload) setReady(true);
|
|
568
|
+
return true;
|
|
569
|
+
} catch (error) {
|
|
570
|
+
if (error instanceof Error && error.name === "AbortError") return false;
|
|
571
|
+
console.error(`[ciao-tools] Failed to load translations for ${language}:`, error);
|
|
572
|
+
if (!isPreload) setReady(true);
|
|
573
|
+
return false;
|
|
574
|
+
} finally {
|
|
575
|
+
if (!isPreload) setLoading(false);
|
|
576
|
+
}
|
|
577
|
+
}, [
|
|
578
|
+
manifestRef,
|
|
579
|
+
loadedUrlsRef,
|
|
580
|
+
hotUpdatesEnabled,
|
|
581
|
+
addLanguage,
|
|
582
|
+
setLoading,
|
|
583
|
+
setReady
|
|
584
|
+
]),
|
|
585
|
+
abortControllerRef
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
//#endregion
|
|
590
|
+
//#region src/utils/browserLanguage.ts
|
|
431
591
|
function detectBrowserLanguage(availableLanguages) {
|
|
432
592
|
if (typeof navigator === "undefined") return null;
|
|
433
593
|
const browserLangs = navigator.languages || [navigator.language];
|
|
@@ -439,43 +599,19 @@ function detectBrowserLanguage(availableLanguages) {
|
|
|
439
599
|
}
|
|
440
600
|
return null;
|
|
441
601
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
const manifestRef = useRef(manifest);
|
|
449
|
-
const loadedUrlsRef = useRef(/* @__PURE__ */ new Map());
|
|
450
|
-
const previousManifestHashRef = useRef("");
|
|
602
|
+
|
|
603
|
+
//#endregion
|
|
604
|
+
//#region src/hooks/useLanguageInit.ts
|
|
605
|
+
function useLanguageInit({ effectiveManifest, availableLanguages, defaultLanguage, detectLanguage }) {
|
|
606
|
+
const isHydrated = useTranslationStore((state) => state.isHydrated);
|
|
607
|
+
const setLanguage = useTranslationStore((state) => state.setLanguage);
|
|
451
608
|
const initializedRef = useRef(false);
|
|
452
|
-
const preloadTimeoutRef = useRef(null);
|
|
453
|
-
useEffect(() => {
|
|
454
|
-
manifestRef.current = manifest;
|
|
455
|
-
const newHash = getManifestUrlsHash(manifest);
|
|
456
|
-
const oldHash = previousManifestHashRef.current;
|
|
457
|
-
if (oldHash && newHash && oldHash !== newHash) {
|
|
458
|
-
loadedUrlsRef.current.clear();
|
|
459
|
-
useTranslationStore.setState({ translations: {} });
|
|
460
|
-
}
|
|
461
|
-
previousManifestHashRef.current = newHash;
|
|
462
|
-
}, [manifest]);
|
|
463
|
-
useEffect(() => {
|
|
464
|
-
if (translations) {
|
|
465
|
-
loadTranslations(translations);
|
|
466
|
-
setReady(true);
|
|
467
|
-
}
|
|
468
|
-
}, [
|
|
469
|
-
translations,
|
|
470
|
-
loadTranslations,
|
|
471
|
-
setReady
|
|
472
|
-
]);
|
|
473
609
|
useEffect(() => {
|
|
474
610
|
if (!isHydrated) return;
|
|
475
611
|
if (initializedRef.current) return;
|
|
476
612
|
initializedRef.current = true;
|
|
477
613
|
const store = useTranslationStore.getState();
|
|
478
|
-
const effectiveLanguages =
|
|
614
|
+
const effectiveLanguages = effectiveManifest ? [...effectiveManifest.languages] : availableLanguages || [];
|
|
479
615
|
if (store.currentLanguage && store.currentLanguage !== "en" && effectiveLanguages.includes(store.currentLanguage)) return;
|
|
480
616
|
if (detectLanguage && effectiveLanguages.length > 0) {
|
|
481
617
|
const detected = detectBrowserLanguage(effectiveLanguages);
|
|
@@ -486,18 +622,95 @@ function CTProvider({ children, translations, manifest, defaultLanguage = "en",
|
|
|
486
622
|
}
|
|
487
623
|
if (defaultLanguage && defaultLanguage !== store.currentLanguage) setLanguage(defaultLanguage);
|
|
488
624
|
}, [
|
|
489
|
-
|
|
625
|
+
effectiveManifest,
|
|
490
626
|
availableLanguages,
|
|
491
627
|
defaultLanguage,
|
|
492
628
|
detectLanguage,
|
|
493
629
|
setLanguage,
|
|
494
630
|
isHydrated
|
|
495
631
|
]);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region src/hooks/usePreloader.ts
|
|
636
|
+
function usePreloader({ effectiveManifest, currentLanguage, delay, enabled, loadLanguage }) {
|
|
637
|
+
const preloadTimeoutRef = useRef(null);
|
|
638
|
+
const isMountedRef = useRef(true);
|
|
639
|
+
useEffect(() => {
|
|
640
|
+
isMountedRef.current = true;
|
|
641
|
+
return () => {
|
|
642
|
+
isMountedRef.current = false;
|
|
643
|
+
};
|
|
644
|
+
}, []);
|
|
645
|
+
useEffect(() => {
|
|
646
|
+
if (!enabled || !effectiveManifest) return;
|
|
647
|
+
if (preloadTimeoutRef.current) clearTimeout(preloadTimeoutRef.current);
|
|
648
|
+
preloadTimeoutRef.current = setTimeout(async () => {
|
|
649
|
+
if (!isMountedRef.current) return;
|
|
650
|
+
const languages = [...effectiveManifest.languages];
|
|
651
|
+
for (const language of languages) {
|
|
652
|
+
if (!isMountedRef.current) break;
|
|
653
|
+
if (language === effectiveManifest.sourceLanguage) continue;
|
|
654
|
+
if (language === currentLanguage) continue;
|
|
655
|
+
await loadLanguage(language, true);
|
|
656
|
+
}
|
|
657
|
+
}, delay);
|
|
658
|
+
return () => {
|
|
659
|
+
if (preloadTimeoutRef.current) clearTimeout(preloadTimeoutRef.current);
|
|
660
|
+
};
|
|
661
|
+
}, [
|
|
662
|
+
effectiveManifest,
|
|
663
|
+
currentLanguage,
|
|
664
|
+
enabled,
|
|
665
|
+
delay,
|
|
666
|
+
loadLanguage
|
|
667
|
+
]);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
//#endregion
|
|
671
|
+
//#region src/components/CiaoProvider.tsx
|
|
672
|
+
const PRELOAD_DELAY_MS = 5e3;
|
|
673
|
+
function CiaoProvider({ children, translations, manifest, defaultLanguage = "en", availableLanguages, onLanguageChange, detectLanguage = true, blockUntilReady = true, fallback = null, preloadLanguages = true, preloadDelay = PRELOAD_DELAY_MS, hotUpdates }) {
|
|
674
|
+
const loadTranslations = useTranslationStore((state) => state.loadTranslations);
|
|
675
|
+
const setReady = useTranslationStore((state) => state.setReady);
|
|
676
|
+
const currentLanguage = useTranslationStore((state) => state.currentLanguage);
|
|
677
|
+
const isReady = useTranslationStore((state) => state.isReady);
|
|
678
|
+
const isHydrated = useTranslationStore((state) => state.isHydrated);
|
|
679
|
+
const { effectiveManifest, manifestRef, loadedUrlsRef } = useManifest({ manifest });
|
|
680
|
+
const { loadLanguage, abortControllerRef } = useTranslationLoader({
|
|
681
|
+
manifestRef,
|
|
682
|
+
loadedUrlsRef,
|
|
683
|
+
hotUpdatesEnabled: hotUpdates?.enabled
|
|
684
|
+
});
|
|
685
|
+
useLanguageInit({
|
|
686
|
+
effectiveManifest,
|
|
687
|
+
availableLanguages,
|
|
688
|
+
defaultLanguage,
|
|
689
|
+
detectLanguage
|
|
690
|
+
});
|
|
691
|
+
usePreloader({
|
|
692
|
+
effectiveManifest,
|
|
693
|
+
currentLanguage,
|
|
694
|
+
delay: preloadDelay,
|
|
695
|
+
enabled: preloadLanguages,
|
|
696
|
+
loadLanguage
|
|
697
|
+
});
|
|
698
|
+
useHotUpdates(hotUpdates, manifest?.projectId, manifest?.sourceLanguage);
|
|
699
|
+
useEffect(() => {
|
|
700
|
+
if (translations) {
|
|
701
|
+
loadTranslations(translations);
|
|
702
|
+
setReady(true);
|
|
703
|
+
}
|
|
704
|
+
}, [
|
|
705
|
+
translations,
|
|
706
|
+
loadTranslations,
|
|
707
|
+
setReady
|
|
708
|
+
]);
|
|
496
709
|
useEffect(() => {
|
|
497
|
-
const
|
|
498
|
-
if (
|
|
710
|
+
const languagesList = effectiveManifest ? [...effectiveManifest.languages] : availableLanguages;
|
|
711
|
+
if (languagesList) {
|
|
499
712
|
const store = useTranslationStore.getState();
|
|
500
|
-
const merged = [...new Set([...store.availableLanguages, ...
|
|
713
|
+
const merged = [...new Set([...store.availableLanguages, ...languagesList])];
|
|
501
714
|
useTranslationStore.setState({
|
|
502
715
|
availableLanguages: merged,
|
|
503
716
|
defaultLanguage
|
|
@@ -505,97 +718,32 @@ function CTProvider({ children, translations, manifest, defaultLanguage = "en",
|
|
|
505
718
|
}
|
|
506
719
|
}, [
|
|
507
720
|
availableLanguages,
|
|
508
|
-
|
|
721
|
+
effectiveManifest,
|
|
509
722
|
defaultLanguage
|
|
510
723
|
]);
|
|
511
|
-
const loadLanguageFromCDN = useCallback(async (language, isPreload = false) => {
|
|
512
|
-
const currentManifest = manifestRef.current;
|
|
513
|
-
if (!currentManifest) {
|
|
514
|
-
if (!isPreload) setReady(true);
|
|
515
|
-
return false;
|
|
516
|
-
}
|
|
517
|
-
const cdnUrl = currentManifest.cdnUrls[language];
|
|
518
|
-
if (!cdnUrl) {
|
|
519
|
-
if (!isPreload) setReady(true);
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
|
-
if (loadedUrlsRef.current.get(language) === cdnUrl) {
|
|
523
|
-
if (!isPreload) setReady(true);
|
|
524
|
-
return true;
|
|
525
|
-
}
|
|
526
|
-
if (!isPreload) {
|
|
527
|
-
setLoading(true);
|
|
528
|
-
setReady(false);
|
|
529
|
-
}
|
|
530
|
-
try {
|
|
531
|
-
const cached = await getCachedTranslation(cdnUrl);
|
|
532
|
-
if (cached) {
|
|
533
|
-
addLanguage(language, cached);
|
|
534
|
-
loadedUrlsRef.current.set(language, cdnUrl);
|
|
535
|
-
if (!isPreload) {
|
|
536
|
-
setReady(true);
|
|
537
|
-
setLoading(false);
|
|
538
|
-
}
|
|
539
|
-
return true;
|
|
540
|
-
}
|
|
541
|
-
const translationData = await fetchTranslationsFromCDN(cdnUrl);
|
|
542
|
-
addLanguage(language, translationData);
|
|
543
|
-
loadedUrlsRef.current.set(language, cdnUrl);
|
|
544
|
-
await cacheTranslation(cdnUrl, language, currentManifest.projectId, translationData);
|
|
545
|
-
if (!isPreload) setReady(true);
|
|
546
|
-
return true;
|
|
547
|
-
} catch (error) {
|
|
548
|
-
console.error(`[ciao-tools] Failed to load translations for ${language}:`, error);
|
|
549
|
-
if (!isPreload) setReady(true);
|
|
550
|
-
return false;
|
|
551
|
-
} finally {
|
|
552
|
-
if (!isPreload) setLoading(false);
|
|
553
|
-
}
|
|
554
|
-
}, [
|
|
555
|
-
addLanguage,
|
|
556
|
-
setLoading,
|
|
557
|
-
setReady
|
|
558
|
-
]);
|
|
559
724
|
useEffect(() => {
|
|
560
725
|
if (!isHydrated) return;
|
|
561
|
-
if (currentLanguage ===
|
|
726
|
+
if (currentLanguage === effectiveManifest?.sourceLanguage) {
|
|
562
727
|
setReady(true);
|
|
563
728
|
return;
|
|
564
729
|
}
|
|
565
|
-
if (
|
|
566
|
-
else if (!
|
|
730
|
+
if (effectiveManifest && currentLanguage) loadLanguage(currentLanguage, false);
|
|
731
|
+
else if (!effectiveManifest) setReady(true);
|
|
567
732
|
}, [
|
|
568
|
-
|
|
733
|
+
effectiveManifest,
|
|
569
734
|
currentLanguage,
|
|
570
|
-
|
|
735
|
+
loadLanguage,
|
|
571
736
|
setReady,
|
|
572
737
|
isHydrated
|
|
573
738
|
]);
|
|
574
|
-
useEffect(() => {
|
|
575
|
-
if (!preloadLanguages || !manifest) return;
|
|
576
|
-
if (preloadTimeoutRef.current) clearTimeout(preloadTimeoutRef.current);
|
|
577
|
-
preloadTimeoutRef.current = setTimeout(async () => {
|
|
578
|
-
const languages = [...manifest.languages];
|
|
579
|
-
for (const language of languages) {
|
|
580
|
-
if (language === manifest.sourceLanguage) continue;
|
|
581
|
-
if (language === currentLanguage) continue;
|
|
582
|
-
await loadLanguageFromCDN(language, true);
|
|
583
|
-
}
|
|
584
|
-
}, preloadDelay);
|
|
585
|
-
return () => {
|
|
586
|
-
if (preloadTimeoutRef.current) clearTimeout(preloadTimeoutRef.current);
|
|
587
|
-
};
|
|
588
|
-
}, [
|
|
589
|
-
manifest,
|
|
590
|
-
currentLanguage,
|
|
591
|
-
preloadLanguages,
|
|
592
|
-
preloadDelay,
|
|
593
|
-
loadLanguageFromCDN
|
|
594
|
-
]);
|
|
595
739
|
useEffect(() => {
|
|
596
740
|
if (onLanguageChange) onLanguageChange(currentLanguage);
|
|
597
741
|
}, [currentLanguage, onLanguageChange]);
|
|
598
|
-
|
|
742
|
+
useEffect(() => {
|
|
743
|
+
return () => {
|
|
744
|
+
abortControllerRef.current?.abort();
|
|
745
|
+
};
|
|
746
|
+
}, [abortControllerRef]);
|
|
599
747
|
if (blockUntilReady && (!isReady || !isHydrated)) return /* @__PURE__ */ React.createElement(React.Fragment, null, fallback);
|
|
600
748
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
|
|
601
749
|
}
|
|
@@ -887,6 +1035,7 @@ function useCt() {
|
|
|
887
1035
|
|
|
888
1036
|
//#endregion
|
|
889
1037
|
//#region src/components/Trans.tsx
|
|
1038
|
+
const TAG_REGEX = /<(\d+)>(.*?)<\/\1>/gs;
|
|
890
1039
|
function parseChildren(children) {
|
|
891
1040
|
const elements = [];
|
|
892
1041
|
let template = "";
|
|
@@ -912,11 +1061,10 @@ function reconstructChildren(translated, elements) {
|
|
|
912
1061
|
if (elements.length === 0) return translated;
|
|
913
1062
|
const result = [];
|
|
914
1063
|
let keyCounter = 0;
|
|
915
|
-
const tagRegex = /<(\d+)>(.*?)<\/\1>/gs;
|
|
916
1064
|
let lastIndex = 0;
|
|
917
1065
|
let match;
|
|
918
|
-
|
|
919
|
-
while ((match =
|
|
1066
|
+
TAG_REGEX.lastIndex = 0;
|
|
1067
|
+
while ((match = TAG_REGEX.exec(translated)) !== null) {
|
|
920
1068
|
if (match.index > lastIndex) {
|
|
921
1069
|
const textBefore = translated.slice(lastIndex, match.index);
|
|
922
1070
|
if (textBefore) result.push(textBefore);
|
|
@@ -986,6 +1134,11 @@ function useLanguage() {
|
|
|
986
1134
|
availableLanguagesInfo: availableLanguages.map(getFullLanguageInfo),
|
|
987
1135
|
setLanguage,
|
|
988
1136
|
cycleLanguage: useCallback(() => {
|
|
1137
|
+
if (availableLanguages.length === 0) {
|
|
1138
|
+
console.warn("[ciao-tools] Cannot cycle language: no languages available");
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
if (availableLanguages.length === 1) return;
|
|
989
1142
|
setLanguage(availableLanguages[(availableLanguages.indexOf(currentLanguage) + 1) % availableLanguages.length]);
|
|
990
1143
|
}, [
|
|
991
1144
|
availableLanguages,
|
|
@@ -998,5 +1151,5 @@ function useLanguage() {
|
|
|
998
1151
|
}
|
|
999
1152
|
|
|
1000
1153
|
//#endregion
|
|
1001
|
-
export { CTContextBlock, CTProvider, LANGUAGE_DATA, LanguageSwitcher, Trans, clearCache, clearFormatterCache, formatLanguageDisplay, getCacheStats, getFullLanguageInfo, getLanguageInfo, interpolate, useAvailableLanguages, useAvailableLanguagesInfo, useCt, useCurrentLanguage, useHotUpdates, useIsLoading, useIsReady, useLanguage, useLanguageInfo, useSetLanguage, useTranslationStore };
|
|
1154
|
+
export { CTContextBlock, CiaoProvider as CTProvider, CiaoProvider, LANGUAGE_DATA, LanguageSwitcher, Trans, clearCache, clearFormatterCache, formatLanguageDisplay, getCacheStats, getFullLanguageInfo, getLanguageInfo, interpolate, useAvailableLanguages, useAvailableLanguagesInfo, useCt, useCurrentLanguage, useHotUpdates, useIsLoading, useIsReady, useLanguage, useLanguageInfo, useSetLanguage, useTranslationStore };
|
|
1002
1155
|
//# sourceMappingURL=index.js.map
|