intor 2.2.2 → 2.2.3

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 CHANGED
@@ -137,11 +137,11 @@ async function collectFileEntries({
137
137
  return results;
138
138
  }
139
139
 
140
- // src/modules/messages/shared/utils/is-namespace-messages.ts
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 isNamespaceMessages(value) {
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(path4, value) {
167
+ function nestObjectFromPath(path5, value) {
173
168
  let obj = value;
174
- for (let i = path4.length - 1; i >= 0; i--) {
175
- obj = { [path4[i]]: 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: { messageFileReader, loggerOptions } = {}
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 json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
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 namespaceMessages = nestObjectFromPath(keyPath, json);
197
- parsedFileEntries.push({ namespace, namespaceMessages });
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, namespaceMessages } of parsedFileEntries) {
210
+ for (const { namespace, messages } of parsedFileEntries) {
210
211
  if (namespace === "index") {
211
- merge__default.default(result, namespaceMessages);
212
+ merge__default.default(result, messages);
212
213
  } else {
213
214
  result[namespace] = merge__default.default(
214
215
  result[namespace] ?? {},
215
- namespaceMessages
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, messageFileReader, loggerOptions } = {}
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: { messageFileReader, loggerOptions }
240
+ extraOptions: { messagesReader, loggerOptions }
240
241
  });
241
242
  const localeMessages = { [locale]: namespaceMessages };
242
243
  return localeMessages;
@@ -454,7 +455,7 @@ var loadLocalMessages = async ({
454
455
  cacheOptions = DEFAULT_CACHE_OPTIONS,
455
456
  loggerOptions = { id: "default" },
456
457
  exts,
457
- messageFileReader
458
+ messagesReader
458
459
  } = {}
459
460
  }) => {
460
461
  const baseLogger = getLogger({ ...loggerOptions });
@@ -489,7 +490,7 @@ var loadLocalMessages = async ({
489
490
  rootDir,
490
491
  locale: candidateLocale,
491
492
  namespaces,
492
- extraOptions: { loggerOptions, exts, messageFileReader }
493
+ extraOptions: { loggerOptions, exts, messagesReader }
493
494
  });
494
495
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
495
496
  messages = result;
@@ -541,7 +542,7 @@ var fetchLocaleMessages = async ({
541
542
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
542
543
  }
543
544
  const data = await response.json();
544
- if (!isNamespaceMessages(data[locale])) {
545
+ if (!isValidMessages(data[locale])) {
545
546
  throw new Error("JSON file does not match NamespaceMessages structure");
546
547
  }
547
548
  return data;
@@ -647,7 +648,7 @@ var loadMessages = async ({
647
648
  config,
648
649
  locale,
649
650
  pathname = "",
650
- extraOptions: { exts, messageFileReader } = {}
651
+ extraOptions: { exts, messagesReader } = {}
651
652
  }) => {
652
653
  const baseLogger = getLogger({ id: config.id, ...config.logger });
653
654
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -683,7 +684,7 @@ var loadMessages = async ({
683
684
  cacheOptions: config.cache,
684
685
  loggerOptions: { id: config.id, ...config.logger },
685
686
  exts,
686
- messageFileReader
687
+ messagesReader
687
688
  }
688
689
  });
689
690
  } else if (type === "remote") {
@@ -728,7 +729,7 @@ var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
728
729
  pathname,
729
730
  extraOptions: {
730
731
  exts: loadMessagesOptions.exts,
731
- messageFileReader: loadMessagesOptions.messageFileReader
732
+ messagesReader: loadMessagesOptions.messagesReader
732
733
  }
733
734
  });
734
735
  }
package/dist/index.d.cts CHANGED
@@ -165,8 +165,44 @@ interface IntorResult {
165
165
  messages: LocaleMessages;
166
166
  }
167
167
 
168
- type NamespaceMessages = Record<string, NestedMessage>;
169
- type MessageFileReader = (filePath: string) => Promise<NamespaceMessages>;
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,7 +210,7 @@ type LoadMessagesOptions = {
174
210
  pathname?: string;
175
211
  extraOptions?: {
176
212
  exts?: string[];
177
- messageFileReader?: MessageFileReader;
213
+ messagesReader?: MessagesReader;
178
214
  };
179
215
  };
180
216
  type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMessages<C> | undefined>;
@@ -187,7 +223,7 @@ type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMe
187
223
  * - Apply fallback locales if needed.
188
224
  * - Cache messages if enabled (handled by underlying loader, not this function directly).
189
225
  */
190
- declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messageFileReader }, }: LoadMessagesOptions) => LoadMessagesResult<C>;
226
+ declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messagesReader }, }: LoadMessagesOptions) => LoadMessagesResult<C>;
191
227
 
