intor 2.2.1 → 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.d.cts +9 -9
- package/dist/next/middleware/index.d.ts +9 -9
- 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.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,172 @@ 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-namespace-messages.ts
|
|
132
|
+
function isPlainObject(value) {
|
|
133
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
134
|
+
}
|
|
135
|
+
function isNamespaceMessages(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
148
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
|
|
153
|
+
async function jsonReader(filePath, readFile = fs.readFile) {
|
|
154
|
+
const raw = await readFile(filePath, "utf8");
|
|
155
|
+
const parsed = JSON.parse(raw);
|
|
156
|
+
if (!isNamespaceMessages(parsed)) {
|
|
157
|
+
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
158
|
+
}
|
|
159
|
+
return parsed;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
|
|
163
|
+
function nestObjectFromPath(path4, value) {
|
|
164
|
+
let obj = value;
|
|
165
|
+
for (let i = path4.length - 1; i >= 0; i--) {
|
|
166
|
+
obj = { [path4[i]]: obj };
|
|
204
167
|
}
|
|
205
|
-
|
|
168
|
+
return obj;
|
|
169
|
+
}
|
|
206
170
|
|
|
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" };
|
|
171
|
+
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.ts
|
|
172
|
+
async function parseFileEntries({
|
|
173
|
+
fileEntries,
|
|
174
|
+
limit,
|
|
175
|
+
extraOptions: { messageFileReader, loggerOptions } = {}
|
|
176
|
+
}) {
|
|
217
177
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
218
|
-
const logger = baseLogger.child({ scope: "
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
namespaceGroups,
|
|
234
|
-
currentDirPath: filePath,
|
|
235
|
-
namespacePathSegments: [...namespacePathSegments, dirent.name],
|
|
236
|
-
options,
|
|
237
|
-
readdir
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}).catch((error) => {
|
|
241
|
-
logger.warn("Failed to process a locale file or directory.", {
|
|
242
|
-
name: dirent.name,
|
|
243
|
-
type: dirent.isFile() ? "file" : "directory",
|
|
244
|
-
path: currentDirPath,
|
|
178
|
+
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
179
|
+
const parsedFileEntries = [];
|
|
180
|
+
const tasks = fileEntries.map(
|
|
181
|
+
({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
182
|
+
try {
|
|
183
|
+
const segsWithoutNs = segments.slice(1);
|
|
184
|
+
const json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
|
|
185
|
+
const isIndex = basename === "index";
|
|
186
|
+
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
187
|
+
const namespaceMessages = nestObjectFromPath(keyPath, json);
|
|
188
|
+
parsedFileEntries.push({ namespace, namespaceMessages });
|
|
189
|
+
logger.trace("Parsed file.", { path: fullPath });
|
|
190
|
+
} catch (error) {
|
|
191
|
+
logger.error("Failed to read or parse file.", {
|
|
192
|
+
path: fullPath,
|
|
245
193
|
error
|
|
246
194
|
});
|
|
247
|
-
}
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
await Promise.all(tasks);
|
|
199
|
+
const result = {};
|
|
200
|
+
for (const { namespace, namespaceMessages } of parsedFileEntries) {
|
|
201
|
+
if (namespace === "index") {
|
|
202
|
+
merge(result, namespaceMessages);
|
|
203
|
+
} else {
|
|
204
|
+
result[namespace] = merge(
|
|
205
|
+
result[namespace] ?? {},
|
|
206
|
+
namespaceMessages
|
|
207
|
+
);
|
|
208
|
+
}
|
|
252
209
|
}
|
|
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
|
-
};
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
267
212
|
|
|
268
|
-
// src/modules/messages/load-local-messages/
|
|
269
|
-
var
|
|
270
|
-
|
|
213
|
+
// src/modules/messages/load-local-messages/read-locale-messages/read-locale-messages.ts
|
|
214
|
+
var readLocaleMessages = async ({
|
|
215
|
+
limit,
|
|
216
|
+
rootDir = "messages",
|
|
271
217
|
locale,
|
|
272
218
|
namespaces,
|
|
273
|
-
|
|
274
|
-
limit,
|
|
275
|
-
logger: loggerOptions = { id: "default" }
|
|
219
|
+
extraOptions: { exts, messageFileReader, loggerOptions } = {}
|
|
276
220
|
}) => {
|
|
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,
|
|
221
|
+
const fileEntries = await collectFileEntries({
|
|
222
|
+
rootDir: path.resolve(process.cwd(), rootDir, locale),
|
|
223
|
+
namespaces,
|
|
296
224
|
limit,
|
|
297
|
-
|
|
298
|
-
logger: loggerOptions
|
|
299
|
-
});
|
|
300
|
-
if (namespaceGroups.size === 0) {
|
|
301
|
-
logger.warn("No namespace groups found.", {
|
|
302
|
-
locale,
|
|
303
|
-
basePath,
|
|
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
|
-
}))
|
|
225
|
+
extraOptions: { exts, loggerOptions }
|
|
314
226
|
});
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
|
227
|
+
const namespaceMessages = await parseFileEntries({
|
|
228
|
+
fileEntries,
|
|
229
|
+
limit,
|
|
230
|
+
extraOptions: { messageFileReader, loggerOptions }
|
|
364
231
|
});
|
|
365
|
-
|
|
232
|
+
const localeMessages = { [locale]: namespaceMessages };
|
|
233
|
+
return localeMessages;
|
|
366
234
|
};
|
|
367
235
|
function getGlobalMessagesPool() {
|
|
368
236
|
if (!globalThis.__INTOR_MESSAGES_POOL__) {
|
|
@@ -374,22 +242,9 @@ function clearMessagesPool() {
|
|
|
374
242
|
const pool = getGlobalMessagesPool();
|
|
375
243
|
pool.clear();
|
|
376
244
|
}
|
|
377
|
-
|
|
378
|
-
// src/shared/utils/merge-messages.ts
|
|
379
245
|
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;
|
|
246
|
+
if (!loadedMessages) return { ...staticMessages };
|
|
247
|
+
return merge({}, staticMessages, loadedMessages);
|
|
393
248
|
};
|
|
394
249
|
|
|
395
250
|
// src/shared/utils/normalize-cache-key.ts
|
|
@@ -421,6 +276,8 @@ var resolveNamespaces = ({
|
|
|
421
276
|
}) => {
|
|
422
277
|
const { loader } = config;
|
|
423
278
|
const { routeNamespaces = {}, namespaces } = loader || {};
|
|
279
|
+
if (Object.keys(routeNamespaces).length === 0 && !namespaces)
|
|
280
|
+
return void 0;
|
|
424
281
|
const standardizedPathname = standardizePathname({ config, pathname });
|
|
425
282
|
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
426
283
|
`/${PREFIX_PLACEHOLDER}`,
|
|
@@ -578,43 +435,35 @@ var standardizePathname = ({
|
|
|
578
435
|
|
|
579
436
|
// src/modules/messages/load-local-messages/load-local-messages.ts
|
|
580
437
|
var loadLocalMessages = async ({
|
|
581
|
-
|
|
438
|
+
pool = getGlobalMessagesPool(),
|
|
439
|
+
rootDir = "messages",
|
|
582
440
|
locale,
|
|
583
441
|
fallbackLocales,
|
|
584
442
|
namespaces,
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
443
|
+
extraOptions: {
|
|
444
|
+
concurrency = 10,
|
|
445
|
+
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
446
|
+
loggerOptions = { id: "default" },
|
|
447
|
+
exts,
|
|
448
|
+
messageFileReader
|
|
449
|
+
} = {}
|
|
588
450
|
}) => {
|
|
589
|
-
basePath = basePath ?? "messages";
|
|
590
|
-
if (!locale || locale.trim() === "") return {};
|
|
591
451
|
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
|
|
452
|
+
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
453
|
+
const start = performance$1.now();
|
|
454
|
+
logger.debug("Loading local messages from directory.", {
|
|
455
|
+
rootDir,
|
|
456
|
+
resolvedRootDir: path.resolve(process.cwd(), rootDir)
|
|
605
457
|
});
|
|
606
|
-
let pool;
|
|
607
|
-
if (cache.enabled) {
|
|
608
|
-
pool = getGlobalMessagesPool();
|
|
609
|
-
}
|
|
610
458
|
const key = normalizeCacheKey([
|
|
611
459
|
loggerOptions.id,
|
|
612
|
-
|
|
460
|
+
"loaderType:local",
|
|
461
|
+
rootDir,
|
|
613
462
|
locale,
|
|
614
|
-
(fallbackLocales
|
|
615
|
-
(namespaces
|
|
463
|
+
(fallbackLocales || []).toSorted().join(","),
|
|
464
|
+
(namespaces || []).toSorted().join(",")
|
|
616
465
|
]);
|
|
617
|
-
if (
|
|
466
|
+
if (cacheOptions.enabled && key) {
|
|
618
467
|
const cached = await pool?.get(key);
|
|
619
468
|
if (cached) {
|
|
620
469
|
logger.debug("Messages cache hit.", { key });
|
|
@@ -622,50 +471,57 @@ var loadLocalMessages = async ({
|
|
|
622
471
|
}
|
|
623
472
|
}
|
|
624
473
|
const limit = pLimit(concurrency);
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
474
|
+
const candidateLocales = [locale, ...fallbackLocales || []];
|
|
475
|
+
let messages;
|
|
476
|
+
for (const candidateLocale of candidateLocales) {
|
|
477
|
+
try {
|
|
478
|
+
const result = await readLocaleMessages({
|
|
479
|
+
limit,
|
|
480
|
+
rootDir,
|
|
481
|
+
locale: candidateLocale,
|
|
482
|
+
namespaces,
|
|
483
|
+
extraOptions: { loggerOptions, exts, messageFileReader }
|
|
484
|
+
});
|
|
485
|
+
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
486
|
+
messages = result;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
} catch (error) {
|
|
490
|
+
logger.error("Failed to read locale messages", {
|
|
491
|
+
locale: candidateLocale,
|
|
492
|
+
error
|
|
493
|
+
});
|
|
494
|
+
}
|
|
636
495
|
}
|
|
637
|
-
|
|
496
|
+
if (cacheOptions.enabled && key && messages) {
|
|
497
|
+
await pool?.set(key, messages, cacheOptions.ttl);
|
|
498
|
+
}
|
|
499
|
+
const end = performance$1.now();
|
|
638
500
|
const duration = Math.round(end - start);
|
|
639
501
|
logger.trace("Finished loading local messages.", {
|
|
640
|
-
|
|
641
|
-
validNamespaces,
|
|
502
|
+
loadedLocale: messages ? Object.keys(messages)[0] : void 0,
|
|
642
503
|
duration: `${duration} ms`
|
|
643
504
|
});
|
|
644
505
|
return messages;
|
|
645
506
|
};
|
|
646
507
|
|
|
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,
|
|
508
|
+
// src/modules/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.ts
|
|
509
|
+
var fetchLocaleMessages = async ({
|
|
510
|
+
remoteUrl,
|
|
511
|
+
remoteHeaders,
|
|
657
512
|
searchParams,
|
|
658
|
-
|
|
513
|
+
locale,
|
|
514
|
+
extraOptions: { loggerOptions } = {}
|
|
659
515
|
}) => {
|
|
660
516
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
661
|
-
const logger = baseLogger.child({ scope: "fetch-messages" });
|
|
517
|
+
const logger = baseLogger.child({ scope: "fetch-locale-messages" });
|
|
662
518
|
try {
|
|
663
519
|
const params = new URLSearchParams(searchParams);
|
|
664
520
|
params.append("locale", locale);
|
|
665
|
-
const url = `${
|
|
521
|
+
const url = `${remoteUrl}?${params.toString()}`;
|
|
666
522
|
const headers = {
|
|
667
523
|
"Content-Type": "application/json",
|
|
668
|
-
...
|
|
524
|
+
...remoteHeaders
|
|
669
525
|
};
|
|
670
526
|
const response = await fetch(url, {
|
|
671
527
|
method: "GET",
|
|
@@ -673,17 +529,17 @@ var fetchMessages = async ({
|
|
|
673
529
|
cache: "no-store"
|
|
674
530
|
});
|
|
675
531
|
if (!response.ok) {
|
|
676
|
-
throw new Error(`
|
|
532
|
+
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
677
533
|
}
|
|
678
534
|
const data = await response.json();
|
|
679
|
-
if (
|
|
680
|
-
throw new Error(
|
|
535
|
+
if (!isNamespaceMessages(data[locale])) {
|
|
536
|
+
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
681
537
|
}
|
|
682
538
|
return data;
|
|
683
539
|
} catch (error) {
|
|
684
|
-
logger.warn(
|
|
540
|
+
logger.warn("Fetching locale messages failed.", {
|
|
685
541
|
locale,
|
|
686
|
-
|
|
542
|
+
remoteUrl,
|
|
687
543
|
searchParams: decodeURIComponent(searchParams.toString()),
|
|
688
544
|
error
|
|
689
545
|
});
|
|
@@ -691,30 +547,7 @@ var fetchMessages = async ({
|
|
|
691
547
|
}
|
|
692
548
|
};
|
|
693
549
|
|
|
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
|
|
550
|
+
// src/modules/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.ts
|
|
718
551
|
var buildSearchParams = (params) => {
|
|
719
552
|
const searchParams = new URLSearchParams();
|
|
720
553
|
const appendParam = (key, value) => {
|
|
@@ -732,119 +565,130 @@ var buildSearchParams = (params) => {
|
|
|
732
565
|
return searchParams;
|
|
733
566
|
};
|
|
734
567
|
|
|
735
|
-
// src/modules/messages/load-
|
|
736
|
-
var
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
568
|
+
// src/modules/messages/load-remote-messages/load-remote-messages.ts
|
|
569
|
+
var loadRemoteMessages = async ({
|
|
570
|
+
pool = getGlobalMessagesPool(),
|
|
571
|
+
rootDir,
|
|
572
|
+
remoteUrl,
|
|
573
|
+
remoteHeaders,
|
|
740
574
|
locale,
|
|
741
575
|
fallbackLocales = [],
|
|
742
576
|
namespaces = [],
|
|
743
|
-
|
|
744
|
-
|
|
577
|
+
extraOptions: {
|
|
578
|
+
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
579
|
+
loggerOptions = { id: "default" }
|
|
580
|
+
} = {}
|
|
745
581
|
}) => {
|
|
746
582
|
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
|
-
}
|
|
583
|
+
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
584
|
+
const start = performance.now();
|
|
585
|
+
logger.debug("Loading remote messages from api.", { remoteUrl });
|
|
756
586
|
const key = normalizeCacheKey([
|
|
757
587
|
loggerOptions.id,
|
|
758
|
-
|
|
588
|
+
"loaderType:remote",
|
|
589
|
+
rootDir,
|
|
759
590
|
locale,
|
|
760
591
|
(fallbackLocales ?? []).toSorted().join(","),
|
|
761
592
|
(namespaces ?? []).toSorted().join(",")
|
|
762
593
|
]);
|
|
763
|
-
if (
|
|
594
|
+
if (cacheOptions.enabled && key) {
|
|
764
595
|
const cached = await pool?.get(key);
|
|
765
596
|
if (cached) {
|
|
766
597
|
logger.debug("Messages cache hit.", { key });
|
|
767
598
|
return cached;
|
|
768
599
|
}
|
|
769
600
|
}
|
|
770
|
-
const searchParams = buildSearchParams({
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
601
|
+
const searchParams = buildSearchParams({ rootDir, namespaces });
|
|
602
|
+
const candidateLocales = [locale, ...fallbackLocales || []];
|
|
603
|
+
let messages;
|
|
604
|
+
for (const candidateLocale of candidateLocales) {
|
|
605
|
+
try {
|
|
606
|
+
const result = await fetchLocaleMessages({
|
|
607
|
+
remoteUrl,
|
|
608
|
+
remoteHeaders,
|
|
609
|
+
searchParams,
|
|
610
|
+
locale: candidateLocale,
|
|
611
|
+
extraOptions: { loggerOptions }
|
|
612
|
+
});
|
|
613
|
+
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
614
|
+
messages = result;
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
} catch (error) {
|
|
618
|
+
logger.error("Failed to fetch locale messages.", {
|
|
619
|
+
locale: candidateLocale,
|
|
620
|
+
error
|
|
621
|
+
});
|
|
781
622
|
}
|
|
782
|
-
return messages;
|
|
783
623
|
}
|
|
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;
|
|
624
|
+
if (cacheOptions.enabled && key && messages) {
|
|
625
|
+
await pool?.set(key, messages, cacheOptions.ttl);
|
|
801
626
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
627
|
+
const end = performance.now();
|
|
628
|
+
const duration = Math.round(end - start);
|
|
629
|
+
logger.trace("Finished loading remote messages.", {
|
|
630
|
+
loadedLocale: messages ? Object.keys(messages)[0] : void 0,
|
|
631
|
+
duration: `${duration} ms`
|
|
805
632
|
});
|
|
806
|
-
return;
|
|
633
|
+
return messages;
|
|
807
634
|
};
|
|
808
635
|
|
|
809
636
|
// src/modules/messages/load-messages.ts
|
|
810
637
|
var loadMessages = async ({
|
|
811
638
|
config,
|
|
812
639
|
locale,
|
|
813
|
-
pathname
|
|
640
|
+
pathname = "",
|
|
641
|
+
extraOptions: { exts, messageFileReader } = {}
|
|
814
642
|
}) => {
|
|
815
643
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
816
|
-
const logger = baseLogger.child({ scope: "messages
|
|
644
|
+
const logger = baseLogger.child({ scope: "load-messages" });
|
|
817
645
|
if (!config.loader) {
|
|
818
646
|
logger.warn(
|
|
819
647
|
"No loader options have been configured in the current config."
|
|
820
648
|
);
|
|
821
649
|
return;
|
|
822
650
|
}
|
|
823
|
-
const {
|
|
651
|
+
const { type, concurrency, rootDir } = config.loader;
|
|
824
652
|
const fallbackLocales = config.fallbackLocales[locale] || [];
|
|
825
653
|
const namespaces = resolveNamespaces({ config, pathname });
|
|
826
|
-
logger.
|
|
827
|
-
|
|
654
|
+
if (logger.core.level === "debug") {
|
|
655
|
+
logger.debug("Starting to load messages.", { locale });
|
|
656
|
+
}
|
|
657
|
+
logger.trace("Starting to load messages with runtime context.", {
|
|
658
|
+
loaderType: type,
|
|
659
|
+
locale,
|
|
660
|
+
fallbackLocales,
|
|
661
|
+
namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : "[ALL]",
|
|
662
|
+
cache: config.cache,
|
|
663
|
+
concurrency: concurrency ?? 10
|
|
828
664
|
});
|
|
829
|
-
logger.debug("Loader type selected.", { loaderType: loader.type });
|
|
830
665
|
let loadedMessages;
|
|
831
|
-
if (
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
...loader,
|
|
666
|
+
if (type === "local") {
|
|
667
|
+
loadedMessages = await loadLocalMessages({
|
|
668
|
+
rootDir,
|
|
835
669
|
locale,
|
|
836
670
|
fallbackLocales,
|
|
837
671
|
namespaces,
|
|
838
|
-
|
|
839
|
-
|
|
672
|
+
extraOptions: {
|
|
673
|
+
concurrency,
|
|
674
|
+
cacheOptions: config.cache,
|
|
675
|
+
loggerOptions: { id: config.id, ...config.logger },
|
|
676
|
+
exts,
|
|
677
|
+
messageFileReader
|
|
678
|
+
}
|
|
840
679
|
});
|
|
841
|
-
} else if (
|
|
842
|
-
loadedMessages = await
|
|
843
|
-
|
|
680
|
+
} else if (type === "remote") {
|
|
681
|
+
loadedMessages = await loadRemoteMessages({
|
|
682
|
+
rootDir,
|
|
683
|
+
remoteUrl: config.loader.remoteUrl,
|
|
684
|
+
remoteHeaders: config.loader.remoteHeaders,
|
|
844
685
|
locale,
|
|
845
686
|
fallbackLocales,
|
|
846
687
|
namespaces,
|
|
847
|
-
|
|
688
|
+
extraOptions: {
|
|
689
|
+
cacheOptions: config.cache,
|
|
690
|
+
loggerOptions: { id: config.id, ...config.logger }
|
|
691
|
+
}
|
|
848
692
|
});
|
|
849
693
|
}
|
|
850
694
|
if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
|
|
@@ -854,7 +698,7 @@ var loadMessages = async ({
|
|
|
854
698
|
};
|
|
855
699
|
|
|
856
700
|
// src/modules/intor/intor.ts
|
|
857
|
-
var intor = async (config, i18nContext) => {
|
|
701
|
+
var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
|
|
858
702
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
859
703
|
const logger = baseLogger.child({ scope: "intor" });
|
|
860
704
|
logger.info("Start Intor initialization.");
|
|
@@ -865,20 +709,28 @@ var intor = async (config, i18nContext) => {
|
|
|
865
709
|
pathname: i18nContext?.pathname || ""
|
|
866
710
|
};
|
|
867
711
|
const { locale, pathname } = context;
|
|
868
|
-
const source = isI18nContextFunction ?
|
|
869
|
-
logger.debug(`
|
|
712
|
+
const source = isI18nContextFunction ? i18nContext.name : "static fallback";
|
|
713
|
+
logger.debug(`I18n context resolved via ${source}.`, context);
|
|
870
714
|
let loadedMessages;
|
|
871
715
|
if (shouldLoadMessages(loader)) {
|
|
872
|
-
loadedMessages = await loadMessages({
|
|
716
|
+
loadedMessages = await loadMessages({
|
|
717
|
+
config,
|
|
718
|
+
locale,
|
|
719
|
+
pathname,
|
|
720
|
+
extraOptions: {
|
|
721
|
+
exts: loadMessagesOptions.exts,
|
|
722
|
+
messageFileReader: loadMessagesOptions.messageFileReader
|
|
723
|
+
}
|
|
724
|
+
});
|
|
873
725
|
}
|
|
874
726
|
const mergedMessages = mergeMessages(messages, loadedMessages);
|
|
875
|
-
logger.info("Messages initialized.", {
|
|
876
|
-
|
|
877
|
-
|
|
727
|
+
logger.info("Messages successfully initialized.", {
|
|
728
|
+
static: { enabled: !!messages },
|
|
729
|
+
loaded: {
|
|
878
730
|
enabled: !!loadedMessages,
|
|
879
731
|
...loader ? { loaderType: loader.type, lazyLoad: !!loader.lazyLoad } : null
|
|
880
732
|
},
|
|
881
|
-
mergedMessages
|
|
733
|
+
merged: mergedMessages
|
|
882
734
|
});
|
|
883
735
|
return {
|
|
884
736
|
config,
|
|
@@ -907,4 +759,4 @@ async function getTranslator(opts) {
|
|
|
907
759
|
};
|
|
908
760
|
}
|
|
909
761
|
|
|
910
|
-
export { PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor,
|
|
762
|
+
export { PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
|