intor 2.3.28 → 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/get-locale.js +4 -1
- 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
|
@@ -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,4 @@
|
|
|
1
|
+
import { PREFIX_PLACEHOLDER } from '../../core/constants/prefix-placeholder.js';
|
|
1
2
|
import '../../core/error/intor-error.js';
|
|
2
3
|
import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
|
|
3
4
|
import 'logry';
|
|
@@ -6,6 +7,8 @@ import 'p-limit';
|
|
|
6
7
|
/**
|
|
7
8
|
* Returns a canonical, locale-agnostic pathname.
|
|
8
9
|
*
|
|
10
|
+
* Accepts `{locale}` as a locale placeholder segment.
|
|
11
|
+
*
|
|
9
12
|
* @example
|
|
10
13
|
* ```ts
|
|
11
14
|
* // config.supportedLocales: ["en-US"]
|
|
@@ -18,9 +21,8 @@ import 'p-limit';
|
|
|
18
21
|
function canonicalizePathname(rawPathname, config) {
|
|
19
22
|
const { routing, supportedLocales } = config;
|
|
20
23
|
const { basePath } = routing;
|
|
21
|
-
// 1. Normalize pathname
|
|
22
24
|
const normalizedPathname = normalizePathname(rawPathname);
|
|
23
|
-
//
|
|
25
|
+
// Strip basePath
|
|
24
26
|
let prefixedPathname = normalizedPathname;
|
|
25
27
|
if (basePath && normalizedPathname === basePath) {
|
|
26
28
|
prefixedPathname = "/";
|
|
@@ -28,12 +30,14 @@ function canonicalizePathname(rawPathname, config) {
|
|
|
28
30
|
else if (basePath && normalizedPathname.startsWith(basePath + "/")) {
|
|
29
31
|
prefixedPathname = normalizedPathname.slice(basePath.length);
|
|
30
32
|
}
|
|
31
|
-
//
|
|
33
|
+
// Detect locale segment
|
|
32
34
|
const firstSegment = prefixedPathname.split("/").find(Boolean);
|
|
33
|
-
const locale = firstSegment
|
|
34
|
-
?
|
|
35
|
-
:
|
|
36
|
-
|
|
35
|
+
const locale = firstSegment === PREFIX_PLACEHOLDER
|
|
36
|
+
? PREFIX_PLACEHOLDER
|
|
37
|
+
: firstSegment && supportedLocales.includes(firstSegment)
|
|
38
|
+
? firstSegment
|
|
39
|
+
: undefined;
|
|
40
|
+
// Strip locale segment
|
|
37
41
|
return locale
|
|
38
42
|
? prefixedPathname.slice(locale.length + 1) || "/"
|
|
39
43
|
: prefixedPathname;
|
|
@@ -2,12 +2,12 @@ import { initTranslator } from '../translator/init-translator.js';
|
|
|
2
2
|
|
|
3
3
|
// Implementation
|
|
4
4
|
async function getTranslator(config, params) {
|
|
5
|
-
const { readers, allowCacheWrite, preKey, handlers, plugins } = params;
|
|
6
|
-
const locale = params.locale;
|
|
5
|
+
const { locale, readers, allowCacheWrite, fetch, preKey, handlers, plugins } = params;
|
|
7
6
|
// Initialize a locale-bound translator snapshot with messages loaded
|
|
8
7
|
const translator = await initTranslator(config, locale, {
|
|
9
8
|
readers,
|
|
10
9
|
allowCacheWrite,
|
|
10
|
+
fetch: fetch || globalThis.fetch,
|
|
11
11
|
preKey,
|
|
12
12
|
plugins,
|
|
13
13
|
handlers,
|
|
@@ -9,21 +9,16 @@ import { initTranslator } from '../translator/init-translator.js';
|
|
|
9
9
|
* Produces a server-side snapshot for SSR and
|
|
10
10
|
* full-stack rendering environments.
|
|
11
11
|
*/
|
|
12
|
-
async function intor(config,
|
|
12
|
+
async function intor(config, locale, options) {
|
|
13
13
|
const baseLogger = getLogger(config.logger);
|
|
14
14
|
const logger = baseLogger.child({ scope: "intor" });
|
|
15
15
|
logger.info("Start Intor initialization.");
|
|
16
|
-
|
|
17
|
-
const isLocaleFunction = typeof localeOrResolver === "function";
|
|
18
|
-
const locale = isLocaleFunction
|
|
19
|
-
? await localeOrResolver(config)
|
|
20
|
-
: localeOrResolver || config.defaultLocale;
|
|
21
|
-
const source = typeof localeOrResolver === "function" ? "resolver" : "static";
|
|
22
|
-
logger.debug(`Initial locale resolved as "${locale}" via "${source}".`);
|
|
16
|
+
logger.debug(`Initializing Intor with locale "${locale}".`);
|
|
23
17
|
// Initialize a locale-bound translator snapshot with messages loaded
|
|
24
18
|
const translator = await initTranslator(config, locale, {
|
|
25
19
|
readers: options?.readers,
|
|
26
20
|
allowCacheWrite: options?.allowCacheWrite,
|
|
21
|
+
fetch: options?.fetch || globalThis.fetch,
|
|
27
22
|
});
|
|
28
23
|
logger.info("Intor initialized.");
|
|
29
24
|
return {
|
|
@@ -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({
|
|
@@ -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,4 @@
|
|
|
1
|
+
import { PREFIX_PLACEHOLDER } from '../../core/constants/prefix-placeholder.js';
|
|
1
2
|
import '../../core/error/intor-error.js';
|
|
2
3
|
import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
|
|
3
4
|
import 'logry';
|
|
@@ -6,6 +7,8 @@ import 'p-limit';
|
|
|
6
7
|
/**
|
|
7
8
|
* Returns a canonical, locale-agnostic pathname.
|
|
8
9
|
*
|
|
10
|
+
* Accepts `{locale}` as a locale placeholder segment.
|
|
11
|
+
*
|
|
9
12
|
* @example
|
|
10
13
|
* ```ts
|
|
11
14
|
* // config.supportedLocales: ["en-US"]
|
|
@@ -18,9 +21,8 @@ import 'p-limit';
|
|
|
18
21
|
function canonicalizePathname(rawPathname, config) {
|
|
19
22
|
const { routing, supportedLocales } = config;
|
|
20
23
|
const { basePath } = routing;
|
|
21
|
-
// 1. Normalize pathname
|
|
22
24
|
const normalizedPathname = normalizePathname(rawPathname);
|
|
23
|
-
//
|
|
25
|
+
// Strip basePath
|
|
24
26
|
let prefixedPathname = normalizedPathname;
|
|
25
27
|
if (basePath && normalizedPathname === basePath) {
|
|
26
28
|
prefixedPathname = "/";
|
|
@@ -28,12 +30,14 @@ function canonicalizePathname(rawPathname, config) {
|
|
|
28
30
|
else if (basePath && normalizedPathname.startsWith(basePath + "/")) {
|
|
29
31
|
prefixedPathname = normalizedPathname.slice(basePath.length);
|
|
30
32
|
}
|
|
31
|
-
//
|
|
33
|
+
// Detect locale segment
|
|
32
34
|
const firstSegment = prefixedPathname.split("/").find(Boolean);
|
|
33
|
-
const locale = firstSegment
|
|
34
|
-
?
|
|
35
|
-
:
|
|
36
|
-
|
|
35
|
+
const locale = firstSegment === PREFIX_PLACEHOLDER
|
|
36
|
+
? PREFIX_PLACEHOLDER
|
|
37
|
+
: firstSegment && supportedLocales.includes(firstSegment)
|
|
38
|
+
? firstSegment
|
|
39
|
+
: undefined;
|
|
40
|
+
// Strip locale segment
|
|
37
41
|
return locale
|
|
38
42
|
? prefixedPathname.slice(locale.length + 1) || "/"
|
|
39
43
|
: prefixedPathname;
|
|
@@ -2,12 +2,12 @@ import { initTranslator } from '../translator/init-translator.js';
|
|
|
2
2
|
|
|
3
3
|
// Implementation
|
|
4
4
|
async function getTranslator(config, params) {
|
|
5
|
-
const { readers, allowCacheWrite, preKey, handlers, plugins } = params;
|
|
6
|
-
const locale = params.locale;
|
|
5
|
+
const { locale, readers, allowCacheWrite, fetch, preKey, handlers, plugins } = params;
|
|
7
6
|
// Initialize a locale-bound translator snapshot with messages loaded
|
|
8
7
|
const translator = await initTranslator(config, locale, {
|
|
9
8
|
readers,
|
|
10
9
|
allowCacheWrite,
|
|
10
|
+
fetch: fetch || globalThis.fetch,
|
|
11
11
|
preKey,
|
|
12
12
|
plugins,
|
|
13
13
|
handlers,
|
|
@@ -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);
|
|
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({
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { Link } from '../../src/adapters/next/navigation/link.js';
|
|
2
|
-
export { usePathname } from '../../src/adapters/next/navigation/use-pathname.js';
|
|
3
2
|
export { useRouter } from '../../src/adapters/next/navigation/use-router.js';
|
|
4
3
|
export { redirect } from '../../src/adapters/next/navigation/redirect.js';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { intor } from '../../../src/adapters/next/server/intor.js';
|
|
2
2
|
export { getLocale } from '../../../src/adapters/next/server/get-locale.js';
|
|
3
|
-
export { getPathname } from '../../../src/adapters/next/server/get-pathname.js';
|
|
4
3
|
export { getTranslator } from '../../../src/adapters/next/server/get-translator.js';
|
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { formatUrl } from 'next/dist/shared/lib/router/utils/format-url';
|
|
4
4
|
import NextLink from 'next/link';
|
|
5
|
+
import { usePathname } from 'next/navigation';
|
|
5
6
|
import 'react';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { executeNavigation } from '../../../client/shared/navigation/execute-navigation.js';
|
|
8
|
+
import { useIntorContext } from 'intor/react';
|
|
9
|
+
import '../../../core/error/intor-error.js';
|
|
10
|
+
import 'logry';
|
|
11
|
+
import 'p-limit';
|
|
12
|
+
import { resolveNavigation } from '../../../routing/navigation/resolve-navigation.js';
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
15
|
* Render a locale-aware link for the current execution context.
|
|
@@ -15,16 +20,12 @@ import { usePathname } from './use-pathname.js';
|
|
|
15
20
|
* @platform Next.js
|
|
16
21
|
*/
|
|
17
22
|
const Link = ({ href, locale, children, onClick, ...props }) => {
|
|
18
|
-
const {
|
|
19
|
-
const
|
|
20
|
-
const executeNavigation = useExecuteNavigation();
|
|
23
|
+
const { config, locale: currentLocale, setLocale } = useIntorContext();
|
|
24
|
+
const currentPathname = usePathname();
|
|
21
25
|
// Normalize href into a string destination
|
|
22
26
|
const rawDestination = typeof href === "string" ? href : href ? formatUrl(href) : undefined;
|
|
23
27
|
// Resolve navigation result for this link
|
|
24
|
-
const navigationResult = resolveNavigation(
|
|
25
|
-
destination: rawDestination,
|
|
26
|
-
locale,
|
|
27
|
-
});
|
|
28
|
+
const navigationResult = resolveNavigation(config, currentLocale, currentPathname, { destination: rawDestination, locale });
|
|
28
29
|
// --------------------------------------------------
|
|
29
30
|
// Execute navigation on user interaction
|
|
30
31
|
// --------------------------------------------------
|
|
@@ -32,7 +33,7 @@ const Link = ({ href, locale, children, onClick, ...props }) => {
|
|
|
32
33
|
onClick?.(e);
|
|
33
34
|
if (e.defaultPrevented)
|
|
34
35
|
return;
|
|
35
|
-
executeNavigation(navigationResult, e);
|
|
36
|
+
executeNavigation(navigationResult, { config, currentLocale, setLocale }, e);
|
|
36
37
|
};
|
|
37
38
|
return (jsx(NextLink, { href: navigationResult.destination, onClick: handleClick, ...props, children: children }));
|
|
38
39
|
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { useRouter as useRouter$1 } from 'next/navigation';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { usePathname, useRouter as useRouter$1 } from 'next/navigation';
|
|
2
|
+
import { executeNavigation } from '../../../client/shared/navigation/execute-navigation.js';
|
|
3
|
+
import { useIntorContext } from 'intor/react';
|
|
4
|
+
import '../../../core/error/intor-error.js';
|
|
5
|
+
import 'logry';
|
|
6
|
+
import 'p-limit';
|
|
7
|
+
import { resolveNavigation } from '../../../routing/navigation/resolve-navigation.js';
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* Locale-aware router hook for the current execution context.
|
|
@@ -11,40 +15,30 @@ import { usePathname } from './use-pathname.js';
|
|
|
11
15
|
* @platform Next.js
|
|
12
16
|
*/
|
|
13
17
|
const useRouter = () => {
|
|
18
|
+
const { config, locale: currentLocale, setLocale } = useIntorContext();
|
|
19
|
+
const currentPathname = usePathname();
|
|
14
20
|
const { push: nextRouterPush, replace: nextRouterReplace, prefetch: nextRouterPrefetch, ...rest } = useRouter$1();
|
|
15
|
-
const { pathname } = usePathname();
|
|
16
|
-
const resolveNavigation = useResolveNavigation();
|
|
17
|
-
const executeNavigation = useExecuteNavigation();
|
|
18
21
|
// --------------------------------------------------
|
|
19
22
|
// push
|
|
20
23
|
// --------------------------------------------------
|
|
21
24
|
const push = (href, options) => {
|
|
22
|
-
const navigationResult = resolveNavigation(
|
|
23
|
-
|
|
24
|
-
locale: options?.locale,
|
|
25
|
-
});
|
|
26
|
-
executeNavigation(navigationResult);
|
|
25
|
+
const navigationResult = resolveNavigation(config, currentLocale, currentPathname, { destination: href, locale: options?.locale });
|
|
26
|
+
executeNavigation(navigationResult, { config, currentLocale, setLocale });
|
|
27
27
|
nextRouterPush(navigationResult.destination, options);
|
|
28
28
|
};
|
|
29
29
|
// --------------------------------------------------
|
|
30
30
|
// replace
|
|
31
31
|
// --------------------------------------------------
|
|
32
32
|
const replace = (href, options) => {
|
|
33
|
-
const navigationResult = resolveNavigation(
|
|
34
|
-
|
|
35
|
-
locale: options?.locale,
|
|
36
|
-
});
|
|
37
|
-
executeNavigation(navigationResult);
|
|
33
|
+
const navigationResult = resolveNavigation(config, currentLocale, currentPathname, { destination: href, locale: options?.locale });
|
|
34
|
+
executeNavigation(navigationResult, { config, currentLocale, setLocale });
|
|
38
35
|
nextRouterReplace(navigationResult.destination, options);
|
|
39
36
|
};
|
|
40
37
|
// --------------------------------------------------
|
|
41
38
|
// prefetch
|
|
42
39
|
// --------------------------------------------------
|
|
43
40
|
const prefetch = (href, options) => {
|
|
44
|
-
const { kind, destination } = resolveNavigation(
|
|
45
|
-
destination: href,
|
|
46
|
-
locale: options?.locale,
|
|
47
|
-
});
|
|
41
|
+
const { kind, destination } = resolveNavigation(config, currentLocale, currentPathname, { destination: href, locale: options?.locale });
|
|
48
42
|
if (kind !== "client")
|
|
49
43
|
return; // Prefetch only makes sense for client-side navigation
|
|
50
44
|
nextRouterPrefetch(destination, options);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { headers, cookies } from 'next/headers';
|
|
2
2
|
import { INTOR_HEADERS } from '../../../core/constants/headers.js';
|
|
3
3
|
import '../../../core/error/intor-error.js';
|
|
4
|
+
import { normalizeLocale } from '../../../core/utils/normalizers/normalize-locale.js';
|
|
4
5
|
import 'logry';
|
|
5
6
|
import 'p-limit';
|
|
6
7
|
|
|
@@ -21,7 +22,9 @@ const getLocale = async (config) => {
|
|
|
21
22
|
const cookieStore = await cookies();
|
|
22
23
|
const cookieLocale = cookieStore.get(config.cookie.name)?.value;
|
|
23
24
|
if (cookieLocale) {
|
|
24
|
-
|
|
25
|
+
const resolved = normalizeLocale(cookieLocale, config.supportedLocales);
|
|
26
|
+
if (resolved)
|
|
27
|
+
return resolved;
|
|
25
28
|
}
|
|
26
29
|
// Explicit default
|
|
27
30
|
return config.defaultLocale;
|
|
@@ -15,7 +15,7 @@ import { getLocale } from './get-locale.js';
|
|
|
15
15
|
* @platform Next.js
|
|
16
16
|
*/
|
|
17
17
|
async function intor(config, options) {
|
|
18
|
-
return await intor$1(config, getLocale, {
|
|
18
|
+
return await intor$1(config, await getLocale(config), {
|
|
19
19
|
readers: options?.readers,
|
|
20
20
|
allowCacheWrite: options?.allowCacheWrite ?? true,
|
|
21
21
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
e?.preventDefault();
|
|
30
|
+
if (shouldSyncLocale(locale, currentLocale)) {
|
|
31
|
+
if (cookie.persist) {
|
|
32
|
+
setLocaleCookie(cookie, locale);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
globalThis.location.href = destination;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// ------------------------------------------------------
|
|
39
|
+
// Client-side navigation only
|
|
40
|
+
// ------------------------------------------------------
|
|
41
|
+
if (shouldSyncLocale(locale, currentLocale)) {
|
|
42
|
+
// Eagerly persist locale to avoid stale cookie during client-side navigation.
|
|
43
|
+
if (cookie.persist) {
|
|
44
|
+
setLocaleCookie(cookie, locale);
|
|
45
|
+
}
|
|
46
|
+
setLocale(locale);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
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 };
|
|
@@ -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,4 @@
|
|
|
1
|
+
import { PREFIX_PLACEHOLDER } from '../../core/constants/prefix-placeholder.js';
|
|
1
2
|
import '../../core/error/intor-error.js';
|
|
2
3
|
import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
|
|
3
4
|
import 'logry';
|
|
@@ -6,6 +7,8 @@ import 'p-limit';
|
|
|
6
7
|
/**
|
|
7
8
|
* Returns a canonical, locale-agnostic pathname.
|
|
8
9
|
*
|
|
10
|
+
* Accepts `{locale}` as a locale placeholder segment.
|
|
11
|
+
*
|
|
9
12
|
* @example
|
|
10
13
|
* ```ts
|
|
11
14
|
* // config.supportedLocales: ["en-US"]
|
|
@@ -18,9 +21,8 @@ import 'p-limit';
|
|
|
18
21
|
function canonicalizePathname(rawPathname, config) {
|
|
19
22
|
const { routing, supportedLocales } = config;
|
|
20
23
|
const { basePath } = routing;
|
|
21
|
-
// 1. Normalize pathname
|
|
22
24
|
const normalizedPathname = normalizePathname(rawPathname);
|
|
23
|
-
//
|
|
25
|
+
// Strip basePath
|
|
24
26
|
let prefixedPathname = normalizedPathname;
|
|
25
27
|
if (basePath && normalizedPathname === basePath) {
|
|
26
28
|
prefixedPathname = "/";
|
|
@@ -28,12 +30,14 @@ function canonicalizePathname(rawPathname, config) {
|
|
|
28
30
|
else if (basePath && normalizedPathname.startsWith(basePath + "/")) {
|
|
29
31
|
prefixedPathname = normalizedPathname.slice(basePath.length);
|
|
30
32
|
}
|
|
31
|
-
//
|
|
33
|
+
// Detect locale segment
|
|
32
34
|
const firstSegment = prefixedPathname.split("/").find(Boolean);
|
|
33
|
-
const locale = firstSegment
|
|
34
|
-
?
|
|
35
|
-
:
|
|
36
|
-
|
|
35
|
+
const locale = firstSegment === PREFIX_PLACEHOLDER
|
|
36
|
+
? PREFIX_PLACEHOLDER
|
|
37
|
+
: firstSegment && supportedLocales.includes(firstSegment)
|
|
38
|
+
? firstSegment
|
|
39
|
+
: undefined;
|
|
40
|
+
// Strip locale segment
|
|
37
41
|
return locale
|
|
38
42
|
? prefixedPathname.slice(locale.length + 1) || "/"
|
|
39
43
|
: prefixedPathname;
|
|
@@ -2,12 +2,12 @@ import { initTranslator } from '../translator/init-translator.js';
|
|
|
2
2
|
|
|
3
3
|
// Implementation
|
|
4
4
|
async function getTranslator(config, params) {
|
|
5
|
-
const { readers, allowCacheWrite, preKey, handlers, plugins } = params;
|
|
6
|
-
const locale = params.locale;
|
|
5
|
+
const { locale, readers, allowCacheWrite, fetch, preKey, handlers, plugins } = params;
|
|
7
6
|
// Initialize a locale-bound translator snapshot with messages loaded
|
|
8
7
|
const translator = await initTranslator(config, locale, {
|
|
9
8
|
readers,
|
|
10
9
|
allowCacheWrite,
|
|
10
|
+
fetch: fetch || globalThis.fetch,
|
|
11
11
|
preKey,
|
|
12
12
|
plugins,
|
|
13
13
|
handlers,
|