intor 2.2.1 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.d.cts +9 -9
- package/dist/config/index.d.ts +9 -9
- package/dist/index.cjs +307 -453
- package/dist/index.d.cts +109 -51
- package/dist/index.d.ts +109 -51
- package/dist/index.js +309 -456
- package/dist/next/index.cjs +141 -148
- package/dist/next/index.d.cts +9 -9
- package/dist/next/index.d.ts +9 -9
- package/dist/next/index.js +139 -147
- package/dist/next/middleware/index.d.cts +9 -9
- package/dist/next/middleware/index.d.ts +9 -9
- package/dist/next/server/index.cjs +287 -430
- package/dist/next/server/index.d.cts +9 -9
- package/dist/next/server/index.d.ts +9 -9
- package/dist/next/server/index.js +289 -433
- package/package.json +6 -12
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { performance } from 'perf_hooks';
|
|
2
|
+
import { performance as performance$1 } from 'perf_hooks';
|
|
3
3
|
import pLimit from 'p-limit';
|
|
4
4
|
import fs from 'fs/promises';
|
|
5
5
|
import { logry } from 'logry';
|
|
6
|
+
import merge from 'lodash.merge';
|
|
6
7
|
import Keyv from 'keyv';
|
|
7
8
|
import { Translator } from 'intor-translator';
|
|
8
9
|
export { Translator } from 'intor-translator';
|
|
@@ -11,7 +12,7 @@ export { Translator } from 'intor-translator';
|
|
|
11
12
|
var shouldLoadMessages = (loader) => {
|
|
12
13
|
if (!loader) return false;
|
|
13
14
|
const { type, lazyLoad } = loader;
|
|
14
|
-
if (type === "
|
|
15
|
+
if (type === "local") return true;
|
|
15
16
|
if (lazyLoad) return false;
|
|
16
17
|
return true;
|
|
17
18
|
};
|
|
@@ -40,17 +41,18 @@ var DEFAULT_FORMATTER_CONFIG = {
|
|
|
40
41
|
node: { meta: { compact: true }, lineBreaksAfter: 1 }
|
|
41
42
|
};
|
|
42
43
|
function getLogger({
|
|
43
|
-
id,
|
|
44
|
+
id = "default",
|
|
44
45
|
formatterConfig,
|
|
45
46
|
preset,
|
|
46
47
|
...options
|
|
47
48
|
}) {
|
|
48
49
|
const pool = getGlobalLoggerPool();
|
|
49
50
|
let logger = pool.get(id);
|
|
51
|
+
const useDefault = !formatterConfig && !preset;
|
|
50
52
|
if (!logger) {
|
|
51
53
|
logger = logry({
|
|
52
54
|
id,
|
|
53
|
-
formatterConfig:
|
|
55
|
+
formatterConfig: useDefault ? DEFAULT_FORMATTER_CONFIG : formatterConfig,
|
|
54
56
|
preset,
|
|
55
57
|
...options
|
|
56
58
|
});
|
|
@@ -63,306 +65,173 @@ function getLogger({
|
|
|
63
65
|
return logger;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
// src/
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// src/modules/messages/load-local-messages/load-namespace-group/parse-message-file.ts
|
|
79
|
-
var MAX_PATH_LENGTH = 260;
|
|
80
|
-
var parseMessageFile = async (filePath, loggerOptions) => {
|
|
68
|
+
// src/modules/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.ts
|
|
69
|
+
async function collectFileEntries({
|
|
70
|
+
readdir = fs.readdir,
|
|
71
|
+
limit,
|
|
72
|
+
rootDir,
|
|
73
|
+
namespaces,
|
|
74
|
+
extraOptions: { exts = [".json"], loggerOptions } = {}
|
|
75
|
+
}) {
|
|
81
76
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
82
|
-
const logger = baseLogger.child({ scope: "
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
const fileName = path.basename(trimmedPath);
|
|
93
|
-
if (!fileName.toLowerCase().endsWith(".json")) {
|
|
94
|
-
logger.trace("Skipped non-JSON file.", { filePath: trimmedPath });
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
try {
|
|
98
|
-
const content = await fs.readFile(trimmedPath, "utf8");
|
|
99
|
-
const parsed = JSON.parse(content);
|
|
100
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
101
|
-
throw new IntorError({
|
|
102
|
-
id: loggerOptions.id,
|
|
103
|
-
code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
|
|
104
|
-
message: "Invalid message format"
|
|
105
|
-
});
|
|
77
|
+
const logger = baseLogger.child({ scope: "collect-file-entries" });
|
|
78
|
+
const results = [];
|
|
79
|
+
const walk = async (currentDir) => {
|
|
80
|
+
let entries = [];
|
|
81
|
+
try {
|
|
82
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.error(`Error reading directory: ${currentDir}`, { error });
|
|
85
|
+
return;
|
|
106
86
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
87
|
+
const tasks = entries.map(
|
|
88
|
+
(entry) => limit(async () => {
|
|
89
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
await walk(fullPath);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!exts.some((ext2) => entry.name.endsWith(ext2))) return;
|
|
95
|
+
const relativePath = path.relative(rootDir, fullPath);
|
|
96
|
+
const ext = path.extname(relativePath);
|
|
97
|
+
const withoutExt = relativePath.slice(0, -ext.length);
|
|
98
|
+
const segments = withoutExt.split(path.sep).filter(Boolean);
|
|
99
|
+
const namespace = segments.at(0);
|
|
100
|
+
if (!namespace) return;
|
|
101
|
+
if (namespaces && namespace !== "index") {
|
|
102
|
+
if (!namespaces.includes(namespace)) return;
|
|
103
|
+
}
|
|
104
|
+
results.push({
|
|
105
|
+
namespace,
|
|
106
|
+
fullPath,
|
|
107
|
+
relativePath,
|
|
108
|
+
segments,
|
|
109
|
+
basename: path.basename(entry.name, ext)
|
|
110
|
+
});
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
await Promise.all(tasks);
|
|
114
|
+
};
|
|
115
|
+
await walk(rootDir);
|
|
116
|
+
if (logger.core.level === "debug") {
|
|
117
|
+
logger.debug("Local message files collected.", {
|
|
118
|
+
count: results.length
|
|
113
119
|
});
|
|
114
|
-
return null;
|
|
115
120
|
}
|
|
116
|
-
|
|
121
|
+
logger.trace("Local message files collected.", {
|
|
122
|
+
count: results.length,
|
|
123
|
+
fileEntries: results.map(({ namespace, relativePath }) => ({
|
|
124
|
+
namespace: namespace === "index" ? null : namespace,
|
|
125
|
+
relativePath
|
|
126
|
+
}))
|
|
127
|
+
});
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
117
130
|
|
|
118
|
-
// src/modules/messages/
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
+
// src/modules/messages/shared/utils/is-valid-messages.ts
|
|
132
|
+
function isPlainObject(value) {
|
|
133
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
134
|
+
}
|
|
135
|
+
function isValidMessages(value) {
|
|
136
|
+
if (!isPlainObject(value)) return false;
|
|
137
|
+
const stack = [value];
|
|
138
|
+
while (stack.length > 0) {
|
|
139
|
+
const current = stack.pop();
|
|
140
|
+
for (const v of Object.values(current)) {
|
|
141
|
+
if (typeof v === "string") continue;
|
|
142
|
+
if (isPlainObject(v)) {
|
|
143
|
+
stack.push(v);
|
|
131
144
|
} else {
|
|
132
|
-
|
|
133
|
-
subEntries[name] = content;
|
|
145
|
+
return false;
|
|
134
146
|
}
|
|
135
|
-
})
|
|
136
|
-
);
|
|
137
|
-
return { base: baseContent, sub: subEntries };
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// src/modules/messages/load-local-messages/load-namespace-group/load-namespace-group.ts
|
|
141
|
-
var loadNamespaceGroup = async ({
|
|
142
|
-
locale,
|
|
143
|
-
namespace,
|
|
144
|
-
messages,
|
|
145
|
-
namespaceGroupValue,
|
|
146
|
-
limit,
|
|
147
|
-
logger: loggerOptions = { id: "default" }
|
|
148
|
-
}) => {
|
|
149
|
-
const baseLogger = getLogger({ ...loggerOptions });
|
|
150
|
-
const logger = baseLogger.child({ scope: "load-namespace-group" });
|
|
151
|
-
const { isAtRoot, filePaths } = namespaceGroupValue;
|
|
152
|
-
if (filePaths.length === 0) {
|
|
153
|
-
logger.trace(
|
|
154
|
-
`Skipped merging ${locale}/${namespace} because filePaths is empty`
|
|
155
|
-
);
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
return limit(async () => {
|
|
159
|
-
const { base, sub } = await mergeNamespaceMessages(
|
|
160
|
-
filePaths,
|
|
161
|
-
isAtRoot,
|
|
162
|
-
loggerOptions
|
|
163
|
-
);
|
|
164
|
-
if (!messages[locale]) {
|
|
165
|
-
messages[locale] = {};
|
|
166
|
-
}
|
|
167
|
-
if (isAtRoot && filePaths.length === 1 && path.basename(filePaths[0]) === "index.json") {
|
|
168
|
-
messages[locale] = { ...messages[locale], ...base };
|
|
169
|
-
return;
|
|
170
147
|
}
|
|
171
|
-
const finalContent = isAtRoot ? base : { ...base, ...sub };
|
|
172
|
-
messages[locale][namespace] = finalContent;
|
|
173
|
-
if (!isAtRoot && Object.keys(finalContent).length > 0) {
|
|
174
|
-
logger.trace(
|
|
175
|
-
`Merged ${locale}/${namespace} from ${filePaths.length} file(s)`,
|
|
176
|
-
{ namespace }
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
};
|
|
181
|
-
var addToNamespaceGroup = ({
|
|
182
|
-
options: { namespaces },
|
|
183
|
-
filePath,
|
|
184
|
-
namespaceGroups,
|
|
185
|
-
namespacePathSegments
|
|
186
|
-
}) => {
|
|
187
|
-
if (!filePath) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
const isAtRoot = namespacePathSegments.length === 0;
|
|
191
|
-
const nsKey = isAtRoot ? path.basename(filePath, ".json") : namespacePathSegments.join(".");
|
|
192
|
-
if (namespaces && namespaces.size > 0 && !namespaces.has(nsKey)) {
|
|
193
|
-
return;
|
|
194
148
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
async function jsonReader(filePath, readFile = fs.readFile) {
|
|
152
|
+
const raw = await readFile(filePath, "utf8");
|
|
153
|
+
const parsed = JSON.parse(raw);
|
|
154
|
+
return parsed;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
|
|
158
|
+
function nestObjectFromPath(path5, value) {
|
|
159
|
+
let obj = value;
|
|
160
|
+
for (let i = path5.length - 1; i >= 0; i--) {
|
|
161
|
+
obj = { [path5[i]]: obj };
|
|
204
162
|
}
|
|
205
|
-
|
|
163
|
+
return obj;
|
|
164
|
+
}
|
|
206
165
|
|
|
207
|
-
// src/modules/messages/load-local-messages/
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
readdir = fs.readdir
|
|
214
|
-
}) => {
|
|
215
|
-
const { limit } = options;
|
|
216
|
-
const loggerOptions = options.logger || { id: "default" };
|
|
166
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.ts
|
|
167
|
+
async function parseFileEntries({
|
|
168
|
+
fileEntries,
|
|
169
|
+
limit,
|
|
170
|
+
extraOptions: { messagesReader, loggerOptions } = {}
|
|
171
|
+
}) {
|
|
217
172
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
218
|
-
const logger = baseLogger.child({ scope: "
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
});
|
|
231
|
-
} else if (dirent.isDirectory()) {
|
|
232
|
-
await traverseDirectory({
|
|
233
|
-
namespaceGroups,
|
|
234
|
-
currentDirPath: filePath,
|
|
235
|
-
namespacePathSegments: [...namespacePathSegments, dirent.name],
|
|
236
|
-
options,
|
|
237
|
-
readdir
|
|
238
|
-
});
|
|
173
|
+
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
174
|
+
const parsedFileEntries = [];
|
|
175
|
+
const tasks = fileEntries.map(
|
|
176
|
+
({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
177
|
+
try {
|
|
178
|
+
const segsWithoutNs = segments.slice(1);
|
|
179
|
+
const ext = path.extname(fullPath);
|
|
180
|
+
const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
|
|
181
|
+
if (!isValidMessages(json)) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
"JSON file does not match NamespaceMessages structure"
|
|
184
|
+
);
|
|
239
185
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
186
|
+
const isIndex = basename === "index";
|
|
187
|
+
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
188
|
+
const nested = nestObjectFromPath(keyPath, json);
|
|
189
|
+
parsedFileEntries.push({ namespace, messages: nested });
|
|
190
|
+
logger.trace("Parsed file.", { path: fullPath });
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logger.error("Failed to read or parse file.", {
|
|
193
|
+
path: fullPath,
|
|
245
194
|
error
|
|
246
195
|
});
|
|
247
|
-
}
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
await Promise.all(tasks);
|
|
200
|
+
const result = {};
|
|
201
|
+
for (const { namespace, messages } of parsedFileEntries) {
|
|
202
|
+
if (namespace === "index") {
|
|
203
|
+
merge(result, messages);
|
|
204
|
+
} else {
|
|
205
|
+
result[namespace] = merge(
|
|
206
|
+
result[namespace] ?? {},
|
|
207
|
+
messages
|
|
208
|
+
);
|
|
209
|
+
}
|
|
252
210
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// src/modules/messages/load-local-messages/prepare-namespace-groups/prepare-namespace-groups.ts
|
|
256
|
-
var prepareNamespaceGroups = async (options) => {
|
|
257
|
-
const { basePath } = options;
|
|
258
|
-
const namespaceGroups = /* @__PURE__ */ new Map();
|
|
259
|
-
await traverseDirectory({
|
|
260
|
-
options,
|
|
261
|
-
currentDirPath: basePath,
|
|
262
|
-
namespaceGroups,
|
|
263
|
-
namespacePathSegments: []
|
|
264
|
-
});
|
|
265
|
-
return namespaceGroups;
|
|
266
|
-
};
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
267
213
|
|
|
268
|
-
// src/modules/messages/load-local-messages/
|
|
269
|
-
var
|
|
270
|
-
|
|
214
|
+
// src/modules/messages/load-local-messages/read-locale-messages/read-locale-messages.ts
|
|
215
|
+
var readLocaleMessages = async ({
|
|
216
|
+
limit,
|
|
217
|
+
rootDir = "messages",
|
|
271
218
|
locale,
|
|
272
219
|
namespaces,
|
|
273
|
-
|
|
274
|
-
limit,
|
|
275
|
-
logger: loggerOptions = { id: "default" }
|
|
220
|
+
extraOptions: { exts, messagesReader, loggerOptions } = {}
|
|
276
221
|
}) => {
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const validNamespaces = [];
|
|
281
|
-
try {
|
|
282
|
-
const stat = await fs.stat(localePath);
|
|
283
|
-
if (!stat.isDirectory()) {
|
|
284
|
-
logger.warn("Locale path is not a directory.", {
|
|
285
|
-
locale,
|
|
286
|
-
path: localePath
|
|
287
|
-
});
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
} catch (error) {
|
|
291
|
-
logger.warn("Error checking locale path.", { locale, error });
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
const namespaceGroups = await prepareNamespaceGroups({
|
|
295
|
-
basePath: localePath,
|
|
222
|
+
const fileEntries = await collectFileEntries({
|
|
223
|
+
rootDir: path.resolve(process.cwd(), rootDir, locale),
|
|
224
|
+
namespaces,
|
|
296
225
|
limit,
|
|
297
|
-
|
|
298
|
-
logger: loggerOptions
|
|
226
|
+
extraOptions: { exts, loggerOptions }
|
|
299
227
|
});
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
namespaces
|
|
305
|
-
});
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
logger.trace("Prepared namespace groups from scanning local files.", {
|
|
309
|
-
namespaceGroups: [...namespaceGroups.entries()].map(([ns, val]) => ({
|
|
310
|
-
namespace: ns,
|
|
311
|
-
isAtRoot: val.isAtRoot,
|
|
312
|
-
fileCount: val.filePaths.length
|
|
313
|
-
}))
|
|
314
|
-
});
|
|
315
|
-
const namespaceGroupTasks = [...namespaceGroups.entries()].filter(
|
|
316
|
-
([ns]) => !namespaces || namespaces.length === 0 || namespaces.includes(ns)
|
|
317
|
-
).map(
|
|
318
|
-
([namespace, namespaceGroupValue]) => loadNamespaceGroup({
|
|
319
|
-
locale,
|
|
320
|
-
namespace,
|
|
321
|
-
messages,
|
|
322
|
-
namespaceGroupValue,
|
|
323
|
-
limit,
|
|
324
|
-
logger: loggerOptions
|
|
325
|
-
}).then(() => validNamespaces.push(namespace))
|
|
326
|
-
);
|
|
327
|
-
await Promise.all(namespaceGroupTasks);
|
|
328
|
-
return validNamespaces;
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
// src/modules/messages/load-local-messages/load-locale-with-fallback/load-locale-with-fallback.ts
|
|
332
|
-
var loadLocaleWithFallback = async ({
|
|
333
|
-
basePath,
|
|
334
|
-
locale: targetLocale,
|
|
335
|
-
fallbackLocales = [],
|
|
336
|
-
namespaces,
|
|
337
|
-
messages,
|
|
338
|
-
limit,
|
|
339
|
-
logger: loggerOptions = { id: "default" }
|
|
340
|
-
}) => {
|
|
341
|
-
const baseLogger = getLogger({ ...loggerOptions });
|
|
342
|
-
const logger = baseLogger.child({ scope: "load-locale-with-fallback" });
|
|
343
|
-
const candidateLocales = [targetLocale, ...fallbackLocales];
|
|
344
|
-
for (const locale of candidateLocales) {
|
|
345
|
-
try {
|
|
346
|
-
const validNamespaces = await loadSingleLocale({
|
|
347
|
-
basePath,
|
|
348
|
-
locale,
|
|
349
|
-
namespaces,
|
|
350
|
-
messages,
|
|
351
|
-
limit,
|
|
352
|
-
logger: loggerOptions
|
|
353
|
-
});
|
|
354
|
-
return validNamespaces;
|
|
355
|
-
} catch (error) {
|
|
356
|
-
logger.warn("Error occurred while processing the locale.", {
|
|
357
|
-
locale,
|
|
358
|
-
error
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
logger.warn("All fallback locales failed.", {
|
|
363
|
-
attemptedLocales: candidateLocales
|
|
228
|
+
const namespaceMessages = await parseFileEntries({
|
|
229
|
+
fileEntries,
|
|
230
|
+
limit,
|
|
231
|
+
extraOptions: { messagesReader, loggerOptions }
|
|
364
232
|
});
|
|
365
|
-
|
|
233
|
+
const localeMessages = { [locale]: namespaceMessages };
|
|
234
|
+
return localeMessages;
|
|
366
235
|
};
|
|
367
236
|
function getGlobalMessagesPool() {
|
|
368
237
|
if (!globalThis.__INTOR_MESSAGES_POOL__) {
|
|
@@ -374,22 +243,9 @@ function clearMessagesPool() {
|
|
|
374
243
|
const pool = getGlobalMessagesPool();
|
|
375
244
|
pool.clear();
|
|
376
245
|
}
|
|
377
|
-
|
|
378
|
-
// src/shared/utils/merge-messages.ts
|
|
379
246
|
var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const loaded = loadedMessages[locale];
|
|
383
|
-
if (!result[locale]) {
|
|
384
|
-
result[locale] = loaded;
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
result[locale] = {
|
|
388
|
-
...result[locale],
|
|
389
|
-
...loaded
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
return result;
|
|
247
|
+
if (!loadedMessages) return { ...staticMessages };
|
|
248
|
+
return merge({}, staticMessages, loadedMessages);
|
|
393
249
|
};
|
|
394
250
|
|
|
395
251
|
// src/shared/utils/normalize-cache-key.ts
|
|
@@ -421,6 +277,8 @@ var resolveNamespaces = ({
|
|
|
421
277
|
}) => {
|
|
422
278
|
const { loader } = config;
|
|
423
279
|
const { routeNamespaces = {}, namespaces } = loader || {};
|
|
280
|
+
if (Object.keys(routeNamespaces).length === 0 && !namespaces)
|
|
281
|
+
return void 0;
|
|
424
282
|
const standardizedPathname = standardizePathname({ config, pathname });
|
|
425
283
|
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
426
284
|
`/${PREFIX_PLACEHOLDER}`,
|
|
@@ -578,43 +436,35 @@ var standardizePathname = ({
|
|
|
578
436
|
|
|
579
437
|
// src/modules/messages/load-local-messages/load-local-messages.ts
|
|
580
438
|
var loadLocalMessages = async ({
|
|
581
|
-
|
|
439
|
+
pool = getGlobalMessagesPool(),
|
|
440
|
+
rootDir = "messages",
|
|
582
441
|
locale,
|
|
583
442
|
fallbackLocales,
|
|
584
443
|
namespaces,
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
444
|
+
extraOptions: {
|
|
445
|
+
concurrency = 10,
|
|
446
|
+
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
447
|
+
loggerOptions = { id: "default" },
|
|
448
|
+
exts,
|
|
449
|
+
messagesReader
|
|
450
|
+
} = {}
|
|
588
451
|
}) => {
|
|
589
|
-
basePath = basePath ?? "messages";
|
|
590
|
-
if (!locale || locale.trim() === "") return {};
|
|
591
452
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
592
|
-
const logger = baseLogger.child({ scope: "load-
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
);
|
|
598
|
-
const start = performance.now();
|
|
599
|
-
logger.trace("Starting to load local messages with configuration.", {
|
|
600
|
-
path: { basePath, resolvedBasePath },
|
|
601
|
-
locale,
|
|
602
|
-
fallbackLocales,
|
|
603
|
-
namespaces: namespaces && namespaces.length > 0 ? { count: namespaces?.length, list: [...namespaces] } : "All Namespaces",
|
|
604
|
-
concurrency
|
|
453
|
+
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
454
|
+
const start = performance$1.now();
|
|
455
|
+
logger.debug("Loading local messages from directory.", {
|
|
456
|
+
rootDir,
|
|
457
|
+
resolvedRootDir: path.resolve(process.cwd(), rootDir)
|
|
605
458
|
});
|
|
606
|
-
let pool;
|
|
607
|
-
if (cache.enabled) {
|
|
608
|
-
pool = getGlobalMessagesPool();
|
|
609
|
-
}
|
|
610
459
|
const key = normalizeCacheKey([
|
|
611
460
|
loggerOptions.id,
|
|
612
|
-
|
|
461
|
+
"loaderType:local",
|
|
462
|
+
rootDir,
|
|
613
463
|
locale,
|
|
614
|
-
(fallbackLocales
|
|
615
|
-
(namespaces
|
|
464
|
+
(fallbackLocales || []).toSorted().join(","),
|
|
465
|
+
(namespaces || []).toSorted().join(",")
|
|
616
466
|
]);
|
|
617
|
-
if (
|
|
467
|
+
if (cacheOptions.enabled && key) {
|
|
618
468
|
const cached = await pool?.get(key);
|
|
619
469
|
if (cached) {
|
|
620
470
|
logger.debug("Messages cache hit.", { key });
|
|
@@ -622,50 +472,57 @@ var loadLocalMessages = async ({
|
|
|
622
472
|
}
|
|
623
473
|
}
|
|
624
474
|
const limit = pLimit(concurrency);
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
475
|
+
const candidateLocales = [locale, ...fallbackLocales || []];
|
|
476
|
+
let messages;
|
|
477
|
+
for (const candidateLocale of candidateLocales) {
|
|
478
|
+
try {
|
|
479
|
+
const result = await readLocaleMessages({
|
|
480
|
+
limit,
|
|
481
|
+
rootDir,
|
|
482
|
+
locale: candidateLocale,
|
|
483
|
+
namespaces,
|
|
484
|
+
extraOptions: { loggerOptions, exts, messagesReader }
|
|
485
|
+
});
|
|
486
|
+
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
487
|
+
messages = result;
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
} catch (error) {
|
|
491
|
+
logger.error("Failed to read locale messages", {
|
|
492
|
+
locale: candidateLocale,
|
|
493
|
+
error
|
|
494
|
+
});
|
|
495
|
+
}
|
|
636
496
|
}
|
|
637
|
-
|
|
497
|
+
if (cacheOptions.enabled && key && messages) {
|
|
498
|
+
await pool?.set(key, messages, cacheOptions.ttl);
|
|
499
|
+
}
|
|
500
|
+
const end = performance$1.now();
|
|
638
501
|
const duration = Math.round(end - start);
|
|
639
502
|
logger.trace("Finished loading local messages.", {
|
|
640
|
-
|
|
641
|
-
validNamespaces,
|
|
503
|
+
loadedLocale: messages ? Object.keys(messages)[0] : void 0,
|
|
642
504
|
duration: `${duration} ms`
|
|
643
505
|
});
|
|
644
506
|
return messages;
|
|
645
507
|
};
|
|
646
508
|
|
|
647
|
-
// src/modules/messages/
|
|
648
|
-
var
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
// src/modules/messages/load-api-messages/fetch-messages.ts
|
|
653
|
-
var fetchMessages = async ({
|
|
654
|
-
apiUrl,
|
|
655
|
-
apiHeaders,
|
|
656
|
-
locale,
|
|
509
|
+
// src/modules/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.ts
|
|
510
|
+
var fetchLocaleMessages = async ({
|
|
511
|
+
remoteUrl,
|
|
512
|
+
remoteHeaders,
|
|
657
513
|
searchParams,
|
|
658
|
-
|
|
514
|
+
locale,
|
|
515
|
+
extraOptions: { loggerOptions } = {}
|
|
659
516
|
}) => {
|
|
660
517
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
661
|
-
const logger = baseLogger.child({ scope: "fetch-messages" });
|
|
518
|
+
const logger = baseLogger.child({ scope: "fetch-locale-messages" });
|
|
662
519
|
try {
|
|
663
520
|
const params = new URLSearchParams(searchParams);
|
|
664
521
|
params.append("locale", locale);
|
|
665
|
-
const url = `${
|
|
522
|
+
const url = `${remoteUrl}?${params.toString()}`;
|
|
666
523
|
const headers = {
|
|
667
524
|
"Content-Type": "application/json",
|
|
668
|
-
...
|
|
525
|
+
...remoteHeaders
|
|
669
526
|
};
|
|
670
527
|
const response = await fetch(url, {
|
|
671
528
|
method: "GET",
|
|
@@ -673,17 +530,17 @@ var fetchMessages = async ({
|
|
|
673
530
|
cache: "no-store"
|
|
674
531
|
});
|
|
675
532
|
if (!response.ok) {
|
|
676
|
-
throw new Error(`
|
|
533
|
+
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
677
534
|
}
|
|
678
535
|
const data = await response.json();
|
|
679
|
-
if (
|
|
680
|
-
throw new Error(
|
|
536
|
+
if (!isValidMessages(data[locale])) {
|
|
537
|
+
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
681
538
|
}
|
|
682
539
|
return data;
|
|
683
540
|
} catch (error) {
|
|
684
|
-
logger.warn(
|
|
541
|
+
logger.warn("Fetching locale messages failed.", {
|
|
685
542
|
locale,
|
|
686
|
-
|
|
543
|
+
remoteUrl,
|
|
687
544
|
searchParams: decodeURIComponent(searchParams.toString()),
|
|
688
545
|
error
|
|
689
546
|
});
|
|
@@ -691,30 +548,7 @@ var fetchMessages = async ({
|
|
|
691
548
|
}
|
|
692
549
|
};
|
|
693
550
|
|
|
694
|
-
// src/modules/messages/load-
|
|
695
|
-
var fetchFallbackMessages = async ({
|
|
696
|
-
apiUrl,
|
|
697
|
-
apiHeaders,
|
|
698
|
-
searchParams,
|
|
699
|
-
fallbackLocales,
|
|
700
|
-
logger
|
|
701
|
-
}) => {
|
|
702
|
-
for (const fallbackLocale of fallbackLocales) {
|
|
703
|
-
const result = await fetchMessages({
|
|
704
|
-
apiUrl,
|
|
705
|
-
searchParams,
|
|
706
|
-
locale: fallbackLocale,
|
|
707
|
-
apiHeaders,
|
|
708
|
-
logger
|
|
709
|
-
});
|
|
710
|
-
if (result) {
|
|
711
|
-
return { locale: fallbackLocale, messages: result };
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
return;
|
|
715
|
-
};
|
|
716
|
-
|
|
717
|
-
// src/modules/messages/load-api-messages/utils/build-search-params.ts
|
|
551
|
+
// src/modules/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.ts
|
|
718
552
|
var buildSearchParams = (params) => {
|
|
719
553
|
const searchParams = new URLSearchParams();
|
|
720
554
|
const appendParam = (key, value) => {
|
|
@@ -732,119 +566,130 @@ var buildSearchParams = (params) => {
|
|
|
732
566
|
return searchParams;
|
|
733
567
|
};
|
|
734
568
|
|
|
735
|
-
// src/modules/messages/load-
|
|
736
|
-
var
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
569
|
+
// src/modules/messages/load-remote-messages/load-remote-messages.ts
|
|
570
|
+
var loadRemoteMessages = async ({
|
|
571
|
+
pool = getGlobalMessagesPool(),
|
|
572
|
+
rootDir,
|
|
573
|
+
remoteUrl,
|
|
574
|
+
remoteHeaders,
|
|
740
575
|
locale,
|
|
741
576
|
fallbackLocales = [],
|
|
742
577
|
namespaces = [],
|
|
743
|
-
|
|
744
|
-
|
|
578
|
+
extraOptions: {
|
|
579
|
+
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
580
|
+
loggerOptions = { id: "default" }
|
|
581
|
+
} = {}
|
|
745
582
|
}) => {
|
|
746
583
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
747
|
-
const logger = baseLogger.child({ scope: "load-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
let pool;
|
|
753
|
-
if (cache.enabled) {
|
|
754
|
-
pool = getGlobalMessagesPool();
|
|
755
|
-
}
|
|
584
|
+
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
585
|
+
const start = performance.now();
|
|
586
|
+
logger.debug("Loading remote messages from api.", { remoteUrl });
|
|
756
587
|
const key = normalizeCacheKey([
|
|
757
588
|
loggerOptions.id,
|
|
758
|
-
|
|
589
|
+
"loaderType:remote",
|
|
590
|
+
rootDir,
|
|
759
591
|
locale,
|
|
760
592
|
(fallbackLocales ?? []).toSorted().join(","),
|
|
761
593
|
(namespaces ?? []).toSorted().join(",")
|
|
762
594
|
]);
|
|
763
|
-
if (
|
|
595
|
+
if (cacheOptions.enabled && key) {
|
|
764
596
|
const cached = await pool?.get(key);
|
|
765
597
|
if (cached) {
|
|
766
598
|
logger.debug("Messages cache hit.", { key });
|
|
767
599
|
return cached;
|
|
768
600
|
}
|
|
769
601
|
}
|
|
770
|
-
const searchParams = buildSearchParams({
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
602
|
+
const searchParams = buildSearchParams({ rootDir, namespaces });
|
|
603
|
+
const candidateLocales = [locale, ...fallbackLocales || []];
|
|
604
|
+
let messages;
|
|
605
|
+
for (const candidateLocale of candidateLocales) {
|
|
606
|
+
try {
|
|
607
|
+
const result = await fetchLocaleMessages({
|
|
608
|
+
remoteUrl,
|
|
609
|
+
remoteHeaders,
|
|
610
|
+
searchParams,
|
|
611
|
+
locale: candidateLocale,
|
|
612
|
+
extraOptions: { loggerOptions }
|
|
613
|
+
});
|
|
614
|
+
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
615
|
+
messages = result;
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
} catch (error) {
|
|
619
|
+
logger.error("Failed to fetch locale messages.", {
|
|
620
|
+
locale: candidateLocale,
|
|
621
|
+
error
|
|
622
|
+
});
|
|
781
623
|
}
|
|
782
|
-
return messages;
|
|
783
624
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
apiHeaders,
|
|
787
|
-
searchParams,
|
|
788
|
-
fallbackLocales,
|
|
789
|
-
logger: loggerOptions
|
|
790
|
-
});
|
|
791
|
-
if (fallbackResult) {
|
|
792
|
-
logger.info("Fallback locale succeeded.", {
|
|
793
|
-
usedLocale: fallbackResult.locale,
|
|
794
|
-
apiUrl,
|
|
795
|
-
searchParams: decodeURIComponent(searchParams.toString())
|
|
796
|
-
});
|
|
797
|
-
if (cache.enabled && key) {
|
|
798
|
-
await pool?.set(key, fallbackResult.messages, cache.ttl);
|
|
799
|
-
}
|
|
800
|
-
return fallbackResult.messages;
|
|
625
|
+
if (cacheOptions.enabled && key && messages) {
|
|
626
|
+
await pool?.set(key, messages, cacheOptions.ttl);
|
|
801
627
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
628
|
+
const end = performance.now();
|
|
629
|
+
const duration = Math.round(end - start);
|
|
630
|
+
logger.trace("Finished loading remote messages.", {
|
|
631
|
+
loadedLocale: messages ? Object.keys(messages)[0] : void 0,
|
|
632
|
+
duration: `${duration} ms`
|
|
805
633
|
});
|
|
806
|
-
return;
|
|
634
|
+
return messages;
|
|
807
635
|
};
|
|
808
636
|
|
|
809
637
|
// src/modules/messages/load-messages.ts
|
|
810
638
|
var loadMessages = async ({
|
|
811
639
|
config,
|
|
812
640
|
locale,
|
|
813
|
-
pathname
|
|
641
|
+
pathname = "",
|
|
642
|
+
extraOptions: { exts, messagesReader } = {}
|
|
814
643
|
}) => {
|
|
815
644
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
816
|
-
const logger = baseLogger.child({ scope: "messages
|
|
645
|
+
const logger = baseLogger.child({ scope: "load-messages" });
|
|
817
646
|
if (!config.loader) {
|
|
818
647
|
logger.warn(
|
|
819
648
|
"No loader options have been configured in the current config."
|
|
820
649
|
);
|
|
821
650
|
return;
|
|
822
651
|
}
|
|
823
|
-
const {
|
|
652
|
+
const { type, concurrency, rootDir } = config.loader;
|
|
824
653
|
const fallbackLocales = config.fallbackLocales[locale] || [];
|
|
825
654
|
const namespaces = resolveNamespaces({ config, pathname });
|
|
826
|
-
logger.
|
|
827
|
-
|
|
655
|
+
if (logger.core.level === "debug") {
|
|
656
|
+
logger.debug("Starting to load messages.", { locale });
|
|
657
|
+
}
|
|
658
|
+
logger.trace("Starting to load messages with runtime context.", {
|
|
659
|
+
loaderType: type,
|
|
660
|
+
locale,
|
|
661
|
+
fallbackLocales,
|
|
662
|
+
namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : "[ALL]",
|
|
663
|
+
cache: config.cache,
|
|
664
|
+
concurrency: concurrency ?? 10
|
|
828
665
|
});
|
|
829
|
-
logger.debug("Loader type selected.", { loaderType: loader.type });
|
|
830
666
|
let loadedMessages;
|
|
831
|
-
if (
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
...loader,
|
|
667
|
+
if (type === "local") {
|
|
668
|
+
loadedMessages = await loadLocalMessages({
|
|
669
|
+
rootDir,
|
|
835
670
|
locale,
|
|
836
671
|
fallbackLocales,
|
|
837
672
|
namespaces,
|
|
838
|
-
|
|
839
|
-
|
|
673
|
+
extraOptions: {
|
|
674
|
+
concurrency,
|
|
675
|
+
cacheOptions: config.cache,
|
|
676
|
+
loggerOptions: { id: config.id, ...config.logger },
|
|
677
|
+
exts,
|
|
678
|
+
messagesReader
|
|
679
|
+
}
|
|
840
680
|
});
|
|
841
|
-
} else if (
|
|
842
|
-
loadedMessages = await
|
|
843
|
-
|
|
681
|
+
} else if (type === "remote") {
|
|
682
|
+
loadedMessages = await loadRemoteMessages({
|
|
683
|
+
rootDir,
|
|
684
|
+
remoteUrl: config.loader.remoteUrl,
|
|
685
|
+
remoteHeaders: config.loader.remoteHeaders,
|
|
844
686
|
locale,
|
|
845
687
|
fallbackLocales,
|
|
846
688
|
namespaces,
|
|
847
|
-
|
|
689
|
+
extraOptions: {
|
|
690
|
+
cacheOptions: config.cache,
|
|
691
|
+
loggerOptions: { id: config.id, ...config.logger }
|
|
692
|
+
}
|
|
848
693
|
});
|
|
849
694
|
}
|
|
850
695
|
if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
|
|
@@ -854,7 +699,7 @@ var loadMessages = async ({
|
|
|
854
699
|
};
|
|
855
700
|
|
|
856
701
|
// src/modules/intor/intor.ts
|
|
857
|
-
var intor = async (config, i18nContext) => {
|
|
702
|
+
var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
|
|
858
703
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
859
704
|
const logger = baseLogger.child({ scope: "intor" });
|
|
860
705
|
logger.info("Start Intor initialization.");
|
|
@@ -865,20 +710,28 @@ var intor = async (config, i18nContext) => {
|
|
|
865
710
|
pathname: i18nContext?.pathname || ""
|
|
866
711
|
};
|
|
867
712
|
const { locale, pathname } = context;
|
|
868
|
-
const source = isI18nContextFunction ?
|
|
869
|
-
logger.debug(`
|
|
713
|
+
const source = isI18nContextFunction ? i18nContext.name : "static fallback";
|
|
714
|
+
logger.debug(`I18n context resolved via ${source}.`, context);
|
|
870
715
|
let loadedMessages;
|
|
871
716
|
if (shouldLoadMessages(loader)) {
|
|
872
|
-
loadedMessages = await loadMessages({
|
|
717
|
+
loadedMessages = await loadMessages({
|
|
718
|
+
config,
|
|
719
|
+
locale,
|
|
720
|
+
pathname,
|
|
721
|
+
extraOptions: {
|
|
722
|
+
exts: loadMessagesOptions.exts,
|
|
723
|
+
messagesReader: loadMessagesOptions.messagesReader
|
|
724
|
+
}
|
|
725
|
+
});
|
|
873
726
|
}
|
|
874
727
|
const mergedMessages = mergeMessages(messages, loadedMessages);
|
|
875
|
-
logger.info("Messages initialized.", {
|
|
876
|
-
|
|
877
|
-
|
|
728
|
+
logger.info("Messages successfully initialized.", {
|
|
729
|
+
static: { enabled: !!messages },
|
|
730
|
+
loaded: {
|
|
878
731
|
enabled: !!loadedMessages,
|
|
879
732
|
...loader ? { loaderType: loader.type, lazyLoad: !!loader.lazyLoad } : null
|
|
880
733
|
},
|
|
881
|
-
mergedMessages
|
|
734
|
+
merged: mergedMessages
|
|
882
735
|
});
|
|
883
736
|
return {
|
|
884
737
|
config,
|
|
@@ -907,4 +760,4 @@ async function getTranslator(opts) {
|
|
|
907
760
|
};
|
|
908
761
|
}
|
|
909
762
|
|
|
910
|
-
export { PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor,
|
|
763
|
+
export { PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
|