192
228
  /**
193
229
  * Global messages pool (cross-module + hot-reload safe)
@@ -215,7 +251,7 @@ interface LoadLocalMessagesOptions {
215
251
  id?: string;
216
252
  };
217
253
  exts?: string[];
218
- messageFileReader?: MessageFileReader;
254
+ messagesReader?: MessagesReader;
219
255
  };
220
256
  }
221
257
 
@@ -227,7 +263,7 @@ interface LoadLocalMessagesOptions {
227
263
  * - Cache messages if enabled.
228
264
  * - Limit concurrent file reads for performance.
229
265
  */
230
- declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messageFileReader, }, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
266
+ declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messagesReader, }, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
231
267
 
232
268
  interface LoadRemoteMessagesOptions {
233
269
  pool?: MessagesPool;
@@ -262,7 +298,7 @@ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, lo
262
298
  */
263
299
  declare const intor: (config: IntorResolvedConfig, i18nContext: GetI18nContext | Partial<I18nContext>, loadMessagesOptions?: {
264
300
  exts?: string[];
265
- messageFileReader?: MessageFileReader;
301
+ messagesReader?: MessagesReader;
266
302
  }) => Promise<IntorResult>;
267
303
 
268
304
  /** Base properties shared by all translator instances. */
@@ -416,4 +452,4 @@ declare global {
416
452
  */
417
453
  declare function clearLoggerPool(): void;
418
454
 
419
- export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type MessageFileReader, type NamespaceMessages, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
455
+ 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
- type NamespaceMessages = Record<string, NestedMessage>;
169
- type MessageFileReader = (filePath: string) => Promise<NamespaceMessages>;
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,7 +210,7 @@ type LoadMessagesOptions = {
174
210
  pathname?: string;
175
211
  extraOptions?: {
176
212
  exts?: string[];
177
- messageFileReader?: MessageFileReader;
213
+ messagesReader?: MessagesReader;
178
214
  };
179
215
  };
180
216
  type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMessages<C> | undefined>;
@@ -187,7 +223,7 @@ type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMe
187
223
  * - Apply fallback locales if needed.
188
224
  * - Cache messages if enabled (handled by underlying loader, not this function directly).
189
225
  */
190
- declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messageFileReader }, }: LoadMessagesOptions) => LoadMessagesResult<C>;
226
+ declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messagesReader }, }: LoadMessagesOptions) => LoadMessagesResult<C>;
191
227
 
192
228
  /**
193
229
  * Global messages pool (cross-module + hot-reload safe)
@@ -215,7 +251,7 @@ interface LoadLocalMessagesOptions {
215
251
  id?: string;
216
252
  };
217
253
  exts?: string[];
218
- messageFileReader?: MessageFileReader;
254
+ messagesReader?: MessagesReader;
219
255
  };
220
256
  }
221
257
 
@@ -227,7 +263,7 @@ interface LoadLocalMessagesOptions {
227
263
  * - Cache messages if enabled.
228
264
  * - Limit concurrent file reads for performance.
229
265
  */
230
- declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messageFileReader, }, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
266
+ declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messagesReader, }, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
231
267
 
