intor 2.4.5 → 2.4.7

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 (48) hide show
  1. package/dist/core/src/routing/pathname/localize-pathname.js +1 -1
  2. package/dist/core/src/server/intor/intor.js +1 -1
  3. package/dist/express/src/routing/pathname/localize-pathname.js +1 -1
  4. package/dist/fastify/src/routing/pathname/localize-pathname.js +1 -1
  5. package/dist/hono/src/routing/pathname/localize-pathname.js +1 -1
  6. package/dist/next/src/routing/pathname/localize-pathname.js +1 -1
  7. package/dist/next/src/server/intor/intor.js +1 -1
  8. package/dist/svelte-kit/export/svelte-kit/index.js +1 -0
  9. package/dist/svelte-kit/src/adapters/svelte-kit/{server/create-intor-handler.js → create-intor-handler.js} +6 -6
  10. package/dist/svelte-kit/src/core/utils/resolve-loader-options.js +1 -5
  11. package/dist/svelte-kit/src/policies/shoud-full-reload.js +1 -1
  12. package/dist/svelte-kit/src/routing/pathname/localize-pathname.js +1 -1
  13. package/dist/types/export/svelte-kit/index.d.ts +1 -1
  14. package/dist/types/src/adapters/next/server/get-translator.d.ts +1 -1
  15. package/dist/types/src/adapters/svelte-kit/{server/create-intor-handler.d.ts → create-intor-handler.d.ts} +1 -1
  16. package/dist/types/src/adapters/svelte-kit/index.d.ts +1 -0
  17. package/dist/types/src/routing/index.d.ts +1 -1
  18. package/dist/types/src/routing/pathname/localize-pathname.d.ts +1 -1
  19. package/dist/types/src/server/intor/intor.d.ts +2 -3
  20. package/package.json +1 -5
  21. package/dist/svelte-kit/export/svelte-kit/server/index.js +0 -2
  22. package/dist/svelte-kit/src/adapters/svelte-kit/server/intor.js +0 -24
  23. package/dist/svelte-kit/src/core/logger/get-logger.js +0 -39
  24. package/dist/svelte-kit/src/core/logger/global-logger-pool.js +0 -8
  25. package/dist/svelte-kit/src/core/messages/load-remote-messages/collect-remote-resources.js +0 -25
  26. package/dist/svelte-kit/src/core/messages/load-remote-messages/fetch-remote-resource.js +0 -47
  27. package/dist/svelte-kit/src/core/messages/load-remote-messages/load-remote-messages.js +0 -93
  28. package/dist/svelte-kit/src/core/messages/load-remote-messages/resolve-remote-resources.js +0 -24
  29. package/dist/svelte-kit/src/core/messages/merge-messages.js +0 -33
  30. package/dist/svelte-kit/src/core/messages/utils/is-valid-messages.js +0 -44
  31. package/dist/svelte-kit/src/core/messages/utils/nest-object-from-path.js +0 -21
  32. package/dist/svelte-kit/src/core/translator/create-translator.js +0 -30
  33. package/dist/svelte-kit/src/core/utils/deep-merge.js +0 -47
  34. package/dist/svelte-kit/src/core/utils/normalizers/normalize-cache-key.js +0 -45
  35. package/dist/svelte-kit/src/server/intor/intor.js +0 -32
  36. package/dist/svelte-kit/src/server/messages/load-local-messages/cache/messages-pool.js +0 -11
  37. package/dist/svelte-kit/src/server/messages/load-local-messages/load-local-messages.js +0 -108
  38. package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +0 -91
  39. package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +0 -103
  40. package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.js +0 -12
  41. package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +0 -42
  42. package/dist/svelte-kit/src/server/messages/load-messages.js +0 -78
  43. package/dist/svelte-kit/src/server/translator/init-translator.js +0 -36
  44. package/dist/types/export/svelte-kit/server/index.d.ts +0 -1
  45. package/dist/types/src/adapters/svelte-kit/server/index.d.ts +0 -2
  46. package/dist/types/src/adapters/svelte-kit/server/intor.d.ts +0 -20
  47. /package/dist/svelte-kit/src/adapters/svelte-kit/{server/utils → utils}/is-svelte-kit-ssg.js +0 -0
  48. /package/dist/types/src/adapters/svelte-kit/{server/utils → utils}/is-svelte-kit-ssg.d.ts +0 -0
