intor 2.3.13 → 2.3.15

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.
Files changed (150) hide show
  1. package/dist/core/export/index.js +1 -2
  2. package/dist/core/src/config/define-intor-config.js +0 -3
  3. package/dist/core/src/config/resolvers/resolve-cookie-options.js +0 -1
  4. package/dist/core/src/config/resolvers/resolve-fallback-locales.js +1 -2
  5. package/dist/core/src/config/resolvers/resolve-routing-options.js +0 -2
  6. package/dist/core/src/config/validators/validate-default-locale.js +0 -1
  7. package/dist/core/src/config/validators/validate-id.js +0 -1
  8. package/dist/core/src/config/validators/validate-supported-locales.js +0 -1
  9. package/dist/core/src/core/messages/global-messages-pool.js +11 -17
  10. package/dist/core/src/core/messages/load-remote-messages/load-remote-messages.js +1 -40
  11. package/dist/core/src/core/messages/utils/is-valid-messages.js +23 -15
  12. package/dist/core/src/routing/pathname/get-unprefixed-pathname.js +0 -1
  13. package/dist/core/src/routing/pathname/locale-prefix-pathname.js +0 -1
  14. package/dist/core/src/routing/pathname/standardize-pathname.js +0 -1
  15. package/dist/core/src/server/helpers/get-translator.js +3 -2
  16. package/dist/core/src/server/helpers/local-messages-from-url.js +1 -1
  17. package/dist/core/src/server/intor/intor.js +0 -1
  18. package/dist/core/src/server/messages/load-local-messages/load-local-messages.js +11 -12
  19. package/dist/core/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +6 -6
  20. package/dist/core/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +14 -6
  21. package/dist/core/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +3 -3
  22. package/dist/core/src/server/messages/load-messages.js +8 -12
  23. package/dist/core/src/server/runtime/create-intor-runtime.js +12 -8
  24. package/dist/core/src/server/translator/create-translator.js +1 -0
  25. package/dist/express/src/adapters/express/helpers/get-translator.js +3 -4
  26. package/dist/express/src/adapters/express/middleware/create-intor.js +5 -5
  27. package/dist/express/src/core/messages/global-messages-pool.js +9 -3
  28. package/dist/express/src/core/messages/load-remote-messages/load-remote-messages.js +1 -40
  29. package/dist/express/src/core/messages/utils/is-valid-messages.js +23 -15
  30. package/dist/express/src/routing/inbound/resolve-inbound.js +5 -6
  31. package/dist/express/src/routing/inbound/resolve-locale/resolve-locale.js +13 -6
  32. package/dist/express/src/routing/locale/get-locale-from-accept-language.js +7 -17
  33. package/dist/express/src/routing/locale/get-locale-from-host.js +13 -15
  34. package/dist/express/src/routing/locale/get-locale-from-pathname.js +4 -14
  35. package/dist/express/src/routing/locale/get-locale-from-query.js +10 -17
  36. package/dist/express/src/routing/pathname/get-unprefixed-pathname.js +0 -1
  37. package/dist/express/src/routing/pathname/locale-prefix-pathname.js +0 -1
  38. package/dist/express/src/routing/pathname/standardize-pathname.js +0 -1
  39. package/dist/express/src/server/helpers/get-translator.js +3 -2
  40. package/dist/express/src/server/messages/load-local-messages/load-local-messages.js +11 -12
  41. package/dist/express/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +6 -6
  42. package/dist/express/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +14 -6
  43. package/dist/express/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +3 -3
  44. package/dist/express/src/server/messages/load-messages.js +8 -12
  45. package/dist/express/src/server/runtime/create-intor-runtime.js +12 -8
  46. package/dist/express/src/server/translator/create-translator.js +1 -0
  47. package/dist/next/src/adapters/next/navigation/redirect.js +0 -1
  48. package/dist/next/src/adapters/next/navigation/use-pathname.js +0 -1
  49. package/dist/next/src/adapters/next/proxy/intor-proxy.js +1 -2
  50. package/dist/next/src/adapters/next/server/get-locale.js +0 -1
  51. package/dist/next/src/adapters/next/server/get-pathname.js +0 -1
  52. package/dist/next/src/adapters/next/server/get-translator.js +3 -4
  53. package/dist/next/src/adapters/next/server/intor.js +2 -3
  54. package/dist/next/src/core/messages/global-messages-pool.js +9 -3
  55. package/dist/next/src/core/messages/load-remote-messages/load-remote-messages.js +1 -40
  56. package/dist/next/src/core/messages/utils/is-valid-messages.js +23 -15
  57. package/dist/next/src/policies/shoud-full-reload.js +0 -1
  58. package/dist/next/src/routing/inbound/resolve-inbound.js +5 -6
  59. package/dist/next/src/routing/inbound/resolve-locale/resolve-locale.js +13 -6
  60. package/dist/next/src/routing/locale/get-locale-from-accept-language.js +7 -17
  61. package/dist/next/src/routing/locale/get-locale-from-host.js +13 -15
  62. package/dist/next/src/routing/locale/get-locale-from-pathname.js +4 -14
  63. package/dist/next/src/routing/locale/get-locale-from-query.js +10 -17
  64. package/dist/next/src/routing/pathname/get-unprefixed-pathname.js +0 -1
  65. package/dist/next/src/routing/pathname/locale-prefix-pathname.js +0 -1
  66. package/dist/next/src/routing/pathname/standardize-pathname.js +0 -1
  67. package/dist/next/src/server/helpers/get-translator.js +3 -2
  68. package/dist/next/src/server/intor/intor.js +0 -1
  69. package/dist/next/src/server/messages/load-local-messages/load-local-messages.js +11 -12
  70. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +6 -6
  71. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +14 -6
  72. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +3 -3
  73. package/dist/next/src/server/messages/load-messages.js +8 -12
  74. package/dist/next/src/server/runtime/create-intor-runtime.js +12 -8
  75. package/dist/next/src/server/translator/create-translator.js +1 -0
  76. package/dist/react/src/client/react/helpers/use-runtime-state.js +0 -1
  77. package/dist/react/src/client/react/navigation/use-execute-navigation.js +0 -1
  78. package/dist/react/src/client/react/navigation/use-resolve-navigation.js +0 -1
  79. package/dist/react/src/client/react/provider/effects/use-locale-effects.js +0 -1
  80. package/dist/react/src/client/react/translator/use-translator.js +1 -0
  81. package/dist/react/src/client/shared/helpers/get-client-locale.js +0 -1
  82. package/dist/react/src/client/shared/messages/create-refetch-messages.js +0 -3
  83. package/dist/react/src/core/messages/load-remote-messages/load-remote-messages.js +1 -40
  84. package/dist/react/src/core/messages/utils/is-valid-messages.js +23 -15
  85. package/dist/react/src/policies/shoud-full-reload.js +0 -1
  86. package/dist/react/src/routing/pathname/get-unprefixed-pathname.js +0 -1
  87. package/dist/react/src/routing/pathname/locale-prefix-pathname.js +0 -1
  88. package/dist/react/src/routing/pathname/standardize-pathname.js +0 -1
  89. package/dist/svelte/src/client/shared/helpers/get-client-locale.js +0 -1
  90. package/dist/svelte/src/client/shared/messages/create-refetch-messages.js +0 -3
  91. package/dist/svelte/src/client/svelte/helpers/create-runtime-state.js +0 -1
  92. package/dist/svelte/src/client/svelte/runtime/create-intor-api.js +4 -0
  93. package/dist/svelte/src/client/svelte/runtime/create-intor.js +2 -1
  94. package/dist/svelte/src/client/svelte/runtime/effects/locale-effects.js +0 -1
  95. package/dist/svelte/src/core/messages/load-remote-messages/load-remote-messages.js +1 -40
  96. package/dist/svelte/src/core/messages/utils/is-valid-messages.js +23 -15
  97. package/dist/types/export/index.d.ts +2 -2
  98. package/dist/types/src/adapters/express/global.d.ts +2 -1
  99. package/dist/types/src/adapters/express/helpers/get-translator.d.ts +1 -1
  100. package/dist/types/src/adapters/next/server/get-translator.d.ts +1 -1
  101. package/dist/types/src/adapters/next/server/intor.d.ts +2 -2
  102. package/dist/types/src/client/svelte/runtime/create-intor-api.d.ts +2 -0
  103. package/dist/types/src/client/svelte/runtime/types.d.ts +3 -1
  104. package/dist/types/src/client/vue/provider/resolver/resolve-runtime.d.ts +1 -1
  105. package/dist/types/src/config/constants/index.d.ts +0 -1
  106. package/dist/types/src/config/index.d.ts +2 -2
  107. package/dist/types/src/config/types/index.d.ts +0 -1
  108. package/dist/types/src/config/types/intor-config.d.ts +0 -4
  109. package/dist/types/src/core/index.d.ts +1 -1
  110. package/dist/types/src/core/messages/global-messages-pool.d.ts +15 -12
  111. package/dist/types/src/core/messages/index.d.ts +2 -2
  112. package/dist/types/src/core/messages/load-remote-messages/load-remote-messages.d.ts +1 -2
  113. package/dist/types/src/core/messages/load-remote-messages/types.d.ts +1 -6
  114. package/dist/types/src/core/messages/types.d.ts +14 -36
  115. package/dist/types/src/core/messages/utils/is-valid-messages.d.ts +5 -10
  116. package/dist/types/src/core/types/translator-instance.d.ts +3 -1
  117. package/dist/types/src/routing/inbound/resolve-locale/resolve-locale.d.ts +4 -3
  118. package/dist/types/src/routing/locale/get-locale-from-accept-language.d.ts +5 -7
  119. package/dist/types/src/routing/locale/get-locale-from-host.d.ts +12 -8
  120. package/dist/types/src/routing/locale/get-locale-from-pathname.d.ts +4 -13
  121. package/dist/types/src/routing/locale/get-locale-from-query.d.ts +9 -10
  122. package/dist/types/src/server/helpers/get-translator.d.ts +2 -3
  123. package/dist/types/src/server/messages/load-local-messages/load-local-messages.d.ts +2 -2
  124. package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.d.ts +2 -2
  125. package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/types.d.ts +4 -3
  126. package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.d.ts +2 -2
  127. package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.d.ts +2 -2
  128. package/dist/types/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.d.ts +1 -1
  129. package/dist/types/src/server/messages/load-local-messages/read-locale-messages/types.d.ts +2 -2
  130. package/dist/types/src/server/messages/load-local-messages/types.d.ts +2 -2
  131. package/dist/types/src/server/messages/load-messages.d.ts +1 -1
  132. package/dist/types/src/server/messages/types.d.ts +2 -2
  133. package/dist/types/src/server/runtime/types.d.ts +2 -2
  134. package/dist/types/src/server/translator/create-translator.d.ts +1 -0
  135. package/dist/vue/src/client/shared/helpers/get-client-locale.js +0 -1
  136. package/dist/vue/src/client/shared/messages/create-refetch-messages.js +0 -3
  137. package/dist/vue/src/client/vue/helpers/use-runtime-state.js +0 -1
  138. package/dist/vue/src/client/vue/provider/effects/use-locale-effects.js +0 -1
  139. package/dist/vue/src/client/vue/translator/use-translator.js +3 -1
  140. package/dist/vue/src/core/messages/load-remote-messages/load-remote-messages.js +1 -40
  141. package/dist/vue/src/core/messages/utils/is-valid-messages.js +23 -15
  142. package/package.json +3 -4
  143. package/dist/core/src/config/constants/cache.js +0 -7
  144. package/dist/core/src/config/resolvers/resolve-cache-options.js +0 -11
  145. package/dist/react/src/core/utils/normalizers/normalize-cache-key.js +0 -45
  146. package/dist/svelte/src/core/utils/normalizers/normalize-cache-key.js +0 -45
  147. package/dist/types/src/config/constants/cache.d.ts +0 -2
  148. package/dist/types/src/config/resolvers/resolve-cache-options.d.ts +0 -2
  149. package/dist/types/src/config/types/cache.d.ts +0 -7
  150. package/dist/vue/src/core/utils/normalizers/normalize-cache-key.js +0 -45