232
268
  interface LoadRemoteMessagesOptions {
233
269
  pool?: MessagesPool;
@@ -262,7 +298,7 @@ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, lo
262
298
  */
263
299
  declare const intor: (config: IntorResolvedConfig, i18nContext: GetI18nContext | Partial<I18nContext>, loadMessagesOptions?: {
264
300
  exts?: string[];
265
- messageFileReader?: MessageFileReader;
301
+ messagesReader?: MessagesReader;
266
302
  }) => Promise<IntorResult>;
267
303
 
268
304
  /** Base properties shared by all translator instances. */
@@ -416,4 +452,4 @@ declare global {
416
452
  */
417
453
  declare function clearLoggerPool(): void;
418
454
 
419
- export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type MessageFileReader, type NamespaceMessages, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
455
+ 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-namespace-messages.ts
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 isNamespaceMessages(value) {
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(path4, value) {
158
+ function nestObjectFromPath(path5, value) {
164
159
  let obj = value;
165
- for (let i = path4.length - 1; i >= 0; i--) {
166
- obj = { [path4[i]]: 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: { messageFileReader, loggerOptions } = {}
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 json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
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 namespaceMessages = nestObjectFromPath(keyPath, json);
188
- parsedFileEntries.push({ namespace, namespaceMessages });
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, namespaceMessages } of parsedFileEntries) {
201
+ for (const { namespace, messages } of parsedFileEntries) {
201
202
  if (namespace === "index") {
202
- merge(result, namespaceMessages);
203
+ merge(result, messages);
203
204
  } else {
204
205
  result[namespace] = merge(
205
206
  result[namespace] ?? {},
206
- namespaceMessages
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, messageFileReader, loggerOptions } = {}
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: { messageFileReader, loggerOptions }
231
+ extraOptions: { messagesReader, loggerOptions }
231
232
  });
232
233
  const localeMessages = { [locale]: namespaceMessages };
233
234
  return localeMessages;
@@ -445,7 +446,7 @@ var loadLocalMessages = async ({
445
446
  cacheOptions = DEFAULT_CACHE_OPTIONS,
446
447
  loggerOptions = { id: "default" },
447
448
  exts,
448
- messageFileReader
449
+ messagesReader
449
450
  } = {}
450
451
  }) => {
451
452
  const baseLogger = getLogger({ ...loggerOptions });
@@ -480,7 +481,7 @@ var loadLocalMessages = async ({
480
481
  rootDir,
481
482
  locale: candidateLocale,
482
483
  namespaces,
483
- extraOptions: { loggerOptions, exts, messageFileReader }
484
+ extraOptions: { loggerOptions, exts, messagesReader }
484
485
  });
485
486
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
486
487
  messages = result;
@@ -532,7 +533,7 @@ var fetchLocaleMessages = async ({
532
533
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
533
534
  }
534
535
  const data = await response.json();
535
- if (!isNamespaceMessages(data[locale])) {
536
+ if (!isValidMessages(data[locale])) {
536
537
  throw new Error("JSON file does not match NamespaceMessages structure");
537
538
  }
538
539
  return data;
@@ -638,7 +639,7 @@ var loadMessages = async ({
638
639
  config,
639
640
  locale,
640
641
  pathname = "",
641
- extraOptions: { exts, messageFileReader } = {}
642
+ extraOptions: { exts, messagesReader } = {}
642
643
  }) => {
643
644
  const baseLogger = getLogger({ id: config.id, ...config.logger });
644
645
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -674,7 +675,7 @@ var loadMessages = async ({
674
675
  cacheOptions: config.cache,
675
676
  loggerOptions: { id: config.id, ...config.logger },
676
677
  exts,
677
- messageFileReader
678
+ messagesReader
678
679
  }
679
680
  });
680
681
  } else if (type === "remote") {
@@ -719,7 +720,7 @@ var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
719
720
  pathname,
720
721
  extraOptions: {
721
722
  exts: loadMessagesOptions.exts,
722
- messageFileReader: loadMessagesOptions.messageFileReader
723
+ messagesReader: loadMessagesOptions.messagesReader
723
724
  }
724
725
  });
725
726
  }
@@ -96,11 +96,11 @@ function getLogger({
96
96
  return logger;
97
97
  }
98
98
 
99
- // src/modules/messages/shared/utils/is-namespace-messages.ts
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 isNamespaceMessages(value) {
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 (!isNamespaceMessages(data[locale])) {
343
+ if (!isValidMessages(data[locale])) {
344
344
  throw new Error("JSON file does not match NamespaceMessages structure");
345
345
  }
346
346
  return data;
@@ -69,11 +69,11 @@ function getLogger({
69
69
  return logger;
70
70
  }
71
71
 
72
- // src/modules/messages/shared/utils/is-namespace-messages.ts
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 isNamespaceMessages(value) {
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 (!isNamespaceMessages(data[locale])) {
316
+ if (!isValidMessages(data[locale])) {
317
317
  throw new Error("JSON file does not match NamespaceMessages structure");
318
318
  }
319
319
  return data;
@@ -309,11 +309,11 @@ async function collectFileEntries({
309
309
  return results;
310
310
  }
311
311
 
312
- // src/modules/messages/shared/utils/is-namespace-messages.ts
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 isNamespaceMessages(value) {
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(path4, value) {
339
+ function nestObjectFromPath(path5, value) {
345
340
  let obj = value;
346
- for (let i = path4.length - 1; i >= 0; i--) {
347
- obj = { [path4[i]]: 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: { messageFileReader, loggerOptions } = {}
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 json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
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 namespaceMessages = nestObjectFromPath(keyPath, json);
369
- parsedFileEntries.push({ namespace, namespaceMessages });
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, namespaceMessages } of parsedFileEntries) {
382
+ for (const { namespace, messages } of parsedFileEntries) {
382
383
  if (namespace === "index") {
383
- merge__default.default(result, namespaceMessages);
384
+ merge__default.default(result, messages);
384
385
  } else {
385
386
  result[namespace] = merge__default.default(
386
387
  result[namespace] ?? {},
387
- namespaceMessages
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, messageFileReader, loggerOptions } = {}
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: { messageFileReader, loggerOptions }
412
+ extraOptions: { messagesReader, loggerOptions }
412
413
  });
413
414
  const localeMessages = { [locale]: namespaceMessages };
414
415
  return localeMessages;
@@ -432,7 +433,7 @@ var loadLocalMessages = async ({
432
433
  cacheOptions = DEFAULT_CACHE_OPTIONS,
433
434
  loggerOptions = { id: "default" },
434
435
  exts,
435
- messageFileReader
436
+ messagesReader
436
437
  } = {}
437
438
  }) => {
438
439
  const baseLogger = getLogger({ ...loggerOptions });
@@ -467,7 +468,7 @@ var loadLocalMessages = async ({
467
468
  rootDir,
468
469
  locale: candidateLocale,
469
470
  namespaces,
470
- extraOptions: { loggerOptions, exts, messageFileReader }
471
+ extraOptions: { loggerOptions, exts, messagesReader }
471
472
  });
472
473
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
473
474
  messages = result;
@@ -519,7 +520,7 @@ var fetchLocaleMessages = async ({
519
520
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
520
521
  }
521
522
  const data = await response.json();
522
- if (!isNamespaceMessages(data[locale])) {
523
+ if (!isValidMessages(data[locale])) {
523
524
  throw new Error("JSON file does not match NamespaceMessages structure");
524
525
  }
525
526
  return data;
@@ -625,7 +626,7 @@ var loadMessages = async ({
625
626
  config,
626
627
  locale,
627
628
  pathname = "",
628
- extraOptions: { exts, messageFileReader } = {}
629
+ extraOptions: { exts, messagesReader } = {}
629
630
  }) => {
630
631
  const baseLogger = getLogger({ id: config.id, ...config.logger });
631
632
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -661,7 +662,7 @@ var loadMessages = async ({
661
662
  cacheOptions: config.cache,
662
663
  loggerOptions: { id: config.id, ...config.logger },
663
664
  exts,
664
- messageFileReader
665
+ messagesReader
665
666
  }
666
667
  });
667
668
  } else if (type === "remote") {
@@ -299,11 +299,11 @@ async function collectFileEntries({
299
299
  return results;
300
300
  }
301
301
 
302
- // src/modules/messages/shared/utils/is-namespace-messages.ts
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 isNamespaceMessages(value) {
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(path4, value) {
329
+ function nestObjectFromPath(path5, value) {
335
330
  let obj = value;
336
- for (let i = path4.length - 1; i >= 0; i--) {
337
- obj = { [path4[i]]: 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: { messageFileReader, loggerOptions } = {}
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 json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
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 namespaceMessages = nestObjectFromPath(keyPath, json);
359
- parsedFileEntries.push({ namespace, namespaceMessages });
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, namespaceMessages } of parsedFileEntries) {
372
+ for (const { namespace, messages } of parsedFileEntries) {
372
373
  if (namespace === "index") {
373
- merge(result, namespaceMessages);
374
+ merge(result, messages);
374
375
  } else {
375
376
  result[namespace] = merge(
376
377
  result[namespace] ?? {},
377
- namespaceMessages
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, messageFileReader, loggerOptions } = {}
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: { messageFileReader, loggerOptions }
402
+ extraOptions: { messagesReader, loggerOptions }
402
403
  });
403
404
  const localeMessages = { [locale]: namespaceMessages };
404
405
  return localeMessages;
@@ -422,7 +423,7 @@ var loadLocalMessages = async ({
422
423
  cacheOptions = DEFAULT_CACHE_OPTIONS,
423
424
  loggerOptions = { id: "default" },
424
425
  exts,
425
- messageFileReader
426
+ messagesReader
426
427
  } = {}
427
428
  }) => {
428
429
  const baseLogger = getLogger({ ...loggerOptions });
@@ -457,7 +458,7 @@ var loadLocalMessages = async ({
457
458
  rootDir,
458
459
  locale: candidateLocale,
459
460
  namespaces,
460
- extraOptions: { loggerOptions, exts, messageFileReader }
461
+ extraOptions: { loggerOptions, exts, messagesReader }
461
462
  });
462
463
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
463
464
  messages = result;
@@ -509,7 +510,7 @@ var fetchLocaleMessages = async ({
509
510
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
510
511
  }
511
512
  const data = await response.json();
512
- if (!isNamespaceMessages(data[locale])) {
513
+ if (!isValidMessages(data[locale])) {
513
514
  throw new Error("JSON file does not match NamespaceMessages structure");
514
515
  }
515
516
  return data;
@@ -615,7 +616,7 @@ var loadMessages = async ({
615
616
  config,
616
617
  locale,
617
618
  pathname = "",
618
- extraOptions: { exts, messageFileReader } = {}
619
+ extraOptions: { exts, messagesReader } = {}
619
620
  }) => {
620
621
  const baseLogger = getLogger({ id: config.id, ...config.logger });
621
622
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -651,7 +652,7 @@ var loadMessages = async ({
651
652
  cacheOptions: config.cache,
652
653
  loggerOptions: { id: config.id, ...config.logger },
653
654
  exts,
654
- messageFileReader
655
+ messagesReader
655
656
  }
656
657
  });
657
658
  } else if (type === "remote") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intor",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
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",
@@ -94,6 +94,8 @@
94
94
  "prettier": "^3.5.3",
95
95
  "react": "^19.1.0",
96
96
  "react-dom": "^19.1.0",
97
+ "remark": "^15.0.1",
98
+ "remark-gfm": "^4.0.1",
97
99
  "tsup": "^8.4.0",
98
100
  "typescript": "^5.8.3",
99
101
  "typescript-eslint": "^8.32.1",