intor 2.2.2 → 2.2.4
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/index.cjs +37 -31
- package/dist/index.d.cts +48 -9
- package/dist/index.d.ts +48 -9
- package/dist/index.js +37 -31
- package/dist/next/index.cjs +6 -5
- package/dist/next/index.js +6 -5
- package/dist/next/server/index.cjs +34 -29
- package/dist/next/server/index.d.cts +1 -1
- package/dist/next/server/index.d.ts +1 -1
- package/dist/next/server/index.js +34 -29
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -137,11 +137,11 @@ async function collectFileEntries({
|
|
|
137
137
|
return results;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
// src/modules/messages/shared/utils/is-
|
|
140
|
+
// src/modules/messages/shared/utils/is-valid-messages.ts
|
|
141
141
|
function isPlainObject(value) {
|
|
142
142
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
143
143
|
}
|
|
144
|
-
function
|
|
144
|
+
function isValidMessages(value) {
|
|
145
145
|
if (!isPlainObject(value)) return false;
|
|
146
146
|
const stack = [value];
|
|
147
147
|
while (stack.length > 0) {
|
|
@@ -157,22 +157,17 @@ function isNamespaceMessages(value) {
|
|
|
157
157
|
}
|
|
158
158
|
return true;
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
|
|
162
160
|
async function jsonReader(filePath, readFile = fs__default.default.readFile) {
|
|
163
161
|
const raw = await readFile(filePath, "utf8");
|
|
164
162
|
const parsed = JSON.parse(raw);
|
|
165
|
-
if (!isNamespaceMessages(parsed)) {
|
|
166
|
-
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
167
|
-
}
|
|
168
163
|
return parsed;
|
|
169
164
|
}
|
|
170
165
|
|
|
171
166
|
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
|
|
172
|
-
function nestObjectFromPath(
|
|
167
|
+
function nestObjectFromPath(path5, value) {
|
|
173
168
|
let obj = value;
|
|
174
|
-
for (let i =
|
|
175
|
-
obj = { [
|
|
169
|
+
for (let i = path5.length - 1; i >= 0; i--) {
|
|
170
|
+
obj = { [path5[i]]: obj };
|
|
176
171
|
}
|
|
177
172
|
return obj;
|
|
178
173
|
}
|
|
@@ -181,7 +176,7 @@ function nestObjectFromPath(path4, value) {
|
|
|
181
176
|
async function parseFileEntries({
|
|
182
177
|
fileEntries,
|
|
183
178
|
limit,
|
|
184
|
-
extraOptions: {
|
|
179
|
+
extraOptions: { messagesReader, loggerOptions } = {}
|
|
185
180
|
}) {
|
|
186
181
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
187
182
|
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
@@ -190,11 +185,17 @@ async function parseFileEntries({
|
|
|
190
185
|
({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
191
186
|
try {
|
|
192
187
|
const segsWithoutNs = segments.slice(1);
|
|
193
|
-
const
|
|
188
|
+
const ext = path__default.default.extname(fullPath);
|
|
189
|
+
const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
|
|
190
|
+
if (!isValidMessages(json)) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
"JSON file does not match NamespaceMessages structure"
|
|
193
|
+
);
|
|
194
|
+
}
|
|
194
195
|
const isIndex = basename === "index";
|
|
195
196
|
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
196
|
-
const
|
|
197
|
-
parsedFileEntries.push({ namespace,
|
|
197
|
+
const nested = nestObjectFromPath(keyPath, json);
|
|
198
|
+
parsedFileEntries.push({ namespace, messages: nested });
|
|
198
199
|
logger.trace("Parsed file.", { path: fullPath });
|
|
199
200
|
} catch (error) {
|
|
200
201
|
logger.error("Failed to read or parse file.", {
|
|
@@ -206,13 +207,13 @@ async function parseFileEntries({
|
|
|
206
207
|
);
|
|
207
208
|
await Promise.all(tasks);
|
|
208
209
|
const result = {};
|
|
209
|
-
for (const { namespace,
|
|
210
|
+
for (const { namespace, messages } of parsedFileEntries) {
|
|
210
211
|
if (namespace === "index") {
|
|
211
|
-
merge__default.default(result,
|
|
212
|
+
merge__default.default(result, messages);
|
|
212
213
|
} else {
|
|
213
214
|
result[namespace] = merge__default.default(
|
|
214
215
|
result[namespace] ?? {},
|
|
215
|
-
|
|
216
|
+
messages
|
|
216
217
|
);
|
|
217
218
|
}
|
|
218
219
|
}
|
|
@@ -225,7 +226,7 @@ var readLocaleMessages = async ({
|
|
|
225
226
|
rootDir = "messages",
|
|
226
227
|
locale,
|
|
227
228
|
namespaces,
|
|
228
|
-
extraOptions: { exts,
|
|
229
|
+
extraOptions: { exts, messagesReader, loggerOptions } = {}
|
|
229
230
|
}) => {
|
|
230
231
|
const fileEntries = await collectFileEntries({
|
|
231
232
|
rootDir: path__default.default.resolve(process.cwd(), rootDir, locale),
|
|
@@ -236,7 +237,7 @@ var readLocaleMessages = async ({
|
|
|
236
237
|
const namespaceMessages = await parseFileEntries({
|
|
237
238
|
fileEntries,
|
|
238
239
|
limit,
|
|
239
|
-
extraOptions: {
|
|
240
|
+
extraOptions: { messagesReader, loggerOptions }
|
|
240
241
|
});
|
|
241
242
|
const localeMessages = { [locale]: namespaceMessages };
|
|
242
243
|
return localeMessages;
|
|
@@ -454,8 +455,9 @@ var loadLocalMessages = async ({
|
|
|
454
455
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
455
456
|
loggerOptions = { id: "default" },
|
|
456
457
|
exts,
|
|
457
|
-
|
|
458
|
-
} = {}
|
|
458
|
+
messagesReader
|
|
459
|
+
} = {},
|
|
460
|
+
allowCacheWrite = false
|
|
459
461
|
}) => {
|
|
460
462
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
461
463
|
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
@@ -489,7 +491,7 @@ var loadLocalMessages = async ({
|
|
|
489
491
|
rootDir,
|
|
490
492
|
locale: candidateLocale,
|
|
491
493
|
namespaces,
|
|
492
|
-
extraOptions: { loggerOptions, exts,
|
|
494
|
+
extraOptions: { loggerOptions, exts, messagesReader }
|
|
493
495
|
});
|
|
494
496
|
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
495
497
|
messages = result;
|
|
@@ -502,7 +504,7 @@ var loadLocalMessages = async ({
|
|
|
502
504
|
});
|
|
503
505
|
}
|
|
504
506
|
}
|
|
505
|
-
if (cacheOptions.enabled && key && messages) {
|
|
507
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
506
508
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
507
509
|
}
|
|
508
510
|
const end = perf_hooks.performance.now();
|
|
@@ -541,7 +543,7 @@ var fetchLocaleMessages = async ({
|
|
|
541
543
|
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
542
544
|
}
|
|
543
545
|
const data = await response.json();
|
|
544
|
-
if (!
|
|
546
|
+
if (!isValidMessages(data[locale])) {
|
|
545
547
|
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
546
548
|
}
|
|
547
549
|
return data;
|
|
@@ -586,7 +588,8 @@ var loadRemoteMessages = async ({
|
|
|
586
588
|
extraOptions: {
|
|
587
589
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
588
590
|
loggerOptions = { id: "default" }
|
|
589
|
-
} = {}
|
|
591
|
+
} = {},
|
|
592
|
+
allowCacheWrite
|
|
590
593
|
}) => {
|
|
591
594
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
592
595
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
@@ -630,7 +633,7 @@ var loadRemoteMessages = async ({
|
|
|
630
633
|
});
|
|
631
634
|
}
|
|
632
635
|
}
|
|
633
|
-
if (cacheOptions.enabled && key && messages) {
|
|
636
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
634
637
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
635
638
|
}
|
|
636
639
|
const end = performance.now();
|
|
@@ -647,7 +650,8 @@ var loadMessages = async ({
|
|
|
647
650
|
config,
|
|
648
651
|
locale,
|
|
649
652
|
pathname = "",
|
|
650
|
-
extraOptions: { exts,
|
|
653
|
+
extraOptions: { exts, messagesReader } = {},
|
|
654
|
+
allowCacheWrite = false
|
|
651
655
|
}) => {
|
|
652
656
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
653
657
|
const logger = baseLogger.child({ scope: "load-messages" });
|
|
@@ -683,8 +687,9 @@ var loadMessages = async ({
|
|
|
683
687
|
cacheOptions: config.cache,
|
|
684
688
|
loggerOptions: { id: config.id, ...config.logger },
|
|
685
689
|
exts,
|
|
686
|
-
|
|
687
|
-
}
|
|
690
|
+
messagesReader
|
|
691
|
+
},
|
|
692
|
+
allowCacheWrite
|
|
688
693
|
});
|
|
689
694
|
} else if (type === "remote") {
|
|
690
695
|
loadedMessages = await loadRemoteMessages({
|
|
@@ -728,8 +733,9 @@ var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
|
|
|
728
733
|
pathname,
|
|
729
734
|
extraOptions: {
|
|
730
735
|
exts: loadMessagesOptions.exts,
|
|
731
|
-
|
|
732
|
-
}
|
|
736
|
+
messagesReader: loadMessagesOptions.messagesReader
|
|
737
|
+
},
|
|
738
|
+
allowCacheWrite: true
|
|
733
739
|
});
|
|
734
740
|
}
|
|
735
741
|
const mergedMessages = mergeMessages(messages, loadedMessages);
|
package/dist/index.d.cts
CHANGED
|
@@ -165,8 +165,44 @@ interface IntorResult {
|
|
|
165
165
|
messages: LocaleMessages;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Represents a collection of localized messages.
|
|
170
|
+
*
|
|
171
|
+
* - Each key is a namespace or message identifier, and the value is a
|
|
172
|
+
* `NestedMessage` object that can contain nested message structures.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* const messages: Messages = {
|
|
177
|
+
* ui: {
|
|
178
|
+
* greeting: "Hello",
|
|
179
|
+
* farewell: "Goodbye"
|
|
180
|
+
* },
|
|
181
|
+
* errors: {
|
|
182
|
+
* network: "Network error occurred"
|
|
183
|
+
* }
|
|
184
|
+
* };
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
type Messages = Record<string, NestedMessage>;
|
|
188
|
+
/**
|
|
189
|
+
* A function that reads messages from a given file path.
|
|
190
|
+
*
|
|
191
|
+
* - This function is expected to return a `Promise` that resolves to a `Messages` object.
|
|
192
|
+
* - It can be implemented to support different file formats such as JSON, YAML, or others.
|
|
193
|
+
*
|
|
194
|
+
* @param filePath - The path to the message file to read.
|
|
195
|
+
* @returns A Promise that resolves to a `Messages` object.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* const reader: MessagesReader = async (filePath) => {
|
|
200
|
+
* const content = await fs.promises.readFile(filePath, "utf-8");
|
|
201
|
+
* return JSON.parse(content) as Messages;
|
|
202
|
+
* };
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
type MessagesReader = (filePath: string) => Promise<Messages>;
|
|
170
206
|
|
|
171
207
|
type LoadMessagesOptions = {
|
|
172
208
|
config: IntorResolvedConfig;
|
|
@@ -174,8 +210,9 @@ type LoadMessagesOptions = {
|
|
|
174
210
|
pathname?: string;
|
|
175
211
|
extraOptions?: {
|
|
176
212
|
exts?: string[];
|
|
177
|
-
|
|
213
|
+
messagesReader?: MessagesReader;
|
|
178
214
|
};
|
|
215
|
+
allowCacheWrite?: boolean;
|
|
179
216
|
};
|
|
180
217
|
type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMessages<C> | undefined>;
|
|
181
218
|
|
|
@@ -187,7 +224,7 @@ type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMe
|
|
|
187
224
|
* - Apply fallback locales if needed.
|
|
188
225
|
* - Cache messages if enabled (handled by underlying loader, not this function directly).
|
|
189
226
|
*/
|
|
190
|
-
declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts,
|
|
227
|
+
declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messagesReader }, allowCacheWrite, }: LoadMessagesOptions) => LoadMessagesResult<C>;
|
|
191
228
|
|
|
192
229
|
/**
|
|
193
230
|
* Global messages pool (cross-module + hot-reload safe)
|
|
@@ -215,8 +252,9 @@ interface LoadLocalMessagesOptions {
|
|
|
215
252
|
id?: string;
|
|
216
253
|
};
|
|
217
254
|
exts?: string[];
|
|
218
|
-
|
|
255
|
+
messagesReader?: MessagesReader;
|
|
219
256
|
};
|
|
257
|
+
allowCacheWrite?: boolean;
|
|
220
258
|
}
|
|
221
259
|
|
|
222
260
|
/**
|
|
@@ -227,7 +265,7 @@ interface LoadLocalMessagesOptions {
|
|
|
227
265
|
* - Cache messages if enabled.
|
|
228
266
|
* - Limit concurrent file reads for performance.
|
|
229
267
|
*/
|
|
230
|
-
declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts,
|
|
268
|
+
declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messagesReader, }, allowCacheWrite, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
|
|
231
269
|
|
|
232
270
|
interface LoadRemoteMessagesOptions {
|
|
233
271
|
pool?: MessagesPool;
|
|
@@ -243,6 +281,7 @@ interface LoadRemoteMessagesOptions {
|
|
|
243
281
|
id?: string;
|
|
244
282
|
};
|
|
245
283
|
};
|
|
284
|
+
allowCacheWrite?: boolean;
|
|
246
285
|
}
|
|
247
286
|
|
|
248
287
|
/**
|
|
@@ -251,7 +290,7 @@ interface LoadRemoteMessagesOptions {
|
|
|
251
290
|
* - Fetch messages for a target locale with optional fallback locales.
|
|
252
291
|
* - Cache messages if enabled.
|
|
253
292
|
*/
|
|
254
|
-
declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
|
|
293
|
+
declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, allowCacheWrite, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
|
|
255
294
|
|
|
256
295
|
/**
|
|
257
296
|
* Entry point for initializing Intor.
|
|
@@ -262,7 +301,7 @@ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, lo
|
|
|
262
301
|
*/
|
|
263
302
|
declare const intor: (config: IntorResolvedConfig, i18nContext: GetI18nContext | Partial<I18nContext>, loadMessagesOptions?: {
|
|
264
303
|
exts?: string[];
|
|
265
|
-
|
|
304
|
+
messagesReader?: MessagesReader;
|
|
266
305
|
}) => Promise<IntorResult>;
|
|
267
306
|
|
|
268
307
|
/** Base properties shared by all translator instances. */
|
|
@@ -416,4 +455,4 @@ declare global {
|
|
|
416
455
|
*/
|
|
417
456
|
declare function clearLoggerPool(): void;
|
|
418
457
|
|
|
419
|
-
export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type
|
|
458
|
+
export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type Messages, type MessagesReader, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
|
package/dist/index.d.ts
CHANGED
|
@@ -165,8 +165,44 @@ interface IntorResult {
|
|
|
165
165
|
messages: LocaleMessages;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Represents a collection of localized messages.
|
|
170
|
+
*
|
|
171
|
+
* - Each key is a namespace or message identifier, and the value is a
|
|
172
|
+
* `NestedMessage` object that can contain nested message structures.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* const messages: Messages = {
|
|
177
|
+
* ui: {
|
|
178
|
+
* greeting: "Hello",
|
|
179
|
+
* farewell: "Goodbye"
|
|
180
|
+
* },
|
|
181
|
+
* errors: {
|
|
182
|
+
* network: "Network error occurred"
|
|
183
|
+
* }
|
|
184
|
+
* };
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
type Messages = Record<string, NestedMessage>;
|
|
188
|
+
/**
|
|
189
|
+
* A function that reads messages from a given file path.
|
|
190
|
+
*
|
|
191
|
+
* - This function is expected to return a `Promise` that resolves to a `Messages` object.
|
|
192
|
+
* - It can be implemented to support different file formats such as JSON, YAML, or others.
|
|
193
|
+
*
|
|
194
|
+
* @param filePath - The path to the message file to read.
|
|
195
|
+
* @returns A Promise that resolves to a `Messages` object.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* const reader: MessagesReader = async (filePath) => {
|
|
200
|
+
* const content = await fs.promises.readFile(filePath, "utf-8");
|
|
201
|
+
* return JSON.parse(content) as Messages;
|
|
202
|
+
* };
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
type MessagesReader = (filePath: string) => Promise<Messages>;
|
|
170
206
|
|
|
171
207
|
type LoadMessagesOptions = {
|
|
172
208
|
config: IntorResolvedConfig;
|
|
@@ -174,8 +210,9 @@ type LoadMessagesOptions = {
|
|
|
174
210
|
pathname?: string;
|
|
175
211
|
extraOptions?: {
|
|
176
212
|
exts?: string[];
|
|
177
|
-
|
|
213
|
+
messagesReader?: MessagesReader;
|
|
178
214
|
};
|
|
215
|
+
allowCacheWrite?: boolean;
|
|
179
216
|
};
|
|
180
217
|
type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMessages<C> | undefined>;
|
|
181
218
|
|
|
@@ -187,7 +224,7 @@ type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMe
|
|
|
187
224
|
* - Apply fallback locales if needed.
|
|
188
225
|
* - Cache messages if enabled (handled by underlying loader, not this function directly).
|
|
189
226
|
*/
|
|
190
|
-
declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts,
|
|
227
|
+
declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messagesReader }, allowCacheWrite, }: LoadMessagesOptions) => LoadMessagesResult<C>;
|
|
191
228
|
|
|
192
229
|
/**
|
|
193
230
|
* Global messages pool (cross-module + hot-reload safe)
|
|
@@ -215,8 +252,9 @@ interface LoadLocalMessagesOptions {
|
|
|
215
252
|
id?: string;
|
|
216
253
|
};
|
|
217
254
|
exts?: string[];
|
|
218
|
-
|
|
255
|
+
messagesReader?: MessagesReader;
|
|
219
256
|
};
|
|
257
|
+
allowCacheWrite?: boolean;
|
|
220
258
|
}
|
|
221
259
|
|
|
222
260
|
/**
|
|
@@ -227,7 +265,7 @@ interface LoadLocalMessagesOptions {
|
|
|
227
265
|
* - Cache messages if enabled.
|
|
228
266
|
* - Limit concurrent file reads for performance.
|
|
229
267
|
*/
|
|
230
|
-
declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts,
|
|
268
|
+
declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messagesReader, }, allowCacheWrite, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
|
|
231
269
|
|
|
232
270
|
interface LoadRemoteMessagesOptions {
|
|
233
271
|
pool?: MessagesPool;
|
|
@@ -243,6 +281,7 @@ interface LoadRemoteMessagesOptions {
|
|
|
243
281
|
id?: string;
|
|
244
282
|
};
|
|
245
283
|
};
|
|
284
|
+
allowCacheWrite?: boolean;
|
|
246
285
|
}
|
|
247
286
|
|
|
248
287
|
/**
|
|
@@ -251,7 +290,7 @@ interface LoadRemoteMessagesOptions {
|
|
|
251
290
|
* - Fetch messages for a target locale with optional fallback locales.
|
|
252
291
|
* - Cache messages if enabled.
|
|
253
292
|
*/
|
|
254
|
-
declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
|
|
293
|
+
declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, allowCacheWrite, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
|
|
255
294
|
|
|
256
295
|
/**
|
|
257
296
|
* Entry point for initializing Intor.
|
|
@@ -262,7 +301,7 @@ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, lo
|
|
|
262
301
|
*/
|
|
263
302
|
declare const intor: (config: IntorResolvedConfig, i18nContext: GetI18nContext | Partial<I18nContext>, loadMessagesOptions?: {
|
|
264
303
|
exts?: string[];
|
|
265
|
-
|
|
304
|
+
messagesReader?: MessagesReader;
|
|
266
305
|
}) => Promise<IntorResult>;
|
|
267
306
|
|
|
268
307
|
/** Base properties shared by all translator instances. */
|
|
@@ -416,4 +455,4 @@ declare global {
|
|
|
416
455
|
*/
|
|
417
456
|
declare function clearLoggerPool(): void;
|
|
418
457
|
|
|
419
|
-
export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type
|
|
458
|
+
export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type Messages, type MessagesReader, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
|
package/dist/index.js
CHANGED
|
@@ -128,11 +128,11 @@ async function collectFileEntries({
|
|
|
128
128
|
return results;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
// src/modules/messages/shared/utils/is-
|
|
131
|
+
// src/modules/messages/shared/utils/is-valid-messages.ts
|
|
132
132
|
function isPlainObject(value) {
|
|
133
133
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
134
134
|
}
|
|
135
|
-
function
|
|
135
|
+
function isValidMessages(value) {
|
|
136
136
|
if (!isPlainObject(value)) return false;
|
|
137
137
|
const stack = [value];
|
|
138
138
|
while (stack.length > 0) {
|
|
@@ -148,22 +148,17 @@ function isNamespaceMessages(value) {
|
|
|
148
148
|
}
|
|
149
149
|
return true;
|
|
150
150
|
}
|
|
151
|
-
|
|
152
|
-
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
|
|
153
151
|
async function jsonReader(filePath, readFile = fs.readFile) {
|
|
154
152
|
const raw = await readFile(filePath, "utf8");
|
|
155
153
|
const parsed = JSON.parse(raw);
|
|
156
|
-
if (!isNamespaceMessages(parsed)) {
|
|
157
|
-
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
158
|
-
}
|
|
159
154
|
return parsed;
|
|
160
155
|
}
|
|
161
156
|
|
|
162
157
|
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
|
|
163
|
-
function nestObjectFromPath(
|
|
158
|
+
function nestObjectFromPath(path5, value) {
|
|
164
159
|
let obj = value;
|
|
165
|
-
for (let i =
|
|
166
|
-
obj = { [
|
|
160
|
+
for (let i = path5.length - 1; i >= 0; i--) {
|
|
161
|
+
obj = { [path5[i]]: obj };
|
|
167
162
|
}
|
|
168
163
|
return obj;
|
|
169
164
|
}
|
|
@@ -172,7 +167,7 @@ function nestObjectFromPath(path4, value) {
|
|
|
172
167
|
async function parseFileEntries({
|
|
173
168
|
fileEntries,
|
|
174
169
|
limit,
|
|
175
|
-
extraOptions: {
|
|
170
|
+
extraOptions: { messagesReader, loggerOptions } = {}
|
|
176
171
|
}) {
|
|
177
172
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
178
173
|
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
@@ -181,11 +176,17 @@ async function parseFileEntries({
|
|
|
181
176
|
({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
182
177
|
try {
|
|
183
178
|
const segsWithoutNs = segments.slice(1);
|
|
184
|
-
const
|
|
179
|
+
const ext = path.extname(fullPath);
|
|
180
|
+
const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
|
|
181
|
+
if (!isValidMessages(json)) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
"JSON file does not match NamespaceMessages structure"
|
|
184
|
+
);
|
|
185
|
+
}
|
|
185
186
|
const isIndex = basename === "index";
|
|
186
187
|
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
187
|
-
const
|
|
188
|
-
parsedFileEntries.push({ namespace,
|
|
188
|
+
const nested = nestObjectFromPath(keyPath, json);
|
|
189
|
+
parsedFileEntries.push({ namespace, messages: nested });
|
|
189
190
|
logger.trace("Parsed file.", { path: fullPath });
|
|
190
191
|
} catch (error) {
|
|
191
192
|
logger.error("Failed to read or parse file.", {
|
|
@@ -197,13 +198,13 @@ async function parseFileEntries({
|
|
|
197
198
|
);
|
|
198
199
|
await Promise.all(tasks);
|
|
199
200
|
const result = {};
|
|
200
|
-
for (const { namespace,
|
|
201
|
+
for (const { namespace, messages } of parsedFileEntries) {
|
|
201
202
|
if (namespace === "index") {
|
|
202
|
-
merge(result,
|
|
203
|
+
merge(result, messages);
|
|
203
204
|
} else {
|
|
204
205
|
result[namespace] = merge(
|
|
205
206
|
result[namespace] ?? {},
|
|
206
|
-
|
|
207
|
+
messages
|
|
207
208
|
);
|
|
208
209
|
}
|
|
209
210
|
}
|
|
@@ -216,7 +217,7 @@ var readLocaleMessages = async ({
|
|
|
216
217
|
rootDir = "messages",
|
|
217
218
|
locale,
|
|
218
219
|
namespaces,
|
|
219
|
-
extraOptions: { exts,
|
|
220
|
+
extraOptions: { exts, messagesReader, loggerOptions } = {}
|
|
220
221
|
}) => {
|
|
221
222
|
const fileEntries = await collectFileEntries({
|
|
222
223
|
rootDir: path.resolve(process.cwd(), rootDir, locale),
|
|
@@ -227,7 +228,7 @@ var readLocaleMessages = async ({
|
|
|
227
228
|
const namespaceMessages = await parseFileEntries({
|
|
228
229
|
fileEntries,
|
|
229
230
|
limit,
|
|
230
|
-
extraOptions: {
|
|
231
|
+
extraOptions: { messagesReader, loggerOptions }
|
|
231
232
|
});
|
|
232
233
|
const localeMessages = { [locale]: namespaceMessages };
|
|
233
234
|
return localeMessages;
|
|
@@ -445,8 +446,9 @@ var loadLocalMessages = async ({
|
|
|
445
446
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
446
447
|
loggerOptions = { id: "default" },
|
|
447
448
|
exts,
|
|
448
|
-
|
|
449
|
-
} = {}
|
|
449
|
+
messagesReader
|
|
450
|
+
} = {},
|
|
451
|
+
allowCacheWrite = false
|
|
450
452
|
}) => {
|
|
451
453
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
452
454
|
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
@@ -480,7 +482,7 @@ var loadLocalMessages = async ({
|
|
|
480
482
|
rootDir,
|
|
481
483
|
locale: candidateLocale,
|
|
482
484
|
namespaces,
|
|
483
|
-
extraOptions: { loggerOptions, exts,
|
|
485
|
+
extraOptions: { loggerOptions, exts, messagesReader }
|
|
484
486
|
});
|
|
485
487
|
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
486
488
|
messages = result;
|
|
@@ -493,7 +495,7 @@ var loadLocalMessages = async ({
|
|
|
493
495
|
});
|
|
494
496
|
}
|
|
495
497
|
}
|
|
496
|
-
if (cacheOptions.enabled && key && messages) {
|
|
498
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
497
499
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
498
500
|
}
|
|
499
501
|
const end = performance$1.now();
|
|
@@ -532,7 +534,7 @@ var fetchLocaleMessages = async ({
|
|
|
532
534
|
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
533
535
|
}
|
|
534
536
|
const data = await response.json();
|
|
535
|
-
if (!
|
|
537
|
+
if (!isValidMessages(data[locale])) {
|
|
536
538
|
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
537
539
|
}
|
|
538
540
|
return data;
|
|
@@ -577,7 +579,8 @@ var loadRemoteMessages = async ({
|
|
|
577
579
|
extraOptions: {
|
|
578
580
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
579
581
|
loggerOptions = { id: "default" }
|
|
580
|
-
} = {}
|
|
582
|
+
} = {},
|
|
583
|
+
allowCacheWrite
|
|
581
584
|
}) => {
|
|
582
585
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
583
586
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
@@ -621,7 +624,7 @@ var loadRemoteMessages = async ({
|
|
|
621
624
|
});
|
|
622
625
|
}
|
|
623
626
|
}
|
|
624
|
-
if (cacheOptions.enabled && key && messages) {
|
|
627
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
625
628
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
626
629
|
}
|
|
627
630
|
const end = performance.now();
|
|
@@ -638,7 +641,8 @@ var loadMessages = async ({
|
|
|
638
641
|
config,
|
|
639
642
|
locale,
|
|
640
643
|
pathname = "",
|
|
641
|
-
extraOptions: { exts,
|
|
644
|
+
extraOptions: { exts, messagesReader } = {},
|
|
645
|
+
allowCacheWrite = false
|
|
642
646
|
}) => {
|
|
643
647
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
644
648
|
const logger = baseLogger.child({ scope: "load-messages" });
|
|
@@ -674,8 +678,9 @@ var loadMessages = async ({
|
|
|
674
678
|
cacheOptions: config.cache,
|
|
675
679
|
loggerOptions: { id: config.id, ...config.logger },
|
|
676
680
|
exts,
|
|
677
|
-
|
|
678
|
-
}
|
|
681
|
+
messagesReader
|
|
682
|
+
},
|
|
683
|
+
allowCacheWrite
|
|
679
684
|
});
|
|
680
685
|
} else if (type === "remote") {
|
|
681
686
|
loadedMessages = await loadRemoteMessages({
|
|
@@ -719,8 +724,9 @@ var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
|
|
|
719
724
|
pathname,
|
|
720
725
|
extraOptions: {
|
|
721
726
|
exts: loadMessagesOptions.exts,
|
|
722
|
-
|
|
723
|
-
}
|
|
727
|
+
messagesReader: loadMessagesOptions.messagesReader
|
|
728
|
+
},
|
|
729
|
+
allowCacheWrite: true
|
|
724
730
|
});
|
|
725
731
|
}
|
|
726
732
|
const mergedMessages = mergeMessages(messages, loadedMessages);
|
package/dist/next/index.cjs
CHANGED
|
@@ -96,11 +96,11 @@ function getLogger({
|
|
|
96
96
|
return logger;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
// src/modules/messages/shared/utils/is-
|
|
99
|
+
// src/modules/messages/shared/utils/is-valid-messages.ts
|
|
100
100
|
function isPlainObject(value) {
|
|
101
101
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
102
102
|
}
|
|
103
|
-
function
|
|
103
|
+
function isValidMessages(value) {
|
|
104
104
|
if (!isPlainObject(value)) return false;
|
|
105
105
|
const stack = [value];
|
|
106
106
|
while (stack.length > 0) {
|
|
@@ -340,7 +340,7 @@ var fetchLocaleMessages = async ({
|
|
|
340
340
|
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
341
341
|
}
|
|
342
342
|
const data = await response.json();
|
|
343
|
-
if (!
|
|
343
|
+
if (!isValidMessages(data[locale])) {
|
|
344
344
|
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
345
345
|
}
|
|
346
346
|
return data;
|
|
@@ -385,7 +385,8 @@ var loadRemoteMessages = async ({
|
|
|
385
385
|
extraOptions: {
|
|
386
386
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
387
387
|
loggerOptions = { id: "default" }
|
|
388
|
-
} = {}
|
|
388
|
+
} = {},
|
|
389
|
+
allowCacheWrite
|
|
389
390
|
}) => {
|
|
390
391
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
391
392
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
@@ -429,7 +430,7 @@ var loadRemoteMessages = async ({
|
|
|
429
430
|
});
|
|
430
431
|
}
|
|
431
432
|
}
|
|
432
|
-
if (cacheOptions.enabled && key && messages) {
|
|
433
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
433
434
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
434
435
|
}
|
|
435
436
|
const end = performance.now();
|
package/dist/next/index.js
CHANGED
|
@@ -69,11 +69,11 @@ function getLogger({
|
|
|
69
69
|
return logger;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// src/modules/messages/shared/utils/is-
|
|
72
|
+
// src/modules/messages/shared/utils/is-valid-messages.ts
|
|
73
73
|
function isPlainObject(value) {
|
|
74
74
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
75
75
|
}
|
|
76
|
-
function
|
|
76
|
+
function isValidMessages(value) {
|
|
77
77
|
if (!isPlainObject(value)) return false;
|
|
78
78
|
const stack = [value];
|
|
79
79
|
while (stack.length > 0) {
|
|
@@ -313,7 +313,7 @@ var fetchLocaleMessages = async ({
|
|
|
313
313
|
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
314
314
|
}
|
|
315
315
|
const data = await response.json();
|
|
316
|
-
if (!
|
|
316
|
+
if (!isValidMessages(data[locale])) {
|
|
317
317
|
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
318
318
|
}
|
|
319
319
|
return data;
|
|
@@ -358,7 +358,8 @@ var loadRemoteMessages = async ({
|
|
|
358
358
|
extraOptions: {
|
|
359
359
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
360
360
|
loggerOptions = { id: "default" }
|
|
361
|
-
} = {}
|
|
361
|
+
} = {},
|
|
362
|
+
allowCacheWrite
|
|
362
363
|
}) => {
|
|
363
364
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
364
365
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
@@ -402,7 +403,7 @@ var loadRemoteMessages = async ({
|
|
|
402
403
|
});
|
|
403
404
|
}
|
|
404
405
|
}
|
|
405
|
-
if (cacheOptions.enabled && key && messages) {
|
|
406
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
406
407
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
407
408
|
}
|
|
408
409
|
const end = performance.now();
|
|
@@ -309,11 +309,11 @@ async function collectFileEntries({
|
|
|
309
309
|
return results;
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
// src/modules/messages/shared/utils/is-
|
|
312
|
+
// src/modules/messages/shared/utils/is-valid-messages.ts
|
|
313
313
|
function isPlainObject(value) {
|
|
314
314
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
315
315
|
}
|
|
316
|
-
function
|
|
316
|
+
function isValidMessages(value) {
|
|
317
317
|
if (!isPlainObject(value)) return false;
|
|
318
318
|
const stack = [value];
|
|
319
319
|
while (stack.length > 0) {
|
|
@@ -329,22 +329,17 @@ function isNamespaceMessages(value) {
|
|
|
329
329
|
}
|
|
330
330
|
return true;
|
|
331
331
|
}
|
|
332
|
-
|
|
333
|
-
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
|
|
334
332
|
async function jsonReader(filePath, readFile = fs__default.default.readFile) {
|
|
335
333
|
const raw = await readFile(filePath, "utf8");
|
|
336
334
|
const parsed = JSON.parse(raw);
|
|
337
|
-
if (!isNamespaceMessages(parsed)) {
|
|
338
|
-
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
339
|
-
}
|
|
340
335
|
return parsed;
|
|
341
336
|
}
|
|
342
337
|
|
|
343
338
|
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
|
|
344
|
-
function nestObjectFromPath(
|
|
339
|
+
function nestObjectFromPath(path5, value) {
|
|
345
340
|
let obj = value;
|
|
346
|
-
for (let i =
|
|
347
|
-
obj = { [
|
|
341
|
+
for (let i = path5.length - 1; i >= 0; i--) {
|
|
342
|
+
obj = { [path5[i]]: obj };
|
|
348
343
|
}
|
|
349
344
|
return obj;
|
|
350
345
|
}
|
|
@@ -353,7 +348,7 @@ function nestObjectFromPath(path4, value) {
|
|
|
353
348
|
async function parseFileEntries({
|
|
354
349
|
fileEntries,
|
|
355
350
|
limit,
|
|
356
|
-
extraOptions: {
|
|
351
|
+
extraOptions: { messagesReader, loggerOptions } = {}
|
|
357
352
|
}) {
|
|
358
353
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
359
354
|
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
@@ -362,11 +357,17 @@ async function parseFileEntries({
|
|
|
362
357
|
({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
363
358
|
try {
|
|
364
359
|
const segsWithoutNs = segments.slice(1);
|
|
365
|
-
const
|
|
360
|
+
const ext = path__default.default.extname(fullPath);
|
|
361
|
+
const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
|
|
362
|
+
if (!isValidMessages(json)) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
"JSON file does not match NamespaceMessages structure"
|
|
365
|
+
);
|
|
366
|
+
}
|
|
366
367
|
const isIndex = basename === "index";
|
|
367
368
|
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
368
|
-
const
|
|
369
|
-
parsedFileEntries.push({ namespace,
|
|
369
|
+
const nested = nestObjectFromPath(keyPath, json);
|
|
370
|
+
parsedFileEntries.push({ namespace, messages: nested });
|
|
370
371
|
logger.trace("Parsed file.", { path: fullPath });
|
|
371
372
|
} catch (error) {
|
|
372
373
|
logger.error("Failed to read or parse file.", {
|
|
@@ -378,13 +379,13 @@ async function parseFileEntries({
|
|
|
378
379
|
);
|
|
379
380
|
await Promise.all(tasks);
|
|
380
381
|
const result = {};
|
|
381
|
-
for (const { namespace,
|
|
382
|
+
for (const { namespace, messages } of parsedFileEntries) {
|
|
382
383
|
if (namespace === "index") {
|
|
383
|
-
merge__default.default(result,
|
|
384
|
+
merge__default.default(result, messages);
|
|
384
385
|
} else {
|
|
385
386
|
result[namespace] = merge__default.default(
|
|
386
387
|
result[namespace] ?? {},
|
|
387
|
-
|
|
388
|
+
messages
|
|
388
389
|
);
|
|
389
390
|
}
|
|
390
391
|
}
|
|
@@ -397,7 +398,7 @@ var readLocaleMessages = async ({
|
|
|
397
398
|
rootDir = "messages",
|
|
398
399
|
locale,
|
|
399
400
|
namespaces,
|
|
400
|
-
extraOptions: { exts,
|
|
401
|
+
extraOptions: { exts, messagesReader, loggerOptions } = {}
|
|
401
402
|
}) => {
|
|
402
403
|
const fileEntries = await collectFileEntries({
|
|
403
404
|
rootDir: path__default.default.resolve(process.cwd(), rootDir, locale),
|
|
@@ -408,7 +409,7 @@ var readLocaleMessages = async ({
|
|
|
408
409
|
const namespaceMessages = await parseFileEntries({
|
|
409
410
|
fileEntries,
|
|
410
411
|
limit,
|
|
411
|
-
extraOptions: {
|
|
412
|
+
extraOptions: { messagesReader, loggerOptions }
|
|
412
413
|
});
|
|
413
414
|
const localeMessages = { [locale]: namespaceMessages };
|
|
414
415
|
return localeMessages;
|
|
@@ -432,8 +433,9 @@ var loadLocalMessages = async ({
|
|
|
432
433
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
433
434
|
loggerOptions = { id: "default" },
|
|
434
435
|
exts,
|
|
435
|
-
|
|
436
|
-
} = {}
|
|
436
|
+
messagesReader
|
|
437
|
+
} = {},
|
|
438
|
+
allowCacheWrite = false
|
|
437
439
|
}) => {
|
|
438
440
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
439
441
|
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
@@ -467,7 +469,7 @@ var loadLocalMessages = async ({
|
|
|
467
469
|
rootDir,
|
|
468
470
|
locale: candidateLocale,
|
|
469
471
|
namespaces,
|
|
470
|
-
extraOptions: { loggerOptions, exts,
|
|
472
|
+
extraOptions: { loggerOptions, exts, messagesReader }
|
|
471
473
|
});
|
|
472
474
|
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
473
475
|
messages = result;
|
|
@@ -480,7 +482,7 @@ var loadLocalMessages = async ({
|
|
|
480
482
|
});
|
|
481
483
|
}
|
|
482
484
|
}
|
|
483
|
-
if (cacheOptions.enabled && key && messages) {
|
|
485
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
484
486
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
485
487
|
}
|
|
486
488
|
const end = perf_hooks.performance.now();
|
|
@@ -519,7 +521,7 @@ var fetchLocaleMessages = async ({
|
|
|
519
521
|
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
520
522
|
}
|
|
521
523
|
const data = await response.json();
|
|
522
|
-
if (!
|
|
524
|
+
if (!isValidMessages(data[locale])) {
|
|
523
525
|
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
524
526
|
}
|
|
525
527
|
return data;
|
|
@@ -564,7 +566,8 @@ var loadRemoteMessages = async ({
|
|
|
564
566
|
extraOptions: {
|
|
565
567
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
566
568
|
loggerOptions = { id: "default" }
|
|
567
|
-
} = {}
|
|
569
|
+
} = {},
|
|
570
|
+
allowCacheWrite
|
|
568
571
|
}) => {
|
|
569
572
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
570
573
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
@@ -608,7 +611,7 @@ var loadRemoteMessages = async ({
|
|
|
608
611
|
});
|
|
609
612
|
}
|
|
610
613
|
}
|
|
611
|
-
if (cacheOptions.enabled && key && messages) {
|
|
614
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
612
615
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
613
616
|
}
|
|
614
617
|
const end = performance.now();
|
|
@@ -625,7 +628,8 @@ var loadMessages = async ({
|
|
|
625
628
|
config,
|
|
626
629
|
locale,
|
|
627
630
|
pathname = "",
|
|
628
|
-
extraOptions: { exts,
|
|
631
|
+
extraOptions: { exts, messagesReader } = {},
|
|
632
|
+
allowCacheWrite = false
|
|
629
633
|
}) => {
|
|
630
634
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
631
635
|
const logger = baseLogger.child({ scope: "load-messages" });
|
|
@@ -661,8 +665,9 @@ var loadMessages = async ({
|
|
|
661
665
|
cacheOptions: config.cache,
|
|
662
666
|
loggerOptions: { id: config.id, ...config.logger },
|
|
663
667
|
exts,
|
|
664
|
-
|
|
665
|
-
}
|
|
668
|
+
messagesReader
|
|
669
|
+
},
|
|
670
|
+
allowCacheWrite
|
|
666
671
|
});
|
|
667
672
|
} else if (type === "remote") {
|
|
668
673
|
loadedMessages = await loadRemoteMessages({
|
|
@@ -160,7 +160,7 @@ interface I18nContext {
|
|
|
160
160
|
*
|
|
161
161
|
* Next.js adapter implementation: uses `next/headers` and `next/cookies`.
|
|
162
162
|
*/
|
|
163
|
-
declare const getI18nContext: <CK extends
|
|
163
|
+
declare const getI18nContext: <CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
|
|
164
164
|
|
|
165
165
|
/** Base properties shared by all translator instances. */
|
|
166
166
|
interface TranslatorBaseProps<M = unknown> {
|
|
@@ -160,7 +160,7 @@ interface I18nContext {
|
|
|
160
160
|
*
|
|
161
161
|
* Next.js adapter implementation: uses `next/headers` and `next/cookies`.
|
|
162
162
|
*/
|
|
163
|
-
declare const getI18nContext: <CK extends
|
|
163
|
+
declare const getI18nContext: <CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
|
|
164
164
|
|
|
165
165
|
/** Base properties shared by all translator instances. */
|
|
166
166
|
interface TranslatorBaseProps<M = unknown> {
|
|
@@ -299,11 +299,11 @@ async function collectFileEntries({
|
|
|
299
299
|
return results;
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
// src/modules/messages/shared/utils/is-
|
|
302
|
+
// src/modules/messages/shared/utils/is-valid-messages.ts
|
|
303
303
|
function isPlainObject(value) {
|
|
304
304
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
305
305
|
}
|
|
306
|
-
function
|
|
306
|
+
function isValidMessages(value) {
|
|
307
307
|
if (!isPlainObject(value)) return false;
|
|
308
308
|
const stack = [value];
|
|
309
309
|
while (stack.length > 0) {
|
|
@@ -319,22 +319,17 @@ function isNamespaceMessages(value) {
|
|
|
319
319
|
}
|
|
320
320
|
return true;
|
|
321
321
|
}
|
|
322
|
-
|
|
323
|
-
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
|
|
324
322
|
async function jsonReader(filePath, readFile = fs.readFile) {
|
|
325
323
|
const raw = await readFile(filePath, "utf8");
|
|
326
324
|
const parsed = JSON.parse(raw);
|
|
327
|
-
if (!isNamespaceMessages(parsed)) {
|
|
328
|
-
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
329
|
-
}
|
|
330
325
|
return parsed;
|
|
331
326
|
}
|
|
332
327
|
|
|
333
328
|
// src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
|
|
334
|
-
function nestObjectFromPath(
|
|
329
|
+
function nestObjectFromPath(path5, value) {
|
|
335
330
|
let obj = value;
|
|
336
|
-
for (let i =
|
|
337
|
-
obj = { [
|
|
331
|
+
for (let i = path5.length - 1; i >= 0; i--) {
|
|
332
|
+
obj = { [path5[i]]: obj };
|
|
338
333
|
}
|
|
339
334
|
return obj;
|
|
340
335
|
}
|
|
@@ -343,7 +338,7 @@ function nestObjectFromPath(path4, value) {
|
|
|
343
338
|
async function parseFileEntries({
|
|
344
339
|
fileEntries,
|
|
345
340
|
limit,
|
|
346
|
-
extraOptions: {
|
|
341
|
+
extraOptions: { messagesReader, loggerOptions } = {}
|
|
347
342
|
}) {
|
|
348
343
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
349
344
|
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
@@ -352,11 +347,17 @@ async function parseFileEntries({
|
|
|
352
347
|
({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
353
348
|
try {
|
|
354
349
|
const segsWithoutNs = segments.slice(1);
|
|
355
|
-
const
|
|
350
|
+
const ext = path.extname(fullPath);
|
|
351
|
+
const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
|
|
352
|
+
if (!isValidMessages(json)) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
"JSON file does not match NamespaceMessages structure"
|
|
355
|
+
);
|
|
356
|
+
}
|
|
356
357
|
const isIndex = basename === "index";
|
|
357
358
|
const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
|
|
358
|
-
const
|
|
359
|
-
parsedFileEntries.push({ namespace,
|
|
359
|
+
const nested = nestObjectFromPath(keyPath, json);
|
|
360
|
+
parsedFileEntries.push({ namespace, messages: nested });
|
|
360
361
|
logger.trace("Parsed file.", { path: fullPath });
|
|
361
362
|
} catch (error) {
|
|
362
363
|
logger.error("Failed to read or parse file.", {
|
|
@@ -368,13 +369,13 @@ async function parseFileEntries({
|
|
|
368
369
|
);
|
|
369
370
|
await Promise.all(tasks);
|
|
370
371
|
const result = {};
|
|
371
|
-
for (const { namespace,
|
|
372
|
+
for (const { namespace, messages } of parsedFileEntries) {
|
|
372
373
|
if (namespace === "index") {
|
|
373
|
-
merge(result,
|
|
374
|
+
merge(result, messages);
|
|
374
375
|
} else {
|
|
375
376
|
result[namespace] = merge(
|
|
376
377
|
result[namespace] ?? {},
|
|
377
|
-
|
|
378
|
+
messages
|
|
378
379
|
);
|
|
379
380
|
}
|
|
380
381
|
}
|
|
@@ -387,7 +388,7 @@ var readLocaleMessages = async ({
|
|
|
387
388
|
rootDir = "messages",
|
|
388
389
|
locale,
|
|
389
390
|
namespaces,
|
|
390
|
-
extraOptions: { exts,
|
|
391
|
+
extraOptions: { exts, messagesReader, loggerOptions } = {}
|
|
391
392
|
}) => {
|
|
392
393
|
const fileEntries = await collectFileEntries({
|
|
393
394
|
rootDir: path.resolve(process.cwd(), rootDir, locale),
|
|
@@ -398,7 +399,7 @@ var readLocaleMessages = async ({
|
|
|
398
399
|
const namespaceMessages = await parseFileEntries({
|
|
399
400
|
fileEntries,
|
|
400
401
|
limit,
|
|
401
|
-
extraOptions: {
|
|
402
|
+
extraOptions: { messagesReader, loggerOptions }
|
|
402
403
|
});
|
|
403
404
|
const localeMessages = { [locale]: namespaceMessages };
|
|
404
405
|
return localeMessages;
|
|
@@ -422,8 +423,9 @@ var loadLocalMessages = async ({
|
|
|
422
423
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
423
424
|
loggerOptions = { id: "default" },
|
|
424
425
|
exts,
|
|
425
|
-
|
|
426
|
-
} = {}
|
|
426
|
+
messagesReader
|
|
427
|
+
} = {},
|
|
428
|
+
allowCacheWrite = false
|
|
427
429
|
}) => {
|
|
428
430
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
429
431
|
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
@@ -457,7 +459,7 @@ var loadLocalMessages = async ({
|
|
|
457
459
|
rootDir,
|
|
458
460
|
locale: candidateLocale,
|
|
459
461
|
namespaces,
|
|
460
|
-
extraOptions: { loggerOptions, exts,
|
|
462
|
+
extraOptions: { loggerOptions, exts, messagesReader }
|
|
461
463
|
});
|
|
462
464
|
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
463
465
|
messages = result;
|
|
@@ -470,7 +472,7 @@ var loadLocalMessages = async ({
|
|
|
470
472
|
});
|
|
471
473
|
}
|
|
472
474
|
}
|
|
473
|
-
if (cacheOptions.enabled && key && messages) {
|
|
475
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
474
476
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
475
477
|
}
|
|
476
478
|
const end = performance$1.now();
|
|
@@ -509,7 +511,7 @@ var fetchLocaleMessages = async ({
|
|
|
509
511
|
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
510
512
|
}
|
|
511
513
|
const data = await response.json();
|
|
512
|
-
if (!
|
|
514
|
+
if (!isValidMessages(data[locale])) {
|
|
513
515
|
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
514
516
|
}
|
|
515
517
|
return data;
|
|
@@ -554,7 +556,8 @@ var loadRemoteMessages = async ({
|
|
|
554
556
|
extraOptions: {
|
|
555
557
|
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
556
558
|
loggerOptions = { id: "default" }
|
|
557
|
-
} = {}
|
|
559
|
+
} = {},
|
|
560
|
+
allowCacheWrite
|
|
558
561
|
}) => {
|
|
559
562
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
560
563
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
@@ -598,7 +601,7 @@ var loadRemoteMessages = async ({
|
|
|
598
601
|
});
|
|
599
602
|
}
|
|
600
603
|
}
|
|
601
|
-
if (cacheOptions.enabled && key && messages) {
|
|
604
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
602
605
|
await pool?.set(key, messages, cacheOptions.ttl);
|
|
603
606
|
}
|
|
604
607
|
const end = performance.now();
|
|
@@ -615,7 +618,8 @@ var loadMessages = async ({
|
|
|
615
618
|
config,
|
|
616
619
|
locale,
|
|
617
620
|
pathname = "",
|
|
618
|
-
extraOptions: { exts,
|
|
621
|
+
extraOptions: { exts, messagesReader } = {},
|
|
622
|
+
allowCacheWrite = false
|
|
619
623
|
}) => {
|
|
620
624
|
const baseLogger = getLogger({ id: config.id, ...config.logger });
|
|
621
625
|
const logger = baseLogger.child({ scope: "load-messages" });
|
|
@@ -651,8 +655,9 @@ var loadMessages = async ({
|
|
|
651
655
|
cacheOptions: config.cache,
|
|
652
656
|
loggerOptions: { id: config.id, ...config.logger },
|
|
653
657
|
exts,
|
|
654
|
-
|
|
655
|
-
}
|
|
658
|
+
messagesReader
|
|
659
|
+
},
|
|
660
|
+
allowCacheWrite
|
|
656
661
|
});
|
|
657
662
|
} else if (type === "remote") {
|
|
658
663
|
loadedMessages = await loadRemoteMessages({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intor",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "A modular and extensible i18n core designed for TypeScript and JavaScript projects. Intor enables custom translation logic with support for both frontend and backend environments, featuring runtime configuration, caching, adapters, and message loaders.",
|
|
5
5
|
"author": "Yiming Liao",
|
|
6
6
|
"license": "MIT",
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"p-limit": "^6.2.0"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
+
"@intor/cli": "../@intor:cli",
|
|
80
81
|
"@types/lodash.merge": "^4.6.9",
|
|
81
82
|
"@types/node": "^24.10.1",
|
|
82
83
|
"@types/react": "^19.1.4",
|
|
@@ -94,6 +95,8 @@
|
|
|
94
95
|
"prettier": "^3.5.3",
|
|
95
96
|
"react": "^19.1.0",
|
|
96
97
|
"react-dom": "^19.1.0",
|
|
98
|
+
"remark": "^15.0.1",
|
|
99
|
+
"remark-gfm": "^4.0.1",
|
|
97
100
|
"tsup": "^8.4.0",
|
|
98
101
|
"typescript": "^5.8.3",
|
|
99
102
|
"typescript-eslint": "^8.32.1",
|