@@ -5,10 +5,9 @@ export { resolveLoaderOptions } from '../src/core/utils/resolve-loader-options.j
5
5
  import 'logry';
6
6
  export { clearLoggerPool } from '../src/core/logger/global-logger-pool.js';
7
7
  export { isValidMessages } from '../src/core/messages/utils/is-valid-messages.js';
8
- export { clearMessagesPool, setGlobalMessagesPool } from '../src/core/messages/global-messages-pool.js';
8
+ export { clearMessagesPool } from '../src/core/messages/global-messages-pool.js';
9
9
  export { mergeMessages } from '../src/core/messages/merge-messages.js';
10
10
  export { defineIntorConfig } from '../src/config/define-intor-config.js';
11
11
  import '../src/config/constants/cookie.js';
12
- import '../src/config/constants/cache.js';
13
12
  export { localizePathname } from '../src/routing/pathname/localize-pathname.js';
14
13
  export { Translator } from 'intor-translator';
@@ -1,4 +1,3 @@
1
- import { resolveCacheOptions } from './resolvers/resolve-cache-options.js';
2
1
  import { resolveCookieOptions } from './resolvers/resolve-cookie-options.js';
3
2
  import { resolveFallbackLocales } from './resolvers/resolve-fallback-locales.js';
4
3
  import { resolveRoutingOptions } from './resolvers/resolve-routing-options.js';
