intor 2.3.29 → 2.3.30
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/core/src/core/messages/load-remote-messages/fetch-remote-resource.js +1 -1
- package/dist/core/src/core/messages/load-remote-messages/load-remote-messages.js +2 -2
- package/dist/core/src/routing/pathname/canonicalize-pathname.js +11 -7
- package/dist/core/src/server/helpers/get-translator.js +2 -2
- package/dist/core/src/server/intor/intor.js +3 -8
- package/dist/core/src/server/messages/load-messages.js +2 -1
- package/dist/core/src/server/translator/init-translator.js +9 -3
- package/dist/express/src/core/messages/load-remote-messages/fetch-remote-resource.js +1 -1
- package/dist/express/src/core/messages/load-remote-messages/load-remote-messages.js +2 -2
- package/dist/express/src/routing/pathname/canonicalize-pathname.js +11 -7
- package/dist/express/src/server/helpers/get-translator.js +2 -2
- package/dist/express/src/server/messages/load-messages.js +2 -1
- package/dist/express/src/server/translator/init-translator.js +9 -3
- package/dist/next/export/next/index.js +0 -1
- package/dist/next/export/next/server/index.js +0 -1
- package/dist/next/src/adapters/next/navigation/link.js +11 -10
- package/dist/next/src/adapters/next/navigation/use-router.js +14 -20
- package/dist/next/src/adapters/next/server/intor.js +1 -1
- package/dist/next/src/client/shared/navigation/execute-navigation.js +50 -0
- package/dist/next/src/client/shared/utils/build-cookie-string.js +30 -0
- package/dist/next/src/client/shared/utils/locale/set-locale-cookie.js +15 -0
- package/dist/next/src/core/messages/load-remote-messages/fetch-remote-resource.js +1 -1
- package/dist/next/src/core/messages/load-remote-messages/load-remote-messages.js +2 -2
- package/dist/next/src/routing/pathname/canonicalize-pathname.js +11 -7
- package/dist/next/src/server/helpers/get-translator.js +2 -2
- package/dist/next/src/server/intor/intor.js +3 -8
- package/dist/next/src/server/messages/load-messages.js +2 -1
- package/dist/next/src/server/translator/init-translator.js +9 -3
- package/dist/react/export/react/index.js +0 -2
- package/dist/react/src/client/shared/messages/create-refetch-messages.js +1 -0
- package/dist/react/src/core/messages/load-remote-messages/fetch-remote-resource.js +1 -1
- package/dist/react/src/core/messages/load-remote-messages/load-remote-messages.js +2 -2
- package/dist/svelte/export/svelte/index.js +4 -2
- package/dist/svelte/src/client/shared/messages/create-refetch-messages.js +1 -0
- package/dist/svelte/src/client/svelte/{store → provider}/create-intor-store.js +7 -14
- package/dist/svelte/src/client/svelte/provider/intor-provider.svelte +7 -0
- package/dist/svelte/src/client/svelte/provider/use-intor-context.js +11 -0
- package/dist/svelte/src/client/svelte/render/create-svelte-renderer.js +5 -6
- package/dist/svelte/src/client/svelte/translator/create-t-rich.js +23 -0
- package/dist/svelte/src/client/svelte/translator/use-translator.js +32 -0
- package/dist/svelte/src/core/messages/load-remote-messages/fetch-remote-resource.js +1 -1
- package/dist/svelte/src/core/messages/load-remote-messages/load-remote-messages.js +2 -2
- package/dist/svelte-kit/export/svelte-kit/index.js +1 -0
- package/dist/svelte-kit/export/svelte-kit/server/index.js +2 -0
- package/dist/svelte-kit/src/adapters/svelte-kit/navigation/use-navigation.js +36 -0
- package/dist/svelte-kit/src/adapters/svelte-kit/server/create-intor-handle.js +58 -0
- package/dist/svelte-kit/src/adapters/svelte-kit/server/intor.js +24 -0
- package/dist/svelte-kit/src/client/shared/navigation/execute-navigation.js +49 -0
- package/dist/svelte-kit/src/client/shared/utils/build-cookie-string.js +30 -0
- package/dist/svelte-kit/src/client/shared/utils/locale/set-locale-cookie.js +15 -0
- package/dist/svelte-kit/src/core/constants/headers.js +6 -0
- package/dist/svelte-kit/src/core/error/intor-error.js +9 -0
- package/dist/svelte-kit/src/core/logger/get-logger.js +39 -0
- package/dist/svelte-kit/src/core/logger/global-logger-pool.js +8 -0
- package/dist/svelte-kit/src/core/messages/load-remote-messages/collect-remote-resources.js +25 -0
- package/dist/svelte-kit/src/core/messages/load-remote-messages/fetch-remote-resource.js +47 -0
- package/dist/svelte-kit/src/core/messages/load-remote-messages/load-remote-messages.js +93 -0
- package/dist/svelte-kit/src/core/messages/load-remote-messages/resolve-remote-resources.js +24 -0
- package/dist/svelte-kit/src/core/messages/merge-messages.js +33 -0
- package/dist/svelte-kit/src/core/messages/utils/is-valid-messages.js +44 -0
- package/dist/svelte-kit/src/core/messages/utils/nest-object-from-path.js +21 -0
- package/dist/svelte-kit/src/core/utils/deep-merge.js +47 -0
- package/dist/svelte-kit/src/core/utils/normalizers/normalize-cache-key.js +45 -0
- package/dist/svelte-kit/src/core/utils/normalizers/normalize-locale.js +59 -0
- package/dist/svelte-kit/src/core/utils/normalizers/normalize-query.js +25 -0
- package/dist/svelte-kit/src/core/utils/resolve-loader-options.js +34 -0
- package/dist/{react → svelte-kit}/src/policies/shoud-full-reload.js +1 -1
- package/dist/svelte-kit/src/policies/should-sync-locale.js +8 -0
- package/dist/svelte-kit/src/routing/inbound/resolve-inbound.js +46 -0
- package/dist/svelte-kit/src/routing/inbound/resolve-locale/resolve-locale.js +33 -0
- package/dist/svelte-kit/src/routing/inbound/resolve-pathname/resolve-pathname.js +42 -0
- package/dist/svelte-kit/src/routing/inbound/resolve-pathname/strategies/all.js +28 -0
- package/dist/svelte-kit/src/routing/inbound/resolve-pathname/strategies/except-default.js +29 -0
- package/dist/svelte-kit/src/routing/inbound/resolve-pathname/strategies/none.js +8 -0
- package/dist/svelte-kit/src/routing/locale/get-locale-from-accept-language.js +38 -0
- package/dist/svelte-kit/src/routing/locale/get-locale-from-host.js +32 -0
- package/dist/svelte-kit/src/routing/locale/get-locale-from-pathname.js +46 -0
- package/dist/svelte-kit/src/routing/locale/get-locale-from-query.js +29 -0
- package/dist/{react → svelte-kit}/src/routing/pathname/canonicalize-pathname.js +11 -7
- package/dist/svelte-kit/src/server/intor/intor.js +31 -0
- package/dist/svelte-kit/src/server/messages/load-local-messages/cache/messages-pool.js +11 -0
- package/dist/svelte-kit/src/server/messages/load-local-messages/load-local-messages.js +107 -0
- package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +90 -0
- package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +102 -0
- package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.js +12 -0
- package/dist/svelte-kit/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +42 -0
- package/dist/svelte-kit/src/server/messages/load-messages.js +77 -0
- package/dist/svelte-kit/src/server/translator/create-translator.js +40 -0
- package/dist/svelte-kit/src/server/translator/init-translator.js +42 -0
- package/dist/types/export/next/index.d.ts +1 -1
- package/dist/types/export/next/server/index.d.ts +1 -1
- package/dist/types/export/react/index.d.ts +2 -2
- package/dist/types/export/svelte/index.d.ts +3 -1
- package/dist/types/export/svelte-kit/index.d.ts +1 -0
- package/dist/types/export/svelte-kit/server/index.d.ts +1 -0
- package/dist/types/src/adapters/express/global.d.ts +5 -8
- package/dist/types/src/adapters/next/navigation/index.d.ts +0 -1
- package/dist/types/src/adapters/next/server/index.d.ts +0 -1
- package/dist/types/src/adapters/svelte-kit/navigation/index.d.ts +1 -0
- package/dist/types/src/adapters/svelte-kit/navigation/use-navigation.d.ts +15 -0
- package/dist/types/src/adapters/svelte-kit/server/create-intor-handle.d.ts +12 -0
- package/dist/types/src/adapters/svelte-kit/server/index.d.ts +2 -0
- package/dist/types/src/adapters/svelte-kit/server/intor.d.ts +16 -0
- package/dist/types/src/client/index.d.ts +1 -0
- package/dist/types/src/client/react/index.d.ts +0 -1
- package/dist/types/src/client/shared/navigation/execute-navigation.d.ts +19 -0
- package/dist/types/src/client/shared/navigation/index.d.ts +1 -0
- package/dist/types/src/client/svelte/index.d.ts +2 -2
- package/dist/types/src/client/svelte/provider/create-intor-store.d.ts +3 -0
- package/dist/types/src/client/svelte/provider/index.d.ts +3 -0
- package/dist/types/src/client/svelte/provider/types.d.ts +18 -0
- package/dist/types/src/client/svelte/provider/use-intor-context.d.ts +2 -0
- package/dist/types/src/client/svelte/render/types.d.ts +7 -13
- package/dist/types/src/client/svelte/translator/create-t-rich.d.ts +15 -0
- package/dist/types/src/client/svelte/translator/index.d.ts +1 -0
- package/dist/types/src/client/svelte/translator/translator-instance.d.ts +20 -0
- package/dist/types/src/client/svelte/translator/use-translator.d.ts +8 -0
- package/dist/types/src/core/index.d.ts +1 -1
- package/dist/types/src/core/messages/load-remote-messages/fetch-remote-resource.d.ts +3 -1
- package/dist/types/src/core/messages/load-remote-messages/load-remote-messages.d.ts +1 -1
- package/dist/types/src/core/messages/load-remote-messages/types.d.ts +2 -0
- package/dist/types/src/core/types/index.d.ts +1 -0
- package/dist/types/src/core/types/runtime-fetch.d.ts +1 -0
- package/dist/types/src/routing/inbound/index.d.ts +1 -0
- package/dist/types/src/routing/inbound/resolve-inbound.d.ts +1 -12
- package/dist/types/src/routing/inbound/types.d.ts +12 -0
- package/dist/types/src/routing/index.d.ts +1 -1
- package/dist/types/src/routing/pathname/canonicalize-pathname.d.ts +2 -0
- package/dist/types/src/server/helpers/get-translator.d.ts +2 -1
- package/dist/types/src/server/intor/intor.d.ts +4 -3
- package/dist/types/src/server/intor/types.d.ts +0 -2
- package/dist/types/src/server/messages/load-messages.d.ts +1 -1
- package/dist/types/src/server/messages/types.d.ts +2 -1
- package/dist/types/src/server/translator/init-translator.d.ts +2 -2
- package/dist/vue/src/client/shared/messages/create-refetch-messages.js +1 -0
- package/dist/vue/src/core/messages/load-remote-messages/fetch-remote-resource.js +1 -1
- package/dist/vue/src/core/messages/load-remote-messages/load-remote-messages.js +2 -2
- package/package.json +11 -1
- package/dist/next/src/adapters/next/navigation/use-pathname.js +0 -26
- package/dist/next/src/adapters/next/server/get-pathname.js +0 -28
- package/dist/react/src/client/react/navigation/use-execute-navigation.js +0 -41
- package/dist/react/src/client/react/navigation/use-resolve-navigation.js +0 -16
- package/dist/svelte/src/client/svelte/helpers/create-intor.js +0 -45
- package/dist/svelte/src/client/svelte/store/create-translator-bindings.js +0 -25
- package/dist/types/src/adapters/next/navigation/use-pathname.d.ts +0 -14
- package/dist/types/src/adapters/next/server/get-pathname.d.ts +0 -16
- package/dist/types/src/client/react/navigation/index.d.ts +0 -2
- package/dist/types/src/client/react/navigation/use-execute-navigation.d.ts +0 -5
- package/dist/types/src/client/react/navigation/use-resolve-navigation.d.ts +0 -5
- package/dist/types/src/client/svelte/helpers/create-intor.d.ts +0 -4
- package/dist/types/src/client/svelte/helpers/index.d.ts +0 -1
- package/dist/types/src/client/svelte/store/create-intor-store.d.ts +0 -2
- package/dist/types/src/client/svelte/store/create-translator-bindings.d.ts +0 -13
- package/dist/types/src/client/svelte/store/index.d.ts +0 -2
- package/dist/types/src/client/svelte/store/types.d.ts +0 -31
- /package/dist/{react → next}/src/policies/should-sync-locale.js +0 -0
- /package/dist/svelte/src/client/svelte/{store → provider}/effects/locale-effects.js +0 -0
- /package/dist/svelte/src/client/svelte/{store → provider}/effects/messages-effects.js +0 -0
- /package/dist/{react → svelte-kit}/src/core/constants/prefix-placeholder.js +0 -0
- /package/dist/{react → svelte-kit}/src/core/utils/normalizers/normalize-pathname.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/navigation/decide-strategy.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/navigation/derive-target.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/navigation/resolve-navigation.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/navigation/utils/derive-host-destination.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/navigation/utils/derive-query-destination.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/navigation/utils/is-external-destination.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/pathname/localize-pathname.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/pathname/materialize-pathname.js +0 -0
- /package/dist/{react → svelte-kit}/src/routing/pathname/standardize-pathname.js +0 -0
- /package/dist/types/src/client/svelte/{store → provider}/effects/locale-effects.d.ts +0 -0
- /package/dist/types/src/client/svelte/{store → provider}/effects/messages-effects.d.ts +0 -0
|
@@ -17,7 +17,7 @@ import { loadLocalMessages } from './load-local-messages/load-local-messages.js'
|
|
|
17
17
|
* Message traversal, parsing, fallback resolution, and caching logic
|
|
18
18
|
* are delegated to the selected loader.
|
|
19
19
|
*/
|
|
20
|
-
const loadMessages = async ({ config, locale, readers, allowCacheWrite = false, }) => {
|
|
20
|
+
const loadMessages = async ({ config, locale, readers, allowCacheWrite = false, fetch, }) => {
|
|
21
21
|
const baseLogger = getLogger(config.logger);
|
|
22
22
|
const logger = baseLogger.child({ scope: "load-messages" });
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
@@ -61,6 +61,7 @@ const loadMessages = async ({ config, locale, readers, allowCacheWrite = false,
|
|
|
61
61
|
fallbackLocales,
|
|
62
62
|
namespaces,
|
|
63
63
|
concurrency,
|
|
64
|
+
fetch,
|
|
64
65
|
url: loader.url,
|
|
65
66
|
headers: loader.headers,
|
|
66
67
|
loggerOptions: config.logger,
|
|
@@ -14,13 +14,19 @@ import { createTranslator } from './create-translator.js';
|
|
|
14
14
|
* - Returns an immutable translator snapshot.
|
|
15
15
|
*/
|
|
16
16
|
async function initTranslator(config, locale, options) {
|
|
17
|
-
const { readers, allowCacheWrite = false, preKey, handlers, plugins, } = options
|
|
17
|
+
const { readers, allowCacheWrite = false, fetch, preKey, handlers, plugins, } = options;
|
|
18
18
|
const loader = resolveLoaderOptions(config, "server");
|
|
19
19
|
// Load messages
|
|
20
20
|
let messages = {};
|
|
21
21
|
if (loader) {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const loaded = await loadMessages({
|
|
23
|
+
config,
|
|
24
|
+
locale,
|
|
25
|
+
readers,
|
|
26
|
+
allowCacheWrite,
|
|
27
|
+
fetch,
|
|
28
|
+
});
|
|
29
|
+
messages = loaded || {};
|
|
24
30
|
}
|
|
25
31
|
// Create immutable translator snapshot
|
|
26
32
|
return createTranslator({
|
|
@@ -2,7 +2,5 @@ export { IntorProvider } from '../../src/client/react/provider/intor-provider.js
|
|
|
2
2
|
export { useIntorContext } from '../../src/client/react/provider/use-intor-context.js';
|
|
3
3
|
export { useTranslator } from '../../src/client/react/translator/use-translator.js';
|
|
4
4
|
export { Trans } from '../../src/client/react/translator/trans.js';
|
|
5
|
-
export { useResolveNavigation } from '../../src/client/react/navigation/use-resolve-navigation.js';
|
|
6
|
-
export { useExecuteNavigation } from '../../src/client/react/navigation/use-execute-navigation.js';
|
|
7
5
|
export { useIntor } from '../../src/client/react/helpers/use-intor.js';
|
|
8
6
|
export { getClientLocale } from '../../src/client/shared/helpers/get-client-locale.js';
|
|
@@ -30,6 +30,7 @@ const createRefetchMessages = ({ config, onLoadingStart, onLoadingEnd, onMessage
|
|
|
30
30
|
fallbackLocales: config.fallbackLocales[newLocale] || [],
|
|
31
31
|
namespaces: loader.namespaces,
|
|
32
32
|
concurrency: loader.concurrency,
|
|
33
|
+
fetch: globalThis.fetch,
|
|
33
34
|
url: loader.url,
|
|
34
35
|
headers: loader.headers,
|
|
35
36
|
signal: currentController.signal,
|
|
@@ -12,7 +12,7 @@ import { isValidMessages } from '../utils/is-valid-messages.js';
|
|
|
12
12
|
* - Validating the returned message structure
|
|
13
13
|
* - Handling abort and network errors
|
|
14
14
|
*/
|
|
15
|
-
async function fetchRemoteResource({ url, headers, signal, loggerOptions, }) {
|
|
15
|
+
async function fetchRemoteResource({ fetch, url, headers, signal, loggerOptions, }) {
|
|
16
16
|
const baseLogger = getLogger(loggerOptions);
|
|
17
17
|
const logger = baseLogger.child({ scope: "fetch-locale-messages" });
|
|
18
18
|
try {
|
|
@@ -18,7 +18,7 @@ import { resolveRemoteResources } from './resolve-remote-resources.js';
|
|
|
18
18
|
*
|
|
19
19
|
* Network requests and response validation are delegated to lower-level utilities.
|
|
20
20
|
*/
|
|
21
|
-
const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurrency, url: baseUrl, headers, signal, loggerOptions, }) => {
|
|
21
|
+
const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurrency, fetch, url: baseUrl, headers, signal, loggerOptions, }) => {
|
|
22
22
|
const baseLogger = getLogger(loggerOptions);
|
|
23
23
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
24
24
|
// Abort early if the request has already been cancelled
|
|
@@ -49,7 +49,7 @@ const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurr
|
|
|
49
49
|
// -----------------------------------------------------------------
|
|
50
50
|
// Fetch all message chunks in parallel
|
|
51
51
|
// -----------------------------------------------------------------
|
|
52
|
-
const fetchUrl = (url) => fetchRemoteResource({ url, headers, signal, loggerOptions });
|
|
52
|
+
const fetchUrl = (url) => fetchRemoteResource({ url, headers, signal, loggerOptions, fetch });
|
|
53
53
|
const results = await Promise.all(resources.map(({ url }) => limit ? limit(() => fetchUrl(url)) : fetchUrl(url)));
|
|
54
54
|
// Guard: no valid remote resources
|
|
55
55
|
if (!results.some(Boolean))
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export { createIntorStore } from '../../src/client/svelte/
|
|
2
|
-
export {
|
|
1
|
+
export { createIntorStore } from '../../src/client/svelte/provider/create-intor-store.js';
|
|
2
|
+
export { default as IntorProvider } from '../../src/client/svelte/provider/intor-provider.svelte';
|
|
3
|
+
export { useIntorContext } from '../../src/client/svelte/provider/use-intor-context.js';
|
|
4
|
+
export { useTranslator } from '../../src/client/svelte/translator/use-translator.js';
|
|
3
5
|
export { getClientLocale } from '../../src/client/shared/helpers/get-client-locale.js';
|
|
@@ -30,6 +30,7 @@ const createRefetchMessages = ({ config, onLoadingStart, onLoadingEnd, onMessage
|
|
|
30
30
|
fallbackLocales: config.fallbackLocales[newLocale] || [],
|
|
31
31
|
namespaces: loader.namespaces,
|
|
32
32
|
concurrency: loader.concurrency,
|
|
33
|
+
fetch: globalThis.fetch,
|
|
33
34
|
url: loader.url,
|
|
34
35
|
headers: loader.headers,
|
|
35
36
|
signal: currentController.signal,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Translator } from 'intor-translator';
|
|
2
|
+
import { setContext } from 'svelte';
|
|
2
3
|
import { writable, readable, derived, get } from 'svelte/store';
|
|
3
|
-
import { createTranslatorBindings } from './create-translator-bindings.js';
|
|
4
4
|
import { attachLocaleEffects } from './effects/locale-effects.js';
|
|
5
5
|
import { attachMessagesEffects } from './effects/messages-effects.js';
|
|
6
6
|
|
|
7
|
+
const INTOR_CONTEXT_KEY = Symbol("intor:svelte");
|
|
7
8
|
function createIntorStore({ config, locale: initialLocale, messages, handlers, plugins, onLocaleChange, isLoading: externalIsLoading, }) {
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
9
10
|
// Internal state
|
|
@@ -25,11 +26,12 @@ function createIntorStore({ config, locale: initialLocale, messages, handlers, p
|
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
26
27
|
// Effective state
|
|
27
28
|
// ---------------------------------------------------------------------------
|
|
28
|
-
const externalIsLoadingStore =
|
|
29
|
+
const externalIsLoadingStore = readable(!!externalIsLoading);
|
|
29
30
|
// external > internal
|
|
30
31
|
const effectiveIsLoading = derived([externalIsLoadingStore, internalIsLoading], ([$external, $internal]) => $external || $internal);
|
|
31
32
|
// runtime (client refetch) > initial > config (static)
|
|
32
|
-
const
|
|
33
|
+
const initialMessagesStore = readable(messages || config.messages || {});
|
|
34
|
+
const effectiveMessages = derived([runtimeMessages, initialMessagesStore], ([$runtime, $initial]) => $runtime || $initial);
|
|
33
35
|
// ---------------------------------------------------------------------------
|
|
34
36
|
// Translator
|
|
35
37
|
// ---------------------------------------------------------------------------
|
|
@@ -48,16 +50,7 @@ function createIntorStore({ config, locale: initialLocale, messages, handlers, p
|
|
|
48
50
|
// ---------------------------------------------------------------------------
|
|
49
51
|
attachLocaleEffects(locale, config);
|
|
50
52
|
attachMessagesEffects({ config, locale, runtimeMessages, internalIsLoading });
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
messages: effectiveMessages,
|
|
54
|
-
locale,
|
|
55
|
-
isLoading: effectiveIsLoading,
|
|
56
|
-
setLocale,
|
|
57
|
-
scoped,
|
|
58
|
-
t,
|
|
59
|
-
tRich,
|
|
60
|
-
};
|
|
53
|
+
setContext(INTOR_CONTEXT_KEY, { config, locale, setLocale, translator });
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
export { createIntorStore };
|
|
56
|
+
export { INTOR_CONTEXT_KEY, createIntorStore };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getContext } from 'svelte';
|
|
2
|
+
import { INTOR_CONTEXT_KEY } from './create-intor-store.js';
|
|
3
|
+
|
|
4
|
+
function useIntorContext() {
|
|
5
|
+
const context = getContext(INTOR_CONTEXT_KEY);
|
|
6
|
+
if (!context)
|
|
7
|
+
throw new Error("useIntorContext must be used within IntorProvider");
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { useIntorContext };
|
|
@@ -18,12 +18,11 @@ const createSvelteRenderer = (options) => {
|
|
|
18
18
|
},
|
|
19
19
|
/** Render semantic tag nodes */
|
|
20
20
|
tag(name, attributes, children) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
: renderer;
|
|
21
|
+
const tagRenderer = tagRenderers?.[name];
|
|
22
|
+
if (tagRenderer) {
|
|
23
|
+
return typeof tagRenderer === "function"
|
|
24
|
+
? tagRenderer(children)
|
|
25
|
+
: tagRenderer;
|
|
27
26
|
}
|
|
28
27
|
// Default behavior: render as native HTML tag
|
|
29
28
|
return `<${name}${renderAttributes(attributes)}>${children.join("")}</${name}>`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { renderRichMessageSvelte } from '../render/render-rich-message-svelte.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a Svelte-specific rich translation function.
|
|
5
|
+
*
|
|
6
|
+
* This adapter bridges the core Translator with the Svelte rich
|
|
7
|
+
* message rendering flow.
|
|
8
|
+
*
|
|
9
|
+
* - Resolves translated messages via `translator.t`
|
|
10
|
+
* - Renders semantic tags using Svelte renderers
|
|
11
|
+
* - Supports optional scoped keys via `preKey`
|
|
12
|
+
*
|
|
13
|
+
* Intended for Svelte client usage only.
|
|
14
|
+
*/
|
|
15
|
+
const createTRich = (translator, preKey) => {
|
|
16
|
+
const t = preKey ? translator.scoped(preKey).t : translator.t;
|
|
17
|
+
return (key, tagRenderers, replacements) => {
|
|
18
|
+
const message = t(key, replacements);
|
|
19
|
+
return renderRichMessageSvelte(message, tagRenderers);
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { createTRich };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { derived } from 'svelte/store';
|
|
2
|
+
import 'intor-translator';
|
|
3
|
+
import 'svelte';
|
|
4
|
+
import '../../../core/error/intor-error.js';
|
|
5
|
+
import 'logry';
|
|
6
|
+
import 'p-limit';
|
|
7
|
+
import '../provider/intor-provider.svelte';
|
|
8
|
+
import { useIntorContext } from '../provider/use-intor-context.js';
|
|
9
|
+
import { createTRich } from './create-t-rich.js';
|
|
10
|
+
|
|
11
|
+
// Implementation
|
|
12
|
+
function useTranslator(preKey) {
|
|
13
|
+
const { translator, locale, setLocale } = useIntorContext();
|
|
14
|
+
const scoped = preKey
|
|
15
|
+
? derived(translator, ($t) => $t.scoped(preKey))
|
|
16
|
+
: translator;
|
|
17
|
+
return {
|
|
18
|
+
messages: derived(translator, ($t) => $t.messages),
|
|
19
|
+
locale,
|
|
20
|
+
isLoading: derived(translator, ($t) => $t.isLoading),
|
|
21
|
+
setLocale,
|
|
22
|
+
hasKey: derived(scoped, ($t) => $t.hasKey),
|
|
23
|
+
t: derived(scoped, ($t) => $t.t),
|
|
24
|
+
tRich: derived(translator, ($t) => createTRich($t, preKey)),
|
|
25
|
+
// NOTE:
|
|
26
|
+
// The runtime implementation is intentionally erased.
|
|
27
|
+
// Type safety is guaranteed by public type contracts.
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { useTranslator };
|
|
@@ -12,7 +12,7 @@ import { isValidMessages } from '../utils/is-valid-messages.js';
|
|
|
12
12
|
* - Validating the returned message structure
|
|
13
13
|
* - Handling abort and network errors
|
|
14
14
|
*/
|
|
15
|
-
async function fetchRemoteResource({ url, headers, signal, loggerOptions, }) {
|
|
15
|
+
async function fetchRemoteResource({ fetch, url, headers, signal, loggerOptions, }) {
|
|
16
16
|
const baseLogger = getLogger(loggerOptions);
|
|
17
17
|
const logger = baseLogger.child({ scope: "fetch-locale-messages" });
|
|
18
18
|
try {
|
|
@@ -18,7 +18,7 @@ import { resolveRemoteResources } from './resolve-remote-resources.js';
|
|
|
18
18
|
*
|
|
19
19
|
* Network requests and response validation are delegated to lower-level utilities.
|
|
20
20
|
*/
|
|
21
|
-
const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurrency, url: baseUrl, headers, signal, loggerOptions, }) => {
|
|
21
|
+
const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurrency, fetch, url: baseUrl, headers, signal, loggerOptions, }) => {
|
|
22
22
|
const baseLogger = getLogger(loggerOptions);
|
|
23
23
|
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
24
24
|
// Abort early if the request has already been cancelled
|
|
@@ -49,7 +49,7 @@ const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurr
|
|
|
49
49
|
// -----------------------------------------------------------------
|
|
50
50
|
// Fetch all message chunks in parallel
|
|
51
51
|
// -----------------------------------------------------------------
|
|
52
|
-
const fetchUrl = (url) => fetchRemoteResource({ url, headers, signal, loggerOptions });
|
|
52
|
+
const fetchUrl = (url) => fetchRemoteResource({ url, headers, signal, loggerOptions, fetch });
|
|
53
53
|
const results = await Promise.all(resources.map(({ url }) => limit ? limit(() => fetchUrl(url)) : fetchUrl(url)));
|
|
54
54
|
// Guard: no valid remote resources
|
|
55
55
|
if (!results.some(Boolean))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useNavigation } from '../../src/adapters/svelte-kit/navigation/use-navigation.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { get } from 'svelte/store';
|
|
2
|
+
import { executeNavigation } from '../../../client/shared/navigation/execute-navigation.js';
|
|
3
|
+
import { useIntorContext } from 'intor/svelte';
|
|
4
|
+
import '../../../core/error/intor-error.js';
|
|
5
|
+
import 'logry';
|
|
6
|
+
import 'p-limit';
|
|
7
|
+
import { resolveNavigation } from '../../../routing/navigation/resolve-navigation.js';
|
|
8
|
+
import { goto } from '$app/navigation';
|
|
9
|
+
import { page } from '$app/state';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Locale-aware navigation utilities for SvelteKit.
|
|
13
|
+
*
|
|
14
|
+
* Provides imperative navigation helpers that integrate
|
|
15
|
+
* Intor's locale-aware routing and side effects.
|
|
16
|
+
*
|
|
17
|
+
* @platform SvelteKit
|
|
18
|
+
*/
|
|
19
|
+
function useNavigation() {
|
|
20
|
+
const { config, locale: currentLocale, setLocale } = useIntorContext();
|
|
21
|
+
async function goto$1(url, opts) {
|
|
22
|
+
const { locale, ...rest } = opts || {};
|
|
23
|
+
const navigationResult = resolveNavigation(config, get(currentLocale), page.url.pathname, { destination: url, locale });
|
|
24
|
+
executeNavigation(navigationResult, {
|
|
25
|
+
config,
|
|
26
|
+
currentLocale: get(currentLocale),
|
|
27
|
+
setLocale,
|
|
28
|
+
});
|
|
29
|
+
return goto(navigationResult.destination, rest);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
goto: goto$1,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { useNavigation };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { redirect } from '@sveltejs/kit';
|
|
2
|
+
import { INTOR_HEADERS } from '../../../core/constants/headers.js';
|
|
3
|
+
import '../../../core/error/intor-error.js';
|
|
4
|
+
import { normalizeQuery } from '../../../core/utils/normalizers/normalize-query.js';
|
|
5
|
+
import 'logry';
|
|
6
|
+
import 'p-limit';
|
|
7
|
+
import { resolveInbound } from '../../../routing/inbound/resolve-inbound.js';
|
|
8
|
+
import { getLocaleFromAcceptLanguage } from '../../../routing/locale/get-locale-from-accept-language.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolves locale-aware routing for the current execution context.
|
|
12
|
+
*
|
|
13
|
+
* The resolved routing state is exposed via response headers.
|
|
14
|
+
*
|
|
15
|
+
* - Acts as the canonical routing authority within the SvelteKit request lifecycle.
|
|
16
|
+
*
|
|
17
|
+
* @platform SvelteKit
|
|
18
|
+
*/
|
|
19
|
+
function createIntorHandle(config) {
|
|
20
|
+
return async ({ event, resolve }) => {
|
|
21
|
+
// ----------------------------------------------------------
|
|
22
|
+
// Locale from Accept-Language header
|
|
23
|
+
// ----------------------------------------------------------
|
|
24
|
+
const acceptLanguage = event.request.headers.get("accept-language");
|
|
25
|
+
const localeFromAcceptLanguage = getLocaleFromAcceptLanguage(acceptLanguage, config.supportedLocales);
|
|
26
|
+
// ----------------------------------------------------------
|
|
27
|
+
// Resolve inbound routing decision (pure computation)
|
|
28
|
+
// ----------------------------------------------------------
|
|
29
|
+
const { locale, localeSource, pathname, shouldRedirect } = await resolveInbound(config, event.url.pathname, false, {
|
|
30
|
+
host: event.url.host,
|
|
31
|
+
query: normalizeQuery(Object.fromEntries(event.url.searchParams.entries())),
|
|
32
|
+
cookie: event.cookies.get(config.cookie.name),
|
|
33
|
+
detected: localeFromAcceptLanguage || config.defaultLocale,
|
|
34
|
+
});
|
|
35
|
+
// ----------------------------------------------------------
|
|
36
|
+
// Redirect if needed
|
|
37
|
+
// ----------------------------------------------------------
|
|
38
|
+
if (shouldRedirect) {
|
|
39
|
+
throw redirect(307, pathname);
|
|
40
|
+
}
|
|
41
|
+
// ----------------------------------------------------------
|
|
42
|
+
// Attach routing metadata
|
|
43
|
+
// ----------------------------------------------------------
|
|
44
|
+
// @ts-expect-error - App.Locals must be extended by user
|
|
45
|
+
event.locals.intor = {
|
|
46
|
+
locale,
|
|
47
|
+
localeSource,
|
|
48
|
+
pathname,
|
|
49
|
+
};
|
|
50
|
+
const response = await resolve(event);
|
|
51
|
+
response.headers.set(INTOR_HEADERS.LOCALE, locale);
|
|
52
|
+
response.headers.set(INTOR_HEADERS.LOCALE_SOURCE, localeSource);
|
|
53
|
+
response.headers.set(INTOR_HEADERS.PATHNAME, pathname);
|
|
54
|
+
return response;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { createIntorHandle };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import '../../../core/error/intor-error.js';
|
|
2
|
+
import 'logry';
|
|
3
|
+
import 'p-limit';
|
|
4
|
+
import { intor as intor$1 } from '../../../server/intor/intor.js';
|
|
5
|
+
import 'node:path';
|
|
6
|
+
import 'node:fs/promises';
|
|
7
|
+
import 'intor-translator';
|
|
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 };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import '../../../core/error/intor-error.js';
|
|
2
|
+
import 'logry';
|
|
3
|
+
import 'p-limit';
|
|
4
|
+
import { shouldSyncLocale } from '../../../policies/should-sync-locale.js';
|
|
5
|
+
import { setLocaleCookie } from '../utils/locale/set-locale-cookie.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Executes a resolved navigation result.
|
|
9
|
+
*
|
|
10
|
+
* Applies all imperative side effects required to complete navigation,
|
|
11
|
+
* including locale synchronization, cookie persistence, and full reloads.
|
|
12
|
+
*
|
|
13
|
+
* This function must be called after `resolveNavigation`.
|
|
14
|
+
*/
|
|
15
|
+
function executeNavigation(navigationResult, context, e) {
|
|
16
|
+
const { config, currentLocale, setLocale } = context;
|
|
17
|
+
const { cookie } = config;
|
|
18
|
+
const { destination, kind, locale } = navigationResult;
|
|
19
|
+
// ------------------------------------------------------
|
|
20
|
+
// External navigation: let browser handle it
|
|
21
|
+
// ------------------------------------------------------
|
|
22
|
+
if (kind === "external") {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// ------------------------------------------------------
|
|
26
|
+
// Full reload: commit locale side effects, then perform document reload
|
|
27
|
+
// ------------------------------------------------------
|
|
28
|
+
if (kind === "reload") {
|
|
29
|
+
if (shouldSyncLocale(locale, currentLocale)) {
|
|
30
|
+
if (cookie.persist) {
|
|
31
|
+
setLocaleCookie(cookie, locale);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
globalThis.location.href = destination;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// ------------------------------------------------------
|
|
38
|
+
// Client-side navigation only
|
|
39
|
+
// ------------------------------------------------------
|
|
40
|
+
if (shouldSyncLocale(locale, currentLocale)) {
|
|
41
|
+
// Eagerly persist locale to avoid stale cookie during client-side navigation.
|
|
42
|
+
if (cookie.persist) {
|
|
43
|
+
setLocaleCookie(cookie, locale);
|
|
44
|
+
}
|
|
45
|
+
setLocale(locale);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { executeNavigation };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a serialized cookie string.
|
|
3
|
+
*/
|
|
4
|
+
const buildCookieString = (cookieOptions, value) => {
|
|
5
|
+
const { name, maxAge, path, domain, sameSite, secure } = cookieOptions;
|
|
6
|
+
// Cookie name and encoded value
|
|
7
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
8
|
+
// Add expiration and max-age if provided
|
|
9
|
+
if (maxAge) {
|
|
10
|
+
const expires = new Date(Date.now() + maxAge * 1000).toUTCString();
|
|
11
|
+
parts.push(`expires=${expires}`, `max-age=${maxAge}`);
|
|
12
|
+
}
|
|
13
|
+
// Set path (default to "/")
|
|
14
|
+
parts.push(`path=${path ?? "/"}`);
|
|
15
|
+
// Add domain if specified
|
|
16
|
+
if (domain) {
|
|
17
|
+
parts.push(`domain=${domain}`);
|
|
18
|
+
}
|
|
19
|
+
// Add SameSite policy (e.g., Lax, Strict)
|
|
20
|
+
if (sameSite) {
|
|
21
|
+
parts.push(`SameSite=${sameSite[0].toUpperCase()}${sameSite.slice(1).toLowerCase()}`);
|
|
22
|
+
}
|
|
23
|
+
// Add Secure flag if not explicitly disabled
|
|
24
|
+
if (secure !== false) {
|
|
25
|
+
parts.push(`Secure`);
|
|
26
|
+
}
|
|
27
|
+
return parts.join("; ");
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { buildCookieString };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { buildCookieString } from '../build-cookie-string.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Persist locale to a cookie.
|
|
5
|
+
*
|
|
6
|
+
* This function relies on `document.cookie`.
|
|
7
|
+
*/
|
|
8
|
+
const setLocaleCookie = (cookieOptions, locale) => {
|
|
9
|
+
if (typeof document === "undefined")
|
|
10
|
+
return;
|
|
11
|
+
// Build and apply the cookie string
|
|
12
|
+
document.cookie = buildCookieString(cookieOptions, locale);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { setLocaleCookie };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var IntorErrorCode;
|
|
2
|
+
(function (IntorErrorCode) {
|
|
3
|
+
// config
|
|
4
|
+
IntorErrorCode["INVALID_CONFIG_ID"] = "INTOR_INVALID_CONFIG_ID";
|
|
5
|
+
IntorErrorCode["MISSING_SUPPORTED_LOCALES"] = "INTOR_MISSING_SUPPORTED_LOCALES";
|
|
6
|
+
IntorErrorCode["UNSUPPORTED_DEFAULT_LOCALE"] = "INTOR_UNSUPPORTED_DEFAULT_LOCALE";
|
|
7
|
+
})(IntorErrorCode || (IntorErrorCode = {}));
|
|
8
|
+
|
|
9
|
+
export { IntorErrorCode };
|
|
@@ -0,0 +1,39 @@
|
|
|
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 };
|
|
@@ -0,0 +1,25 @@
|
|
|
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 };
|
|
@@ -0,0 +1,47 @@
|
|
|
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 };
|