@@ -11,7 +11,7 @@ import { standardizePathname } from './standardize-pathname.js';
11
11
  * // config.supportedLocales: ["en-US"]
12
12
  * // config.routing.basePath: "/app"
13
13
  * // config.routing.prefix: "all"
14
- * localizePathname(config, "/app/en-US/about", "en-US");
14
+ * localizePathname("/app/en-US/about", config, "en-US");
15
15
  * // => {
16
16
  * // pathname: '/app/en-US/about'
17
17
  * // unprefixedPathname: '/about',
@@ -18,7 +18,7 @@ async function intor(config, locale, options) {
18
18
  // Initialize a locale-bound translator snapshot with messages loaded
19
19
  const translator = await initTranslator(config, locale, {
20
20
  readers: options?.readers,
21
- allowCacheWrite: options?.allowCacheWrite,
21
+ allowCacheWrite: options?.allowCacheWrite ?? true,
22
22
  fetch: options?.fetch || globalThis.fetch,
23
23
  });
24
24
  logger.info("Intor initialized.");
@@ -11,7 +11,7 @@ import { standardizePathname } from './standardize-pathname.js';
11
11
  * // config.supportedLocales: ["en-US"]
12
12
  * // config.routing.basePath: "/app"
13
13
  * // config.routing.prefix: "all"
14
- * localizePathname(config, "/app/en-US/about", "en-US");
14
+ * localizePathname("/app/en-US/about", config, "en-US");
15
15
  * // => {
16
16
  * // pathname: '/app/en-US/about'
17
17
  * // unprefixedPathname: '/about',
@@ -11,7 +11,7 @@ import { standardizePathname } from './standardize-pathname.js';
11
11
  * // config.supportedLocales: ["en-US"]
12
12
  * // config.routing.basePath: "/app"
13
13
  * // config.routing.prefix: "all"
14
- * localizePathname(config, "/app/en-US/about", "en-US");
14
+ * localizePathname("/app/en-US/about", config, "en-US");
15
15
  * // => {
16
16
  * // pathname: '/app/en-US/about'
17
17
  * // unprefixedPathname: '/about',
@@ -11,7 +11,7 @@ import { standardizePathname } from './standardize-pathname.js';
11
11
  * // config.supportedLocales: ["en-US"]
12
12
  * // config.routing.basePath: "/app"
13
13
  * // config.routing.prefix: "all"
14
- * localizePathname(config, "/app/en-US/about", "en-US");
14
+ * localizePathname("/app/en-US/about", config, "en-US");
15
15
  * // => {
16
16
  * // pathname: '/app/en-US/about'
17
17
  * // unprefixedPathname: '/about',
@@ -11,7 +11,7 @@ import { standardizePathname } from './standardize-pathname.js';
11
11
  * // config.supportedLocales: ["en-US"]
12
12
  * // config.routing.basePath: "/app"
13
13
  * // config.routing.prefix: "all"
14
- * localizePathname(config, "/app/en-US/about", "en-US");
14
+ * localizePathname("/app/en-US/about", config, "en-US");
15
15
  * // => {
16
16
  * // pathname: '/app/en-US/about'
17
17
  * // unprefixedPathname: '/about',
@@ -18,7 +18,7 @@ async function intor(config, locale, options) {
18
18
  // Initialize a locale-bound translator snapshot with messages loaded
19
19
  const translator = await initTranslator(config, locale, {
20
20
  readers: options?.readers,
21
- allowCacheWrite: options?.allowCacheWrite,
21
+ allowCacheWrite: options?.allowCacheWrite ?? true,
22
22
  fetch: options?.fetch || globalThis.fetch,
23
23
  });
24
24
  logger.info("Intor initialized.");
@@ -1 +1,2 @@
1
+ export { createIntorHandler } from '../../src/adapters/svelte-kit/create-intor-handler.js';
1
2
  export { useNavigation } from '../../src/adapters/svelte-kit/use-navigation.js';
@@ -1,12 +1,12 @@
1
1
  import { redirect } from '@sveltejs/kit';
2
2
  import { isSvelteKitSSG } from './utils/is-svelte-kit-ssg.js';
3
- import '../../../core/error/intor-error.js';
4
- import { normalizeQuery } from '../../../core/utils/normalizers/normalize-query.js';
3
+ import '../../core/error/intor-error.js';
4
+ import { normalizeQuery } from '../../core/utils/normalizers/normalize-query.js';
5
5
  import 'logry';
6
6
  import 'p-limit';
7
7
  import 'intor-translator';
8
- import { resolveInbound } from '../../../routing/inbound/resolve-inbound.js';
9
- import { getLocaleFromAcceptLanguage } from '../../../routing/locale/get-locale-from-accept-language.js';
8
+ import { resolveInbound } from '../../routing/inbound/resolve-inbound.js';
9
+ import { getLocaleFromAcceptLanguage } from '../../routing/locale/get-locale-from-accept-language.js';
10
10
 
11
11
  /**
12
12
  * Resolves locale-aware routing for the current execution context.
@@ -17,7 +17,6 @@ import { getLocaleFromAcceptLanguage } from '../../../routing/locale/get-locale-
17
17
  */
18
18
  function createIntorHandler(config) {
19
19
  return async function intorHandler({ event, resolve }) {
20
- const { host, searchParams, pathname: rawPathname } = event.url;
21
20
  // Locale from Accept-Language header
22
21
  const acceptLanguage = event.request.headers.get("accept-language");
23
22
  const localeFromAcceptLanguage = getLocaleFromAcceptLanguage(acceptLanguage, config.supportedLocales);
@@ -29,11 +28,12 @@ function createIntorHandler(config) {
29
28
  inboundResult = {
30
29
  locale: event.params?.locale,
31
30
  localeSource: "path",
32
- pathname: rawPathname,
31
+ pathname: event.url.pathname,
33
32
  shouldRedirect: false,
34
33
  };
35
34
  }
36
35
  else {
36
+ const { host, searchParams, pathname: rawPathname } = event.url;
37
37
  inboundResult = await resolveInbound(config, rawPathname, {
38
38
  host,
39
39
  query: normalizeQuery(Object.fromEntries(searchParams.entries())),
@@ -21,7 +21,7 @@ const resolveLoaderOptions = (config, runtime) => {
21
21
  // ------------------------------------------------
22
22
  // runtime: client
23
23
  // ------------------------------------------------
24
- if (runtime === "client") {
24
+ {
25
25
  const client = config.client?.loader;
26
26
  if (client) {
27
27
  // Client loader is always remote by design
@@ -29,10 +29,6 @@ const resolveLoaderOptions = (config, runtime) => {
29
29
  }
30
30
  return config.server?.loader ?? config.loader;
31
31
  }
32
- // ------------------------------------------------
33
- // runtime: server
34
- // ------------------------------------------------
35
- return config.server?.loader ?? config.loader;
36
32
  };
37
33
 
38
34
  export { resolveLoaderOptions };
@@ -8,7 +8,7 @@ import 'intor-translator';
8
8
  * Determine whether client-side navigation must be forced to reload.
9
9
  */
10
10
  function shouldFullReload(config) {
11
- const loader = resolveLoaderOptions(config, "client");
11
+ const loader = resolveLoaderOptions(config);
12
12
  return (loader?.mode === "local" || config.routing.outbound.forceFullReload === true);
13
13
  }
14
14
 
@@ -11,7 +11,7 @@ import { standardizePathname } from './standardize-pathname.js';
11
11
  * // config.supportedLocales: ["en-US"]
12
12
  * // config.routing.basePath: "/app"
13
13
  * // config.routing.prefix: "all"
14
- * localizePathname(config, "/app/en-US/about", "en-US");
14
+ * localizePathname("/app/en-US/about", config, "en-US");
15
15
  * // => {
16
16
  * // pathname: '/app/en-US/about'
17
17
  * // unprefixedPathname: '/about',
@@ -1 +1 @@
1
- export { useNavigation } from "../../src/adapters/svelte-kit";
1
+ export { createIntorHandler, useNavigation } from "../../src/adapters/svelte-kit";
@@ -10,7 +10,7 @@ type GetTranslatorNextParams<CK extends GenConfigKeys = "__default__"> = Omit<Ge
10
10
  *
11
11
  * @platform Next.js
12
12
  */
13
- export declare function getTranslator<CK extends GenConfigKeys = "__default__", ReplacementSchema = GenReplacements<CK>, RichSchema = GenRich<CK>, PK extends LocalizedPreKey<GenMessages<CK>> | undefined = undefined>(config: IntorResolvedConfig, params: GetTranslatorNextParams<CK> & {
13
+ export declare function getTranslator<CK extends GenConfigKeys = "__default__", ReplacementSchema = GenReplacements<CK>, RichSchema = GenRich<CK>, PK extends LocalizedPreKey<GenMessages<CK>> | undefined = undefined>(config: IntorResolvedConfig, params?: GetTranslatorNextParams<CK> & {
14
14
  preKey?: PK;
15
15
  }): Promise<TranslatorInstance<GenMessages<CK>, ReplacementSchema, RichSchema, PK>>;
16
16
  export {};
@@ -1,4 +1,4 @@
1
- import type { IntorResolvedConfig } from "../../../config";
1
+ import type { IntorResolvedConfig } from "../../config";
2
2
  import type { Handle } from "@sveltejs/kit";
3
3
  /**
4
4
  * Resolves locale-aware routing for the current execution context.
@@ -1 +1,2 @@
1
+ export { createIntorHandler } from "./create-intor-handler";
1
2
  export { useNavigation } from "./use-navigation";
@@ -1,4 +1,4 @@
1
1
  export { resolveInbound, type InboundResult, type InboundContext, resolveInboundFromRequest, } from "./inbound";
2
+ export { resolveOutbound, type OutboundResult } from "./outbound";
2
3
  export { localizePathname, type LocalizedPathname } from "./pathname";
3
4
  export { getLocaleFromAcceptLanguage, getLocaleFromPathname, getLocaleFromHost, getLocaleFromQuery, } from "./locale";
4
- export { resolveOutbound, type OutboundResult } from "./outbound";
@@ -37,7 +37,7 @@ export interface LocalizedPathname {
37
37
  * // config.supportedLocales: ["en-US"]
38
38
  * // config.routing.basePath: "/app"
39
39
  * // config.routing.prefix: "all"
40
- * localizePathname(config, "/app/en-US/about", "en-US");
40
+ * localizePathname("/app/en-US/about", config, "en-US");
41
41
  * // => {
42
42
  * // pathname: '/app/en-US/about'
43
43
  * // unprefixedPathname: '/about',
@@ -1,14 +1,13 @@
1
1
  import type { IntorValue } from "./types";
2
2
  import type { IntorResolvedConfig } from "../../config";
3
- import type { Locale } from "intor-translator";
4
- import { type GenConfigKeys, type MessagesReaders, type RuntimeFetch } from "../../core";
3
+ import { type GenConfigKeys, type GenLocale, type MessagesReaders, type RuntimeFetch } from "../../core";
5
4
  /**
6
5
  * Initializes Intor for the current execution context.
7
6
  *
8
7
  * Produces a server-side snapshot for SSR and
9
8
  * full-stack rendering environments.
10
9
  */
11
- export declare function intor<CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig, locale: Locale, options?: {
10
+ export declare function intor<CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig, locale: GenLocale<CK> | (string & {}), options?: {
12
11
  readers?: MessagesReaders;
13
12
  allowCacheWrite?: boolean;
14
13
  fetch?: RuntimeFetch;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intor",
3
- "version": "2.4.5",
3
+ "version": "2.4.7",
4
4
  "description": "The i18n library for modern JavaScript",
5
5
  "author": "Yiming Liao",
6
6
  "homepage": "https://github.com/yiming-liao/intor#readme",
@@ -73,10 +73,6 @@
73
73
  "import": "./dist/svelte-kit/export/svelte-kit/index.js",
74
74
  "types": "./dist/types/export/svelte-kit/index.d.ts"
75
75
  },
76
- "./svelte-kit/server": {
77
- "import": "./dist/svelte-kit/export/svelte-kit/server/index.js",
78
- "types": "./dist/types/export/svelte-kit/server/index.d.ts"
79
- },
80
76
  "./express": {
81
77
  "import": "./dist/express/export/express/index.js",
82
78
  "types": "./dist/types/export/express/index.d.ts",
@@ -1,2 +0,0 @@
1
- export { createIntorHandler } from '../../../src/adapters/svelte-kit/server/create-intor-handler.js';
2
- export { intor } from '../../../src/adapters/svelte-kit/server/intor.js';
@@ -1,24 +0,0 @@
1
- import { intor as intor$1 } from '../../../server/intor/intor.js';
2
- import '../../../core/error/intor-error.js';
3
- import 'logry';
4
- import 'p-limit';
5
- import 'intor-translator';
6
- import 'node:path';
7
- import 'node:fs/promises';
8
-
9
- /**
10
- * Initializes Intor for the current execution context.
11
- *
12
- * - Uses the locale resolved by the SvelteKit request lifecycle.
13
- * - Permits cache writes during server execution.
14
- * @platform SvelteKit
15
- */
16
- async function intor(config, locale, fetch, options) {
17
- return await intor$1(config, locale, {
18
- readers: options?.readers,
19
- allowCacheWrite: options?.allowCacheWrite ?? true,
20
- fetch,
21
- });
22
- }
23
-
24
- export { intor };
@@ -1,39 +0,0 @@
1
- import { logry } from 'logry';
2
- import { getGlobalLoggerPool } from './global-logger-pool.js';
3
-
4
- const DEFAULT_FORMAT_CONFIG = {
5
- timestamp: { withDate: false },
6
- };
7
- const DEFAULT_RENDER_CONFIG = {
8
- timestamp: {},
9
- id: { visible: true, prefix: "<", suffix: ">" },
10
- meta: { lineBreaksAfter: 1 },
11
- };
12
- /**
13
- * Get a shared logger instance by id.
14
- * - Safe across hot reloads
15
- * - Prevents unbounded memory usage via soft LRU
16
- */
17
- function getLogger({ id = "default", formatConfig, renderConfig, preset, ...options }) {
18
- const pool = getGlobalLoggerPool();
19
- let logger = pool.get(id);
20
- if (!logger) {
21
- logger = logry({
22
- id,
23
- formatConfig: !formatConfig && !preset ? DEFAULT_FORMAT_CONFIG : formatConfig,
24
- renderConfig: !renderConfig && !preset ? DEFAULT_RENDER_CONFIG : renderConfig,
25
- preset,
26
- ...options,
27
- });
28
- pool.set(id, logger);
29
- // Soft LRU: keep pool size under control
30
- if (pool.size > 1000) {
31
- const keys = [...pool.keys()];
32
- for (const key of keys.slice(0, 200))
33
- pool.delete(key);
34
- }
35
- }
36
- return logger;
37
- }
38
-
39
- export { getLogger };
@@ -1,8 +0,0 @@
1
- function getGlobalLoggerPool() {
2
- if (!globalThis.__INTOR_LOGGER_POOL__) {
3
- globalThis.__INTOR_LOGGER_POOL__ = new Map();
4
- }
5
- return globalThis.__INTOR_LOGGER_POOL__;
6
- }
7
-
8
- export { getGlobalLoggerPool };
@@ -1,25 +0,0 @@
1
- /**
2
- * Collect remote message resources for a given locale.
3
- *
4
- * - Always includes the root `index.json`
5
- * - Optionally includes namespace-specific resources
6
- * - Produces semantic paths for later message nesting
7
- *
8
- * This function performs no I/O and does not validate resource existence.
9
- */
10
- function collectRemoteResources({ locale, baseUrl, namespaces, }) {
11
- const basePath = `${baseUrl}/${locale}`;
12
- // Root translation resource (always loaded)
13
- const indexResource = { url: `${basePath}/index.json`, path: [] };
14
- // When no namespaces are provided, the locale domain consists of index only
15
- if (!namespaces || namespaces.length === 0)
16
- return [indexResource];
17
- // Namespace-specific resources are nested under their namespace key
18
- const nsResources = namespaces.map((ns) => ({
19
- url: `${basePath}/${ns}.json`,
20
- path: [ns],
21
- }));
22
- return [indexResource, ...nsResources];
23
- }
24
-
25
- export { collectRemoteResources };
@@ -1,47 +0,0 @@
1
- import { getLogger } from '../../logger/get-logger.js';
2
- import { isValidMessages } from '../utils/is-valid-messages.js';
3
-
4
- /**
5
- * Fetch a single remote messages resource.
6
- *
7
- * This function performs a single HTTP request to retrieve
8
- * a remote translation messages payload.
9
- *
10
- * It is responsible for:
11
- * - Issuing the network request
12
- * - Validating the returned message structure
13
- * - Handling abort and network errors
14
- */
15
- async function fetchRemoteResource({ fetch, url, headers, signal, loggerOptions, }) {
16
- const baseLogger = getLogger(loggerOptions);
17
- const logger = baseLogger.child({ scope: "fetch-locale-messages" });
18
- try {
19
- // Fetch
20
- const response = await fetch(url, {
21
- method: "GET",
22
- headers: { "Content-Type": "application/json", ...headers },
23
- cache: "no-store",
24
- signal,
25
- });
26
- if (!response.ok) {
27
- throw new Error(`HTTP ${response.status} ${response.statusText}`);
28
- }
29
- // Parse JSON body
30
- const data = await response.json();
31
- // Validate messages structure
32
- if (!isValidMessages(data)) {
33
- throw new Error("Invalid messages structure");
34
- }
35
- return data;
36
- }
37
- catch (error) {
38
- if (error instanceof Error && error.name === "AbortError") {
39
- logger.debug("Remote fetch aborted.", { url });
40
- return;
41
- }
42
- logger.warn("Failed to fetch remote messages.", { url, error });
43
- return;
44
- }
45
- }
46
-
47
- export { fetchRemoteResource };
@@ -1,93 +0,0 @@
1
- import pLimit from 'p-limit';
2
- import { getLogger } from '../../logger/get-logger.js';
3
- import { collectRemoteResources } from './collect-remote-resources.js';
4
- import { fetchRemoteResource } from './fetch-remote-resource.js';
5
- import { resolveRemoteResources } from './resolve-remote-resources.js';
6
-
7
- /**
8
- * Load locale messages from a remote source.
9
- *
10
- * This function serves as the orchestration layer for remote message loading.
11
- * It coordinates:
12
- *
13
- * - Locale resolution with fallbacks
14
- * - Concurrency control for network requests
15
- * - Remote resource fetching and message merging
16
- *
17
- * Remote messages are fetched on demand and are not memoized at the process level.
18
- *
19
- * Network requests and response validation are delegated to lower-level utilities.
20
- */
21
- const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurrency, fetch, url: baseUrl, headers, signal, loggerOptions, }) => {
22
- const baseLogger = getLogger(loggerOptions);
23
- const logger = baseLogger.child({ scope: "load-remote-messages" });
24
- // Abort early if the request has already been cancelled
25
- if (signal?.aborted) {
26
- logger.debug("Remote message loading aborted before fetch.");
27
- return;
28
- }
29
- const start = performance.now();
30
- logger.debug("Loading remote messages.", { baseUrl });
31
- // ----------------------------------------------------------------
32
- // Resolve locale messages with ordered fallback strategy
33
- // ----------------------------------------------------------------
34
- const limit = concurrency ? pLimit(concurrency) : undefined;
35
- const candidateLocales = [locale, ...(fallbackLocales || [])];
36
- let messages;
37
- for (let i = 0; i < candidateLocales.length; i++) {
38
- const candidateLocale = candidateLocales[i];
39
- const isLast = i === candidateLocales.length - 1;
40
- try {
41
- // -----------------------------------------------------------------
42
- // Collect remote message resources for the locale
43
- // -----------------------------------------------------------------
44
- const resources = collectRemoteResources({
45
- locale: candidateLocale,
46
- baseUrl,
47
- namespaces,
48
- });
49
- // -----------------------------------------------------------------
50
- // Fetch all message chunks in parallel
51
- // -----------------------------------------------------------------
52
- const fetchUrl = (url) => fetchRemoteResource({ url, headers, signal, loggerOptions, fetch });
53
- const results = await Promise.all(resources.map(({ url }) => limit ? limit(() => fetchUrl(url)) : fetchUrl(url)));
54
- // Guard: no valid remote resources
55
- if (!results.some(Boolean))
56
- continue;
57
- // -----------------------------------------------------------------
58
- // Resolve and merge remote message resources
59
- // -----------------------------------------------------------------
60
- const resolved = resolveRemoteResources(resources.map((res, i) => ({ path: res.path, data: results[i] })));
61
- // -----------------------------------------------------------------
62
- // Wrap resolved messages into locale-scoped LocaleMessages
63
- // -----------------------------------------------------------------
64
- messages = { [candidateLocale]: resolved };
65
- break;
66
- }
67
- catch {
68
- if (signal?.aborted) {
69
- logger.debug("Remote message loading aborted.");
70
- return;
71
- }
72
- if (isLast) {
73
- logger.warn("Failed to load messages for all candidate locales.", {
74
- locale,
75
- fallbackLocales,
76
- });
77
- }
78
- else {
79
- logger.warn(`Failed to load locale messages for "${candidateLocale}", trying next fallback.`);
80
- }
81
- }
82
- }
83
- // Final success log with resolved locale and timing
84
- if (messages) {
85
- logger.trace("Finished loading remote messages.", {
86
- loadedLocale: Object.keys(messages)[0],
87
- duration: `${Math.round(performance.now() - start)} ms`,
88
- });
89
- }
90
- return messages;
91
- };
92
-
93
- export { loadRemoteMessages };
@@ -1,24 +0,0 @@
1
- import { deepMerge } from '../../utils/deep-merge.js';
2
- import { nestObjectFromPath } from '../utils/nest-object-from-path.js';
3
-
4
- /**
5
- * Resolve remote message resources into a single MessageObject.
6
- *
7
- * - Applies semantic nesting based on resource path
8
- * - Merges all resolved message chunks
9
- *
10
- * Always returns a MessageObject.
11
- * An empty object represents an empty translation domain.
12
- */
13
- function resolveRemoteResources(resources) {
14
- let result = {};
15
- for (const { path, data } of resources) {
16
- if (!data)
17
- continue;
18
- const resolved = path.length > 0 ? nestObjectFromPath(path, data) : data;
19
- result = deepMerge(result, resolved);
20
- }
21
- return result;
22
- }
23
-
24
- export { resolveRemoteResources };
@@ -1,33 +0,0 @@
1
- import { getLogger } from '../logger/get-logger.js';
2
- import { deepMerge } from '../utils/deep-merge.js';
3
-
4
- /**
5
- * Merge locale-specific messages with runtime overrides.
6
- *
7
- * - Only merges messages under the given locale
8
- * - Emits debug logs for add / override events
9
- */
10
- function mergeMessages(a, b, { config, locale, onEvent }) {
11
- const baseLogger = getLogger({ ...config.logger, id: config.id });
12
- const logger = baseLogger.child({ scope: "merge-messages" });
13
- // Merge messages for the active locale only
14
- const merged = deepMerge(a?.[locale] ?? {}, b?.[locale] ?? {}, {
15
- onOverride: (event) => {
16
- if (onEvent) {
17
- onEvent(event);
18
- return;
19
- }
20
- const { kind, path, next, prev } = event;
21
- if (kind === "add")
22
- return;
23
- logger.debug(`Override | ${locale}: "${path}"`, { prev, next });
24
- },
25
- });
26
- // Preserve other locales, update only the target one
27
- return {
28
- ...a,
29
- [locale]: merged,
30
- };
31
- }
32
-
33
- export { mergeMessages };
@@ -1,44 +0,0 @@
1
- /** Check if a value is a plain object (not null, not array) */
2
- function isPlainObject(value) {
3
- return typeof value === "object" && value !== null && !Array.isArray(value);
4
- }
5
- /**
6
- * Check if a value is a valid MessageObject.
7
- *
8
- * - Supports all MessageValue variants (primitive, array, object).
9
- * - Uses an iterative approach to avoid stack overflow.
10
- */
11
- function isValidMessages(value) {
12
- if (!isPlainObject(value))
13
- return false;
14
- const stack = [value];
15
- while (stack.length > 0) {
16
- const current = stack.pop();
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);
28
- }
29
- continue;
30
- }
31
- // object → validate each value
32
- if (isPlainObject(current)) {
33
- for (const v of Object.values(current)) {
34
- stack.push(v);
35
- }
36
- continue;
37
- }
38
- // everything else is invalid
39
- return false;
40
- }
41
- return true;
42
- }
43
-
44
- export { isPlainObject, isValidMessages };
@@ -1,21 +0,0 @@
1
- /**
2
- * Wraps a value inside nested objects according to a given path.
3
- *
4
- * @example
5
- * ```ts
6
- * const value = { a: "A" };
7
- *
8
- * nestObjectFromPath(["auth", "verify"], value); // → { auth: { verify: { a: "A" } } }
9
- *
10
- * nestObjectFromPath([], value); // → { a: "A" }
11
- * ```
12
- */
13
- function nestObjectFromPath(path, value) {
14
- let obj = value;
15
- for (let i = path.length - 1; i >= 0; i--) {
16
- obj = { [path[i]]: obj };
17
- }
18
- return obj;
19
- }
20
-
21
- export { nestObjectFromPath };
@@ -1,30 +0,0 @@
1
- import { Translator } from 'intor-translator';
2
- import { mergeMessages } from '../messages/merge-messages.js';
3
-
4
- /**
5
- * Create a server-side Translator instance for a fixed locale.
6
- *
7
- * - Merges static config messages with runtime-loaded messages
8
- * - Initializes a Translator bound to a specific locale
9
- * - Injects fallback rules, handlers, and plugins from config
10
- */
11
- function createTranslator(params) {
12
- const { config, locale, messages, handlers, plugins } = params;
13
- // Merge static config messages with runtime-loaded messages
14
- const finalMessages = mergeMessages(config.messages, messages, {
15
- config,
16
- locale,
17
- });
18
- const translator = new Translator({
19
- locale,
20
- messages: finalMessages,
21
- fallbackLocales: config.fallbackLocales,
22
- loadingMessage: config.translator?.loadingMessage,
23
- missingMessage: config.translator?.missingMessage,
24
- handlers,
25
- plugins,
26
- });
27
- return translator;
28
- }
29
-
30
- export { createTranslator };