@@ -30,7 +29,6 @@ const defineIntorConfig = (config) => {
30
29
  const fallbackLocales = resolveFallbackLocales(config, id, supportedSet);
31
30
  const cookie = resolveCookieOptions(config.cookie);
32
31
  const routing = resolveRoutingOptions(config.routing);
33
- const cache = resolveCacheOptions(config.cache);
34
32
  return {
35
33
  id,
36
34
  messages: config.messages,
@@ -42,7 +40,6 @@ const defineIntorConfig = (config) => {
42
40
  cookie,
43
41
  routing,
44
42
  logger: { id, ...config.logger },
45
- cache,
46
43
  client: config.client,
47
44
  server: config.server,
48
45
  };
@@ -1,5 +1,4 @@
1
1
  import { DEFAULT_COOKIE_OPTIONS } from '../constants/cookie.js';
2
- import '../constants/cache.js';
3
2
 
4
3
  const resolveCookieOptions = (cookie = {}) => {
5
4
  return {
@@ -1,6 +1,5 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { getLogger } from '../../core/logger/get-logger.js';
3
- import 'keyv';
4
3
 
5
4
  /**
6
5
  * Resolves fallbackLocales into a runtime-safe mapping.
@@ -13,7 +12,7 @@ const resolveFallbackLocales = (config, id, supportedSet) => {
13
12
  if (!fallbackLocales || typeof fallbackLocales !== "object") {
14
13
  return {};
15
14
  }
16
- const logger = getLogger({ id }).child({
15
+ const logger = getLogger({ ...config.logger, id }).child({
17
16
  scope: "resolve-fallback-locales",
18
17
  });
19
18
  const resolvedMap = {};
@@ -1,10 +1,8 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { deepMerge } from '../../core/utils/deep-merge.js';
3
3
  import 'logry';
4
- import 'keyv';
5
4
  import { DEFAULT_ROUTING_OPTIONS } from '../constants/routing.js';
6
5
  import '../constants/cookie.js';
7
- import '../constants/cache.js';
8
6
 
9
7
  /**
10
8
  * Resolves routing configuration into a fully normalized form.
@@ -1,6 +1,5 @@
1
1
  import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
2
2
  import 'logry';
3
- import 'keyv';
4
3
 
5
4
  /**
6
5
  * Validates that the configured defaultLocale is supported.
@@ -1,6 +1,5 @@
1
1
  import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
2
2
  import 'logry';
3
- import 'keyv';
4
3
 
5
4
  /**
6
5
  * Validates that the given id is a non-empty string.
@@ -1,6 +1,5 @@
1
1
  import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
2
2
  import 'logry';
3
- import 'keyv';
4
3
 
5
4
  /**
6
5
  * Validates that supportedLocales is provided and non-empty.
@@ -1,27 +1,21 @@
1
- import Keyv from 'keyv';
2
-
1
+ /**
2
+ * Get the global messages pool.
3
+ *
4
+ * Lazily initialized to ensure:
5
+ * - Cross-module sharing
6
+ * - Dev / HMR safety
7
+ */
3
8
  function getGlobalMessagesPool() {
4
9
  if (!globalThis.__INTOR_MESSAGES_POOL__) {
5
- globalThis.__INTOR_MESSAGES_POOL__ = new Keyv();
10
+ const pool = new Map();
11
+ globalThis.__INTOR_MESSAGES_POOL__ = pool;
6
12
  }
7
13
  return globalThis.__INTOR_MESSAGES_POOL__;
8
14
  }
9
- /**
10
- * Replace the global messages pool.
11
- *
12
- * - Intended for advanced usage (e.g. Redis, custom cache backends).
13
- * - Must be called during application bootstrap.
14
- */
15
- function setGlobalMessagesPool(pool) {
16
- globalThis.__INTOR_MESSAGES_POOL__ = pool;
17
- }
18
- /**
19
- * Optional: clear all cache
20
- * - Useful in tests or dynamic reloads.
21
- */
15
+ /** Clear all cached messages. */
22
16
  function clearMessagesPool() {
23
17
  const pool = getGlobalMessagesPool();
24
18
  pool.clear();
25
19
  }
26
20
 
27
- export { clearMessagesPool, getGlobalMessagesPool, setGlobalMessagesPool };
21
+ export { clearMessagesPool, getGlobalMessagesPool };
@@ -1,5 +1,4 @@
1
1
  import { getLogger } from '../../logger/get-logger.js';
2
- import { normalizeCacheKey } from '../../utils/normalizers/normalize-cache-key.js';
3
2
  import { fetchLocaleMessages } from './fetch-locale-messages/fetch-locale-messages.js';
4
3
 
5
4
  /**
@@ -9,12 +8,11 @@ import { fetchLocaleMessages } from './fetch-locale-messages/fetch-locale-messag
9
8
  * It coordinates:
10
9
  *
11
10
  * - Locale resolution with fallbacks
12
- * - Cache read / write behavior
13
11
  * - Respecting abort signals across the entire async flow
14
12
  *
15
13
  * Network fetching and data validation are delegated to lower-level utilities.
16
14
  */
17
- const loadRemoteMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir, url, headers, signal, pool, cacheOptions, allowCacheWrite = false, loggerOptions, }) => {
15
+ const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, rootDir, url, headers, signal, loggerOptions, }) => {
18
16
  const baseLogger = getLogger(loggerOptions);
19
17
  const logger = baseLogger.child({ scope: "load-remote-messages" });
20
18
  // Abort early if the request has already been cancelled
@@ -25,31 +23,6 @@ const loadRemoteMessages = async ({ id, locale, fallbackLocales, namespaces, roo
25
23
  const start = performance.now();
26
24
  logger.debug("Loading remote messages.", { url });
27
25
  // ---------------------------------------------------------------------------
28
- // Cache key resolution
29
- // ---------------------------------------------------------------------------
30
- const cacheKey = normalizeCacheKey([
31
- id,
32
- "loaderType:remote",
33
- rootDir,
34
- locale,
35
- (fallbackLocales || []).toSorted().join(","),
36
- (namespaces || []).toSorted().join(","),
37
- ]);
38
- // ---------------------------------------------------------------------------
39
- // Cache read
40
- // ---------------------------------------------------------------------------
41
- if (cacheOptions.enabled && cacheKey) {
42
- const cached = await pool?.get(cacheKey);
43
- if (signal?.aborted) {
44
- logger.debug("Remote message loading aborted after cache read.");
45
- return;
46
- }
47
- if (cached) {
48
- logger.debug("Messages cache hit.", { key: cacheKey });
49
- return cached;
50
- }
51
- }
52
- // ---------------------------------------------------------------------------
53
26
  // Resolve locale messages with ordered fallback strategy
54
27
  // ---------------------------------------------------------------------------
55
28
  const candidateLocales = [locale, ...(fallbackLocales || [])];
@@ -93,18 +66,6 @@ const loadRemoteMessages = async ({ id, locale, fallbackLocales, namespaces, roo
93
66
  });
94
67
  }
95
68
  }
96
- // ---------------------------------------------------------------------------
97
- // Cache write (explicitly permitted)
98
- // ---------------------------------------------------------------------------
99
- if (cacheOptions.enabled && allowCacheWrite) {
100
- if (signal?.aborted) {
101
- logger.debug("Remote message loading aborted before cache write.");
102
- return;
103
- }
104
- if (cacheKey && messages) {
105
- await pool?.set(cacheKey, messages, cacheOptions.ttl);
106
- }
107
- }
108
69
  // Final success log with resolved locale and timing
109
70
  if (messages) {
110
71
  logger.trace("Finished loading remote messages.", {
@@ -3,15 +3,10 @@ function isPlainObject(value) {
3
3
  return typeof value === "object" && value !== null && !Array.isArray(value);
4
4
  }
5
5
  /**
6
- * Check if a value is a valid **Messages** object.
6
+ * Check if a value is a valid MessageObject.
7
7
  *
8
- * - Uses an iterative approach to avoid stack overflow with deeply nested objects.
9
- *
10
- * @example
11
- * ```ts
12
- * isValidMessages({ en: { hello: "Hello" } }) // true
13
- * isValidMessages({ en: { count: 5 } }) // false
14
- * ```
8
+ * - Supports all MessageValue variants (primitive, array, object).
9
+ * - Uses an iterative approach to avoid stack overflow.
15
10
  */
16
11
  function isValidMessages(value) {
17
12
  if (!isPlainObject(value))
@@ -19,16 +14,29 @@ function isValidMessages(value) {
19
14
  const stack = [value];
20
15
  while (stack.length > 0) {
21
16
  const current = stack.pop();
22
- for (const v of Object.values(current)) {
23
- if (typeof v === "string")
24
- continue;
25
- if (isPlainObject(v)) {
26
- stack.push(v);
17
+ // primitives are always valid
18
+ if (current === null ||
19
+ typeof current === "string" ||
20
+ typeof current === "number" ||
21
+ typeof current === "boolean") {
22
+ continue;
23
+ }
24
+ // array → validate each item
25
+ if (Array.isArray(current)) {
26
+ for (const item of current) {
27
+ stack.push(item);
27
28
  }
28
- else {
29
- return false;
29
+ continue;
30
+ }
31
+ // object → validate each value
32
+ if (isPlainObject(current)) {
33
+ for (const v of Object.values(current)) {
34
+ stack.push(v);
30
35
  }
36
+ continue;
31
37
  }
38
+ // everything else is invalid
39
+ return false;
32
40
  }
33
41
  return true;
34
42
  }
@@ -1,7 +1,6 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
3
3
  import 'logry';
4
- import 'keyv';
5
4
 
6
5
  /**
7
6
  * Returns a canonical, locale-agnostic pathname.
@@ -2,7 +2,6 @@ import { PREFIX_PLACEHOLDER } from '../../core/constants/prefix-placeholder.js';
2
2
  import '../../core/error/intor-error.js';
3
3
  import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
4
4
  import 'logry';
5
- import 'keyv';
6
5
 
7
6
  /**
8
7
  * Applies the configured locale prefix behavior to a standardized pathname.
@@ -2,7 +2,6 @@ import { PREFIX_PLACEHOLDER } from '../../core/constants/prefix-placeholder.js';
2
2
  import '../../core/error/intor-error.js';
3
3
  import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
4
4
  import 'logry';
5
- import 'keyv';
6
5
 
7
6
  /**
8
7
  * Standardizes a canonical pathname by applying the base path
@@ -2,11 +2,11 @@ import { createIntorRuntime } from '../runtime/create-intor-runtime.js';
2
2
 
3
3
  // Implementation
4
4
  async function getTranslator(config, params) {
5
- const { readOptions, allowCacheWrite, preKey, handlers, plugins } = params;
5
+ const { readers, allowCacheWrite, preKey, handlers, plugins } = params;
6
6
  const locale = params.locale;
7
7
  // Create runtime (request-scoped, no cache write)
8
8
  const runtime = createIntorRuntime(config, {
9
- readOptions,
9
+ readers,
10
10
  allowCacheWrite,
11
11
  });
12
12
  // Ensure messages & create translator snapshot
@@ -21,6 +21,7 @@ async function getTranslator(config, params) {
21
21
  locale,
22
22
  hasKey: translator.hasKey,
23
23
  t: translator.t,
24
+ tRaw: translator.tRaw,
24
25
  };
25
26
  }
26
27
 
@@ -43,7 +43,7 @@ async function loadMessagesFromUrl(url, options) {
43
43
  namespaces,
44
44
  fallbackLocales,
45
45
  concurrency: options?.concurrency,
46
- readOptions: options?.readOptions,
46
+ readers: options?.readers,
47
47
  pool: options?.pool,
48
48
  allowCacheWrite: options?.allowCacheWrite,
49
49
  loggerOptions: options?.loggerOptions || { id: "default" },
@@ -1,6 +1,5 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { getLogger } from '../../core/logger/get-logger.js';
3
- import 'keyv';
4
3
  import { createIntorRuntime } from '../runtime/create-intor-runtime.js';
5
4
 
6
5
  /**
@@ -13,16 +13,15 @@ import { readLocaleMessages } from './read-locale-messages/read-locale-messages.
13
13
  * It coordinates:
14
14
  *
15
15
  * - Locale resolution with fallbacks
16
- * - Process-level memoization (read by default, write by ownership)
17
16
  * - Concurrency control for file system access
17
+ * - Process-level memoization (read by default, write by ownership)
18
18
  *
19
19
  * Local messages are cached for the lifetime of the process.
20
20
  * Cache writes are restricted to the primary initialization flow.
21
21
  *
22
22
  * File traversal, parsing, and validation are delegated to lower-level utilities.
23
23
  */
24
- const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir = "messages", concurrency = 10, readOptions, pool = getGlobalMessagesPool(), allowCacheWrite = false, loggerOptions, }) => {
25
- const isProd = process.env.NODE_ENV === "production";
24
+ const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir = "messages", concurrency = 10, readers, pool = getGlobalMessagesPool(), allowCacheWrite = false, loggerOptions, }) => {
26
25
  const baseLogger = getLogger(loggerOptions);
27
26
  const logger = baseLogger.child({ scope: "load-local-messages" });
28
27
  const start = performance.now();
@@ -38,14 +37,14 @@ const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, root
38
37
  "loaderType:local",
39
38
  rootDir,
40
39
  locale,
41
- (fallbackLocales || []).toSorted().join(","),
42
- (namespaces || []).toSorted().join(","),
43
- ]);
40
+ fallbackLocales?.toSorted().join(","),
41
+ namespaces?.toSorted().join(","),
42
+ ].filter(Boolean));
44
43
  // ---------------------------------------------------------------------------
45
44
  // Cache read
46
45
  // ---------------------------------------------------------------------------
47
46
  if (cacheKey) {
48
- const cached = await pool?.get(cacheKey);
47
+ const cached = pool.get(cacheKey);
49
48
  if (cached) {
50
49
  logger.debug("Messages cache hit.", { key: cacheKey });
51
50
  return cached;
@@ -66,7 +65,7 @@ const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, root
66
65
  namespaces,
67
66
  rootDir,
68
67
  limit,
69
- readOptions,
68
+ readers,
70
69
  loggerOptions,
71
70
  });
72
71
  // Stop at the first locale that yields non-empty messages
@@ -90,10 +89,10 @@ const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, root
90
89
  // ---------------------------------------------------------------------------
91
90
  // Cache write (explicitly permitted)
92
91
  // ---------------------------------------------------------------------------
93
- if (allowCacheWrite && isProd) {
94
- if (cacheKey && messages) {
95
- await pool?.set(cacheKey, messages);
96
- }
92
+ const isProd = process.env.NODE_ENV === "production";
93
+ const canWriteCache = isProd && allowCacheWrite;
94
+ if (canWriteCache && cacheKey && messages) {
95
+ pool.set(cacheKey, messages);
97
96
  }
98
97
  // Final success log with resolved locale and timing
99
98
  if (messages) {
@@ -2,7 +2,6 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import '../../../../../core/error/intor-error.js';
4
4
  import { getLogger } from '../../../../../core/logger/get-logger.js';
5
- import 'keyv';
6
5
 
7
6
  /**
8
7
  * Recursively collects message file metadata under a given locale root.
@@ -21,9 +20,10 @@ import 'keyv';
21
20
  * }, ... ];
22
21
  * ```
23
22
  */
24
- async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, limit, exts = [".json"], loggerOptions, }) {
23
+ async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, limit, exts = ["json"], loggerOptions, }) {
25
24
  const baseLogger = getLogger(loggerOptions);
26
25
  const logger = baseLogger.child({ scope: "collect-file-entries" });
26
+ const supportedExts = new Set(["json", ...exts]);
27
27
  const fileEntries = [];
28
28
  // Recursive directory walk
29
29
  const walk = async (currentDir) => {
@@ -51,14 +51,14 @@ async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, l
51
51
  return;
52
52
  }
53
53
  // Skip unsupported file extensions
54
- if (!exts.some((ext) => entry.name.endsWith(ext)))
54
+ const ext = path.extname(entry.name).slice(1); // "json", "yaml"
55
+ if (!ext || !supportedExts.has(ext))
55
56
  return;
56
57
  // ---------------------------------------------------------------------
57
58
  // Resolve file entry
58
59
  // ---------------------------------------------------------------------
59
60
  const relativePath = path.relative(rootDir, fullPath);
60
- const ext = path.extname(relativePath);
61
- const withoutExt = relativePath.slice(0, -ext.length);
61
+ const withoutExt = relativePath.slice(0, relativePath.length - (ext.length + 1));
62
62
  const segments = withoutExt.split(path.sep).filter(Boolean);
63
63
  const namespace = segments.at(0);
64
64
  if (!namespace)
@@ -73,7 +73,7 @@ async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, l
73
73
  fullPath,
74
74
  relativePath,
75
75
  segments,
76
- basename: path.basename(entry.name, ext),
76
+ basename: path.basename(entry.name, `.${ext}`),
77
77
  });
78
78
  }));
