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