@shuo-li/i18n 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 +0 -0
- package/dist/cacheEvents-kOPDM3Ed.d.cts +132 -0
- package/dist/cacheEvents-kOPDM3Ed.d.ts +132 -0
- package/dist/chunk-AJJKJPNB.cjs +88 -0
- package/dist/chunk-JDOLFHBS.cjs +664 -0
- package/dist/chunk-ONDHZEK4.js +664 -0
- package/dist/chunk-VUDMWZAA.js +88 -0
- package/dist/hooks-ClO29Chr.d.cts +16 -0
- package/dist/hooks-ClO29Chr.d.ts +16 -0
- package/dist/index.cjs +223 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +223 -0
- package/dist/mp/index.cjs +231 -0
- package/dist/mp/index.d.cts +10 -0
- package/dist/mp/index.d.ts +10 -0
- package/dist/mp/index.js +231 -0
- package/dist/native/index.cjs +175 -0
- package/dist/native/index.d.cts +12 -0
- package/dist/native/index.d.ts +12 -0
- package/dist/native/index.js +175 -0
- package/dist/workers/preload-worker-rn.cjs +91 -0
- package/dist/workers/preload-worker-rn.d.cts +2 -0
- package/dist/workers/preload-worker-rn.d.ts +2 -0
- package/dist/workers/preload-worker-rn.js +91 -0
- package/dist/workers/preload-worker.cjs +169 -0
- package/dist/workers/preload-worker.d.cts +2 -0
- package/dist/workers/preload-worker.d.ts +2 -0
- package/dist/workers/preload-worker.js +169 -0
- package/package.json +59 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WebWorkerAdapter,
|
|
3
|
+
useDict,
|
|
4
|
+
useTranslation
|
|
5
|
+
} from "../chunk-VUDMWZAA.js";
|
|
6
|
+
import {
|
|
7
|
+
I18N_RESOURCES_UPDATED_EVENT,
|
|
8
|
+
createI18nManager,
|
|
9
|
+
emitI18nResourcesUpdated
|
|
10
|
+
} from "../chunk-ONDHZEK4.js";
|
|
11
|
+
|
|
12
|
+
// src/native/index.ts
|
|
13
|
+
import i18n from "i18next";
|
|
14
|
+
import { initReactI18next } from "react-i18next";
|
|
15
|
+
|
|
16
|
+
// src/storage/sqlite.ts
|
|
17
|
+
var DB_NAME = "i18n_cache";
|
|
18
|
+
var SCHEMA_VERSION = 2;
|
|
19
|
+
function resourceTable(name) {
|
|
20
|
+
return `i18n_resources_${name}`;
|
|
21
|
+
}
|
|
22
|
+
var dbInstance = null;
|
|
23
|
+
async function getDB() {
|
|
24
|
+
if (dbInstance) return dbInstance;
|
|
25
|
+
const { open } = await import("@op-engineering/op-sqlite");
|
|
26
|
+
dbInstance = open({ name: DB_NAME });
|
|
27
|
+
return dbInstance;
|
|
28
|
+
}
|
|
29
|
+
async function execute(sql, params = []) {
|
|
30
|
+
const db = await getDB();
|
|
31
|
+
return db.executeAsync(sql, params);
|
|
32
|
+
}
|
|
33
|
+
var SQLiteAdapter = class {
|
|
34
|
+
async ensureSchemaVersion(stores) {
|
|
35
|
+
await execute(`
|
|
36
|
+
CREATE TABLE IF NOT EXISTS i18n_meta (
|
|
37
|
+
key TEXT PRIMARY KEY,
|
|
38
|
+
value TEXT NOT NULL
|
|
39
|
+
)
|
|
40
|
+
`);
|
|
41
|
+
const stored = await this.getMeta("schema_version");
|
|
42
|
+
const storedVersion = Number(stored ?? 0);
|
|
43
|
+
if (storedVersion === 0) {
|
|
44
|
+
await this.createTables(stores);
|
|
45
|
+
await this.setMeta("schema_version", String(SCHEMA_VERSION));
|
|
46
|
+
} else if (storedVersion < SCHEMA_VERSION) {
|
|
47
|
+
for (const store of stores) {
|
|
48
|
+
await execute(`DROP TABLE IF EXISTS ${resourceTable(store.name)}`);
|
|
49
|
+
}
|
|
50
|
+
await this.createTables(stores);
|
|
51
|
+
await this.setMeta("schema_version", String(SCHEMA_VERSION));
|
|
52
|
+
} else if (storedVersion > SCHEMA_VERSION) {
|
|
53
|
+
throw new Error(`[i18n] SQLite schema version ${storedVersion} > current ${SCHEMA_VERSION}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async createTables(stores) {
|
|
57
|
+
for (const store of stores) {
|
|
58
|
+
const table = resourceTable(store.name);
|
|
59
|
+
await execute(`
|
|
60
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
61
|
+
key TEXT PRIMARY KEY,
|
|
62
|
+
module_code TEXT NOT NULL,
|
|
63
|
+
lang_code TEXT NOT NULL,
|
|
64
|
+
version INTEGER NOT NULL DEFAULT 0,
|
|
65
|
+
resources TEXT NOT NULL
|
|
66
|
+
)
|
|
67
|
+
`);
|
|
68
|
+
await execute(
|
|
69
|
+
`CREATE INDEX IF NOT EXISTS idx_${store.name}_module ON ${table}(module_code)`
|
|
70
|
+
);
|
|
71
|
+
await execute(
|
|
72
|
+
`CREATE INDEX IF NOT EXISTS idx_${store.name}_language ON ${table}(lang_code)`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async getRecord(storeName, key) {
|
|
77
|
+
const { rows } = await execute(
|
|
78
|
+
`SELECT * FROM ${resourceTable(storeName)} WHERE key = ?`,
|
|
79
|
+
[key]
|
|
80
|
+
);
|
|
81
|
+
if (!rows.length) return void 0;
|
|
82
|
+
return rowToRecord(rows[0]);
|
|
83
|
+
}
|
|
84
|
+
async putRecord(storeName, record) {
|
|
85
|
+
await execute(
|
|
86
|
+
`INSERT OR REPLACE INTO ${resourceTable(storeName)}
|
|
87
|
+
(key, module_code, lang_code, version, resources)
|
|
88
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
89
|
+
[
|
|
90
|
+
record.key,
|
|
91
|
+
record.moduleCode,
|
|
92
|
+
record.langCode,
|
|
93
|
+
record.version,
|
|
94
|
+
JSON.stringify(record.resources)
|
|
95
|
+
]
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
async getAllByModule(storeName, moduleCode) {
|
|
99
|
+
const { rows } = await execute(
|
|
100
|
+
`SELECT * FROM ${resourceTable(storeName)} WHERE module_code = ?`,
|
|
101
|
+
[moduleCode]
|
|
102
|
+
);
|
|
103
|
+
return rows.map((r) => rowToRecord(r));
|
|
104
|
+
}
|
|
105
|
+
async clearStore(storeName) {
|
|
106
|
+
await execute(`DELETE FROM ${resourceTable(storeName)}`);
|
|
107
|
+
}
|
|
108
|
+
async getMeta(key) {
|
|
109
|
+
const { rows } = await execute("SELECT value FROM i18n_meta WHERE key = ?", [key]);
|
|
110
|
+
if (!rows.length) return null;
|
|
111
|
+
return rows[0].value;
|
|
112
|
+
}
|
|
113
|
+
async setMeta(key, value) {
|
|
114
|
+
await execute(
|
|
115
|
+
"INSERT OR REPLACE INTO i18n_meta (key, value) VALUES (?, ?)",
|
|
116
|
+
[key, value]
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
async hasLoginResources(stores) {
|
|
120
|
+
for (const store of stores) {
|
|
121
|
+
const { rows } = await execute(
|
|
122
|
+
`SELECT key FROM ${resourceTable(store.name)}
|
|
123
|
+
WHERE key NOT LIKE 'UNLOGIN_%' LIMIT 1`
|
|
124
|
+
);
|
|
125
|
+
if (rows.length > 0) return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
function rowToRecord(row) {
|
|
131
|
+
return {
|
|
132
|
+
key: row.key,
|
|
133
|
+
moduleCode: row.module_code,
|
|
134
|
+
langCode: row.lang_code,
|
|
135
|
+
version: row.version,
|
|
136
|
+
resources: JSON.parse(row.resources)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/native/index.ts
|
|
141
|
+
if (!i18n.isInitialized) {
|
|
142
|
+
void i18n.use(initReactI18next).init({
|
|
143
|
+
lng: "__loading__",
|
|
144
|
+
fallbackLng: "zh_CN",
|
|
145
|
+
ns: ["COMMON"],
|
|
146
|
+
defaultNS: "COMMON",
|
|
147
|
+
resources: {},
|
|
148
|
+
interpolation: { escapeValue: false },
|
|
149
|
+
react: {
|
|
150
|
+
useSuspense: false,
|
|
151
|
+
bindI18n: "languageChanged",
|
|
152
|
+
bindI18nStore: "added removed",
|
|
153
|
+
transSupportBasicHtmlNodes: true,
|
|
154
|
+
transKeepBasicHtmlNodesFor: ["br", "p", "strong", "i", "em", "b", "a", "span"]
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
var { initI18n, closeSSE, ensureModules, getResource, getAllRecordsByModule } = createI18nManager({
|
|
159
|
+
storage: new SQLiteAdapter(),
|
|
160
|
+
// Hermes Worker(RN 0.71+),API 与 Web Worker 一致
|
|
161
|
+
createWorker: () => new WebWorkerAdapter(
|
|
162
|
+
new Worker("./workers/preload-worker-rn", { type: "module" })
|
|
163
|
+
)
|
|
164
|
+
});
|
|
165
|
+
export {
|
|
166
|
+
I18N_RESOURCES_UPDATED_EVENT,
|
|
167
|
+
closeSSE,
|
|
168
|
+
emitI18nResourcesUpdated,
|
|
169
|
+
ensureModules,
|
|
170
|
+
getAllRecordsByModule,
|
|
171
|
+
getResource,
|
|
172
|
+
initI18n,
|
|
173
|
+
useDict,
|
|
174
|
+
useTranslation
|
|
175
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict"; function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/workers/preload-worker-rn.ts
|
|
2
|
+
function flatToNested(flat) {
|
|
3
|
+
const result = {};
|
|
4
|
+
for (const [key, value] of Object.entries(flat)) {
|
|
5
|
+
const parts = key.split(".");
|
|
6
|
+
let cur = result;
|
|
7
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
8
|
+
const part = parts[i];
|
|
9
|
+
if (typeof cur[part] !== "object" || cur[part] === null) cur[part] = {};
|
|
10
|
+
cur = cur[part];
|
|
11
|
+
}
|
|
12
|
+
cur[parts[parts.length - 1]] = value;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
function parseI18nValues(values) {
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const { termCode, langValue } of values) {
|
|
19
|
+
if (termCode && langValue !== void 0) result[termCode] = _nullishCoalesce(langValue, () => ( ""));
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
function buildKey(moduleCode, langCode, store) {
|
|
24
|
+
return store.cacheGroupKey ? `${moduleCode}_${langCode}_${store.cacheGroupKey}` : `${moduleCode}_${langCode}`;
|
|
25
|
+
}
|
|
26
|
+
function toQueryString(params) {
|
|
27
|
+
return new URLSearchParams(
|
|
28
|
+
Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
|
|
29
|
+
).toString();
|
|
30
|
+
}
|
|
31
|
+
async function processTask(task, payload) {
|
|
32
|
+
const { baseURL, headers, pullPath, pullMethod, stores } = payload;
|
|
33
|
+
const params = {};
|
|
34
|
+
if (task.storeName) params["storeName"] = task.storeName;
|
|
35
|
+
if (task.langCode) params["langCode"] = task.langCode;
|
|
36
|
+
if (task.moduleCode) params["moduleCode"] = task.moduleCode;
|
|
37
|
+
if (task.version != null) params["version"] = String(task.version);
|
|
38
|
+
const url = baseURL + pullPath;
|
|
39
|
+
let res;
|
|
40
|
+
if (pullMethod === "POST") {
|
|
41
|
+
res = await fetch(url, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
44
|
+
body: JSON.stringify(params)
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
res = await fetch(`${url}?${toQueryString(params)}`, { headers });
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok) throw new Error(`[i18n rn-worker] fetch failed: ${res.status}`);
|
|
50
|
+
const blocks = await res.json();
|
|
51
|
+
for (const block of blocks) {
|
|
52
|
+
for (const mod of _nullishCoalesce(block.modules, () => ( []))) {
|
|
53
|
+
const store = stores.find((s) => s.name === task.storeName);
|
|
54
|
+
if (!store) continue;
|
|
55
|
+
const storeIndex = stores.indexOf(store);
|
|
56
|
+
const key = buildKey(mod.moduleCode, block.langCode, store);
|
|
57
|
+
const parsed = parseI18nValues(_nullishCoalesce(mod.i18nValues, () => ( [])));
|
|
58
|
+
const resources = storeIndex === 0 ? flatToNested(parsed) : parsed;
|
|
59
|
+
self.postMessage({
|
|
60
|
+
type: "dataReady",
|
|
61
|
+
storeName: store.name,
|
|
62
|
+
storeIndex,
|
|
63
|
+
record: {
|
|
64
|
+
key,
|
|
65
|
+
moduleCode: mod.moduleCode,
|
|
66
|
+
langCode: block.langCode,
|
|
67
|
+
version: mod.version,
|
|
68
|
+
resources
|
|
69
|
+
},
|
|
70
|
+
task
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
self.onmessage = async (event) => {
|
|
76
|
+
if (event.data.type !== "start") return;
|
|
77
|
+
const { payload } = event.data;
|
|
78
|
+
for (const task of payload.tasks) {
|
|
79
|
+
try {
|
|
80
|
+
await processTask(task, payload);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
self.postMessage({
|
|
83
|
+
type: "moduleError",
|
|
84
|
+
storeName: task.storeName,
|
|
85
|
+
langCode: task.langCode,
|
|
86
|
+
moduleCode: task.moduleCode
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
self.postMessage({ type: "done" });
|
|
91
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/workers/preload-worker-rn.ts
|
|
2
|
+
function flatToNested(flat) {
|
|
3
|
+
const result = {};
|
|
4
|
+
for (const [key, value] of Object.entries(flat)) {
|
|
5
|
+
const parts = key.split(".");
|
|
6
|
+
let cur = result;
|
|
7
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
8
|
+
const part = parts[i];
|
|
9
|
+
if (typeof cur[part] !== "object" || cur[part] === null) cur[part] = {};
|
|
10
|
+
cur = cur[part];
|
|
11
|
+
}
|
|
12
|
+
cur[parts[parts.length - 1]] = value;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
function parseI18nValues(values) {
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const { termCode, langValue } of values) {
|
|
19
|
+
if (termCode && langValue !== void 0) result[termCode] = langValue ?? "";
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
function buildKey(moduleCode, langCode, store) {
|
|
24
|
+
return store.cacheGroupKey ? `${moduleCode}_${langCode}_${store.cacheGroupKey}` : `${moduleCode}_${langCode}`;
|
|
25
|
+
}
|
|
26
|
+
function toQueryString(params) {
|
|
27
|
+
return new URLSearchParams(
|
|
28
|
+
Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
|
|
29
|
+
).toString();
|
|
30
|
+
}
|
|
31
|
+
async function processTask(task, payload) {
|
|
32
|
+
const { baseURL, headers, pullPath, pullMethod, stores } = payload;
|
|
33
|
+
const params = {};
|
|
34
|
+
if (task.storeName) params["storeName"] = task.storeName;
|
|
35
|
+
if (task.langCode) params["langCode"] = task.langCode;
|
|
36
|
+
if (task.moduleCode) params["moduleCode"] = task.moduleCode;
|
|
37
|
+
if (task.version != null) params["version"] = String(task.version);
|
|
38
|
+
const url = baseURL + pullPath;
|
|
39
|
+
let res;
|
|
40
|
+
if (pullMethod === "POST") {
|
|
41
|
+
res = await fetch(url, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
44
|
+
body: JSON.stringify(params)
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
res = await fetch(`${url}?${toQueryString(params)}`, { headers });
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok) throw new Error(`[i18n rn-worker] fetch failed: ${res.status}`);
|
|
50
|
+
const blocks = await res.json();
|
|
51
|
+
for (const block of blocks) {
|
|
52
|
+
for (const mod of block.modules ?? []) {
|
|
53
|
+
const store = stores.find((s) => s.name === task.storeName);
|
|
54
|
+
if (!store) continue;
|
|
55
|
+
const storeIndex = stores.indexOf(store);
|
|
56
|
+
const key = buildKey(mod.moduleCode, block.langCode, store);
|
|
57
|
+
const parsed = parseI18nValues(mod.i18nValues ?? []);
|
|
58
|
+
const resources = storeIndex === 0 ? flatToNested(parsed) : parsed;
|
|
59
|
+
self.postMessage({
|
|
60
|
+
type: "dataReady",
|
|
61
|
+
storeName: store.name,
|
|
62
|
+
storeIndex,
|
|
63
|
+
record: {
|
|
64
|
+
key,
|
|
65
|
+
moduleCode: mod.moduleCode,
|
|
66
|
+
langCode: block.langCode,
|
|
67
|
+
version: mod.version,
|
|
68
|
+
resources
|
|
69
|
+
},
|
|
70
|
+
task
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
self.onmessage = async (event) => {
|
|
76
|
+
if (event.data.type !== "start") return;
|
|
77
|
+
const { payload } = event.data;
|
|
78
|
+
for (const task of payload.tasks) {
|
|
79
|
+
try {
|
|
80
|
+
await processTask(task, payload);
|
|
81
|
+
} catch {
|
|
82
|
+
self.postMessage({
|
|
83
|
+
type: "moduleError",
|
|
84
|
+
storeName: task.storeName,
|
|
85
|
+
langCode: task.langCode,
|
|
86
|
+
moduleCode: task.moduleCode
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
self.postMessage({ type: "done" });
|
|
91
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict"; function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/workers/preload-worker.ts
|
|
2
|
+
function flatToNested(flat) {
|
|
3
|
+
const result = {};
|
|
4
|
+
for (const [key, value] of Object.entries(flat)) {
|
|
5
|
+
const parts = key.split(".");
|
|
6
|
+
let cur = result;
|
|
7
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
8
|
+
const part = parts[i];
|
|
9
|
+
if (typeof cur[part] !== "object" || cur[part] === null) cur[part] = {};
|
|
10
|
+
cur = cur[part];
|
|
11
|
+
}
|
|
12
|
+
cur[parts[parts.length - 1]] = value;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
function deepMerge(target, source) {
|
|
17
|
+
const result = { ...target };
|
|
18
|
+
for (const [key, sv] of Object.entries(source)) {
|
|
19
|
+
const tv = result[key];
|
|
20
|
+
if (sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv)) {
|
|
21
|
+
result[key] = deepMerge(tv, sv);
|
|
22
|
+
} else {
|
|
23
|
+
result[key] = sv;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function parseI18nValues(values) {
|
|
29
|
+
const result = {};
|
|
30
|
+
for (const { termCode, langValue } of values) {
|
|
31
|
+
if (termCode && langValue !== void 0) result[termCode] = _nullishCoalesce(langValue, () => ( ""));
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function buildKey(moduleCode, langCode, store) {
|
|
36
|
+
return store.cacheGroupKey ? `${moduleCode}_${langCode}_${store.cacheGroupKey}` : `${moduleCode}_${langCode}`;
|
|
37
|
+
}
|
|
38
|
+
function toQueryString(params) {
|
|
39
|
+
return new URLSearchParams(
|
|
40
|
+
Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
|
|
41
|
+
).toString();
|
|
42
|
+
}
|
|
43
|
+
var db = null;
|
|
44
|
+
function openDB(dbName, storeNames) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const req = indexedDB.open(dbName, 2);
|
|
47
|
+
req.onupgradeneeded = (event) => {
|
|
48
|
+
const database = event.target.result;
|
|
49
|
+
for (const name of storeNames) {
|
|
50
|
+
const storeName = `i18n_resources_${name}`;
|
|
51
|
+
if (!database.objectStoreNames.contains(storeName)) {
|
|
52
|
+
const s = database.createObjectStore(storeName, { keyPath: "key" });
|
|
53
|
+
s.createIndex("by_module", "moduleCode");
|
|
54
|
+
s.createIndex("by_language", "langCode");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!database.objectStoreNames.contains("i18n_meta")) {
|
|
58
|
+
database.createObjectStore("i18n_meta", { keyPath: "key" });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
req.onsuccess = () => resolve(req.result);
|
|
62
|
+
req.onerror = () => reject(req.error);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function idbGet(database, storeName, key) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const tx = database.transaction(storeName, "readonly");
|
|
68
|
+
const req = tx.objectStore(storeName).get(key);
|
|
69
|
+
req.onsuccess = () => resolve(req.result);
|
|
70
|
+
req.onerror = () => reject(req.error);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function idbPut(database, storeName, value) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const tx = database.transaction(storeName, "readwrite");
|
|
76
|
+
const req = tx.objectStore(storeName).put(value);
|
|
77
|
+
req.onsuccess = () => resolve();
|
|
78
|
+
req.onerror = () => reject(req.error);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function processTask(task, payload) {
|
|
82
|
+
const { baseURL, headers, pullPath, pullMethod, stores } = payload;
|
|
83
|
+
const params = {};
|
|
84
|
+
if (task.storeName) params["storeName"] = task.storeName;
|
|
85
|
+
if (task.langCode) params["langCode"] = task.langCode;
|
|
86
|
+
if (task.moduleCode) params["moduleCode"] = task.moduleCode;
|
|
87
|
+
if (task.version != null) params["version"] = String(task.version);
|
|
88
|
+
const url = baseURL + pullPath;
|
|
89
|
+
let res;
|
|
90
|
+
if (pullMethod === "POST") {
|
|
91
|
+
res = await fetch(url, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
94
|
+
body: JSON.stringify(params)
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
res = await fetch(`${url}?${toQueryString(params)}`, { headers });
|
|
98
|
+
}
|
|
99
|
+
if (!res.ok) throw new Error(`[i18n worker] fetch failed: ${res.status}`);
|
|
100
|
+
const blocks = await res.json();
|
|
101
|
+
for (const block of blocks) {
|
|
102
|
+
for (const mod of _nullishCoalesce(block.modules, () => ( []))) {
|
|
103
|
+
const store = stores.find((s) => s.name === task.storeName);
|
|
104
|
+
if (!store) continue;
|
|
105
|
+
const storeIndex = stores.indexOf(store);
|
|
106
|
+
const idbStoreName = `i18n_resources_${store.name}`;
|
|
107
|
+
const key = buildKey(mod.moduleCode, block.langCode, store);
|
|
108
|
+
const parsed = parseI18nValues(_nullishCoalesce(mod.i18nValues, () => ( [])));
|
|
109
|
+
let resources;
|
|
110
|
+
if (storeIndex === 0) {
|
|
111
|
+
const existing = await idbGet(
|
|
112
|
+
db,
|
|
113
|
+
idbStoreName,
|
|
114
|
+
key
|
|
115
|
+
);
|
|
116
|
+
resources = deepMerge(
|
|
117
|
+
_nullishCoalesce(_optionalChain([existing, 'optionalAccess', _ => _.resources]), () => ( {})),
|
|
118
|
+
flatToNested(parsed)
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
const existing = await idbGet(
|
|
122
|
+
db,
|
|
123
|
+
idbStoreName,
|
|
124
|
+
key
|
|
125
|
+
);
|
|
126
|
+
resources = { ..._nullishCoalesce(_optionalChain([existing, 'optionalAccess', _2 => _2.resources]), () => ( {})), ...parsed };
|
|
127
|
+
}
|
|
128
|
+
const record = {
|
|
129
|
+
key,
|
|
130
|
+
moduleCode: mod.moduleCode,
|
|
131
|
+
langCode: block.langCode,
|
|
132
|
+
version: mod.version,
|
|
133
|
+
resources
|
|
134
|
+
};
|
|
135
|
+
await idbPut(db, idbStoreName, record);
|
|
136
|
+
self.postMessage({
|
|
137
|
+
type: "moduleLoaded",
|
|
138
|
+
storeName: task.storeName,
|
|
139
|
+
langCode: block.langCode,
|
|
140
|
+
moduleCode: mod.moduleCode,
|
|
141
|
+
version: mod.version
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
self.onmessage = async (event) => {
|
|
147
|
+
if (event.data.type !== "start") return;
|
|
148
|
+
const { payload } = event.data;
|
|
149
|
+
const storeNames = payload.stores.map((s) => s.name);
|
|
150
|
+
try {
|
|
151
|
+
db = await openDB(payload.dbName, storeNames);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
self.postMessage({ type: "error", message: String(err) });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
for (const task of payload.tasks) {
|
|
157
|
+
try {
|
|
158
|
+
await processTask(task, payload);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
self.postMessage({
|
|
161
|
+
type: "moduleError",
|
|
162
|
+
storeName: task.storeName,
|
|
163
|
+
langCode: task.langCode,
|
|
164
|
+
moduleCode: task.moduleCode
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
self.postMessage({ type: "done" });
|
|
169
|
+
};
|