intor 2.1.0 → 2.2.1
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.cjs +4 -8
- package/dist/config/index.d.cts +3 -3
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.js +4 -8
- package/dist/index.cjs +88 -120
- package/dist/index.d.cts +65 -39
- package/dist/index.d.ts +65 -39
- package/dist/index.js +88 -120
- package/dist/next/index.cjs +112 -131
- package/dist/next/index.d.cts +54 -27
- package/dist/next/index.d.ts +54 -27
- package/dist/next/index.js +112 -131
- package/dist/next/middleware/index.cjs +31 -55
- package/dist/next/middleware/index.d.cts +4 -8
- package/dist/next/middleware/index.d.ts +4 -8
- package/dist/next/middleware/index.js +31 -55
- package/dist/next/server/index.cjs +69 -132
- package/dist/next/server/index.d.cts +52 -25
- package/dist/next/server/index.d.ts +52 -25
- package/dist/next/server/index.js +69 -132
- package/package.json +13 -9
package/dist/index.js
CHANGED
|
@@ -23,33 +23,6 @@ var DEFAULT_CACHE_OPTIONS = {
|
|
|
23
23
|
// 1 hour
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
// src/shared/error/intor-error.ts
|
|
27
|
-
var IntorError = class extends Error {
|
|
28
|
-
constructor({ message, code, id }) {
|
|
29
|
-
const fullMessage = id ? `[${id}] ${message}` : message;
|
|
30
|
-
super(fullMessage);
|
|
31
|
-
this.name = "IntorError";
|
|
32
|
-
this.id = id;
|
|
33
|
-
this.code = code;
|
|
34
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// src/modules/messages/load-local-messages/utils/read-message-record-file.ts
|
|
39
|
-
var readMessageRecordFile = async (filePath, loggerOptions) => {
|
|
40
|
-
const fileName = path.basename(filePath, ".json");
|
|
41
|
-
const content = await fs.readFile(filePath, "utf-8");
|
|
42
|
-
const parsed = JSON.parse(content);
|
|
43
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
44
|
-
throw new IntorError({
|
|
45
|
-
id: loggerOptions.id,
|
|
46
|
-
code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
|
|
47
|
-
message: "Invalid message format"
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
return { fileName, content: parsed };
|
|
51
|
-
};
|
|
52
|
-
|
|
53
26
|
// src/shared/logger/global-logger-pool.ts
|
|
54
27
|
function getGlobalLoggerPool() {
|
|
55
28
|
if (!globalThis.__INTOR_LOGGER_POOL__) {
|
|
@@ -83,13 +56,25 @@ function getLogger({
|
|
|
83
56
|
});
|
|
84
57
|
pool.set(id, logger);
|
|
85
58
|
if (pool.size > 1e3) {
|
|
86
|
-
const keys =
|
|
59
|
+
const keys = [...pool.keys()];
|
|
87
60
|
for (const key of keys.slice(0, 200)) pool.delete(key);
|
|
88
61
|
}
|
|
89
62
|
}
|
|
90
63
|
return logger;
|
|
91
64
|
}
|
|
92
65
|
|
|
66
|
+
// src/shared/error/intor-error.ts
|
|
67
|
+
var IntorError = class extends Error {
|
|
68
|
+
constructor({ message, code, id }) {
|
|
69
|
+
const fullMessage = id ? `[${id}] ${message}` : message;
|
|
70
|
+
super(fullMessage);
|
|
71
|
+
this.name = "IntorError";
|
|
72
|
+
this.id = id;
|
|
73
|
+
this.code = code;
|
|
74
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
93
78
|
// src/modules/messages/load-local-messages/load-namespace-group/parse-message-file.ts
|
|
94
79
|
var MAX_PATH_LENGTH = 260;
|
|
95
80
|
var parseMessageFile = async (filePath, loggerOptions) => {
|
|
@@ -110,9 +95,17 @@ var parseMessageFile = async (filePath, loggerOptions) => {
|
|
|
110
95
|
return null;
|
|
111
96
|
}
|
|
112
97
|
try {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
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
|
+
});
|
|
106
|
+
}
|
|
107
|
+
logger.trace("Message file loaded.", { filePath: trimmedPath });
|
|
108
|
+
return parsed;
|
|
116
109
|
} catch (error) {
|
|
117
110
|
logger.warn("Failed to parse message file.", {
|
|
118
111
|
filePath: trimmedPath,
|
|
@@ -206,7 +199,7 @@ var addToNamespaceGroup = ({
|
|
|
206
199
|
const filePathsSet = new Set(group.filePaths);
|
|
207
200
|
if (!filePathsSet.has(filePath)) {
|
|
208
201
|
filePathsSet.add(filePath);
|
|
209
|
-
group.filePaths =
|
|
202
|
+
group.filePaths = [...filePathsSet];
|
|
210
203
|
namespaceGroups.set(nsKey, group);
|
|
211
204
|
}
|
|
212
205
|
};
|
|
@@ -216,14 +209,15 @@ var traverseDirectory = async ({
|
|
|
216
209
|
options,
|
|
217
210
|
currentDirPath,
|
|
218
211
|
namespaceGroups,
|
|
219
|
-
namespacePathSegments
|
|
212
|
+
namespacePathSegments,
|
|
213
|
+
readdir = fs.readdir
|
|
220
214
|
}) => {
|
|
221
215
|
const { limit } = options;
|
|
222
216
|
const loggerOptions = options.logger || { id: "default" };
|
|
223
217
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
224
218
|
const logger = baseLogger.child({ scope: "traverse-directory" });
|
|
225
219
|
try {
|
|
226
|
-
const dirents = await
|
|
220
|
+
const dirents = await readdir(currentDirPath, { withFileTypes: true });
|
|
227
221
|
const dirPromises = dirents.map(
|
|
228
222
|
(dirent) => limit(async () => {
|
|
229
223
|
const filePath = path.join(currentDirPath, dirent.name);
|
|
@@ -239,7 +233,8 @@ var traverseDirectory = async ({
|
|
|
239
233
|
namespaceGroups,
|
|
240
234
|
currentDirPath: filePath,
|
|
241
235
|
namespacePathSegments: [...namespacePathSegments, dirent.name],
|
|
242
|
-
options
|
|
236
|
+
options,
|
|
237
|
+
readdir
|
|
243
238
|
});
|
|
244
239
|
}
|
|
245
240
|
}).catch((error) => {
|
|
@@ -314,7 +309,7 @@ var loadSingleLocale = async ({
|
|
|
314
309
|
namespaceGroups: [...namespaceGroups.entries()].map(([ns, val]) => ({
|
|
315
310
|
namespace: ns,
|
|
316
311
|
isAtRoot: val.isAtRoot,
|
|
317
|
-
|
|
312
|
+
fileCount: val.filePaths.length
|
|
318
313
|
}))
|
|
319
314
|
});
|
|
320
315
|
const namespaceGroupTasks = [...namespaceGroups.entries()].filter(
|
|
@@ -345,8 +340,8 @@ var loadLocaleWithFallback = async ({
|
|
|
345
340
|
}) => {
|
|
346
341
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
347
342
|
const logger = baseLogger.child({ scope: "load-locale-with-fallback" });
|
|
348
|
-
const
|
|
349
|
-
for (const locale of
|
|
343
|
+
const candidateLocales = [targetLocale, ...fallbackLocales];
|
|
344
|
+
for (const locale of candidateLocales) {
|
|
350
345
|
try {
|
|
351
346
|
const validNamespaces = await loadSingleLocale({
|
|
352
347
|
basePath,
|
|
@@ -365,7 +360,7 @@ var loadLocaleWithFallback = async ({
|
|
|
365
360
|
}
|
|
366
361
|
}
|
|
367
362
|
logger.warn("All fallback locales failed.", {
|
|
368
|
-
attemptedLocales:
|
|
363
|
+
attemptedLocales: candidateLocales
|
|
369
364
|
});
|
|
370
365
|
return;
|
|
371
366
|
};
|
|
@@ -382,7 +377,7 @@ function clearMessagesPool() {
|
|
|
382
377
|
|
|
383
378
|
// src/shared/utils/merge-messages.ts
|
|
384
379
|
var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
|
|
385
|
-
const result = Object.keys(staticMessages).length ? { ...staticMessages } : {};
|
|
380
|
+
const result = Object.keys(staticMessages).length > 0 ? { ...staticMessages } : {};
|
|
386
381
|
for (const locale in loadedMessages) {
|
|
387
382
|
const loaded = loadedMessages[locale];
|
|
388
383
|
if (!result[locale]) {
|
|
@@ -401,7 +396,7 @@ var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
|
|
|
401
396
|
var CACHE_KEY_DELIMITER = "|";
|
|
402
397
|
var sanitize = (k) => k.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").replaceAll(/[\r\n]/g, "").trim();
|
|
403
398
|
var normalizeCacheKey = (key, delimiter = CACHE_KEY_DELIMITER) => {
|
|
404
|
-
if (
|
|
399
|
+
if (key === null || key === void 0) return null;
|
|
405
400
|
if (Array.isArray(key)) {
|
|
406
401
|
if (key.length === 0) return null;
|
|
407
402
|
const normalized = key.map((k) => {
|
|
@@ -425,56 +420,44 @@ var resolveNamespaces = ({
|
|
|
425
420
|
pathname
|
|
426
421
|
}) => {
|
|
427
422
|
const { loader } = config;
|
|
428
|
-
const {
|
|
429
|
-
|
|
430
|
-
namespaces: fallbackNamespaces
|
|
431
|
-
} = loader;
|
|
432
|
-
const { unprefixedPathname } = extractPathname({ config, pathname });
|
|
433
|
-
const standardizedPathname = standardizePathname({
|
|
434
|
-
config,
|
|
435
|
-
pathname: unprefixedPathname
|
|
436
|
-
});
|
|
423
|
+
const { routeNamespaces = {}, namespaces } = loader || {};
|
|
424
|
+
const standardizedPathname = standardizePathname({ config, pathname });
|
|
437
425
|
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
438
426
|
`/${PREFIX_PLACEHOLDER}`,
|
|
439
427
|
""
|
|
440
428
|
);
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
429
|
+
const collected = [
|
|
430
|
+
...routeNamespaces.default || [],
|
|
431
|
+
// default
|
|
432
|
+
...namespaces || [],
|
|
433
|
+
// default
|
|
434
|
+
...routeNamespaces[standardizedPathname] || [],
|
|
435
|
+
// exact match
|
|
436
|
+
...routeNamespaces[placeholderRemovedPathname] || []
|
|
437
|
+
// exact match
|
|
438
|
+
];
|
|
448
439
|
const prefixPatterns = Object.keys(routeNamespaces).filter(
|
|
449
440
|
(pattern) => pattern.endsWith("/*")
|
|
450
441
|
);
|
|
451
442
|
for (const pattern of prefixPatterns) {
|
|
452
443
|
const basePath = pattern.replace(/\/\*$/, "");
|
|
453
|
-
if (standardizedPathname.startsWith(basePath)) {
|
|
454
|
-
|
|
455
|
-
bestMatch = basePath;
|
|
456
|
-
bestNamespaces = routeNamespaces[pattern];
|
|
457
|
-
}
|
|
444
|
+
if (standardizedPathname.startsWith(basePath) || placeholderRemovedPathname.startsWith(basePath)) {
|
|
445
|
+
collected.push(...routeNamespaces[pattern] || []);
|
|
458
446
|
}
|
|
459
447
|
}
|
|
460
|
-
|
|
461
|
-
if (matchedNamespaces.length > 0) {
|
|
462
|
-
return [...defaultNamespaces, ...matchedNamespaces];
|
|
463
|
-
} else {
|
|
464
|
-
return [...defaultNamespaces];
|
|
465
|
-
}
|
|
448
|
+
return [...new Set(collected)];
|
|
466
449
|
};
|
|
467
450
|
|
|
468
451
|
// src/shared/utils/locale/normalize-locale.ts
|
|
452
|
+
var toCanonical = (input) => {
|
|
453
|
+
try {
|
|
454
|
+
return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
|
|
455
|
+
} catch {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
469
459
|
var normalizeLocale = (locale = "", supportedLocales = []) => {
|
|
470
460
|
if (!locale || supportedLocales.length === 0) return;
|
|
471
|
-
const toCanonical = (input) => {
|
|
472
|
-
try {
|
|
473
|
-
return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
|
|
474
|
-
} catch {
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
461
|
const canonicalLocale = toCanonical(locale);
|
|
479
462
|
if (!canonicalLocale) return;
|
|
480
463
|
const supportedCanonicalMap = /* @__PURE__ */ new Map();
|
|
@@ -505,12 +488,12 @@ var resolvePreferredLocale = (acceptLanguageHeader, supportedLocales) => {
|
|
|
505
488
|
const supportedLocalesSet = new Set(supportedLocales);
|
|
506
489
|
const preferred = acceptLanguageHeader.split(",").map((part) => {
|
|
507
490
|
const [lang, qValue] = part.split(";");
|
|
508
|
-
const q = qValue ? parseFloat(qValue.split("=")[1]) : 1;
|
|
509
|
-
if (isNaN(q)) {
|
|
491
|
+
const q = qValue ? Number.parseFloat(qValue.split("=")[1]) : 1;
|
|
492
|
+
if (Number.isNaN(q)) {
|
|
510
493
|
return { lang: lang.trim(), q: 0 };
|
|
511
494
|
}
|
|
512
495
|
return { lang: lang.trim(), q };
|
|
513
|
-
}).
|
|
496
|
+
}).toSorted((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
|
|
514
497
|
return preferred;
|
|
515
498
|
};
|
|
516
499
|
|
|
@@ -519,8 +502,8 @@ var normalizePathname = (rawPathname, options = {}) => {
|
|
|
519
502
|
const length = rawPathname.length;
|
|
520
503
|
let start = 0;
|
|
521
504
|
let end = length - 1;
|
|
522
|
-
while (start <= end && rawPathname.
|
|
523
|
-
while (end >= start && rawPathname.
|
|
505
|
+
while (start <= end && (rawPathname.codePointAt(start) ?? 0) <= 32) start++;
|
|
506
|
+
while (end >= start && (rawPathname.codePointAt(end) ?? 0) <= 32) end--;
|
|
524
507
|
if (start > end) return "/";
|
|
525
508
|
let result = "";
|
|
526
509
|
let hasSlash = false;
|
|
@@ -531,11 +514,7 @@ var normalizePathname = (rawPathname, options = {}) => {
|
|
|
531
514
|
hasSlash = true;
|
|
532
515
|
}
|
|
533
516
|
} else {
|
|
534
|
-
|
|
535
|
-
result += "/" + char;
|
|
536
|
-
} else {
|
|
537
|
-
result += char;
|
|
538
|
-
}
|
|
517
|
+
result += hasSlash || result === "" ? "/" + char : char;
|
|
539
518
|
hasSlash = false;
|
|
540
519
|
}
|
|
541
520
|
}
|
|
@@ -559,8 +538,8 @@ var extractPathname = ({
|
|
|
559
538
|
} else if (basePath && normalizedPathname === basePath) {
|
|
560
539
|
prefixedPathname = "/";
|
|
561
540
|
}
|
|
562
|
-
const
|
|
563
|
-
const maybeLocale =
|
|
541
|
+
const pathPart = prefixedPathname.split("/").find(Boolean);
|
|
542
|
+
const maybeLocale = pathPart || "";
|
|
564
543
|
const isLocalePrefixed = config.supportedLocales?.includes(maybeLocale);
|
|
565
544
|
let unprefixedPathname = prefixedPathname;
|
|
566
545
|
if (prefix === "all") {
|
|
@@ -593,7 +572,7 @@ var standardizePathname = ({
|
|
|
593
572
|
PREFIX_PLACEHOLDER,
|
|
594
573
|
normalizePathname(pathname)
|
|
595
574
|
];
|
|
596
|
-
const standardizedPathname = parts.join("/").
|
|
575
|
+
const standardizedPathname = parts.join("/").replaceAll(/\/{2,}/g, "/");
|
|
597
576
|
return normalizePathname(standardizedPathname);
|
|
598
577
|
};
|
|
599
578
|
|
|
@@ -632,8 +611,8 @@ var loadLocalMessages = async ({
|
|
|
632
611
|
loggerOptions.id,
|
|
633
612
|
resolvedBasePath,
|
|
634
613
|
locale,
|
|
635
|
-
|
|
636
|
-
|
|
614
|
+
(fallbackLocales ?? []).toSorted().join(","),
|
|
615
|
+
(namespaces ?? []).toSorted().join(",")
|
|
637
616
|
]);
|
|
638
617
|
if (cache.enabled && key) {
|
|
639
618
|
const cached = await pool?.get(key);
|
|
@@ -778,8 +757,8 @@ var loadApiMessages = async ({
|
|
|
778
757
|
loggerOptions.id,
|
|
779
758
|
basePath,
|
|
780
759
|
locale,
|
|
781
|
-
|
|
782
|
-
|
|
760
|
+
(fallbackLocales ?? []).toSorted().join(","),
|
|
761
|
+
(namespaces ?? []).toSorted().join(",")
|
|
783
762
|
]);
|
|
784
763
|
if (cache.enabled && key) {
|
|
785
764
|
const cached = await pool?.get(key);
|
|
@@ -841,7 +820,7 @@ var loadMessages = async ({
|
|
|
841
820
|
);
|
|
842
821
|
return;
|
|
843
822
|
}
|
|
844
|
-
const { loader } = config;
|
|
823
|
+
const { id, loader, cache } = config;
|
|
845
824
|
const fallbackLocales = config.fallbackLocales[locale] || [];
|
|
846
825
|
const namespaces = resolveNamespaces({ config, pathname });
|
|
847
826
|
logger.debug("Namespaces ready for loading.", {
|
|
@@ -856,8 +835,8 @@ var loadMessages = async ({
|
|
|
856
835
|
locale,
|
|
857
836
|
fallbackLocales,
|
|
858
837
|
namespaces,
|
|
859
|
-
cache
|
|
860
|
-
logger: { id
|
|
838
|
+
cache,
|
|
839
|
+
logger: { id }
|
|
861
840
|
});
|
|
862
841
|
} else if (loader.type === "api") {
|
|
863
842
|
loadedMessages = await loadApiMessages({
|
|
@@ -865,7 +844,7 @@ var loadMessages = async ({
|
|
|
865
844
|
locale,
|
|
866
845
|
fallbackLocales,
|
|
867
846
|
namespaces,
|
|
868
|
-
logger: { id
|
|
847
|
+
logger: { id }
|
|
869
848
|
});
|
|
870
849
|
}
|
|
871
850
|
if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
|
|
@@ -881,15 +860,10 @@ var intor = async (config, i18nContext) => {
|
|
|
881
860
|
logger.info("Start Intor initialization.");
|
|
882
861
|
const { messages, loader } = config;
|
|
883
862
|
const isI18nContextFunction = typeof i18nContext === "function";
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
}
|
|
888
|
-
context = {
|
|
889
|
-
locale: i18nContext?.locale || config.defaultLocale,
|
|
890
|
-
pathname: i18nContext?.pathname || ""
|
|
891
|
-
};
|
|
892
|
-
}
|
|
863
|
+
const context = isI18nContextFunction ? await i18nContext(config) : {
|
|
864
|
+
locale: i18nContext?.locale || config.defaultLocale,
|
|
865
|
+
pathname: i18nContext?.pathname || ""
|
|
866
|
+
};
|
|
893
867
|
const { locale, pathname } = context;
|
|
894
868
|
const source = isI18nContextFunction ? "[function]" : "[static object]";
|
|
895
869
|
logger.debug(`Context resolved via ${source}.`, context);
|
|
@@ -914,29 +888,23 @@ var intor = async (config, i18nContext) => {
|
|
|
914
888
|
};
|
|
915
889
|
};
|
|
916
890
|
async function getTranslator(opts) {
|
|
917
|
-
const { config, locale, pathname = "", preKey } = opts;
|
|
891
|
+
const { config, locale, pathname = "", preKey, handlers } = opts;
|
|
918
892
|
const messages = await loadMessages({ config, locale, pathname });
|
|
919
893
|
const translator = new Translator({
|
|
920
894
|
locale,
|
|
921
895
|
messages,
|
|
922
896
|
fallbackLocales: config.fallbackLocales,
|
|
923
897
|
loadingMessage: config.translator?.loadingMessage,
|
|
924
|
-
placeholder: config.translator?.placeholder
|
|
898
|
+
placeholder: config.translator?.placeholder,
|
|
899
|
+
handlers
|
|
925
900
|
});
|
|
926
|
-
const props = {
|
|
927
|
-
|
|
928
|
-
|
|
901
|
+
const props = { messages, locale };
|
|
902
|
+
const scoped = translator.scoped(preKey);
|
|
903
|
+
return {
|
|
904
|
+
...props,
|
|
905
|
+
hasKey: preKey ? scoped.hasKey : translator.hasKey,
|
|
906
|
+
t: preKey ? scoped.t : translator.t
|
|
929
907
|
};
|
|
930
|
-
if (preKey) {
|
|
931
|
-
const scoped = translator.scoped(preKey);
|
|
932
|
-
return { ...props, ...scoped };
|
|
933
|
-
} else {
|
|
934
|
-
return {
|
|
935
|
-
...props,
|
|
936
|
-
t: translator.t,
|
|
937
|
-
hasKey: translator.hasKey
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
908
|
}
|
|
941
909
|
|
|
942
910
|
export { PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadApiMessages, loadLocalMessages, loadMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
|