intor 1.0.39 → 2.0.0
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 +44 -79
- package/dist/config/index.d.cts +69 -36
- package/dist/config/index.d.ts +69 -36
- package/dist/config/index.js +42 -80
- package/dist/index.cjs +482 -540
- package/dist/index.d.cts +163 -139
- package/dist/index.d.ts +163 -139
- package/dist/index.js +475 -537
- package/dist/next/index.cjs +399 -359
- package/dist/next/index.d.cts +82 -113
- package/dist/next/index.d.ts +82 -113
- package/dist/next/index.js +396 -351
- package/dist/next/middleware/index.cjs +79 -93
- package/dist/next/middleware/index.d.cts +59 -28
- package/dist/next/middleware/index.d.ts +59 -28
- package/dist/next/middleware/index.js +79 -93
- package/dist/next/server/index.cjs +886 -0
- package/dist/next/server/index.d.cts +149 -0
- package/dist/next/server/index.d.ts +149 -0
- package/dist/next/server/index.js +875 -0
- package/package.json +9 -12
- package/exports/next/provider/intor-provider.tsx +0 -25
- package/exports/next/provider/translate-handlers-provider.tsx +0 -19
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var logry = require('logry');
|
|
4
|
-
var headers = require('next/headers');
|
|
5
3
|
var path = require('path');
|
|
6
4
|
var perf_hooks = require('perf_hooks');
|
|
7
5
|
var pLimit = require('p-limit');
|
|
8
6
|
var fs = require('fs/promises');
|
|
7
|
+
var logry = require('logry');
|
|
8
|
+
var Keyv = require('keyv');
|
|
9
9
|
var intorTranslator = require('intor-translator');
|
|
10
10
|
|
|
11
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -13,10 +13,25 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
13
13
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
14
14
|
var pLimit__default = /*#__PURE__*/_interopDefault(pLimit);
|
|
15
15
|
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
16
|
+
var Keyv__default = /*#__PURE__*/_interopDefault(Keyv);
|
|
17
|
+
|
|
18
|
+
// src/modules/intor/utils/should-load-messages.ts
|
|
19
|
+
var shouldLoadMessages = (loader) => {
|
|
20
|
+
if (!loader) return false;
|
|
21
|
+
const { type, lazyLoad } = loader;
|
|
22
|
+
if (type === "import") return true;
|
|
23
|
+
if (lazyLoad) return false;
|
|
24
|
+
return true;
|
|
25
|
+
};
|
|
16
26
|
|
|
17
|
-
// src/modules/
|
|
27
|
+
// src/modules/config/constants/cache.constants.ts
|
|
28
|
+
var DEFAULT_CACHE_OPTIONS = {
|
|
29
|
+
enabled: process.env.NODE_ENV === "production",
|
|
30
|
+
ttl: 60 * 60 * 1e3
|
|
31
|
+
// 1 hour
|
|
32
|
+
};
|
|
18
33
|
|
|
19
|
-
// src/
|
|
34
|
+
// src/shared/error/intor-error.ts
|
|
20
35
|
var IntorError = class extends Error {
|
|
21
36
|
constructor({ message, code, id }) {
|
|
22
37
|
const fullMessage = id ? `[${id}] ${message}` : message;
|
|
@@ -28,218 +43,95 @@ var IntorError = class extends Error {
|
|
|
28
43
|
}
|
|
29
44
|
};
|
|
30
45
|
|
|
31
|
-
// src/modules/
|
|
32
|
-
var
|
|
33
|
-
IntorErrorCode2["MISSING_DEFAULT_LOCALE"] = "INTOR_MISSING_DEFAULT_LOCALE";
|
|
34
|
-
IntorErrorCode2["UNSUPPORTED_DEFAULT_LOCALE"] = "INTOR_UNSUPPORTED_DEFAULT_LOCALE";
|
|
35
|
-
IntorErrorCode2["MISSING_SUPPORTED_LOCALES"] = "INTOR_MISSING_SUPPORTED_LOCALES";
|
|
36
|
-
IntorErrorCode2["UNSUPPORTED_ADAPTER"] = "INTOR_UNSUPPORTED_ADAPTER";
|
|
37
|
-
IntorErrorCode2["ADAPTER_RUNTIME_LOAD_FAILED"] = "INTOR_ADAPTER_RUNTIME_LOAD_FAILED";
|
|
38
|
-
IntorErrorCode2["UNKNOWN_LOADER_TYPE"] = "INTOR_UNKNOWN_LOADER_TYPE";
|
|
39
|
-
return IntorErrorCode2;
|
|
40
|
-
})(IntorErrorCode || {});
|
|
41
|
-
|
|
42
|
-
// src/adapters/next-client/constants/header-key-constants.ts
|
|
43
|
-
var DEFAULT_PATHNAME_HEADER_NAME = "x-intor-pathname";
|
|
44
|
-
|
|
45
|
-
// src/shared/utils/locale/normalize-locale.ts
|
|
46
|
-
var normalizeLocale = (locale = "", supportedLocales = []) => {
|
|
47
|
-
if (!locale || supportedLocales.length === 0) return void 0;
|
|
48
|
-
const toCanonical = (input) => {
|
|
49
|
-
try {
|
|
50
|
-
return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
|
|
51
|
-
} catch {
|
|
52
|
-
return void 0;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
const canonicalLocale = toCanonical(locale);
|
|
56
|
-
if (!canonicalLocale) return void 0;
|
|
57
|
-
const supportedCanonicalMap = /* @__PURE__ */ new Map();
|
|
58
|
-
for (const l of supportedLocales) {
|
|
59
|
-
const normalized = toCanonical(l);
|
|
60
|
-
if (normalized) {
|
|
61
|
-
supportedCanonicalMap.set(normalized, l);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
65
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
66
|
-
}
|
|
67
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
68
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
69
|
-
const supportedBase = key.split("-")[0];
|
|
70
|
-
if (supportedBase === baseLang) {
|
|
71
|
-
return original;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return void 0;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// src/shared/utils/locale/resolve-preferred-locale.ts
|
|
78
|
-
var resolvePreferredLocale = (acceptLanguageHeader, supportedLocales) => {
|
|
79
|
-
if (!acceptLanguageHeader || !supportedLocales || supportedLocales.length === 0) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const supportedLocalesSet = new Set(supportedLocales);
|
|
83
|
-
const preferred = acceptLanguageHeader.split(",").map((part) => {
|
|
84
|
-
const [lang, qValue] = part.split(";");
|
|
85
|
-
const q = qValue ? parseFloat(qValue.split("=")[1]) : 1;
|
|
86
|
-
if (isNaN(q)) {
|
|
87
|
-
return { lang: lang.trim(), q: 0 };
|
|
88
|
-
}
|
|
89
|
-
return { lang: lang.trim(), q };
|
|
90
|
-
}).sort((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
|
|
91
|
-
return preferred;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// src/adapters/next-client/next-client-runtime/next-client-runtime.ts
|
|
95
|
-
var nextClientRuntime = async ({
|
|
96
|
-
config
|
|
97
|
-
}) => {
|
|
98
|
-
const cookiesStore = await headers.cookies();
|
|
99
|
-
const headersStore = await headers.headers();
|
|
100
|
-
const logger = logry.logry({ id: config.id, scope: "nextClientRuntime" });
|
|
101
|
-
const { defaultLocale, supportedLocales = [], cookie, routing } = config;
|
|
102
|
-
let locale;
|
|
103
|
-
if (!cookie.disabled) {
|
|
104
|
-
const localeFromCookie = cookiesStore.get(cookie.name)?.value;
|
|
105
|
-
locale = normalizeLocale(localeFromCookie, supportedLocales);
|
|
106
|
-
if (locale) {
|
|
107
|
-
logger.debug("Get locale from cookie:", { locale });
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (!locale && routing.firstVisit.localeSource === "browser") {
|
|
111
|
-
const aLHeader = headersStore.get("accept-language") || void 0;
|
|
112
|
-
const preferredLocale = resolvePreferredLocale(aLHeader, supportedLocales);
|
|
113
|
-
locale = normalizeLocale(preferredLocale, supportedLocales);
|
|
114
|
-
logger.debug("Get locale from browser:", { locale });
|
|
115
|
-
}
|
|
116
|
-
const pathname = headersStore.get(DEFAULT_PATHNAME_HEADER_NAME);
|
|
117
|
-
if (pathname) {
|
|
118
|
-
logger.debug("Get pathname from next headers:", { pathname });
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
locale: locale || defaultLocale,
|
|
122
|
-
pathname: pathname || ""
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
var nextServerRuntime = async ({
|
|
126
|
-
config,
|
|
127
|
-
request
|
|
128
|
-
}) => {
|
|
129
|
-
const cookiesStore = await headers.cookies();
|
|
130
|
-
const headersStore = await headers.headers();
|
|
131
|
-
const logger = logry.logry({ id: config.id, scope: "nextServerRuntime" });
|
|
132
|
-
const { defaultLocale, supportedLocales = [], cookie, routing } = config;
|
|
133
|
-
let locale;
|
|
134
|
-
if (!cookie.disabled) {
|
|
135
|
-
const localeFromCookie = cookiesStore.get(cookie.name)?.value;
|
|
136
|
-
locale = normalizeLocale(localeFromCookie, supportedLocales);
|
|
137
|
-
if (locale) {
|
|
138
|
-
logger.debug("Get locale from cookie:", { locale });
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (!locale && routing.firstVisit.localeSource === "browser") {
|
|
142
|
-
const aLHeader = headersStore.get("accept-language") || void 0;
|
|
143
|
-
const preferredLocale = resolvePreferredLocale(aLHeader, supportedLocales);
|
|
144
|
-
locale = normalizeLocale(preferredLocale, supportedLocales);
|
|
145
|
-
logger.debug("Get locale from browser:", { locale });
|
|
146
|
-
}
|
|
147
|
-
let pathname = "";
|
|
148
|
-
if (request && typeof request === "object" && "nextUrl" in request) {
|
|
149
|
-
pathname = request.nextUrl.pathname;
|
|
150
|
-
if (pathname) {
|
|
151
|
-
logger.debug("Get pathname from next request:", { pathname });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (!pathname) {
|
|
155
|
-
pathname = headersStore.get(DEFAULT_PATHNAME_HEADER_NAME);
|
|
156
|
-
if (pathname) {
|
|
157
|
-
logger.debug("Get pathname from next headers:", { pathname });
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (!cookie.disabled && cookie.autoSetCookie) {
|
|
161
|
-
cookiesStore.set({
|
|
162
|
-
name: cookie.name,
|
|
163
|
-
value: locale || defaultLocale,
|
|
164
|
-
...cookie.domain ? { domain: cookie.domain } : {},
|
|
165
|
-
path: cookie.path,
|
|
166
|
-
maxAge: cookie.maxAge,
|
|
167
|
-
httpOnly: cookie.httpOnly,
|
|
168
|
-
secure: cookie.secure,
|
|
169
|
-
sameSite: cookie.sameSite
|
|
170
|
-
});
|
|
171
|
-
logger.debug("Set locale to cookie:", {
|
|
172
|
-
cookie: { name: cookie.name, value: locale || defaultLocale }
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
locale: locale || defaultLocale,
|
|
177
|
-
pathname: pathname || ""
|
|
178
|
-
};
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
// src/modules/intor-adapter/resolve-adapter-runtime-loader.ts
|
|
182
|
-
var resolceAdapterRuntimeLoader = async ({
|
|
183
|
-
config
|
|
184
|
-
}) => {
|
|
185
|
-
const { adapter } = config;
|
|
186
|
-
let loadedRuntime;
|
|
187
|
-
if (adapter === "next-client") {
|
|
188
|
-
loadedRuntime = nextClientRuntime;
|
|
189
|
-
} else if (adapter === "next-server") {
|
|
190
|
-
loadedRuntime = nextServerRuntime;
|
|
191
|
-
}
|
|
192
|
-
return loadedRuntime;
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
// src/modules/intor-config/types/intor-adapter-types.ts
|
|
196
|
-
var intorAdapters = ["next-client", "next-server"];
|
|
197
|
-
var readMessageRecordFile = async (filePath) => {
|
|
46
|
+
// src/modules/messages/load-local-messages/utils/read-message-record-file.ts
|
|
47
|
+
var readMessageRecordFile = async (filePath, loggerOptions) => {
|
|
198
48
|
const fileName = path__default.default.basename(filePath, ".json");
|
|
199
49
|
const content = await fs__default.default.readFile(filePath, "utf-8");
|
|
200
50
|
const parsed = JSON.parse(content);
|
|
201
51
|
if (typeof parsed !== "object" || parsed === null) {
|
|
202
|
-
throw new
|
|
52
|
+
throw new IntorError({
|
|
53
|
+
id: loggerOptions.id,
|
|
54
|
+
code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
|
|
55
|
+
message: "Invalid message format"
|
|
56
|
+
});
|
|
203
57
|
}
|
|
204
58
|
return { fileName, content: parsed };
|
|
205
59
|
};
|
|
206
60
|
|
|
207
|
-
// src/
|
|
61
|
+
// src/shared/logger/global-logger-pool.ts
|
|
62
|
+
function getGlobalLoggerPool() {
|
|
63
|
+
if (!globalThis.__INTOR_LOGGER_POOL__) {
|
|
64
|
+
globalThis.__INTOR_LOGGER_POOL__ = /* @__PURE__ */ new Map();
|
|
65
|
+
}
|
|
66
|
+
return globalThis.__INTOR_LOGGER_POOL__;
|
|
67
|
+
}
|
|
68
|
+
function clearLoggerPool() {
|
|
69
|
+
const pool = getGlobalLoggerPool();
|
|
70
|
+
pool.clear();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/shared/logger/get-logger.ts
|
|
74
|
+
var DEFAULT_FORMATTER_CONFIG = {
|
|
75
|
+
node: { meta: { compact: true }, lineBreaksAfter: 1 }
|
|
76
|
+
};
|
|
77
|
+
function getLogger({
|
|
78
|
+
id,
|
|
79
|
+
formatterConfig = DEFAULT_FORMATTER_CONFIG,
|
|
80
|
+
...options
|
|
81
|
+
}) {
|
|
82
|
+
const pool = getGlobalLoggerPool();
|
|
83
|
+
let logger = pool.get(id);
|
|
84
|
+
if (!logger) {
|
|
85
|
+
logger = logry.logry({ id, formatterConfig, ...options });
|
|
86
|
+
pool.set(id, logger);
|
|
87
|
+
if (pool.size > 1e3) {
|
|
88
|
+
const keys = Array.from(pool.keys());
|
|
89
|
+
for (const key of keys.slice(0, 200)) pool.delete(key);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return logger;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/modules/messages/load-local-messages/load-namespace-group/parse-message-file.ts
|
|
208
96
|
var MAX_PATH_LENGTH = 260;
|
|
209
|
-
var parseMessageFile = async (filePath,
|
|
210
|
-
const
|
|
97
|
+
var parseMessageFile = async (filePath, loggerOptions) => {
|
|
98
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
99
|
+
const logger = baseLogger.child({ scope: "parse-message-file" });
|
|
211
100
|
const trimmedPath = filePath.trim();
|
|
212
101
|
if (!trimmedPath) {
|
|
213
|
-
logger
|
|
102
|
+
logger.warn("File path is empty.", { filePath: trimmedPath });
|
|
214
103
|
return null;
|
|
215
104
|
}
|
|
216
105
|
if (trimmedPath.length > MAX_PATH_LENGTH) {
|
|
217
|
-
logger
|
|
106
|
+
logger.warn("File path exceeds maximum length.", { filePath: trimmedPath });
|
|
218
107
|
return null;
|
|
219
108
|
}
|
|
220
109
|
const fileName = path__default.default.basename(trimmedPath);
|
|
221
110
|
if (!fileName.toLowerCase().endsWith(".json")) {
|
|
222
|
-
logger
|
|
111
|
+
logger.trace("Skipped non-JSON file.", { filePath: trimmedPath });
|
|
223
112
|
return null;
|
|
224
113
|
}
|
|
225
114
|
try {
|
|
226
|
-
const { content } = await readMessageRecordFile(trimmedPath);
|
|
227
|
-
logger
|
|
115
|
+
const { content } = await readMessageRecordFile(trimmedPath, loggerOptions);
|
|
116
|
+
logger.trace(`Message file loaded.`, { filePath: trimmedPath });
|
|
228
117
|
return content;
|
|
229
118
|
} catch (error) {
|
|
230
|
-
logger
|
|
119
|
+
logger.warn("Failed to parse message file.", {
|
|
120
|
+
filePath: trimmedPath,
|
|
121
|
+
error
|
|
122
|
+
});
|
|
231
123
|
return null;
|
|
232
124
|
}
|
|
233
125
|
};
|
|
234
126
|
|
|
235
|
-
// src/modules/
|
|
236
|
-
var mergeNamespaceMessages = async (filePaths, isAtRoot,
|
|
127
|
+
// src/modules/messages/load-local-messages/load-namespace-group/merge-namespace-messages.ts
|
|
128
|
+
var mergeNamespaceMessages = async (filePaths, isAtRoot, loggerOptions) => {
|
|
237
129
|
const baseContent = {};
|
|
238
130
|
const subEntries = {};
|
|
239
131
|
await Promise.all(
|
|
240
132
|
filePaths.map(async (filePath) => {
|
|
241
133
|
const fileName = path__default.default.basename(filePath);
|
|
242
|
-
const content = await parseMessageFile(filePath,
|
|
134
|
+
const content = await parseMessageFile(filePath, loggerOptions);
|
|
243
135
|
if (!content) {
|
|
244
136
|
return;
|
|
245
137
|
}
|
|
@@ -254,20 +146,21 @@ var mergeNamespaceMessages = async (filePaths, isAtRoot, loggerId) => {
|
|
|
254
146
|
return { base: baseContent, sub: subEntries };
|
|
255
147
|
};
|
|
256
148
|
|
|
257
|
-
// src/modules/
|
|
149
|
+
// src/modules/messages/load-local-messages/load-namespace-group/load-namespace-group.ts
|
|
258
150
|
var loadNamespaceGroup = async ({
|
|
259
151
|
locale,
|
|
260
152
|
namespace,
|
|
261
153
|
messages,
|
|
262
154
|
namespaceGroupValue,
|
|
263
155
|
limit,
|
|
264
|
-
|
|
156
|
+
logger: loggerOptions = { id: "default" }
|
|
265
157
|
}) => {
|
|
266
|
-
const
|
|
158
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
159
|
+
const logger = baseLogger.child({ scope: "load-namespace-group" });
|
|
267
160
|
const { isAtRoot, filePaths } = namespaceGroupValue;
|
|
268
161
|
if (filePaths.length === 0) {
|
|
269
|
-
logger
|
|
270
|
-
`Skipped merging
|
|
162
|
+
logger.trace(
|
|
163
|
+
`Skipped merging ${locale}/${namespace} because filePaths is empty`
|
|
271
164
|
);
|
|
272
165
|
return;
|
|
273
166
|
}
|
|
@@ -275,15 +168,19 @@ var loadNamespaceGroup = async ({
|
|
|
275
168
|
const { base, sub } = await mergeNamespaceMessages(
|
|
276
169
|
filePaths,
|
|
277
170
|
isAtRoot,
|
|
278
|
-
|
|
171
|
+
loggerOptions
|
|
279
172
|
);
|
|
280
173
|
if (!messages[locale]) {
|
|
281
174
|
messages[locale] = {};
|
|
282
175
|
}
|
|
176
|
+
if (isAtRoot && filePaths.length === 1 && path__default.default.basename(filePaths[0]) === "index.json") {
|
|
177
|
+
messages[locale] = { ...messages[locale], ...base };
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
283
180
|
const finalContent = isAtRoot ? base : { ...base, ...sub };
|
|
284
181
|
messages[locale][namespace] = finalContent;
|
|
285
182
|
if (!isAtRoot && Object.keys(finalContent).length > 0) {
|
|
286
|
-
logger
|
|
183
|
+
logger.trace(
|
|
287
184
|
`Merged ${locale}/${namespace} from ${filePaths.length} file(s)`,
|
|
288
185
|
{ namespace }
|
|
289
186
|
);
|
|
@@ -316,15 +213,17 @@ var addToNamespaceGroup = ({
|
|
|
316
213
|
}
|
|
317
214
|
};
|
|
318
215
|
|
|
319
|
-
// src/modules/
|
|
216
|
+
// src/modules/messages/load-local-messages/prepare-namespace-groups/traverse-directory.ts
|
|
320
217
|
var traverseDirectory = async ({
|
|
321
218
|
options,
|
|
322
219
|
currentDirPath,
|
|
323
220
|
namespaceGroups,
|
|
324
221
|
namespacePathSegments
|
|
325
222
|
}) => {
|
|
326
|
-
const { limit
|
|
327
|
-
const
|
|
223
|
+
const { limit } = options;
|
|
224
|
+
const loggerOptions = options.logger || { id: "default" };
|
|
225
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
226
|
+
const logger = baseLogger.child({ scope: "traverse-directory" });
|
|
328
227
|
try {
|
|
329
228
|
const dirents = await fs__default.default.readdir(currentDirPath, { withFileTypes: true });
|
|
330
229
|
const dirPromises = dirents.map(
|
|
@@ -360,7 +259,7 @@ var traverseDirectory = async ({
|
|
|
360
259
|
}
|
|
361
260
|
};
|
|
362
261
|
|
|
363
|
-
// src/modules/
|
|
262
|
+
// src/modules/messages/load-local-messages/prepare-namespace-groups/prepare-namespace-groups.ts
|
|
364
263
|
var prepareNamespaceGroups = async (options) => {
|
|
365
264
|
const { basePath } = options;
|
|
366
265
|
const namespaceGroups = /* @__PURE__ */ new Map();
|
|
@@ -373,16 +272,17 @@ var prepareNamespaceGroups = async (options) => {
|
|
|
373
272
|
return namespaceGroups;
|
|
374
273
|
};
|
|
375
274
|
|
|
376
|
-
// src/modules/
|
|
275
|
+
// src/modules/messages/load-local-messages/load-single-locale/load-single-locale.ts
|
|
377
276
|
var loadSingleLocale = async ({
|
|
378
277
|
basePath,
|
|
379
278
|
locale,
|
|
380
279
|
namespaces,
|
|
381
280
|
messages,
|
|
382
281
|
limit,
|
|
383
|
-
|
|
282
|
+
logger: loggerOptions = { id: "default" }
|
|
384
283
|
}) => {
|
|
385
|
-
const
|
|
284
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
285
|
+
const logger = baseLogger.child({ scope: "load-single-locale" });
|
|
386
286
|
const localePath = path__default.default.join(basePath, locale);
|
|
387
287
|
const validNamespaces = [];
|
|
388
288
|
try {
|
|
@@ -395,14 +295,14 @@ var loadSingleLocale = async ({
|
|
|
395
295
|
return;
|
|
396
296
|
}
|
|
397
297
|
} catch (error) {
|
|
398
|
-
logger.warn("Error checking locale path
|
|
298
|
+
logger.warn("Error checking locale path.", { locale, error });
|
|
399
299
|
return;
|
|
400
300
|
}
|
|
401
301
|
const namespaceGroups = await prepareNamespaceGroups({
|
|
402
302
|
basePath: localePath,
|
|
403
303
|
limit,
|
|
404
304
|
namespaces: new Set(namespaces || []),
|
|
405
|
-
|
|
305
|
+
logger: loggerOptions
|
|
406
306
|
});
|
|
407
307
|
if (namespaceGroups.size === 0) {
|
|
408
308
|
logger.warn("No namespace groups found.", {
|
|
@@ -412,7 +312,7 @@ var loadSingleLocale = async ({
|
|
|
412
312
|
});
|
|
413
313
|
return;
|
|
414
314
|
}
|
|
415
|
-
logger.
|
|
315
|
+
logger.trace("Prepared namespace groups from scanning local files.", {
|
|
416
316
|
namespaceGroups: [...namespaceGroups.entries()].map(([ns, val]) => ({
|
|
417
317
|
namespace: ns,
|
|
418
318
|
isAtRoot: val.isAtRoot,
|
|
@@ -428,14 +328,14 @@ var loadSingleLocale = async ({
|
|
|
428
328
|
messages,
|
|
429
329
|
namespaceGroupValue,
|
|
430
330
|
limit,
|
|
431
|
-
|
|
331
|
+
logger: loggerOptions
|
|
432
332
|
}).then(() => validNamespaces.push(namespace))
|
|
433
333
|
);
|
|
434
334
|
await Promise.all(namespaceGroupTasks);
|
|
435
335
|
return validNamespaces;
|
|
436
336
|
};
|
|
437
337
|
|
|
438
|
-
// src/modules/
|
|
338
|
+
// src/modules/messages/load-local-messages/load-locale-with-fallback/load-locale-with-fallback.ts
|
|
439
339
|
var loadLocaleWithFallback = async ({
|
|
440
340
|
basePath,
|
|
441
341
|
locale: targetLocale,
|
|
@@ -443,9 +343,10 @@ var loadLocaleWithFallback = async ({
|
|
|
443
343
|
namespaces,
|
|
444
344
|
messages,
|
|
445
345
|
limit,
|
|
446
|
-
|
|
346
|
+
logger: loggerOptions = { id: "default" }
|
|
447
347
|
}) => {
|
|
448
|
-
const
|
|
348
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
349
|
+
const logger = baseLogger.child({ scope: "load-locale-with-fallback" });
|
|
449
350
|
const localesToTry = [targetLocale, ...fallbackLocales];
|
|
450
351
|
for (const locale of localesToTry) {
|
|
451
352
|
try {
|
|
@@ -455,7 +356,7 @@ var loadLocaleWithFallback = async ({
|
|
|
455
356
|
namespaces,
|
|
456
357
|
messages,
|
|
457
358
|
limit,
|
|
458
|
-
|
|
359
|
+
logger: loggerOptions
|
|
459
360
|
});
|
|
460
361
|
return validNamespaces;
|
|
461
362
|
} catch (error) {
|
|
@@ -468,14 +369,155 @@ var loadLocaleWithFallback = async ({
|
|
|
468
369
|
logger.warn("All fallback locales failed.", {
|
|
469
370
|
attemptedLocales: localesToTry
|
|
470
371
|
});
|
|
471
|
-
return
|
|
372
|
+
return;
|
|
373
|
+
};
|
|
374
|
+
function getGlobalMessagesPool() {
|
|
375
|
+
if (!globalThis.__INTOR_MESSAGES_POOL__) {
|
|
376
|
+
globalThis.__INTOR_MESSAGES_POOL__ = new Keyv__default.default();
|
|
377
|
+
}
|
|
378
|
+
return globalThis.__INTOR_MESSAGES_POOL__;
|
|
379
|
+
}
|
|
380
|
+
function clearMessagesPool() {
|
|
381
|
+
const pool = getGlobalMessagesPool();
|
|
382
|
+
pool.clear();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/shared/utils/merge-messages.ts
|
|
386
|
+
var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
|
|
387
|
+
const result = Object.keys(staticMessages).length ? { ...staticMessages } : {};
|
|
388
|
+
for (const locale in loadedMessages) {
|
|
389
|
+
const loaded = loadedMessages[locale];
|
|
390
|
+
if (!result[locale]) {
|
|
391
|
+
result[locale] = loaded;
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
result[locale] = {
|
|
395
|
+
...result[locale],
|
|
396
|
+
...loaded
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
return result;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/shared/utils/normalize-cache-key.ts
|
|
403
|
+
var CACHE_KEY_DELIMITER = "|";
|
|
404
|
+
var sanitize = (k) => k.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").replaceAll(/[\r\n]/g, "").trim();
|
|
405
|
+
var normalizeCacheKey = (key, delimiter = CACHE_KEY_DELIMITER) => {
|
|
406
|
+
if (!key) return null;
|
|
407
|
+
if (Array.isArray(key)) {
|
|
408
|
+
if (key.length === 0) return null;
|
|
409
|
+
const normalized = key.map((k) => {
|
|
410
|
+
if (k === null) return "__null";
|
|
411
|
+
if (k === void 0) return "__undefined";
|
|
412
|
+
if (typeof k === "boolean") return k ? "__true" : "__false";
|
|
413
|
+
return sanitize(String(k));
|
|
414
|
+
});
|
|
415
|
+
return normalized.join(delimiter);
|
|
416
|
+
}
|
|
417
|
+
if (typeof key === "boolean") return key ? "__true" : "__false";
|
|
418
|
+
return String(key);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/shared/constants/prefix-placeholder.ts
|
|
422
|
+
var PREFIX_PLACEHOLDER = "{locale}";
|
|
423
|
+
|
|
424
|
+
// src/shared/utils/resolve-namespaces.ts
|
|
425
|
+
var resolveNamespaces = ({
|
|
426
|
+
config,
|
|
427
|
+
pathname
|
|
428
|
+
}) => {
|
|
429
|
+
const { loader } = config;
|
|
430
|
+
const {
|
|
431
|
+
routeNamespaces = {},
|
|
432
|
+
namespaces: fallbackNamespaces
|
|
433
|
+
} = loader;
|
|
434
|
+
const { unprefixedPathname } = extractPathname({ config, pathname });
|
|
435
|
+
const standardizedPathname = standardizePathname({
|
|
436
|
+
config,
|
|
437
|
+
pathname: unprefixedPathname
|
|
438
|
+
});
|
|
439
|
+
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
440
|
+
`/${PREFIX_PLACEHOLDER}`,
|
|
441
|
+
""
|
|
442
|
+
);
|
|
443
|
+
const defaultNamespaces = routeNamespaces.default ?? [];
|
|
444
|
+
const exactMatchNamespaces = routeNamespaces[standardizedPathname] ?? routeNamespaces[placeholderRemovedPathname];
|
|
445
|
+
if (exactMatchNamespaces) {
|
|
446
|
+
return [...defaultNamespaces, ...exactMatchNamespaces];
|
|
447
|
+
}
|
|
448
|
+
let bestMatch = "";
|
|
449
|
+
let bestNamespaces;
|
|
450
|
+
const prefixPatterns = Object.keys(routeNamespaces).filter(
|
|
451
|
+
(pattern) => pattern.endsWith("/*")
|
|
452
|
+
);
|
|
453
|
+
for (const pattern of prefixPatterns) {
|
|
454
|
+
const basePath = pattern.replace(/\/\*$/, "");
|
|
455
|
+
if (standardizedPathname.startsWith(basePath)) {
|
|
456
|
+
if (basePath.length > bestMatch.length) {
|
|
457
|
+
bestMatch = basePath;
|
|
458
|
+
bestNamespaces = routeNamespaces[pattern];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const matchedNamespaces = bestNamespaces ?? routeNamespaces["/*"] ?? fallbackNamespaces ?? [];
|
|
463
|
+
if (matchedNamespaces.length > 0) {
|
|
464
|
+
return [...defaultNamespaces, ...matchedNamespaces];
|
|
465
|
+
} else {
|
|
466
|
+
return [...defaultNamespaces];
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// src/shared/utils/locale/normalize-locale.ts
|
|
471
|
+
var normalizeLocale = (locale = "", supportedLocales = []) => {
|
|
472
|
+
if (!locale || supportedLocales.length === 0) return;
|
|
473
|
+
const toCanonical = (input) => {
|
|
474
|
+
try {
|
|
475
|
+
return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
|
|
476
|
+
} catch {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
const canonicalLocale = toCanonical(locale);
|
|
481
|
+
if (!canonicalLocale) return;
|
|
482
|
+
const supportedCanonicalMap = /* @__PURE__ */ new Map();
|
|
483
|
+
for (const l of supportedLocales) {
|
|
484
|
+
const normalized = toCanonical(l);
|
|
485
|
+
if (normalized) {
|
|
486
|
+
supportedCanonicalMap.set(normalized, l);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
490
|
+
return supportedCanonicalMap.get(canonicalLocale);
|
|
491
|
+
}
|
|
492
|
+
const baseLang = canonicalLocale.split("-")[0];
|
|
493
|
+
for (const [key, original] of supportedCanonicalMap) {
|
|
494
|
+
const supportedBase = key.split("-")[0];
|
|
495
|
+
if (supportedBase === baseLang) {
|
|
496
|
+
return original;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return;
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// src/shared/utils/locale/resolve-preferred-locale.ts
|
|
503
|
+
var resolvePreferredLocale = (acceptLanguageHeader, supportedLocales) => {
|
|
504
|
+
if (!acceptLanguageHeader || !supportedLocales || supportedLocales.length === 0) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const supportedLocalesSet = new Set(supportedLocales);
|
|
508
|
+
const preferred = acceptLanguageHeader.split(",").map((part) => {
|
|
509
|
+
const [lang, qValue] = part.split(";");
|
|
510
|
+
const q = qValue ? parseFloat(qValue.split("=")[1]) : 1;
|
|
511
|
+
if (isNaN(q)) {
|
|
512
|
+
return { lang: lang.trim(), q: 0 };
|
|
513
|
+
}
|
|
514
|
+
return { lang: lang.trim(), q };
|
|
515
|
+
}).sort((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
|
|
516
|
+
return preferred;
|
|
472
517
|
};
|
|
473
518
|
|
|
474
519
|
// src/shared/utils/pathname/normalize-pathname.ts
|
|
475
520
|
var normalizePathname = (rawPathname, options = {}) => {
|
|
476
|
-
if (typeof rawPathname !== "string") {
|
|
477
|
-
throw new TypeError("Expected rawPathname to be a string");
|
|
478
|
-
}
|
|
479
521
|
const length = rawPathname.length;
|
|
480
522
|
let start = 0;
|
|
481
523
|
let end = length - 1;
|
|
@@ -505,33 +547,101 @@ var normalizePathname = (rawPathname, options = {}) => {
|
|
|
505
547
|
return result || "/";
|
|
506
548
|
};
|
|
507
549
|
|
|
508
|
-
// src/
|
|
550
|
+
// src/shared/utils/pathname/extract-pathname.ts
|
|
551
|
+
var extractPathname = ({
|
|
552
|
+
config,
|
|
553
|
+
pathname: rawPathname
|
|
554
|
+
}) => {
|
|
555
|
+
const { routing, defaultLocale } = config;
|
|
556
|
+
const { basePath, prefix } = routing;
|
|
557
|
+
const normalizedPathname = normalizePathname(rawPathname);
|
|
558
|
+
let prefixedPathname = normalizedPathname;
|
|
559
|
+
if (basePath && normalizedPathname.startsWith(basePath + "/")) {
|
|
560
|
+
prefixedPathname = normalizedPathname.slice(basePath.length) || "/";
|
|
561
|
+
} else if (basePath && normalizedPathname === basePath) {
|
|
562
|
+
prefixedPathname = "/";
|
|
563
|
+
}
|
|
564
|
+
const pathParts = prefixedPathname.split("/").filter(Boolean);
|
|
565
|
+
const maybeLocale = pathParts[0] || "";
|
|
566
|
+
const isLocalePrefixed = config.supportedLocales?.includes(maybeLocale);
|
|
567
|
+
let unprefixedPathname = prefixedPathname;
|
|
568
|
+
if (prefix === "all") {
|
|
569
|
+
if (isLocalePrefixed) {
|
|
570
|
+
unprefixedPathname = prefixedPathname.slice(maybeLocale.length + 1) || "/";
|
|
571
|
+
}
|
|
572
|
+
} else if (prefix === "except-default") {
|
|
573
|
+
if (maybeLocale && maybeLocale !== defaultLocale && isLocalePrefixed) {
|
|
574
|
+
unprefixedPathname = prefixedPathname.slice(maybeLocale.length + 1) || "/";
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
basePath,
|
|
579
|
+
prefixedPathname,
|
|
580
|
+
unprefixedPathname,
|
|
581
|
+
maybeLocale,
|
|
582
|
+
isLocalePrefixed: Boolean(isLocalePrefixed)
|
|
583
|
+
};
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
// src/shared/utils/pathname/standardize-pathname.ts
|
|
587
|
+
var standardizePathname = ({
|
|
588
|
+
config,
|
|
589
|
+
pathname
|
|
590
|
+
}) => {
|
|
591
|
+
const { routing } = config;
|
|
592
|
+
const { basePath } = routing;
|
|
593
|
+
const parts = [
|
|
594
|
+
normalizePathname(basePath),
|
|
595
|
+
PREFIX_PLACEHOLDER,
|
|
596
|
+
normalizePathname(pathname)
|
|
597
|
+
];
|
|
598
|
+
const standardizedPathname = parts.join("/").replace(/\/{2,}/g, "/");
|
|
599
|
+
return normalizePathname(standardizedPathname);
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
// src/modules/messages/load-local-messages/load-local-messages.ts
|
|
509
603
|
var loadLocalMessages = async ({
|
|
510
|
-
basePath
|
|
604
|
+
basePath,
|
|
511
605
|
locale,
|
|
512
606
|
fallbackLocales,
|
|
513
607
|
namespaces,
|
|
514
608
|
concurrency = 10,
|
|
515
|
-
|
|
609
|
+
cache = DEFAULT_CACHE_OPTIONS,
|
|
610
|
+
logger: loggerOptions = { id: "default" }
|
|
516
611
|
}) => {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
const logger =
|
|
612
|
+
basePath = basePath ?? "messages";
|
|
613
|
+
if (!locale || locale.trim() === "") return {};
|
|
614
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
615
|
+
const logger = baseLogger.child({ scope: "load-locale-messages" });
|
|
616
|
+
const messages = {};
|
|
521
617
|
const resolvedBasePath = path__default.default.resolve(
|
|
522
618
|
process.cwd(),
|
|
523
619
|
normalizePathname(basePath, { removeLeadingSlash: true })
|
|
524
620
|
);
|
|
525
|
-
const messages = {};
|
|
526
|
-
const limit = pLimit__default.default(concurrency);
|
|
527
621
|
const start = perf_hooks.performance.now();
|
|
528
|
-
logger.
|
|
622
|
+
logger.trace("Starting to load local messages with configuration.", {
|
|
529
623
|
path: { basePath, resolvedBasePath },
|
|
530
624
|
locale,
|
|
531
625
|
fallbackLocales,
|
|
532
|
-
namespaces: namespaces && namespaces.length > 0 ? { count: namespaces?.length, list: [...namespaces] } : "All",
|
|
626
|
+
namespaces: namespaces && namespaces.length > 0 ? { count: namespaces?.length, list: [...namespaces] } : "All Namespaces",
|
|
533
627
|
concurrency
|
|
534
628
|
});
|
|
629
|
+
const pool = getGlobalMessagesPool();
|
|
630
|
+
const key = normalizeCacheKey([
|
|
631
|
+
loggerOptions.id,
|
|
632
|
+
resolvedBasePath,
|
|
633
|
+
locale,
|
|
634
|
+
[...fallbackLocales ?? []].sort().join(","),
|
|
635
|
+
[...namespaces ?? []].sort().join(",")
|
|
636
|
+
]);
|
|
637
|
+
if (cache.enabled && key) {
|
|
638
|
+
const cached = await pool.get(key);
|
|
639
|
+
if (cached) {
|
|
640
|
+
logger.debug("Messages cache hit.", { key });
|
|
641
|
+
return cached;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
const limit = pLimit__default.default(concurrency);
|
|
535
645
|
const validNamespaces = await loadLocaleWithFallback({
|
|
536
646
|
basePath: resolvedBasePath,
|
|
537
647
|
locale,
|
|
@@ -539,430 +649,260 @@ var loadLocalMessages = async ({
|
|
|
539
649
|
namespaces,
|
|
540
650
|
messages,
|
|
541
651
|
limit,
|
|
542
|
-
|
|
652
|
+
logger: loggerOptions
|
|
543
653
|
});
|
|
654
|
+
if (cache.enabled && key) {
|
|
655
|
+
await pool.set(key, messages, cache.ttl);
|
|
656
|
+
}
|
|
544
657
|
const end = perf_hooks.performance.now();
|
|
545
658
|
const duration = Math.round(end - start);
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
});
|
|
552
|
-
} else {
|
|
553
|
-
logger.warn("No valid namespaces found for the locale.", {
|
|
554
|
-
locale,
|
|
555
|
-
namespaces: validNamespaces,
|
|
556
|
-
duration: `${duration} ms`
|
|
557
|
-
});
|
|
558
|
-
}
|
|
659
|
+
logger.trace("Finished loading local messages.", {
|
|
660
|
+
locale,
|
|
661
|
+
validNamespaces,
|
|
662
|
+
duration: `${duration} ms`
|
|
663
|
+
});
|
|
559
664
|
return messages;
|
|
560
665
|
};
|
|
561
666
|
|
|
562
|
-
// src/modules/
|
|
563
|
-
var
|
|
667
|
+
// src/modules/messages/create-load-local-messages.ts
|
|
668
|
+
var createLoadLocalMessages = (basePath) => {
|
|
564
669
|
return (options) => loadLocalMessages({ basePath, ...options });
|
|
565
670
|
};
|
|
566
671
|
|
|
567
|
-
// src/modules/
|
|
568
|
-
var
|
|
569
|
-
const searchParams = new URLSearchParams();
|
|
570
|
-
const appendParam = (key, value) => {
|
|
571
|
-
if (value === void 0 || value === null) {
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
575
|
-
value.forEach((v) => v && searchParams.append(key, v));
|
|
576
|
-
} else {
|
|
577
|
-
searchParams.append(key, value);
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
581
|
-
appendParam(key, value);
|
|
582
|
-
});
|
|
583
|
-
return searchParams;
|
|
584
|
-
};
|
|
585
|
-
var fetcher = async ({
|
|
672
|
+
// src/modules/messages/load-api-messages/fetch-messages.ts
|
|
673
|
+
var fetchMessages = async ({
|
|
586
674
|
apiUrl,
|
|
675
|
+
apiHeaders,
|
|
587
676
|
locale,
|
|
588
677
|
searchParams,
|
|
589
|
-
|
|
678
|
+
logger: loggerOptions = { id: "default" }
|
|
590
679
|
}) => {
|
|
591
|
-
const
|
|
680
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
681
|
+
const logger = baseLogger.child({ scope: "fetch-messages" });
|
|
592
682
|
try {
|
|
593
683
|
const params = new URLSearchParams(searchParams);
|
|
594
684
|
params.append("locale", locale);
|
|
595
685
|
const url = `${apiUrl}?${params.toString()}`;
|
|
686
|
+
const headers = {
|
|
687
|
+
"Content-Type": "application/json",
|
|
688
|
+
...apiHeaders
|
|
689
|
+
};
|
|
596
690
|
const response = await fetch(url, {
|
|
597
691
|
method: "GET",
|
|
598
|
-
headers
|
|
692
|
+
headers,
|
|
599
693
|
cache: "no-store"
|
|
600
694
|
});
|
|
601
695
|
if (!response.ok) {
|
|
602
|
-
throw new Error(
|
|
603
|
-
`Fetch failed for locale "${locale}" at URL: ${url} - ${response.status} ${response.statusText}`
|
|
604
|
-
);
|
|
696
|
+
throw new Error(`Fetch failed: ${locale} (${response.status})`);
|
|
605
697
|
}
|
|
606
698
|
const data = await response.json();
|
|
607
699
|
if (data == null || typeof data === "object" && Object.keys(data).length === 0) {
|
|
608
|
-
throw new Error(
|
|
609
|
-
`Missing or invalid messages for locale "${locale}" at URL: ${url}`
|
|
610
|
-
);
|
|
700
|
+
throw new Error(`Invalid messages: ${locale}`);
|
|
611
701
|
}
|
|
612
702
|
return data;
|
|
613
|
-
} catch {
|
|
703
|
+
} catch (error) {
|
|
614
704
|
logger.warn(`Failed to fetch messages for locale "${locale}".`, {
|
|
615
705
|
locale,
|
|
616
706
|
apiUrl,
|
|
617
|
-
searchParams: decodeURIComponent(searchParams.toString())
|
|
707
|
+
searchParams: decodeURIComponent(searchParams.toString()),
|
|
708
|
+
error
|
|
618
709
|
});
|
|
619
|
-
return
|
|
710
|
+
return;
|
|
620
711
|
}
|
|
621
712
|
};
|
|
622
713
|
|
|
623
|
-
// src/modules/
|
|
624
|
-
var fetchFallbackMessages = async (
|
|
714
|
+
// src/modules/messages/load-api-messages/fetch-fallback-messages.ts
|
|
715
|
+
var fetchFallbackMessages = async ({
|
|
716
|
+
apiUrl,
|
|
717
|
+
apiHeaders,
|
|
718
|
+
searchParams,
|
|
719
|
+
fallbackLocales,
|
|
720
|
+
logger
|
|
721
|
+
}) => {
|
|
625
722
|
for (const fallbackLocale of fallbackLocales) {
|
|
626
|
-
const result = await
|
|
723
|
+
const result = await fetchMessages({
|
|
627
724
|
apiUrl,
|
|
628
725
|
searchParams,
|
|
629
726
|
locale: fallbackLocale,
|
|
630
|
-
|
|
727
|
+
apiHeaders,
|
|
728
|
+
logger
|
|
631
729
|
});
|
|
632
730
|
if (result) {
|
|
633
731
|
return { locale: fallbackLocale, messages: result };
|
|
634
732
|
}
|
|
635
733
|
}
|
|
636
|
-
return
|
|
734
|
+
return;
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
// src/modules/messages/load-api-messages/utils/build-search-params.ts
|
|
738
|
+
var buildSearchParams = (params) => {
|
|
739
|
+
const searchParams = new URLSearchParams();
|
|
740
|
+
const appendParam = (key, value) => {
|
|
741
|
+
if (value === void 0 || value === null) return;
|
|
742
|
+
if (Array.isArray(value) && value.length === 0) return;
|
|
743
|
+
if (Array.isArray(value)) {
|
|
744
|
+
value.forEach((v) => v && searchParams.append(key, v));
|
|
745
|
+
} else {
|
|
746
|
+
searchParams.append(key, value);
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
750
|
+
appendParam(key, value);
|
|
751
|
+
});
|
|
752
|
+
return searchParams;
|
|
637
753
|
};
|
|
638
754
|
|
|
639
|
-
// src/modules/
|
|
640
|
-
var
|
|
755
|
+
// src/modules/messages/load-api-messages/load-api-messages.ts
|
|
756
|
+
var loadApiMessages = async ({
|
|
641
757
|
apiUrl,
|
|
758
|
+
apiHeaders,
|
|
642
759
|
basePath,
|
|
643
760
|
locale,
|
|
644
761
|
fallbackLocales = [],
|
|
645
762
|
namespaces = [],
|
|
646
|
-
|
|
763
|
+
cache = DEFAULT_CACHE_OPTIONS,
|
|
764
|
+
logger: loggerOptions = { id: "default" }
|
|
647
765
|
}) => {
|
|
648
|
-
const
|
|
766
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
767
|
+
const logger = baseLogger.child({ scope: "load-api-messages" });
|
|
649
768
|
if (!apiUrl) {
|
|
650
|
-
logger.warn("No apiUrl provided
|
|
651
|
-
return
|
|
769
|
+
logger.warn("No apiUrl provided. Skipping fetch.");
|
|
770
|
+
return;
|
|
652
771
|
}
|
|
653
|
-
const
|
|
772
|
+
const pool = getGlobalMessagesPool();
|
|
773
|
+
const key = normalizeCacheKey([
|
|
774
|
+
loggerOptions.id,
|
|
654
775
|
basePath,
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
776
|
+
locale,
|
|
777
|
+
[...fallbackLocales ?? []].sort().join(","),
|
|
778
|
+
[...namespaces ?? []].sort().join(",")
|
|
779
|
+
]);
|
|
780
|
+
if (cache.enabled && key) {
|
|
781
|
+
const cached = await pool.get(key);
|
|
782
|
+
if (cached) {
|
|
783
|
+
logger.debug("Messages cache hit.", { key });
|
|
784
|
+
return cached;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
const searchParams = buildSearchParams({ basePath, namespaces });
|
|
788
|
+
const messages = await fetchMessages({
|
|
659
789
|
apiUrl,
|
|
790
|
+
apiHeaders,
|
|
660
791
|
searchParams,
|
|
661
792
|
locale,
|
|
662
|
-
|
|
793
|
+
logger: loggerOptions
|
|
663
794
|
});
|
|
664
795
|
if (messages) {
|
|
796
|
+
if (cache.enabled && key) {
|
|
797
|
+
await pool.set(key, messages, cache.ttl);
|
|
798
|
+
}
|
|
665
799
|
return messages;
|
|
666
800
|
}
|
|
667
|
-
const fallbackResult = await fetchFallbackMessages(
|
|
801
|
+
const fallbackResult = await fetchFallbackMessages({
|
|
668
802
|
apiUrl,
|
|
803
|
+
apiHeaders,
|
|
669
804
|
searchParams,
|
|
670
805
|
fallbackLocales,
|
|
671
|
-
|
|
672
|
-
);
|
|
806
|
+
logger: loggerOptions
|
|
807
|
+
});
|
|
673
808
|
if (fallbackResult) {
|
|
674
809
|
logger.info("Fallback locale succeeded.", {
|
|
675
810
|
usedLocale: fallbackResult.locale,
|
|
676
811
|
apiUrl,
|
|
677
812
|
searchParams: decodeURIComponent(searchParams.toString())
|
|
678
813
|
});
|
|
814
|
+
if (cache.enabled && key) {
|
|
815
|
+
await pool.set(key, fallbackResult.messages, cache.ttl);
|
|
816
|
+
}
|
|
679
817
|
return fallbackResult.messages;
|
|
680
818
|
}
|
|
681
819
|
logger.warn("Failed to fetch messages for all locales.", {
|
|
682
820
|
locale,
|
|
683
821
|
fallbackLocales
|
|
684
822
|
});
|
|
685
|
-
return
|
|
823
|
+
return;
|
|
686
824
|
};
|
|
687
825
|
|
|
688
|
-
// src/
|
|
689
|
-
var
|
|
690
|
-
config,
|
|
691
|
-
pathname: rawPathname
|
|
692
|
-
}) => {
|
|
693
|
-
const { routing, defaultLocale } = config;
|
|
694
|
-
const { basePath, prefix } = routing;
|
|
695
|
-
const normalizedPathname = normalizePathname(rawPathname);
|
|
696
|
-
let prefixedPathname = normalizedPathname;
|
|
697
|
-
if (basePath && normalizedPathname.startsWith(basePath + "/")) {
|
|
698
|
-
prefixedPathname = normalizedPathname.slice(basePath.length) || "/";
|
|
699
|
-
} else if (basePath && normalizedPathname === basePath) {
|
|
700
|
-
prefixedPathname = "/";
|
|
701
|
-
}
|
|
702
|
-
const pathParts = prefixedPathname.split("/").filter(Boolean);
|
|
703
|
-
const maybeLocale = pathParts[0] || "";
|
|
704
|
-
const isLocalePrefixed = config.supportedLocales?.includes(maybeLocale);
|
|
705
|
-
let unprefixedPathname = prefixedPathname;
|
|
706
|
-
if (prefix === "all") {
|
|
707
|
-
if (isLocalePrefixed) {
|
|
708
|
-
unprefixedPathname = prefixedPathname.slice(maybeLocale.length + 1) || "/";
|
|
709
|
-
}
|
|
710
|
-
} else if (prefix === "except-default") {
|
|
711
|
-
if (maybeLocale && maybeLocale !== defaultLocale && isLocalePrefixed) {
|
|
712
|
-
unprefixedPathname = prefixedPathname.slice(maybeLocale.length + 1) || "/";
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
return {
|
|
716
|
-
basePath,
|
|
717
|
-
prefixedPathname,
|
|
718
|
-
unprefixedPathname,
|
|
719
|
-
maybeLocale,
|
|
720
|
-
isLocalePrefixed: Boolean(isLocalePrefixed)
|
|
721
|
-
};
|
|
722
|
-
};
|
|
723
|
-
|
|
724
|
-
// src/shared/utils/pathname/standardize-pathname.ts
|
|
725
|
-
var standardizePathname = ({
|
|
726
|
-
config,
|
|
727
|
-
pathname
|
|
728
|
-
}) => {
|
|
729
|
-
const { routing, prefixPlaceHolder } = config;
|
|
730
|
-
const { basePath } = routing;
|
|
731
|
-
const parts = [
|
|
732
|
-
normalizePathname(basePath),
|
|
733
|
-
normalizePathname(prefixPlaceHolder),
|
|
734
|
-
normalizePathname(pathname)
|
|
735
|
-
];
|
|
736
|
-
const standardizedPathname = parts.join("/").replace(/\/{2,}/g, "/");
|
|
737
|
-
return normalizePathname(standardizedPathname);
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
// src/shared/utils/resolve-namespaces.ts
|
|
741
|
-
var resolveNamespaces = ({
|
|
742
|
-
config,
|
|
743
|
-
pathname
|
|
744
|
-
}) => {
|
|
745
|
-
const { loaderOptions, prefixPlaceHolder } = config;
|
|
746
|
-
const {
|
|
747
|
-
routeNamespaces = {},
|
|
748
|
-
namespaces: fallbackNamespaces
|
|
749
|
-
} = loaderOptions;
|
|
750
|
-
const { unprefixedPathname } = extractPathname({ config, pathname });
|
|
751
|
-
const standardizedPathname = standardizePathname({
|
|
752
|
-
config,
|
|
753
|
-
pathname: unprefixedPathname
|
|
754
|
-
});
|
|
755
|
-
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
756
|
-
`/${prefixPlaceHolder}`,
|
|
757
|
-
""
|
|
758
|
-
);
|
|
759
|
-
const defaultNamespaces = routeNamespaces.default ?? [];
|
|
760
|
-
const exactMatchNamespaces = routeNamespaces[standardizedPathname] ?? routeNamespaces[placeholderRemovedPathname];
|
|
761
|
-
if (exactMatchNamespaces) {
|
|
762
|
-
return [...defaultNamespaces, ...exactMatchNamespaces];
|
|
763
|
-
}
|
|
764
|
-
let bestMatch = "";
|
|
765
|
-
let bestNamespaces;
|
|
766
|
-
const prefixPatterns = Object.keys(routeNamespaces).filter(
|
|
767
|
-
(pattern) => pattern.endsWith("/*")
|
|
768
|
-
);
|
|
769
|
-
for (const pattern of prefixPatterns) {
|
|
770
|
-
const basePath = pattern.replace(/\/\*$/, "");
|
|
771
|
-
if (standardizedPathname.startsWith(basePath)) {
|
|
772
|
-
if (basePath.length > bestMatch.length) {
|
|
773
|
-
bestMatch = basePath;
|
|
774
|
-
bestNamespaces = routeNamespaces[pattern];
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
const matchedNamespaces = bestNamespaces ?? routeNamespaces["/*"] ?? fallbackNamespaces ?? [];
|
|
779
|
-
if (matchedNamespaces.length > 0) {
|
|
780
|
-
return [...defaultNamespaces, ...matchedNamespaces];
|
|
781
|
-
} else {
|
|
782
|
-
return [...defaultNamespaces];
|
|
783
|
-
}
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
// src/modules/intor-messages-loader/intor-messages-loader.ts
|
|
787
|
-
var intorMessagesLoader = async ({
|
|
826
|
+
// src/modules/messages/get-messages.ts
|
|
827
|
+
var getMessages = async ({
|
|
788
828
|
config,
|
|
789
829
|
locale,
|
|
790
830
|
pathname
|
|
791
831
|
}) => {
|
|
792
|
-
const
|
|
793
|
-
const
|
|
832
|
+
const baseLogger = getLogger({ id: config.id });
|
|
833
|
+
const logger = baseLogger.child({ scope: "messages-loader" });
|
|
834
|
+
const loaderOptions = config.loader;
|
|
794
835
|
const fallbackLocales = config.fallbackLocales[locale] || [];
|
|
795
836
|
const namespaces = resolveNamespaces({ config, pathname });
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
namespaces: namespaces.length > 0 ? { count: namespaces.length, list: [...namespaces] } : "All"
|
|
799
|
-
});
|
|
800
|
-
logger.debug("Loader type selected:", {
|
|
801
|
-
loaderType: loaderOptions.type
|
|
837
|
+
logger.debug("Namespaces ready for loading.", {
|
|
838
|
+
namespaces: namespaces.length > 0 ? { count: namespaces.length, list: [...namespaces] } : "All Namespaces"
|
|
802
839
|
});
|
|
840
|
+
logger.debug("Loader type selected.", { loaderType: loaderOptions.type });
|
|
841
|
+
let loadedMessages;
|
|
803
842
|
if (loaderOptions.type === "import") {
|
|
804
|
-
const loadLocalMessages2 =
|
|
805
|
-
|
|
843
|
+
const loadLocalMessages2 = createLoadLocalMessages(loaderOptions.basePath);
|
|
844
|
+
loadedMessages = await loadLocalMessages2({
|
|
806
845
|
...loaderOptions,
|
|
807
846
|
locale,
|
|
808
847
|
fallbackLocales,
|
|
809
848
|
namespaces,
|
|
810
|
-
|
|
849
|
+
cache: config.cache,
|
|
850
|
+
logger: { id: config.id }
|
|
811
851
|
});
|
|
812
852
|
} else if (loaderOptions.type === "api") {
|
|
813
|
-
|
|
853
|
+
loadedMessages = await loadApiMessages({
|
|
814
854
|
...loaderOptions,
|
|
815
855
|
locale,
|
|
816
856
|
fallbackLocales,
|
|
817
857
|
namespaces,
|
|
818
|
-
|
|
819
|
-
});
|
|
820
|
-
} else {
|
|
821
|
-
logger.error("Unknown loader type.", {
|
|
822
|
-
type: loaderOptions.type
|
|
823
|
-
});
|
|
824
|
-
throw new IntorError({
|
|
825
|
-
id: config.id,
|
|
826
|
-
code: "INTOR_UNKNOWN_LOADER_TYPE" /* UNKNOWN_LOADER_TYPE */,
|
|
827
|
-
message: `Unknown loader type: ${loaderOptions.type}`
|
|
858
|
+
logger: { id: config.id }
|
|
828
859
|
});
|
|
829
860
|
}
|
|
830
|
-
if (!
|
|
861
|
+
if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
|
|
831
862
|
logger.warn("No messages found.", { locale, namespaces });
|
|
832
863
|
}
|
|
833
|
-
return
|
|
834
|
-
};
|
|
835
|
-
|
|
836
|
-
// src/modules/intor-runtime/utils/should-load-dynamic-messages.ts
|
|
837
|
-
var shouldLoadDynamicMessages = (loaderOptions, adapter) => {
|
|
838
|
-
if (!loaderOptions) {
|
|
839
|
-
return false;
|
|
840
|
-
}
|
|
841
|
-
const { type, lazyLoad } = loaderOptions;
|
|
842
|
-
if (type === "import") {
|
|
843
|
-
return true;
|
|
844
|
-
}
|
|
845
|
-
if (type === "api") {
|
|
846
|
-
if (adapter === "next-client" && lazyLoad) {
|
|
847
|
-
return false;
|
|
848
|
-
}
|
|
849
|
-
return true;
|
|
850
|
-
}
|
|
851
|
-
return false;
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
// src/shared/utils/merge-static-and-dynamic-messages.ts
|
|
855
|
-
var mergeStaticAndDynamicMessages = (staticMessages = {}, dynamicMessages = {}) => {
|
|
856
|
-
const result = Object.keys(staticMessages).length ? { ...staticMessages } : {};
|
|
857
|
-
for (const locale in dynamicMessages) {
|
|
858
|
-
const dynamic = dynamicMessages[locale];
|
|
859
|
-
if (!result[locale]) {
|
|
860
|
-
result[locale] = dynamic;
|
|
861
|
-
continue;
|
|
862
|
-
}
|
|
863
|
-
result[locale] = {
|
|
864
|
-
...result[locale],
|
|
865
|
-
...dynamic
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
return result;
|
|
864
|
+
return loadedMessages;
|
|
869
865
|
};
|
|
870
866
|
|
|
871
|
-
// src/modules/intor
|
|
872
|
-
var
|
|
873
|
-
|
|
874
|
-
|
|
867
|
+
// src/modules/intor/intor.ts
|
|
868
|
+
var intor = async ({
|
|
869
|
+
config,
|
|
870
|
+
adapter,
|
|
871
|
+
adapterRuntime
|
|
875
872
|
}) => {
|
|
876
|
-
const
|
|
877
|
-
const {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
message: `Unsupported adapter: ${adapter}`
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
const adapterRuntime = await resolceAdapterRuntimeLoader({ config });
|
|
892
|
-
const { locale, pathname } = await adapterRuntime({ config, request });
|
|
893
|
-
logger.info("Initialized locale and pathname:", { locale, pathname });
|
|
894
|
-
let dynamicMessages;
|
|
895
|
-
if (shouldLoadDynamicMessages(loaderOptions, adapter)) {
|
|
896
|
-
dynamicMessages = await intorMessagesLoader({ config, locale, pathname });
|
|
873
|
+
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
874
|
+
const logger = baseLogger.child({ scope: "intor" });
|
|
875
|
+
logger.info("Start Intor initialization.");
|
|
876
|
+
const { messages, loader } = config;
|
|
877
|
+
let runtime;
|
|
878
|
+
if (adapter) {
|
|
879
|
+
runtime = await adapter(config);
|
|
880
|
+
} else {
|
|
881
|
+
runtime = {
|
|
882
|
+
locale: adapterRuntime?.locale || config.defaultLocale,
|
|
883
|
+
pathname: adapterRuntime?.pathname || ""
|
|
884
|
+
};
|
|
897
885
|
}
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
)
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
886
|
+
const { locale, pathname } = runtime;
|
|
887
|
+
logger.debug("Runtime resolved via adapter/fallback", runtime);
|
|
888
|
+
let loadedMessages;
|
|
889
|
+
if (shouldLoadMessages(loader)) {
|
|
890
|
+
loadedMessages = await getMessages({ config, locale, pathname });
|
|
891
|
+
}
|
|
892
|
+
const mergedMessages = mergeMessages(messages, loadedMessages);
|
|
893
|
+
logger.info("Messages initialized.", {
|
|
894
|
+
staticMessages: { enabled: !!messages },
|
|
895
|
+
loadedMessages: {
|
|
896
|
+
enabled: !!loadedMessages,
|
|
897
|
+
...loader ? { loaderType: loader.type, lazyLoad: !!loader.lazyLoad } : null
|
|
905
898
|
},
|
|
906
|
-
|
|
907
|
-
fromLoader: !!dynamicMessages,
|
|
908
|
-
...loaderOptions ? {
|
|
909
|
-
loaderType: loaderOptions.type,
|
|
910
|
-
lazyLoad: !!loaderOptions.lazyLoad
|
|
911
|
-
} : null
|
|
912
|
-
},
|
|
913
|
-
mergedMessages: messages
|
|
899
|
+
mergedMessages
|
|
914
900
|
});
|
|
915
901
|
return {
|
|
916
902
|
config,
|
|
917
903
|
initialLocale: locale,
|
|
918
904
|
pathname,
|
|
919
|
-
messages
|
|
920
|
-
};
|
|
921
|
-
};
|
|
922
|
-
|
|
923
|
-
// src/modules/intor/intor.ts
|
|
924
|
-
var intor = async ({
|
|
925
|
-
request,
|
|
926
|
-
config
|
|
927
|
-
}) => {
|
|
928
|
-
const logger = logry.logry({
|
|
929
|
-
id: config.id,
|
|
930
|
-
scope: "intor",
|
|
931
|
-
...config.logger
|
|
932
|
-
});
|
|
933
|
-
logger.info("Starting Intor initialization:", {
|
|
934
|
-
adapter: config.adapter
|
|
935
|
-
});
|
|
936
|
-
const runtime = await intorRuntime({ config, request });
|
|
937
|
-
switch (config.adapter) {
|
|
938
|
-
case "next-server": {
|
|
939
|
-
return runtime;
|
|
940
|
-
}
|
|
941
|
-
case "next-client": {
|
|
942
|
-
return runtime;
|
|
943
|
-
}
|
|
944
|
-
default: {
|
|
945
|
-
logger.error("Unsupported adapter:", { adapter: config.adapter });
|
|
946
|
-
throw new IntorError({
|
|
947
|
-
id: config.id,
|
|
948
|
-
code: "INTOR_UNSUPPORTED_ADAPTER" /* UNSUPPORTED_ADAPTER */,
|
|
949
|
-
message: `Unsupported adapter: ${config.adapter}`
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
};
|
|
954
|
-
var createIntor = (config, translateHandlers) => {
|
|
955
|
-
return async (request) => {
|
|
956
|
-
const { initialLocale, messages } = await intor({ config, request });
|
|
957
|
-
const translator = new intorTranslator.Translator({
|
|
958
|
-
locale: initialLocale,
|
|
959
|
-
messages,
|
|
960
|
-
fallbackLocales: config.fallbackLocales,
|
|
961
|
-
loadingMessage: config.translator?.loadingMessage,
|
|
962
|
-
placeholder: config.translator?.placeholder,
|
|
963
|
-
handlers: translateHandlers
|
|
964
|
-
});
|
|
965
|
-
return translator;
|
|
905
|
+
messages: mergedMessages
|
|
966
906
|
};
|
|
967
907
|
};
|
|
968
908
|
|
|
@@ -970,14 +910,16 @@ Object.defineProperty(exports, "Translator", {
|
|
|
970
910
|
enumerable: true,
|
|
971
911
|
get: function () { return intorTranslator.Translator; }
|
|
972
912
|
});
|
|
973
|
-
exports.
|
|
974
|
-
exports.
|
|
975
|
-
exports.
|
|
913
|
+
exports.PREFIX_PLACEHOLDER = PREFIX_PLACEHOLDER;
|
|
914
|
+
exports.clearLoggerPool = clearLoggerPool;
|
|
915
|
+
exports.clearMessagesPool = clearMessagesPool;
|
|
976
916
|
exports.extractPathname = extractPathname;
|
|
977
|
-
exports.
|
|
917
|
+
exports.getMessages = getMessages;
|
|
978
918
|
exports.intor = intor;
|
|
919
|
+
exports.loadApiMessages = loadApiMessages;
|
|
979
920
|
exports.loadLocalMessages = loadLocalMessages;
|
|
980
|
-
exports.
|
|
921
|
+
exports.mergeMessages = mergeMessages;
|
|
922
|
+
exports.normalizeCacheKey = normalizeCacheKey;
|
|
981
923
|
exports.normalizeLocale = normalizeLocale;
|
|
982
924
|
exports.normalizePathname = normalizePathname;
|
|
983
925
|
exports.resolveNamespaces = resolveNamespaces;
|