intor 2.2.0 → 2.2.2
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 +306 -453
- package/dist/index.d.cts +76 -54
- package/dist/index.d.ts +76 -54
- package/dist/index.js +309 -457
- 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.cjs +7 -19
- package/dist/next/middleware/index.d.cts +10 -14
- package/dist/next/middleware/index.d.ts +10 -14
- package/dist/next/middleware/index.js +7 -19
- package/dist/next/server/index.cjs +286 -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 -434
- package/package.json +4 -12
|
@@ -2,9 +2,10 @@ import { cookies, headers } from 'next/headers';
|
|
|
2
2
|
import { logry } from 'logry';
|
|
3
3
|
import { Translator } from 'intor-translator';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { performance } from 'perf_hooks';
|
|
5
|
+
import { performance as performance$1 } from 'perf_hooks';
|
|
6
6
|
import pLimit from 'p-limit';
|
|
7
7
|
import fs from 'fs/promises';
|
|
8
|
+
import merge from 'lodash.merge';
|
|
8
9
|
import Keyv from 'keyv';
|
|
9
10
|
|
|
10
11
|
// src/adapters/next/server/get-i18n-context.ts
|
|
@@ -25,17 +26,18 @@ var DEFAULT_FORMATTER_CONFIG = {
|
|
|
25
26
|
node: { meta: { compact: true }, lineBreaksAfter: 1 }
|
|
26
27
|
};
|
|
27
28
|
function getLogger({
|
|
28
|
-
id,
|
|
29
|
+
id = "default",
|
|
29
30
|
formatterConfig,
|
|
30
31
|
preset,
|
|
31
32
|
...options
|
|
32
33
|
}) {
|
|
33
34
|
const pool = getGlobalLoggerPool();
|
|
34
35
|
let logger = pool.get(id);
|
|
36
|
+
const useDefault = !formatterConfig && !preset;
|
|
35
37
|
if (!logger) {
|
|
36
38
|
logger = logry({
|
|
37
39
|
id,
|
|
38
|
-
formatterConfig:
|
|
40
|
+
formatterConfig: useDefault ? DEFAULT_FORMATTER_CONFIG : formatterConfig,
|
|
39
41
|
preset,
|
|
40
42
|
...options
|
|
41
43
|
});
|
|
@@ -77,6 +79,8 @@ var resolveNamespaces = ({
|
|
|
77
79
|
}) => {
|
|
78
80
|
const { loader } = config;
|
|
79
81
|
const { routeNamespaces = {}, namespaces } = loader || {};
|
|
82
|
+
if (Object.keys(routeNamespaces).length === 0 && !namespaces)
|
|
83
|
+
return void 0;
|
|
80
84
|
const standardizedPathname = standardizePathname({ config, pathname });
|
|
81
85
|
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
82
86
|
`/${PREFIX_PLACEHOLDER}`,
|
|
@@ -233,307 +237,171 @@ var DEFAULT_CACHE_OPTIONS = {
|
|
|
233
237
|
ttl: 60 * 60 * 1e3
|
|
234
238
|
// 1 hour
|
|
235
239
|
};
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
this.id = id;
|
|
244
|
-
this.code = code;
|
|
245
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// src/modules/messages/load-local-messages/load-namespace-group/parse-message-file.ts
|
|
250
|
-
var MAX_PATH_LENGTH = 260;
|
|
251
|
-
var parseMessageFile = async (filePath, loggerOptions) => {
|
|
240
|
+
async function collectFileEntries({
|
|
241
|
+
readdir = fs.readdir,
|
|
242
|
+
limit,
|
|
243
|
+
rootDir,
|
|
244
|
+
namespaces,
|
|
245
|
+
extraOptions: { exts = [".json"], loggerOptions } = {}
|
|
246
|
+
}) {
|
|
252
247
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
253
|
-
const logger = baseLogger.child({ scope: "
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
const fileName = path.basename(trimmedPath);
|
|
264
|
-
if (!fileName.toLowerCase().endsWith(".json")) {
|
|
265
|
-
logger.trace("Skipped non-JSON file.", { filePath: trimmedPath });
|
|
266
|
-
return null;
|
|
267
|
-
}
|
|
268
|
-
try {
|
|
269
|
-
const content = await fs.readFile(trimmedPath, "utf8");
|
|
270
|
-
const parsed = JSON.parse(content);
|
|
271
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
272
|
-
throw new IntorError({
|
|
273
|
-
id: loggerOptions.id,
|
|
274
|
-
code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
|
|
275
|
-
message: "Invalid message format"
|
|
276
|
-
});
|
|
248
|
+
const logger = baseLogger.child({ scope: "collect-file-entries" });
|
|
249
|
+
const results = [];
|
|
250
|
+
const walk = async (currentDir) => {
|
|
251
|
+
let entries = [];
|
|
252
|
+
try {
|
|
253
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logger.error(`Error reading directory: ${currentDir}`, { error });
|
|
256
|
+
return;
|
|
277
257
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
258
|
+
const tasks = entries.map(
|
|
259
|
+
(entry) => limit(async () => {
|
|
260
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
261
|
+
if (entry.isDirectory()) {
|
|
262
|
+
await walk(fullPath);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (!exts.some((ext2) => entry.name.endsWith(ext2))) return;
|
|
266
|
+
const relativePath = path.relative(rootDir, fullPath);
|
|
267
|
+
const ext = path.extname(relativePath);
|
|
268
|
+
const withoutExt = relativePath.slice(0, -ext.length);
|
|
269
|
+
const segments = withoutExt.split(path.sep).filter(Boolean);
|
|
270
|
+
const namespace = segments.at(0);
|
|
271
|
+
if (!namespace) return;
|
|
272
|
+
if (namespaces && namespace !== "index") {
|
|
273
|
+
if (!namespaces.includes(namespace)) return;
|
|
274
|
+
}
|
|
275
|
+
results.push({
|
|
276
|
+
namespace,
|
|
277
|
+
fullPath,
|
|
278
|
+
relativePath,
|
|
279
|
+
segments,
|
|
280
|
+
basename: path.basename(entry.name, ext)
|
|
281
|
+
});
|
|
282
|
+
})
|
|
283
|
+
);
|
|
284
|
+
await Promise.all(tasks);
|
|
285
|
+
};
|
|
286
|
+
await walk(rootDir);
|
|
287
|
+
if (logger.core.level === "debug") {
|
|
288
|
+
logger.debug("Local message files collected.", {
|
|
289
|
+
count: results.length
|
|
284
290
|
});
|
|
285
|
-
return null;
|
|
286
291
|
}
|
|
287
|
-
|
|
292
|
+
logger.trace("Local message files collected.", {
|
|
293
|
+
count: results.length,
|
|
294
|
+
fileEntries: results.map(({ namespace, relativePath }) => ({
|
|
295
|
+
namespace: namespace === "index" ? null : namespace,
|
|
296
|
+
relativePath
|
|
297
|
+
}))
|
|
298
|
+
});
|
|
299
|
+
return results;
|
|
300
|
+
}
|
|
288
301
|
|
|
289
|
-
// src/modules/messages/
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (
|
|
301
|
-
|
|
302
|
+
// src/modules/messages/shared/utils/is-namespace-messages.ts
|
|
303
|
+
function isPlainObject(value) {
|
|
304
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
305
|
+
}
|
|
306
|
+
function isNamespaceMessages(value) {
|
|
307
|
+
if (!isPlainObject(value)) return false;
|
|
308
|
+
const stack = [value];
|
|
309
|
+
while (stack.length > 0) {
|
|
310
|
+
const current = stack.pop();
|
|
311
|
+
for (const v of Object.values(current)) {
|
|
312
|
+
if (typeof v === "string") continue;
|
|
313
|
+
if (isPlainObject(v)) {
|
|
314
|
+
stack.push(v);
|
|
302
315
|
} else {
|
|
303
|
-
|
|
304
|
-
subEntries[name] = content;
|
|
316
|
+
return false;
|
|
305
317
|
}
|
|
306
|
-
})
|
|
307
|
-
);
|
|
308
|
-
return { base: baseContent, sub: subEntries };
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
// src/modules/messages/load-local-messages/load-namespace-group/load-namespace-group.ts
|
|
312
|
-
var loadNamespaceGroup = async ({
|
|
313
|
-
locale,
|
|
314
|
-
namespace,
|
|
315
|
-
messages,
|
|
316
|
-
namespaceGroupValue,
|
|
317
|
-
limit,
|
|
318
|
-
logger: loggerOptions = { id: "default" }
|
|
319
|
-
}) => {
|
|
320
|
-
const baseLogger = getLogger({ ...loggerOptions });
|
|
321
|
-
const logger = baseLogger.child({ scope: "load-namespace-group" });
|
|
322
|
-
const { isAtRoot, filePaths } = namespaceGroupValue;
|
|
323
|
-
if (filePaths.length === 0) {
|
|
324
|
-
logger.trace(
|
|
325
|
-
`Skipped merging ${locale}/${namespace} because filePaths is empty`
|
|
326
|
-
);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
return limit(async () => {
|
|
330
|
-
const { base, sub } = await mergeNamespaceMessages(
|
|
331
|
-
filePaths,
|
|
332
|
-
isAtRoot,
|
|
333
|
-
loggerOptions
|
|
334
|
-
);
|
|
335
|
-
if (!messages[locale]) {
|
|
336
|
-
messages[locale] = {};
|
|
337
|
-
}
|
|
338
|
-
if (isAtRoot && filePaths.length === 1 && path.basename(filePaths[0]) === "index.json") {
|
|
339
|
-
messages[locale] = { ...messages[locale], ...base };
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
const finalContent = isAtRoot ? base : { ...base, ...sub };
|
|
343
|
-
messages[locale][namespace] = finalContent;
|
|
344
|
-
if (!isAtRoot && Object.keys(finalContent).length > 0) {
|
|
345
|
-
logger.trace(
|
|
346
|
-
`Merged ${locale}/${namespace} from ${filePaths.length} file(s)`,
|
|
347
|
-
{ namespace }
|
|
348
|
-
);
|
|
349
318
|
}
|
|
350
|
-
});
|
|
351
|
-
};
|
|
352
|
-
var addToNamespaceGroup = ({
|
|
353
|
-
options: { namespaces },
|
|
354
|
-
filePath,
|
|
355
|
-
namespaceGroups,
|
|
356
|
-
namespacePathSegments
|
|
357
|
-
}) => {
|
|
358
|
-
if (!filePath) {
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
const isAtRoot = namespacePathSegments.length === 0;
|
|
362
|
-
const nsKey = isAtRoot ? path.basename(filePath, ".json") : namespacePathSegments.join(".");
|
|
363
|
-
if (namespaces && namespaces.size > 0 && !namespaces.has(nsKey)) {
|
|
364
|
-
return;
|
|
365
319
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
|
|
324
|
+
async function jsonReader(filePath, readFile = fs.readFile) {
|
|
325
|
+
const raw = await readFile(filePath, "utf8");
|
|
326
|
+
const parsed = JSON.parse(raw);
|
|
327
|
+
if (!isNamespaceMessages(parsed)) {
|
|
328
|
+
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
329
|
+
}
|
|
330
|
+
return parsed;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
|
|
334
|
+
function nestObjectFromPath(path4, value) {
|
|
335
|
+
let obj = value;
|
|
336
|
+
for (let i = path4.length - 1; i >= 0; i--) {
|
|
337
|
+
obj = { [path4[i]]: obj };
|
|
375
338
|
}
|
|
376
|
-
|
|
339
|
+
return obj;
|
|
340
|
+
}
|
|
377
341
|
|
|
378
|
-
// src/modules/messages/load-local-messages/
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
readdir = fs.readdir
|
|
385
|
-
}) => {
|
|
386
|
-
const { limit } = options;
|
|
387
|
-
const loggerOptions = options.logger || { id: "default" };
|
|
342
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.ts
|
|
343
|
+
async function parseFileEntries({
|
|
344
|
+
fileEntries,
|
|
345
|
+
limit,
|
|
346
|
+
extraOptions: { messageFileReader, loggerOptions } = {}
|
|
347
|
+
}) {
|
|
388
348
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
389
|
-
const logger = baseLogger.child({ scope: "
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
namespaceGroups,
|
|
405
|
-
currentDirPath: filePath,
|
|
406
|
-
namespacePathSegments: [...namespacePathSegments, dirent.name],
|
|
407
|
-
options,
|
|
408
|
-
readdir
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
}).catch((error) => {
|
|
412
|
-
logger.warn("Failed to process a locale file or directory.", {
|
|
413
|
-
name: dirent.name,
|
|
414
|
-
type: dirent.isFile() ? "file" : "directory",
|
|
415
|
-
path: currentDirPath,
|
|
349
|
+
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
350
|
+
const parsedFileEntries = [];
|
|
351
|
+
const tasks = fileEntries.map(
|
|
352
|
+
({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
353
|
+
try {
|
|
354
|
+
const segsWithoutNs = segments.slice(1);
|
|
355
|
+
const json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
|
|
356
|
+
const isIndex = basename === "index";
|
|
357
|
+
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
358
|
+
const namespaceMessages = nestObjectFromPath(keyPath, json);
|
|
359
|
+
parsedFileEntries.push({ namespace, namespaceMessages });
|
|
360
|
+
logger.trace("Parsed file.", { path: fullPath });
|
|
361
|
+
} catch (error) {
|
|
362
|
+
logger.error("Failed to read or parse file.", {
|
|
363
|
+
path: fullPath,
|
|
416
364
|
error
|
|
417
365
|
});
|
|
418
|
-
}
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
);
|
|
369
|
+
await Promise.all(tasks);
|
|
370
|
+
const result = {};
|
|
371
|
+
for (const { namespace, namespaceMessages } of parsedFileEntries) {
|
|
372
|
+
if (namespace === "index") {
|
|
373
|
+
merge(result, namespaceMessages);
|
|
374
|
+
} else {
|
|
375
|
+
result[namespace] = merge(
|
|
376
|
+
result[namespace] ?? {},
|
|
377
|
+
namespaceMessages
|
|
378
|
+
);
|
|
379
|
+
}
|
|
423
380
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
// src/modules/messages/load-local-messages/prepare-namespace-groups/prepare-namespace-groups.ts
|
|
427
|
-
var prepareNamespaceGroups = async (options) => {
|
|
428
|
-
const { basePath } = options;
|
|
429
|
-
const namespaceGroups = /* @__PURE__ */ new Map();
|
|
430
|
-
await traverseDirectory({
|
|
431
|
-
options,
|
|
432
|
-
currentDirPath: basePath,
|
|
433
|
-
namespaceGroups,
|
|
434
|
-
namespacePathSegments: []
|
|
435
|
-
});
|
|
436
|
-
return namespaceGroups;
|
|
437
|
-
};
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
438
383
|
|
|
439
|
-
// src/modules/messages/load-local-messages/
|
|
440
|
-
var
|
|
441
|
-
|
|
384
|
+
// src/modules/messages/load-local-messages/read-locale-messages/read-locale-messages.ts
|
|
385
|
+
var readLocaleMessages = async ({
|
|
386
|
+
limit,
|
|
387
|
+
rootDir = "messages",
|
|
442
388
|
locale,
|
|
443
389
|
namespaces,
|
|
444
|
-
|
|
445
|
-
limit,
|
|
446
|
-
logger: loggerOptions = { id: "default" }
|
|
390
|
+
extraOptions: { exts, messageFileReader, loggerOptions } = {}
|
|
447
391
|
}) => {
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const validNamespaces = [];
|
|
452
|
-
try {
|
|
453
|
-
const stat = await fs.stat(localePath);
|
|
454
|
-
if (!stat.isDirectory()) {
|
|
455
|
-
logger.warn("Locale path is not a directory.", {
|
|
456
|
-
locale,
|
|
457
|
-
path: localePath
|
|
458
|
-
});
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
} catch (error) {
|
|
462
|
-
logger.warn("Error checking locale path.", { locale, error });
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
const namespaceGroups = await prepareNamespaceGroups({
|
|
466
|
-
basePath: localePath,
|
|
392
|
+
const fileEntries = await collectFileEntries({
|
|
393
|
+
rootDir: path.resolve(process.cwd(), rootDir, locale),
|
|
394
|
+
namespaces,
|
|
467
395
|
limit,
|
|
468
|
-
|
|
469
|
-
logger: loggerOptions
|
|
470
|
-
});
|
|
471
|
-
if (namespaceGroups.size === 0) {
|
|
472
|
-
logger.warn("No namespace groups found.", {
|
|
473
|
-
locale,
|
|
474
|
-
basePath,
|
|
475
|
-
namespaces
|
|
476
|
-
});
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
logger.trace("Prepared namespace groups from scanning local files.", {
|
|
480
|
-
namespaceGroups: [...namespaceGroups.entries()].map(([ns, val]) => ({
|
|
481
|
-
namespace: ns,
|
|
482
|
-
isAtRoot: val.isAtRoot,
|
|
483
|
-
fileCount: val.filePaths.length
|
|
484
|
-
}))
|
|
396
|
+
extraOptions: { exts, loggerOptions }
|
|
485
397
|
});
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
locale,
|
|
491
|
-
namespace,
|
|
492
|
-
messages,
|
|
493
|
-
namespaceGroupValue,
|
|
494
|
-
limit,
|
|
495
|
-
logger: loggerOptions
|
|
496
|
-
}).then(() => validNamespaces.push(namespace))
|
|
497
|
-
);
|
|
498
|
-
await Promise.all(namespaceGroupTasks);
|
|
499
|
-
return validNamespaces;
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// src/modules/messages/load-local-messages/load-locale-with-fallback/load-locale-with-fallback.ts
|
|
503
|
-
var loadLocaleWithFallback = async ({
|
|
504
|
-
basePath,
|
|
505
|
-
locale: targetLocale,
|
|
506
|
-
fallbackLocales = [],
|
|
507
|
-
namespaces,
|
|
508
|
-
messages,
|
|
509
|
-
limit,
|
|
510
|
-
logger: loggerOptions = { id: "default" }
|
|
511
|
-
}) => {
|
|
512
|
-
const baseLogger = getLogger({ ...loggerOptions });
|
|
513
|
-
const logger = baseLogger.child({ scope: "load-locale-with-fallback" });
|
|
514
|
-
const candidateLocales = [targetLocale, ...fallbackLocales];
|
|
515
|
-
for (const locale of candidateLocales) {
|
|
516
|
-
try {
|
|
517
|
-
const validNamespaces = await loadSingleLocale({
|
|
518
|
-
basePath,
|
|
519
|
-
locale,
|
|
520
|
-
namespaces,
|
|
521
|
-
messages,
|
|
522
|
-
limit,
|
|
523
|
-
logger: loggerOptions
|
|
524
|
-
});
|
|
525
|
-
return validNamespaces;
|
|
526
|
-
} catch (error) {
|
|
527
|
-
logger.warn("Error occurred while processing the locale.", {
|
|
528
|
-
locale,
|
|
529
|
-
error
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
logger.warn("All fallback locales failed.", {
|
|
534
|
-
attemptedLocales: candidateLocales
|
|
398
|
+
const namespaceMessages = await parseFileEntries({
|
|
399
|
+
fileEntries,
|
|
400
|
+
limit,
|
|
401
|
+
extraOptions: { messageFileReader, loggerOptions }
|
|
535
402
|
});
|
|
536
|
-
|
|
403
|
+
const localeMessages = { [locale]: namespaceMessages };
|
|
404
|
+
return localeMessages;
|
|
537
405
|
};
|
|
538
406
|
function getGlobalMessagesPool() {
|
|
539
407
|
if (!globalThis.__INTOR_MESSAGES_POOL__) {
|
|
@@ -544,43 +412,35 @@ function getGlobalMessagesPool() {
|
|
|
544
412
|
|
|
545
413
|
// src/modules/messages/load-local-messages/load-local-messages.ts
|
|
546
414
|
var loadLocalMessages = async ({
|
|
547
|
-
|
|
415
|
+
pool = getGlobalMessagesPool(),
|
|
416
|
+
rootDir = "messages",
|
|
548
417
|
locale,
|
|
549
418
|
fallbackLocales,
|
|
550
419
|
namespaces,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
420
|
+
extraOptions: {
|
|
421
|
+
concurrency = 10,
|
|
422
|
+
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
423
|
+
loggerOptions = { id: "default" },
|
|
424
|
+
exts,
|
|
425
|
+
messageFileReader
|
|
426
|
+
} = {}
|
|
554
427
|
}) => {
|
|
555
|
-
basePath = basePath ?? "messages";
|
|
556
|
-
if (!locale || locale.trim() === "") return {};
|
|
557
428
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
558
|
-
const logger = baseLogger.child({ scope: "load-
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
);
|
|
564
|
-
const start = performance.now();
|
|
565
|
-
logger.trace("Starting to load local messages with configuration.", {
|
|
566
|
-
path: { basePath, resolvedBasePath },
|
|
567
|
-
locale,
|
|
568
|
-
fallbackLocales,
|
|
569
|
-
namespaces: namespaces && namespaces.length > 0 ? { count: namespaces?.length, list: [...namespaces] } : "All Namespaces",
|
|
570
|
-
concurrency
|
|
429
|
+
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
430
|
+
const start = performance$1.now();
|
|
431
|
+
logger.debug("Loading local messages from directory.", {
|
|
432
|
+
rootDir,
|
|
433
|
+
resolvedRootDir: path.resolve(process.cwd(), rootDir)
|
|
571
434
|
});
|
|
572
|
-
let pool;
|
|
573
|
-
if (cache.enabled) {
|
|
574
|
-
pool = getGlobalMessagesPool();
|
|
575
|
-
}
|
|
576
435
|
const key = normalizeCacheKey([
|
|
577
436
|
loggerOptions.id,
|
|
578
|
-
|
|
437
|
+
"loaderType:local",
|
|
438
|
+
rootDir,
|
|
579
439
|
locale,
|
|
580
|
-
(fallbackLocales
|
|
581
|
-
(namespaces
|
|
440
|
+
(fallbackLocales || []).toSorted().join(","),
|
|
441
|
+
(namespaces || []).toSorted().join(",")
|
|
582
442
|
]);
|
|
583
|
-
if (
|
|
443
|
+
if (cacheOptions.enabled && key) {
|
|
584
444
|
const cached = await pool?.get(key);
|
|
585
445
|
if (cached) {
|
|
586
446
|
logger.debug("Messages cache hit.", { key });
|
|
@@ -588,50 +448,57 @@ var loadLocalMessages = async ({
|
|
|
588
448
|
}
|
|
589
449
|
}
|
|
590
450
|
const limit = pLimit(concurrency);
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
451
|
+
const candidateLocales = [locale, ...fallbackLocales || []];
|
|
452
|
+
let messages;
|
|
453
|
+
for (const candidateLocale of candidateLocales) {
|
|
454
|
+
try {
|
|
455
|
+
const result = await readLocaleMessages({
|
|
456
|
+
limit,
|
|
457
|
+
rootDir,
|
|
458
|
+
locale: candidateLocale,
|
|
459
|
+
namespaces,
|
|
460
|
+
extraOptions: { loggerOptions, exts, messageFileReader }
|
|
461
|
+
});
|
|
462
|
+
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
463
|
+
messages = result;
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
} catch (error) {
|
|
467
|
+
logger.error("Failed to read locale messages", {
|
|
468
|
+
locale: candidateLocale,
|
|
469
|
+
error
|
|
470
|
+
});
|
|
471
|
+
}
|
|
602
472
|
}
|
|
603
|
-
|
|
473
|
+
if (cacheOptions.enabled && key && messages) {
|
|
474
|
+
await pool?.set(key, messages, cacheOptions.ttl);
|
|
475
|
+
}
|
|
476
|
+
const end = performance$1.now();
|
|
604
477
|
const duration = Math.round(end - start);
|
|
605
478
|
logger.trace("Finished loading local messages.", {
|
|
606
|
-
|
|
607
|
-
validNamespaces,
|
|
479
|
+
loadedLocale: messages ? Object.keys(messages)[0] : void 0,
|
|
608
480
|
duration: `${duration} ms`
|
|
609
481
|
});
|
|
610
482
|
return messages;
|
|
611
483
|
};
|
|
612
484
|
|
|
613
|
-
// src/modules/messages/
|
|
614
|
-
var
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
// src/modules/messages/load-api-messages/fetch-messages.ts
|
|
619
|
-
var fetchMessages = async ({
|
|
620
|
-
apiUrl,
|
|
621
|
-
apiHeaders,
|
|
622
|
-
locale,
|
|
485
|
+
// src/modules/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.ts
|
|
486
|
+
var fetchLocaleMessages = async ({
|
|
487
|
+
remoteUrl,
|
|
488
|
+
remoteHeaders,
|
|
623
489
|
searchParams,
|
|
624
|
-
|
|
490
|
+
locale,
|
|
491
|
+
extraOptions: { loggerOptions } = {}
|
|
625
492
|
}) => {
|
|
626
493
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
627
|
-
const logger = baseLogger.child({ scope: "fetch-messages" });
|
|
494
|
+
const logger = baseLogger.child({ scope: "fetch-locale-messages" });
|
|
628
495
|
try {
|
|
629
496
|
const params = new URLSearchParams(searchParams);
|
|
630
497
|
params.append("locale", locale);
|
|
631
|
-
const url = `${
|
|
498
|
+
const url = `${remoteUrl}?${params.toString()}`;
|
|
632
499
|
const headers2 = {
|
|
633
500
|
"Content-Type": "application/json",
|
|
634
|
-
...
|
|
501
|
+
...remoteHeaders
|
|
635
502
|
};
|
|
636
503
|
const response = await fetch(url, {
|
|
637
504
|
method: "GET",
|
|
@@ -639,17 +506,17 @@ var fetchMessages = async ({
|
|
|
639
506
|
cache: "no-store"
|
|
640
507
|
});
|
|
641
508
|
if (!response.ok) {
|
|
642
|
-
throw new Error(`
|
|
509
|
+
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
643
510
|
}
|
|
644
511
|
const data = await response.json();
|
|
645
|
-
if (
|
|
646
|
-
throw new Error(
|
|
512
|
+
if (!isNamespaceMessages(data[locale])) {
|
|
513
|
+
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
647
514
|
}
|
|
648
515
|
return data;
|
|
649
516
|
} catch (error) {
|
|
650
|
-
logger.warn(
|
|
517
|
+
logger.warn("Fetching locale messages failed.", {
|
|
651
518
|
locale,
|
|
652
|
-
|
|
519
|
+
remoteUrl,
|
|
653
520
|
searchParams: decodeURIComponent(searchParams.toString()),
|
|
654
521
|
error
|
|
655
522
|
});
|
|
@@ -657,30 +524,7 @@ var fetchMessages = async ({
|
|
|
657
524
|
}
|
|
658
525
|
};
|
|
659
526
|
|
|
660
|
-
// src/modules/messages/load-
|
|
661
|
-
var fetchFallbackMessages = async ({
|
|
662
|
-
apiUrl,
|
|
663
|
-
apiHeaders,
|
|
664
|
-
searchParams,
|
|
665
|
-
fallbackLocales,
|
|
666
|
-
logger
|
|
667
|
-
}) => {
|
|
668
|
-
for (const fallbackLocale of fallbackLocales) {
|
|
669
|
-
const result = await fetchMessages({
|
|
670
|
-
apiUrl,
|
|
671
|
-
searchParams,
|
|
672
|
-
locale: fallbackLocale,
|
|
673
|
-
apiHeaders,
|
|
674
|
-
logger
|
|
675
|
-
});
|
|
676
|
-
if (result) {
|
|
677
|
-
return { locale: fallbackLocale, messages: result };
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
return;
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
// src/modules/messages/load-api-messages/utils/build-search-params.ts
|
|
527
|
+
// src/modules/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.ts
|
|
684
528
|
var buildSearchParams = (params) => {
|
|
685
529
|
const searchParams = new URLSearchParams();
|
|
686
530
|
const appendParam = (key, value) => {
|
|
@@ -698,119 +542,130 @@ var buildSearchParams = (params) => {
|
|
|
698
542
|
return searchParams;
|
|
699
543
|
};
|
|
700
544
|
|
|
701
|
-
// src/modules/messages/load-
|
|
702
|
-
var
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
545
|
+
// src/modules/messages/load-remote-messages/load-remote-messages.ts
|
|
546
|
+
var loadRemoteMessages = async ({
|
|
547
|
+
pool = getGlobalMessagesPool(),
|
|
548
|
+
rootDir,
|
|
549
|
+
remoteUrl,
|
|
550
|
+
remoteHeaders,
|
|
706
551
|
locale,
|
|
707
552
|
fallbackLocales = [],
|
|
708
553
|
namespaces = [],
|
|
709
|
-
|
|
710
|
-
|
|
554
|
+
extraOptions: {
|
|
555
|
+
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
556
|
+
loggerOptions = { id: "default" }
|
|
557
|
+
} = {}
|
|
711
558
|
}) => {
|
|
712
559
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
713
|
-
const logger = baseLogger.child({ scope: "load-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
let pool;
|
|
719
|
-
if (cache.enabled) {
|
|
720
|
-
pool = getGlobalMessagesPool();
|
|
721
|
-
}
|
|
560
|
+
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
561
|
+
const start = performance.now();
|
|
562
|
+
logger.debug("Loading remote messages from api.", { remoteUrl });
|
|
722
563
|
const key = normalizeCacheKey([
|
|
723
564
|
loggerOptions.id,
|
|
724
|
-
|
|
565
|
+
"loaderType:remote",
|
|
566
|
+
rootDir,
|
|
725
567
|
locale,
|
|
726
568
|
(fallbackLocales ?? []).toSorted().join(","),
|
|
727
569
|
(namespaces ?? []).toSorted().join(",")
|
|
728
570
|
]);
|
|
729
|
-
if (
|
|
571
|
+
if (cacheOptions.enabled && key) {
|
|
730
572
|
const cached = await pool?.get(key);
|
|
731
573
|
if (cached) {
|
|
732
574
|
logger.debug("Messages cache hit.", { key });
|
|
733
575
|
return cached;
|
|
734
576
|
}
|
|
735
577
|
}
|
|
736
|
-
const searchParams = buildSearchParams({
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
578
|
+
const searchParams = buildSearchParams({ rootDir, namespaces });
|
|
579
|
+
const candidateLocales = [locale, ...fallbackLocales || []];
|
|
580
|
+
let messages;
|
|
581
|
+
for (const candidateLocale of candidateLocales) {
|
|
582
|
+
try {
|
|
583
|
+
const result = await fetchLocaleMessages({
|
|
584
|
+
remoteUrl,
|
|
585
|
+
remoteHeaders,
|
|
586
|
+
searchParams,
|
|
587
|
+
locale: candidateLocale,
|
|
588
|
+
extraOptions: { loggerOptions }
|
|
589
|
+
});
|
|
590
|
+
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
591
|
+
messages = result;
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
} catch (error) {
|
|
595
|
+
logger.error("Failed to fetch locale messages.", {
|
|
596
|
+
locale: candidateLocale,
|
|
597
|
+
error
|
|
598
|
+
});
|
|
747
599
|
}
|
|
748
|
-
return messages;
|
|
749
600
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
apiHeaders,
|
|
753
|
-
searchParams,
|
|
754
|
-
fallbackLocales,
|
|
755
|
-
logger: loggerOptions
|
|
756
|
-
});
|
|
757
|
-
if (fallbackResult) {
|
|
758
|
-
logger.info("Fallback locale succeeded.", {
|
|
759
|
-
usedLocale: fallbackResult.locale,
|
|
760
|
-
apiUrl,
|
|
761
|
-
searchParams: decodeURIComponent(searchParams.toString())
|
|
762
|
-
});
|
|
763
|
-
if (cache.enabled && key) {
|
|
764
|
-
await pool?.set(key, fallbackResult.messages, cache.ttl);
|
|
765
|
-
}
|
|
766
|
-
return fallbackResult.messages;
|
|
601
|
+
if (cacheOptions.enabled && key && messages) {
|
|
602
|
+
await pool?.set(key, messages, cacheOptions.ttl);
|
|
767
603
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
604
|
+
const end = performance.now();
|
|
605
|
+
const duration = Math.round(end - start);
|
|
606
|
+
logger.trace("Finished loading remote messages.", {
|
|
607
|
+
loadedLocale: messages ? Object.keys(messages)[0] : void 0,
|
|
608
|
+
duration: `${duration} ms`
|
|
771
609
|
});
|
|
772
|
-
return;
|
|
610
|
+
return messages;
|
|
773
611
|
};
|
|
774
612
|
|
|
775
613
|
// src/modules/messages/load-messages.ts
|
|
776
614
|
var loadMessages = async ({
|
|
777
615
|
config,
|
|
778
616
|
locale,
|
|
779
|
-
pathname
|
|
617
|
+
pathname = "",
|
|
618
|
+
extraOptions: { exts, messageFileReader } = {}
|
|
780
619
|
}) => {
|
|
781
620
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
782
|
-
const logger = baseLogger.child({ scope: "messages
|
|
621
|
+
const logger = baseLogger.child({ scope: "load-messages" });
|
|
783
622
|
if (!config.loader) {
|
|
784
623
|
logger.warn(
|
|
785
624
|
"No loader options have been configured in the current config."
|
|
786
625
|
);
|
|
787
626
|
return;
|
|
788
627
|
}
|
|
789
|
-
const {
|
|
628
|
+
const { type, concurrency, rootDir } = config.loader;
|
|
790
629
|
const fallbackLocales = config.fallbackLocales[locale] || [];
|
|
791
630
|
const namespaces = resolveNamespaces({ config, pathname });
|
|
792
|
-
logger.
|
|
793
|
-
|
|
631
|
+
if (logger.core.level === "debug") {
|
|
632
|
+
logger.debug("Starting to load messages.", { locale });
|
|
633
|
+
}
|
|
634
|
+
logger.trace("Starting to load messages with runtime context.", {
|
|
635
|
+
loaderType: type,
|
|
636
|
+
locale,
|
|
637
|
+
fallbackLocales,
|
|
638
|
+
namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : "[ALL]",
|
|
639
|
+
cache: config.cache,
|
|
640
|
+
concurrency: concurrency ?? 10
|
|
794
641
|
});
|
|
795
|
-
logger.debug("Loader type selected.", { loaderType: loader.type });
|
|
796
642
|
let loadedMessages;
|
|
797
|
-
if (
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
...loader,
|
|
643
|
+
if (type === "local") {
|
|
644
|
+
loadedMessages = await loadLocalMessages({
|
|
645
|
+
rootDir,
|
|
801
646
|
locale,
|
|
802
647
|
fallbackLocales,
|
|
803
648
|
namespaces,
|
|
804
|
-
|
|
805
|
-
|
|
649
|
+
extraOptions: {
|
|
650
|
+
concurrency,
|
|
651
|
+
cacheOptions: config.cache,
|
|
652
|
+
loggerOptions: { id: config.id, ...config.logger },
|
|
653
|
+
exts,
|
|
654
|
+
messageFileReader
|
|
655
|
+
}
|
|
806
656
|
});
|
|
807
|
-
} else if (
|
|
808
|
-
loadedMessages = await
|
|
809
|
-
|
|
657
|
+
} else if (type === "remote") {
|
|
658
|
+
loadedMessages = await loadRemoteMessages({
|
|
659
|
+
rootDir,
|
|
660
|
+
remoteUrl: config.loader.remoteUrl,
|
|
661
|
+
remoteHeaders: config.loader.remoteHeaders,
|
|
810
662
|
locale,
|
|
811
663
|
fallbackLocales,
|
|
812
664
|
namespaces,
|
|
813
|
-
|
|
665
|
+
extraOptions: {
|
|
666
|
+
cacheOptions: config.cache,
|
|
667
|
+
loggerOptions: { id: config.id, ...config.logger }
|
|
668
|
+
}
|
|
814
669
|
});
|
|
815
670
|
}
|
|
816
671
|
if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
|
|
@@ -819,7 +674,7 @@ var loadMessages = async ({
|
|
|
819
674
|
return loadedMessages;
|
|
820
675
|
};
|
|
821
676
|
|
|
822
|
-
// src/modules/
|
|
677
|
+
// src/modules/translator/get-translator.ts
|
|
823
678
|
async function getTranslator(opts) {
|
|
824
679
|
const { config, locale, pathname = "", preKey, handlers } = opts;
|
|
825
680
|
const messages = await loadMessages({ config, locale, pathname });
|