79
79
  await Promise.all(tasks);
@@ -3,7 +3,6 @@ import '../../../../../core/error/intor-error.js';
3
3
  import { deepMerge } from '../../../../../core/utils/deep-merge.js';
4
4
  import { getLogger } from '../../../../../core/logger/get-logger.js';
5
5
  import { isValidMessages } from '../../../../../core/messages/utils/is-valid-messages.js';
6
- import 'keyv';
7
6
  import { jsonReader } from './utils/json-reader.js';
8
7
  import { nestObjectFromPath } from './utils/nest-object-from-path.js';
9
8
 
@@ -36,7 +35,7 @@ import { nestObjectFromPath } from './utils/nest-object-from-path.js';
36
35
  * }
37
36
  * ```
38
37
  */
39
- async function parseFileEntries({ fileEntries, limit, messagesReader, loggerOptions, }) {
38
+ async function parseFileEntries({ fileEntries, limit, readers, loggerOptions, }) {
40
39
  const baseLogger = getLogger(loggerOptions);
41
40
  const logger = baseLogger.child({ scope: "parse-file-entries" });
42
41
  // Read and parse all file entries
@@ -46,10 +45,19 @@ async function parseFileEntries({ fileEntries, limit, messagesReader, loggerOpti
46
45
  // -------------------------------------------------------------------
47
46
  // Read and validate file content
48
47
  // -------------------------------------------------------------------
49
- const ext = path.extname(fullPath);
50
- const raw = ext !== ".json" && messagesReader
51
- ? await messagesReader(fullPath)
52
- : await jsonReader(fullPath);
48
+ const ext = path.extname(fullPath).slice(1); // remove dot
49
+ let raw;
50
+ if (ext === "json") {
51
+ raw = await jsonReader(fullPath);
52
+ }
53
+ else {
54
+ const reader = readers?.[ext];
55
+ if (!reader) {
56
+ throw new Error(`No message reader registered for .${ext} files. ` +
57
+ `Please register a reader for the "${ext}" extension.`);
58
+ }
59
+ raw = await reader(fullPath);
60
+ }
53
61
  // Validate messages structure
54
62
  if (!isValidMessages(raw)) {
55
63
  throw new Error("Parsed content does not match expected Messages structure");
@@ -12,7 +12,7 @@ import { parseFileEntries } from './parse-file-entries/parse-file-entries.js';
12
12
  *
13
13
  * It does not perform validation or transformation itself.
14
14
  */
15
- const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", limit, readOptions: { exts, messagesReader } = {}, loggerOptions, }) => {
15
+ const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", limit, readers, loggerOptions, }) => {
16
16
  // ---------------------------------------------------------------------------
17
17
  // Collect message file entries for the locale
18
18
  // ---------------------------------------------------------------------------
@@ -20,7 +20,7 @@ const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", li
20
20
  namespaces,
21
21
  rootDir: path.resolve(rootDir, locale),
22
22
  limit,
23
- exts,
23
+ exts: Object.keys(readers || {}),
24
24
  loggerOptions,
25
25
  });
26
26
  // ---------------------------------------------------------------------------
@@ -29,7 +29,7 @@ const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", li
29
29
  const messages = await parseFileEntries({
30
30
  fileEntries,
31
31
  limit,
32
- messagesReader,
32
+ readers,
33
33
  loggerOptions,
34
34
  });
35
35
  // ---------------------------------------------------------------------------
@@ -2,7 +2,6 @@ import '../../core/error/intor-error.js';
2
2
  import { resolveLoaderOptions } from '../../core/utils/resolve-loader-options.js';
3
3
  import { getLogger } from '../../core/logger/get-logger.js';
4
4
  import { loadRemoteMessages } from '../../core/messages/load-remote-messages/load-remote-messages.js';
5
- import 'keyv';
6
5
  import { loadLocalMessages } from './load-local-messages/load-local-messages.js';
7
6
 
8
7
  /**
@@ -18,7 +17,7 @@ import { loadLocalMessages } from './load-local-messages/load-local-messages.js'
18
17
  * Message traversal, parsing, fallback resolution, and caching logic
19
18
  * are delegated to the selected loader.
20
19
  */
21
- const loadMessages = async ({ config, locale, readOptions, allowCacheWrite = false, }) => {
20
+ const loadMessages = async ({ config, locale, readers, allowCacheWrite = false, }) => {
22
21
  const baseLogger = getLogger(config.logger);
23
22
  const logger = baseLogger.child({ scope: "load-messages" });
24
23
  // ---------------------------------------------------------------------------
@@ -29,15 +28,15 @@ const loadMessages = async ({ config, locale, readOptions, allowCacheWrite = fal
29
28
  logger.warn("No loader options have been configured in the current config.");
30
29
  return;
31
30
  }
32
- const { type, namespaces } = loader;
31
+ const { type, namespaces, rootDir } = loader;
33
32
  const fallbackLocales = config.fallbackLocales[locale] || [];
34
33
  logger.info(`Loading messages for locale "${locale}".`);
35
34
  logger.trace("Starting to load messages with runtime context.", {
36
35
  loaderType: type,
36
+ rootDir,
37
37
  locale,
38
- fallbackLocales,
39
- namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : ["*"],
40
- cache: config.cache,
38
+ fallbackLocales: fallbackLocales.join(", "),
39
+ namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : "*",
41
40
  });
42
41
  // ---------------------------------------------------------------------------
43
42
  // Dispatch to loader implementation
@@ -49,24 +48,21 @@ const loadMessages = async ({ config, locale, readOptions, allowCacheWrite = fal
49
48
  locale,
50
49
  fallbackLocales,
51
50
  namespaces,
52
- rootDir: loader.rootDir,
51
+ rootDir,
53
52
  concurrency: loader.concurrency,
54
- readOptions,
53
+ readers,
55
54
  allowCacheWrite,
56
55
  loggerOptions: config.logger,
57
56
  });
58
57
  }
59
58
  else if (type === "remote") {
60
59
  loadedMessages = await loadRemoteMessages({
61
- id: config.id,
62
60
  locale,
63
61
  fallbackLocales,
64
62
  namespaces,
65
- rootDir: loader.rootDir,
63
+ rootDir,
66
64
  url: loader.url,
67
65
  headers: loader.headers,
68
- allowCacheWrite,
69
- cacheOptions: config.cache,
70
66
  loggerOptions: config.logger,
71
67
  });
72
68
  }
@@ -1,6 +1,6 @@
1
1
  import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
2
+ import { resolveLoaderOptions } from '../../core/utils/resolve-loader-options.js';
2
3
  import 'logry';
3
- import 'keyv';
4
4
  import { loadMessages } from '../messages/load-messages.js';
5
5
  import { createTranslator } from '../translator/create-translator.js';
6
6
 
@@ -14,20 +14,24 @@ import { createTranslator } from '../translator/create-translator.js';
14
14
  * before a translator snapshot can be created.
15
15
  */
16
16
  function createIntorRuntime(config, options) {
17
+ const loader = resolveLoaderOptions(config, "server");
17
18
  // Locale that has completed the ensureMessages() phase
18
19
  let ensuredLocale;
19
20
  // Messages prepared during ensureMessages(); may be empty
20
21
  let ensuredMessages;
21
22
  return {
22
23
  async ensureMessages(locale) {
23
- const messages = await loadMessages({
24
- config,
25
- locale,
26
- readOptions: options?.readOptions,
27
- allowCacheWrite: options?.allowCacheWrite ?? false,
28
- });
24
+ let messages;
25
+ if (loader) {
26
+ messages = await loadMessages({
27
+ config,
28
+ locale,
29
+ readers: options?.readers,
30
+ allowCacheWrite: options?.allowCacheWrite || false,
31
+ });
32
+ }
29
33
  ensuredLocale = locale;
30
- ensuredMessages = messages;
34
+ ensuredMessages = messages || {};
31
35
  },
32
36
  translator(locale, options) {
33
37
  // Guard: translator requires ensureMessages() to be completed for this locale
@@ -34,6 +34,7 @@ function createTranslator(params) {
34
34
  locale,
35
35
  hasKey: scoped ? scoped.hasKey : translator.hasKey,
36
36
  t: scoped ? scoped.t : translator.t,
37
+ tRaw: scoped ? scoped.tRaw : translator.tRaw,
37
38
  };
38
39
  }
39
40
 
@@ -1,6 +1,5 @@
1
1
  import '../../../core/error/intor-error.js';
2
2
  import 'logry';
3
- import 'keyv';
4
3
  import 'node:path';
5
4
  import 'p-limit';
6
5
  import 'node:fs/promises';
@@ -9,14 +8,14 @@ import { getTranslator as getTranslator$1 } from '../../../server/helpers/get-tr
9
8
 
10
9
  // Implementation
11
10
  async function getTranslator(config, req, params) {
12
- const { preKey, handlers, plugins, readOptions } = params || {};
11
+ const { preKey, handlers, plugins, readers, allowCacheWrite } = params || {};
13
12
  return getTranslator$1(config, {
14
13
  locale: req.intor?.locale || config.defaultLocale,
15
14
  preKey,
16
15
  handlers,
17
16
  plugins,
18
- readOptions,
19
- allowCacheWrite: false,
17
+ readers,
18
+ allowCacheWrite,
20
19
  });
21
20
  }
22
21