intor 2.3.14 → 2.3.16
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/export/index.js +1 -1
- package/dist/core/src/config/resolvers/resolve-fallback-locales.js +1 -1
- package/dist/core/src/core/messages/utils/is-valid-messages.js +23 -15
- package/dist/core/src/routing/pathname/{get-unprefixed-pathname.js → canonicalize-pathname.js} +3 -3
- package/dist/core/src/routing/pathname/localize-pathname.js +15 -15
- package/dist/{next/src/routing/pathname/locale-prefix-pathname.js → core/src/routing/pathname/materialize-pathname.js} +6 -5
- package/dist/core/src/routing/pathname/standardize-pathname.js +9 -9
- package/dist/core/src/server/helpers/get-translator.js +2 -2
- package/dist/core/src/server/helpers/local-messages-from-url.js +1 -1
- package/dist/core/src/server/intor/intor.js +2 -2
- package/dist/core/src/server/messages/load-local-messages/load-local-messages.js +2 -2
- package/dist/core/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +6 -5
- package/dist/core/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +14 -5
- package/dist/core/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +3 -3
- package/dist/core/src/server/messages/load-messages.js +4 -4
- package/dist/core/src/server/runtime/create-intor-runtime.js +12 -7
- package/dist/express/src/adapters/express/helpers/get-translator.js +7 -3
- package/dist/express/src/adapters/express/middleware/create-intor.js +6 -7
- package/dist/express/src/core/messages/utils/is-valid-messages.js +23 -15
- package/dist/express/src/routing/inbound/resolve-inbound.js +5 -5
- package/dist/express/src/routing/inbound/resolve-locale/resolve-locale.js +13 -6
- package/dist/express/src/routing/inbound/resolve-pathname/resolve-pathname.js +1 -1
- package/dist/express/src/routing/locale/get-locale-from-accept-language.js +7 -16
- package/dist/express/src/routing/locale/get-locale-from-host.js +13 -14
- package/dist/express/src/routing/locale/get-locale-from-pathname.js +4 -13
- package/dist/express/src/routing/locale/get-locale-from-query.js +10 -16
- package/dist/{next/src/routing/pathname/get-unprefixed-pathname.js → express/src/routing/pathname/canonicalize-pathname.js} +3 -3
- package/dist/express/src/routing/pathname/localize-pathname.js +15 -15
- package/dist/{core/src/routing/pathname/locale-prefix-pathname.js → express/src/routing/pathname/materialize-pathname.js} +6 -5
- package/dist/express/src/routing/pathname/standardize-pathname.js +9 -9
- package/dist/express/src/server/helpers/get-translator.js +2 -2
- package/dist/express/src/server/messages/load-local-messages/load-local-messages.js +2 -2
- package/dist/express/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +6 -5
- package/dist/express/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +14 -5
- package/dist/express/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +3 -3
- package/dist/express/src/server/messages/load-messages.js +4 -4
- package/dist/express/src/server/runtime/create-intor-runtime.js +12 -7
- package/dist/next/src/adapters/next/navigation/use-pathname.js +4 -4
- package/dist/next/src/adapters/next/proxy/intor-proxy.js +2 -2
- package/dist/next/src/adapters/next/server/get-pathname.js +4 -4
- package/dist/next/src/adapters/next/server/get-translator.js +7 -3
- package/dist/next/src/adapters/next/server/intor.js +3 -3
- package/dist/next/src/core/messages/utils/is-valid-messages.js +23 -15
- package/dist/next/src/routing/inbound/resolve-inbound.js +5 -5
- package/dist/next/src/routing/inbound/resolve-locale/resolve-locale.js +13 -6
- package/dist/next/src/routing/inbound/resolve-pathname/resolve-pathname.js +1 -1
- package/dist/next/src/routing/locale/get-locale-from-accept-language.js +7 -16
- package/dist/next/src/routing/locale/get-locale-from-host.js +13 -14
- package/dist/next/src/routing/locale/get-locale-from-pathname.js +4 -13
- package/dist/next/src/routing/locale/get-locale-from-query.js +10 -16
- package/dist/next/src/routing/navigation/derive-target.js +5 -5
- package/dist/next/src/routing/navigation/utils/derive-host-destination.js +1 -1
- package/dist/next/src/routing/navigation/utils/derive-query-destination.js +1 -1
- package/dist/{express/src/routing/pathname/get-unprefixed-pathname.js → next/src/routing/pathname/canonicalize-pathname.js} +3 -3
- package/dist/next/src/routing/pathname/localize-pathname.js +15 -15
- package/dist/{react/src/routing/pathname/locale-prefix-pathname.js → next/src/routing/pathname/materialize-pathname.js} +6 -5
- package/dist/next/src/routing/pathname/standardize-pathname.js +9 -9
- package/dist/next/src/server/helpers/get-translator.js +2 -2
- package/dist/next/src/server/intor/intor.js +2 -2
- package/dist/next/src/server/messages/load-local-messages/load-local-messages.js +2 -2
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +6 -5
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +14 -5
- package/dist/next/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.js +3 -3
- package/dist/next/src/server/messages/load-messages.js +4 -4
- package/dist/next/src/server/runtime/create-intor-runtime.js +12 -7
- package/dist/react/export/react/index.js +1 -1
- package/dist/react/src/client/react/navigation/use-execute-navigation.js +3 -3
- package/dist/react/src/client/react/provider/effects/use-locale-effects.js +5 -5
- package/dist/react/src/client/react/render/create-react-renderer.js +5 -0
- package/dist/react/src/client/react/translator/create-t-rich.js +2 -2
- package/dist/react/src/client/react/translator/{t.js → trans.js} +4 -4
- package/dist/react/src/client/react/translator/use-translator.js +0 -4
- package/dist/react/src/client/shared/helpers/get-client-locale.js +2 -2
- package/dist/react/src/client/shared/utils/locale/detect-browser-locale.js +1 -1
- package/dist/{vue/src/client/shared/utils/locale/get-locale-cookie-browser.js → react/src/client/shared/utils/locale/get-locale-from-cookie.js} +3 -3
- package/dist/react/src/client/shared/utils/locale/set-document-locale.js +2 -2
- package/dist/{svelte/src/client/shared/utils/locale/set-locale-cookie-browser.js → react/src/client/shared/utils/locale/set-locale-cookie.js} +4 -4
- package/dist/react/src/core/messages/utils/is-valid-messages.js +23 -15
- package/dist/react/src/routing/navigation/derive-target.js +5 -5
- package/dist/react/src/routing/navigation/utils/derive-host-destination.js +1 -1
- package/dist/react/src/routing/navigation/utils/derive-query-destination.js +1 -1
- package/dist/react/src/routing/pathname/{get-unprefixed-pathname.js → canonicalize-pathname.js} +3 -3
- package/dist/react/src/routing/pathname/localize-pathname.js +15 -15
- package/dist/{express/src/routing/pathname/locale-prefix-pathname.js → react/src/routing/pathname/materialize-pathname.js} +6 -5
- package/dist/react/src/routing/pathname/standardize-pathname.js +9 -9
- package/dist/svelte/src/client/shared/helpers/get-client-locale.js +2 -2
- package/dist/svelte/src/client/shared/utils/locale/detect-browser-locale.js +1 -1
- package/dist/{react/src/client/shared/utils/locale/get-locale-cookie-browser.js → svelte/src/client/shared/utils/locale/get-locale-from-cookie.js} +3 -3
- package/dist/svelte/src/client/shared/utils/locale/set-document-locale.js +2 -2
- package/dist/{vue/src/client/shared/utils/locale/set-locale-cookie-browser.js → svelte/src/client/shared/utils/locale/set-locale-cookie.js} +4 -4
- package/dist/svelte/src/client/svelte/render/create-svelte-renderer.js +12 -0
- package/dist/svelte/src/client/svelte/runtime/create-intor.js +2 -12
- package/dist/svelte/src/client/svelte/runtime/effects/locale-effects.js +5 -5
- package/dist/svelte/src/core/messages/utils/is-valid-messages.js +23 -15
- package/dist/types/export/index.d.ts +2 -2
- package/dist/types/export/internal/index.d.ts +2 -1
- package/dist/types/export/react/index.d.ts +1 -1
- package/dist/types/export/server/index.d.ts +1 -1
- package/dist/types/export/vue/index.d.ts +1 -1
- package/dist/types/src/adapters/express/global.d.ts +4 -4
- package/dist/types/src/adapters/express/helpers/get-translator.d.ts +6 -6
- package/dist/types/src/adapters/express/middleware/create-intor.d.ts +1 -1
- package/dist/types/src/adapters/next/navigation/use-pathname.d.ts +3 -3
- package/dist/types/src/adapters/next/proxy/intor-proxy.d.ts +1 -1
- package/dist/types/src/adapters/next/server/get-pathname.d.ts +3 -3
- package/dist/types/src/adapters/next/server/get-translator.d.ts +6 -6
- package/dist/types/src/adapters/next/server/intor.d.ts +4 -4
- package/dist/types/src/client/react/helpers/use-runtime-state.d.ts +5 -3
- package/dist/types/src/client/react/index.d.ts +1 -1
- package/dist/types/src/client/react/provider/types.d.ts +10 -9
- package/dist/types/src/client/react/render/create-react-renderer.d.ts +1 -0
- package/dist/types/src/client/react/render/render-rich-message-react.d.ts +2 -1
- package/dist/types/src/client/react/render/types.d.ts +15 -10
- package/dist/types/src/client/react/translator/index.d.ts +1 -1
- package/dist/types/src/client/react/translator/trans.d.ts +27 -0
- package/dist/types/src/client/react/translator/translator-instance.d.ts +4 -4
- package/dist/types/src/client/react/translator/use-translator.d.ts +4 -4
- package/dist/types/src/client/shared/utils/index.d.ts +1 -1
- package/dist/types/src/client/shared/utils/locale/detect-browser-locale.d.ts +1 -1
- package/dist/types/src/client/shared/utils/locale/get-locale-from-cookie.d.ts +6 -0
- package/dist/types/src/client/shared/utils/locale/index.d.ts +2 -2
- package/dist/types/src/client/shared/utils/locale/set-document-locale.d.ts +2 -2
- package/dist/types/src/client/shared/utils/locale/set-locale-cookie.d.ts +7 -0
- package/dist/types/src/client/svelte/helpers/create-runtime-state.d.ts +7 -6
- package/dist/types/src/client/svelte/render/render-rich-message-svelte.d.ts +2 -1
- package/dist/types/src/client/svelte/runtime/create-intor.d.ts +1 -2
- package/dist/types/src/client/svelte/runtime/types.d.ts +19 -21
- package/dist/types/src/client/vue/helpers/use-runtime-state.d.ts +7 -6
- package/dist/types/src/client/vue/index.d.ts +1 -1
- package/dist/types/src/client/vue/provider/inject.d.ts +1 -2
- package/dist/types/src/client/vue/provider/resolver/resolve-runtime.d.ts +2 -2
- package/dist/types/src/client/vue/provider/types.d.ts +11 -11
- package/dist/types/src/client/vue/render/create-vue-renderer.d.ts +1 -0
- package/dist/types/src/client/vue/render/render-rich-message-vue.d.ts +2 -1
- package/dist/types/src/client/vue/render/types.d.ts +14 -6
- package/dist/types/src/client/vue/translator/index.d.ts +1 -1
- package/dist/types/src/client/vue/translator/{t.d.ts → trans.d.ts} +4 -5
- package/dist/types/src/client/vue/translator/translator-instance.d.ts +4 -4
- package/dist/types/src/client/vue/translator/use-translator.d.ts +5 -5
- package/dist/types/src/core/index.d.ts +2 -2
- package/dist/types/src/core/messages/index.d.ts +1 -1
- package/dist/types/src/core/messages/types.d.ts +14 -36
- package/dist/types/src/core/messages/utils/is-valid-messages.d.ts +5 -10
- package/dist/types/src/core/types/generated.d.ts +14 -5
- package/dist/types/src/core/types/index.d.ts +2 -3
- package/dist/types/src/core/types/translator-instance.d.ts +5 -19
- package/dist/types/src/routing/inbound/resolve-locale/resolve-locale.d.ts +4 -3
- package/dist/types/src/routing/locale/get-locale-from-accept-language.d.ts +5 -7
- package/dist/types/src/routing/locale/get-locale-from-host.d.ts +12 -8
- package/dist/types/src/routing/locale/get-locale-from-pathname.d.ts +4 -13
- package/dist/types/src/routing/locale/get-locale-from-query.d.ts +9 -10
- package/dist/types/src/routing/navigation/utils/derive-host-destination.d.ts +1 -1
- package/dist/types/src/routing/navigation/utils/derive-query-destination.d.ts +1 -1
- package/dist/types/src/routing/pathname/{get-unprefixed-pathname.d.ts → canonicalize-pathname.d.ts} +2 -2
- package/dist/types/src/routing/pathname/localize-pathname.d.ts +16 -13
- package/dist/types/src/routing/pathname/materialize-pathname.d.ts +17 -0
- package/dist/types/src/routing/pathname/standardize-pathname.d.ts +7 -6
- package/dist/types/src/server/helpers/get-translator.d.ts +6 -7
- package/dist/types/src/server/index.d.ts +1 -1
- package/dist/types/src/server/intor/index.d.ts +1 -1
- package/dist/types/src/server/intor/intor.d.ts +4 -4
- package/dist/types/src/server/intor/types.d.ts +5 -3
- package/dist/types/src/server/messages/load-local-messages/load-local-messages.d.ts +1 -1
- package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.d.ts +2 -2
- package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/types.d.ts +4 -3
- package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.d.ts +2 -2
- package/dist/types/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.d.ts +2 -2
- package/dist/types/src/server/messages/load-local-messages/read-locale-messages/read-locale-messages.d.ts +1 -1
- package/dist/types/src/server/messages/load-local-messages/read-locale-messages/types.d.ts +2 -2
- package/dist/types/src/server/messages/load-local-messages/types.d.ts +2 -2
- package/dist/types/src/server/messages/load-messages.d.ts +1 -1
- package/dist/types/src/server/messages/types.d.ts +2 -2
- package/dist/types/src/server/runtime/create-intor-runtime.d.ts +1 -2
- package/dist/types/src/server/runtime/types.d.ts +7 -13
- package/dist/types/src/server/translator/create-translator.d.ts +1 -1
- package/dist/types/src/server/translator/translator-instance.d.ts +3 -3
- package/dist/vue/export/vue/index.js +1 -1
- package/dist/vue/src/client/shared/helpers/get-client-locale.js +2 -2
- package/dist/vue/src/client/shared/utils/locale/detect-browser-locale.js +1 -1
- package/dist/{svelte/src/client/shared/utils/locale/get-locale-cookie-browser.js → vue/src/client/shared/utils/locale/get-locale-from-cookie.js} +3 -3
- package/dist/vue/src/client/shared/utils/locale/set-document-locale.js +2 -2
- package/dist/{react/src/client/shared/utils/locale/set-locale-cookie-browser.js → vue/src/client/shared/utils/locale/set-locale-cookie.js} +4 -4
- package/dist/vue/src/client/vue/provider/effects/use-locale-effects.js +5 -5
- package/dist/vue/src/client/vue/render/create-vue-renderer.js +5 -0
- package/dist/vue/src/client/vue/translator/{t.js → trans.js} +3 -3
- package/dist/vue/src/client/vue/translator/use-translator.js +1 -1
- package/dist/vue/src/core/messages/utils/is-valid-messages.js +23 -15
- package/package.json +2 -2
- package/dist/types/src/client/react/translator/t.d.ts +0 -27
- package/dist/types/src/client/shared/types/index.d.ts +0 -1
- package/dist/types/src/client/shared/types/runtime-state.d.ts +0 -13
- package/dist/types/src/client/shared/utils/locale/get-locale-cookie-browser.d.ts +0 -6
- package/dist/types/src/client/shared/utils/locale/set-locale-cookie-browser.d.ts +0 -7
- package/dist/types/src/core/types/bootstrap.d.ts +0 -13
- package/dist/types/src/routing/pathname/locale-prefix-pathname.d.ts +0 -16
|
@@ -10,4 +10,4 @@ export { mergeMessages } from '../src/core/messages/merge-messages.js';
|
|
|
10
10
|
export { defineIntorConfig } from '../src/config/define-intor-config.js';
|
|
11
11
|
import '../src/config/constants/cookie.js';
|
|
12
12
|
export { localizePathname } from '../src/routing/pathname/localize-pathname.js';
|
|
13
|
-
export { Translator } from 'intor-translator';
|
|
13
|
+
export { Translator, tokenize } from 'intor-translator';
|
|
@@ -12,7 +12,7 @@ const resolveFallbackLocales = (config, id, supportedSet) => {
|
|
|
12
12
|
if (!fallbackLocales || typeof fallbackLocales !== "object") {
|
|
13
13
|
return {};
|
|
14
14
|
}
|
|
15
|
-
const logger = getLogger({ id }).child({
|
|
15
|
+
const logger = getLogger({ ...config.logger, id }).child({
|
|
16
16
|
scope: "resolve-fallback-locales",
|
|
17
17
|
});
|
|
18
18
|
const resolvedMap = {};
|
|
@@ -3,15 +3,10 @@ function isPlainObject(value) {
|
|
|
3
3
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* Check if a value is a valid
|
|
6
|
+
* Check if a value is a valid MessageObject.
|
|
7
7
|
*
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* isValidMessages({ en: { hello: "Hello" } }) // true
|
|
13
|
-
* isValidMessages({ en: { count: 5 } }) // false
|
|
14
|
-
* ```
|
|
8
|
+
* - Supports all MessageValue variants (primitive, array, object).
|
|
9
|
+
* - Uses an iterative approach to avoid stack overflow.
|
|
15
10
|
*/
|
|
16
11
|
function isValidMessages(value) {
|
|
17
12
|
if (!isPlainObject(value))
|
|
@@ -19,16 +14,29 @@ function isValidMessages(value) {
|
|
|
19
14
|
const stack = [value];
|
|
20
15
|
while (stack.length > 0) {
|
|
21
16
|
const current = stack.pop();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
// primitives are always valid
|
|
18
|
+
if (current === null ||
|
|
19
|
+
typeof current === "string" ||
|
|
20
|
+
typeof current === "number" ||
|
|
21
|
+
typeof current === "boolean") {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
// array → validate each item
|
|
25
|
+
if (Array.isArray(current)) {
|
|
26
|
+
for (const item of current) {
|
|
27
|
+
stack.push(item);
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// object → validate each value
|
|
32
|
+
if (isPlainObject(current)) {
|
|
33
|
+
for (const v of Object.values(current)) {
|
|
34
|
+
stack.push(v);
|
|
30
35
|
}
|
|
36
|
+
continue;
|
|
31
37
|
}
|
|
38
|
+
// everything else is invalid
|
|
39
|
+
return false;
|
|
32
40
|
}
|
|
33
41
|
return true;
|
|
34
42
|
}
|
package/dist/core/src/routing/pathname/{get-unprefixed-pathname.js → canonicalize-pathname.js}
RENAMED
|
@@ -10,11 +10,11 @@ import 'logry';
|
|
|
10
10
|
* // config.supportedLocales: ["en-US"]
|
|
11
11
|
* // config.routing.basePath: "/app"
|
|
12
12
|
* // config.routing.prefix: "all"
|
|
13
|
-
*
|
|
13
|
+
* canonicalizePathname("/app/en-US/about", config);
|
|
14
14
|
* // => "/about"
|
|
15
15
|
*```
|
|
16
16
|
*/
|
|
17
|
-
function
|
|
17
|
+
function canonicalizePathname(rawPathname, config) {
|
|
18
18
|
const { routing, supportedLocales } = config;
|
|
19
19
|
const { basePath } = routing;
|
|
20
20
|
// 1. Normalize pathname
|
|
@@ -38,4 +38,4 @@ function getUnprefixedPathname(config, rawPathname) {
|
|
|
38
38
|
: prefixedPathname;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export {
|
|
41
|
+
export { canonicalizePathname };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { canonicalizePathname } from './canonicalize-pathname.js';
|
|
2
|
+
import { materializePathname } from './materialize-pathname.js';
|
|
3
3
|
import { standardizePathname } from './standardize-pathname.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -11,25 +11,25 @@ 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
|
-
*
|
|
14
|
+
* localizePathname(config, "/app/en-US/about", "en-US");
|
|
15
15
|
* // => {
|
|
16
|
+
* // pathname: '/app/en-US/about'
|
|
16
17
|
* // unprefixedPathname: '/about',
|
|
17
|
-
* //
|
|
18
|
-
* // localizedPathname: '/app/en-US/about'
|
|
18
|
+
* // templatedPathname: '/app/{locale}/about',
|
|
19
19
|
* // }
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
const localizePathname = (
|
|
23
|
-
// 1. Canonicalize:
|
|
24
|
-
const
|
|
25
|
-
// 2. Standardize:
|
|
26
|
-
const
|
|
27
|
-
// 3.
|
|
28
|
-
const
|
|
22
|
+
const localizePathname = (rawPathname, config, locale) => {
|
|
23
|
+
// 1. Canonicalize: normalize and remove routing-specific prefixes
|
|
24
|
+
const canonicalized = canonicalizePathname(rawPathname, config);
|
|
25
|
+
// 2. Standardize: convert to internal pathname shape with locale placeholder
|
|
26
|
+
const standardized = standardizePathname(canonicalized, config);
|
|
27
|
+
// 3. Materialize: apply routing rules to produce the final pathname
|
|
28
|
+
const materialized = materializePathname(standardized, config, locale);
|
|
29
29
|
return {
|
|
30
|
-
pathname,
|
|
31
|
-
unprefixedPathname,
|
|
32
|
-
|
|
30
|
+
pathname: materialized,
|
|
31
|
+
unprefixedPathname: canonicalized,
|
|
32
|
+
templatedPathname: standardized,
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
35
|
|
|
@@ -4,20 +4,21 @@ import { normalizePathname } from '../../core/utils/normalizers/normalize-pathna
|
|
|
4
4
|
import 'logry';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Materializes a standardized pathname by applying
|
|
8
|
+
* the configured locale prefix behavior.
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
10
11
|
* ```ts
|
|
11
12
|
* // config.routing.localePrefix: "all"
|
|
12
|
-
*
|
|
13
|
+
* materializePathname("/app/{locale}/about", config, "en-US");
|
|
13
14
|
* // => /app/en-US/about
|
|
14
15
|
*
|
|
15
16
|
* // config.routing.localePrefix: "none"
|
|
16
|
-
*
|
|
17
|
+
* materializePathname("/app/{locale}/about", config, "en-US");
|
|
17
18
|
* // => /app/about
|
|
18
19
|
* ```
|
|
19
20
|
*/
|
|
20
|
-
const
|
|
21
|
+
const materializePathname = (standardizedPathname, config, locale) => {
|
|
21
22
|
const { localePrefix } = config.routing;
|
|
22
23
|
if (localePrefix !== "none" && !locale) {
|
|
23
24
|
throw new Error('No locale when using localePrefix "all", "except-default"');
|
|
@@ -36,4 +37,4 @@ const localePrefixPathname = (config, standardizedPathname, locale) => {
|
|
|
36
37
|
return normalizePathname(standardizedPathname.replaceAll(`/${PREFIX_PLACEHOLDER}`, ""));
|
|
37
38
|
};
|
|
38
39
|
|
|
39
|
-
export {
|
|
40
|
+
export { materializePathname };
|
|
@@ -4,24 +4,24 @@ import { normalizePathname } from '../../core/utils/normalizers/normalize-pathna
|
|
|
4
4
|
import 'logry';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Standardizes a canonical pathname
|
|
8
|
-
* and injecting
|
|
7
|
+
* Standardizes a canonical pathname into an internal routing template
|
|
8
|
+
* by applying the base path and injecting a locale placeholder.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
|
-
* // routing.basePath: "/app",
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* // config.routing.basePath: "/app",
|
|
13
|
+
* // config.routing.prefix: "all"
|
|
14
|
+
* standardizePathname("/about", config);
|
|
15
|
+
* // => "/app/{locale}/about"
|
|
15
16
|
* ```
|
|
16
17
|
*/
|
|
17
|
-
const standardizePathname = (
|
|
18
|
-
const {
|
|
19
|
-
const { basePath } = routing;
|
|
18
|
+
const standardizePathname = (canonicalizedPathname, config) => {
|
|
19
|
+
const { basePath } = config.routing;
|
|
20
20
|
// Normalize each segment before join to avoid redundant slashes
|
|
21
21
|
const parts = [
|
|
22
22
|
normalizePathname(basePath),
|
|
23
23
|
PREFIX_PLACEHOLDER,
|
|
24
|
-
normalizePathname(
|
|
24
|
+
normalizePathname(canonicalizedPathname),
|
|
25
25
|
];
|
|
26
26
|
// Avoid double slashes between segments
|
|
27
27
|
const standardizedPathname = parts.join("/").replaceAll(/\/{2,}/g, "/");
|
|
@@ -2,11 +2,11 @@ import { createIntorRuntime } from '../runtime/create-intor-runtime.js';
|
|
|
2
2
|
|
|
3
3
|
// Implementation
|
|
4
4
|
async function getTranslator(config, params) {
|
|
5
|
-
const {
|
|
5
|
+
const { readers, allowCacheWrite, preKey, handlers, plugins } = params;
|
|
6
6
|
const locale = params.locale;
|
|
7
7
|
// Create runtime (request-scoped, no cache write)
|
|
8
8
|
const runtime = createIntorRuntime(config, {
|
|
9
|
-
|
|
9
|
+
readers,
|
|
10
10
|
allowCacheWrite,
|
|
11
11
|
});
|
|
12
12
|
// Ensure messages & create translator snapshot
|
|
@@ -43,7 +43,7 @@ async function loadMessagesFromUrl(url, options) {
|
|
|
43
43
|
namespaces,
|
|
44
44
|
fallbackLocales,
|
|
45
45
|
concurrency: options?.concurrency,
|
|
46
|
-
|
|
46
|
+
readers: options?.readers,
|
|
47
47
|
pool: options?.pool,
|
|
48
48
|
allowCacheWrite: options?.allowCacheWrite,
|
|
49
49
|
loggerOptions: options?.loggerOptions || { id: "default" },
|
|
@@ -5,8 +5,8 @@ import { createIntorRuntime } from '../runtime/create-intor-runtime.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* Initializes Intor for the current execution context.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* full-stack
|
|
8
|
+
* Produces a server-side snapshot for SSR and
|
|
9
|
+
* full-stack rendering environments.
|
|
10
10
|
*/
|
|
11
11
|
async function intor(config, localeOrResolver, options) {
|
|
12
12
|
const baseLogger = getLogger(config.logger);
|
|
@@ -21,7 +21,7 @@ import { readLocaleMessages } from './read-locale-messages/read-locale-messages.
|
|
|
21
21
|
*
|
|
22
22
|
* File traversal, parsing, and validation are delegated to lower-level utilities.
|
|
23
23
|
*/
|
|
24
|
-
const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir = "messages", concurrency = 10,
|
|
24
|
+
const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir = "messages", concurrency = 10, readers, pool = getGlobalMessagesPool(), allowCacheWrite = false, loggerOptions, }) => {
|
|
25
25
|
const baseLogger = getLogger(loggerOptions);
|
|
26
26
|
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
27
27
|
const start = performance.now();
|
|
@@ -65,7 +65,7 @@ const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, root
|
|
|
65
65
|
namespaces,
|
|
66
66
|
rootDir,
|
|
67
67
|
limit,
|
|
68
|
-
|
|
68
|
+
readers,
|
|
69
69
|
loggerOptions,
|
|
70
70
|
});
|
|
71
71
|
// Stop at the first locale that yields non-empty messages
|
|
@@ -20,9 +20,10 @@ import { getLogger } from '../../../../../core/logger/get-logger.js';
|
|
|
20
20
|
* }, ... ];
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, limit, exts = ["
|
|
23
|
+
async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, limit, exts = ["json"], loggerOptions, }) {
|
|
24
24
|
const baseLogger = getLogger(loggerOptions);
|
|
25
25
|
const logger = baseLogger.child({ scope: "collect-file-entries" });
|
|
26
|
+
const supportedExts = new Set(["json", ...exts]);
|
|
26
27
|
const fileEntries = [];
|
|
27
28
|
// Recursive directory walk
|
|
28
29
|
const walk = async (currentDir) => {
|
|
@@ -50,14 +51,14 @@ async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, l
|
|
|
50
51
|
return;
|
|
51
52
|
}
|
|
52
53
|
// Skip unsupported file extensions
|
|
53
|
-
|
|
54
|
+
const ext = path.extname(entry.name).slice(1); // "json", "yaml"
|
|
55
|
+
if (!ext || !supportedExts.has(ext))
|
|
54
56
|
return;
|
|
55
57
|
// ---------------------------------------------------------------------
|
|
56
58
|
// Resolve file entry
|
|
57
59
|
// ---------------------------------------------------------------------
|
|
58
60
|
const relativePath = path.relative(rootDir, fullPath);
|
|
59
|
-
const
|
|
60
|
-
const withoutExt = relativePath.slice(0, -ext.length);
|
|
61
|
+
const withoutExt = relativePath.slice(0, relativePath.length - (ext.length + 1));
|
|
61
62
|
const segments = withoutExt.split(path.sep).filter(Boolean);
|
|
62
63
|
const namespace = segments.at(0);
|
|
63
64
|
if (!namespace)
|
|
@@ -72,7 +73,7 @@ async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, l
|
|
|
72
73
|
fullPath,
|
|
73
74
|
relativePath,
|
|
74
75
|
segments,
|
|
75
|
-
basename: path.basename(entry.name, ext),
|
|
76
|
+
basename: path.basename(entry.name, `.${ext}`),
|
|
76
77
|
});
|
|
77
78
|
}));
|
|
78
79
|
await Promise.all(tasks);
|
|
@@ -35,7 +35,7 @@ import { nestObjectFromPath } from './utils/nest-object-from-path.js';
|
|
|
35
35
|
* }
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
|
-
async function parseFileEntries({ fileEntries, limit,
|
|
38
|
+
async function parseFileEntries({ fileEntries, limit, readers, loggerOptions, }) {
|
|
39
39
|
const baseLogger = getLogger(loggerOptions);
|
|
40
40
|
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
41
41
|
// Read and parse all file entries
|
|
@@ -45,10 +45,19 @@ async function parseFileEntries({ fileEntries, limit, messagesReader, loggerOpti
|
|
|
45
45
|
// -------------------------------------------------------------------
|
|
46
46
|
// Read and validate file content
|
|
47
47
|
// -------------------------------------------------------------------
|
|
48
|
-
const ext = path.extname(fullPath);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
const ext = path.extname(fullPath).slice(1); // remove dot
|
|
49
|
+
let raw;
|
|
50
|
+
if (ext === "json") {
|
|
51
|
+
raw = await jsonReader(fullPath);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const reader = readers?.[ext];
|
|
55
|
+
if (!reader) {
|
|
56
|
+
throw new Error(`No message reader registered for .${ext} files. ` +
|
|
57
|
+
`Please register a reader for the "${ext}" extension.`);
|
|
58
|
+
}
|
|
59
|
+
raw = await reader(fullPath);
|
|
60
|
+
}
|
|
52
61
|
// Validate messages structure
|
|
53
62
|
if (!isValidMessages(raw)) {
|
|
54
63
|
throw new Error("Parsed content does not match expected Messages structure");
|
|
@@ -12,7 +12,7 @@ import { parseFileEntries } from './parse-file-entries/parse-file-entries.js';
|
|
|
12
12
|
*
|
|
13
13
|
* It does not perform validation or transformation itself.
|
|
14
14
|
*/
|
|
15
|
-
const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", limit,
|
|
15
|
+
const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", limit, readers, loggerOptions, }) => {
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
// Collect message file entries for the locale
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
@@ -20,7 +20,7 @@ const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", li
|
|
|
20
20
|
namespaces,
|
|
21
21
|
rootDir: path.resolve(rootDir, locale),
|
|
22
22
|
limit,
|
|
23
|
-
exts,
|
|
23
|
+
exts: Object.keys(readers || {}),
|
|
24
24
|
loggerOptions,
|
|
25
25
|
});
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
@@ -29,7 +29,7 @@ const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", li
|
|
|
29
29
|
const messages = await parseFileEntries({
|
|
30
30
|
fileEntries,
|
|
31
31
|
limit,
|
|
32
|
-
|
|
32
|
+
readers,
|
|
33
33
|
loggerOptions,
|
|
34
34
|
});
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
@@ -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,
|
|
20
|
+
const loadMessages = async ({ config, locale, readers, allowCacheWrite = false, }) => {
|
|
21
21
|
const baseLogger = getLogger(config.logger);
|
|
22
22
|
const logger = baseLogger.child({ scope: "load-messages" });
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
@@ -35,8 +35,8 @@ const loadMessages = async ({ config, locale, readOptions, allowCacheWrite = fal
|
|
|
35
35
|
loaderType: type,
|
|
36
36
|
rootDir,
|
|
37
37
|
locale,
|
|
38
|
-
fallbackLocales,
|
|
39
|
-
namespaces: namespaces && namespaces.length > 0 ? [...namespaces] :
|
|
38
|
+
fallbackLocales: fallbackLocales.join(", "),
|
|
39
|
+
namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : "*",
|
|
40
40
|
});
|
|
41
41
|
// ---------------------------------------------------------------------------
|
|
42
42
|
// Dispatch to loader implementation
|
|
@@ -50,7 +50,7 @@ const loadMessages = async ({ config, locale, readOptions, allowCacheWrite = fal
|
|
|
50
50
|
namespaces,
|
|
51
51
|
rootDir,
|
|
52
52
|
concurrency: loader.concurrency,
|
|
53
|
-
|
|
53
|
+
readers,
|
|
54
54
|
allowCacheWrite,
|
|
55
55
|
loggerOptions: config.logger,
|
|
56
56
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
|
|
2
|
+
import { resolveLoaderOptions } from '../../core/utils/resolve-loader-options.js';
|
|
2
3
|
import 'logry';
|
|
3
4
|
import { loadMessages } from '../messages/load-messages.js';
|
|
4
5
|
import { createTranslator } from '../translator/create-translator.js';
|
|
@@ -13,20 +14,24 @@ import { createTranslator } from '../translator/create-translator.js';
|
|
|
13
14
|
* before a translator snapshot can be created.
|
|
14
15
|
*/
|
|
15
16
|
function createIntorRuntime(config, options) {
|
|
17
|
+
const loader = resolveLoaderOptions(config, "server");
|
|
16
18
|
// Locale that has completed the ensureMessages() phase
|
|
17
19
|
let ensuredLocale;
|
|
18
20
|
// Messages prepared during ensureMessages(); may be empty
|
|
19
21
|
let ensuredMessages;
|
|
20
22
|
return {
|
|
21
23
|
async ensureMessages(locale) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
let messages;
|
|
25
|
+
if (loader) {
|
|
26
|
+
messages = await loadMessages({
|
|
27
|
+
config,
|
|
28
|
+
locale,
|
|
29
|
+
readers: options?.readers,
|
|
30
|
+
allowCacheWrite: options?.allowCacheWrite || false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
28
33
|
ensuredLocale = locale;
|
|
29
|
-
ensuredMessages = messages;
|
|
34
|
+
ensuredMessages = messages || {};
|
|
30
35
|
},
|
|
31
36
|
translator(locale, options) {
|
|
32
37
|
// Guard: translator requires ensureMessages() to be completed for this locale
|
|
@@ -8,14 +8,18 @@ import { getTranslator as getTranslator$1 } from '../../../server/helpers/get-tr
|
|
|
8
8
|
|
|
9
9
|
// Implementation
|
|
10
10
|
async function getTranslator(config, req, params) {
|
|
11
|
-
const { preKey, handlers, plugins,
|
|
11
|
+
const { preKey, handlers, plugins, readers, allowCacheWrite } = params || {};
|
|
12
12
|
return getTranslator$1(config, {
|
|
13
13
|
locale: req.intor?.locale || config.defaultLocale,
|
|
14
14
|
preKey,
|
|
15
15
|
handlers,
|
|
16
16
|
plugins,
|
|
17
|
-
|
|
18
|
-
allowCacheWrite
|
|
17
|
+
readers,
|
|
18
|
+
allowCacheWrite,
|
|
19
|
+
// NOTE:
|
|
20
|
+
// The runtime implementation is intentionally erased.
|
|
21
|
+
// Type safety is guaranteed by public type contracts.
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
23
|
});
|
|
20
24
|
}
|
|
21
25
|
|
|
@@ -15,7 +15,7 @@ import { getTranslator } from '../../../server/helpers/get-translator.js';
|
|
|
15
15
|
*
|
|
16
16
|
* The resolved routing state is exposed via response headers.
|
|
17
17
|
*
|
|
18
|
-
* -
|
|
18
|
+
* - Permits cache writes during server execution.
|
|
19
19
|
* - Convenience routing shortcuts are also bound to the request for downstream consumption.
|
|
20
20
|
*
|
|
21
21
|
* @platform Express
|
|
@@ -24,7 +24,7 @@ function createIntor(config, options) {
|
|
|
24
24
|
return async function intorMiddleware(req, _res, next) {
|
|
25
25
|
// locale from accept-language header
|
|
26
26
|
const acceptLanguage = req.headers["accept-language"];
|
|
27
|
-
const localeFromAcceptLanguage = getLocaleFromAcceptLanguage(
|
|
27
|
+
const localeFromAcceptLanguage = getLocaleFromAcceptLanguage(acceptLanguage, config.supportedLocales);
|
|
28
28
|
// ----------------------------------------------------------
|
|
29
29
|
// Resolve inbound routing decision (pure computation)
|
|
30
30
|
// ----------------------------------------------------------
|
|
@@ -43,19 +43,18 @@ function createIntor(config, options) {
|
|
|
43
43
|
// --------------------------------------------------
|
|
44
44
|
// Bind inbound routing context
|
|
45
45
|
// --------------------------------------------------
|
|
46
|
-
const {
|
|
46
|
+
const { hasKey, t } = (await getTranslator(config, {
|
|
47
47
|
locale,
|
|
48
48
|
handlers: options?.handlers,
|
|
49
49
|
plugins: options?.plugins,
|
|
50
|
-
|
|
50
|
+
readers: options?.readers,
|
|
51
51
|
allowCacheWrite: true,
|
|
52
52
|
}));
|
|
53
|
-
req.intor = { locale, localeSource };
|
|
53
|
+
req.intor = { locale, localeSource, pathname };
|
|
54
54
|
// DX shortcuts (optional)
|
|
55
55
|
req.locale = locale;
|
|
56
|
-
req.localeSource = localeSource;
|
|
57
|
-
req.t = t;
|
|
58
56
|
req.hasKey = hasKey;
|
|
57
|
+
req.t = t;
|
|
59
58
|
return next();
|
|
60
59
|
};
|
|
61
60
|
}
|
|
@@ -3,15 +3,10 @@ function isPlainObject(value) {
|
|
|
3
3
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* Check if a value is a valid
|
|
6
|
+
* Check if a value is a valid MessageObject.
|
|
7
7
|
*
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* isValidMessages({ en: { hello: "Hello" } }) // true
|
|
13
|
-
* isValidMessages({ en: { count: 5 } }) // false
|
|
14
|
-
* ```
|
|
8
|
+
* - Supports all MessageValue variants (primitive, array, object).
|
|
9
|
+
* - Uses an iterative approach to avoid stack overflow.
|
|
15
10
|
*/
|
|
16
11
|
function isValidMessages(value) {
|
|
17
12
|
if (!isPlainObject(value))
|
|
@@ -19,16 +14,29 @@ function isValidMessages(value) {
|
|
|
19
14
|
const stack = [value];
|
|
20
15
|
while (stack.length > 0) {
|
|
21
16
|
const current = stack.pop();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
// primitives are always valid
|
|
18
|
+
if (current === null ||
|
|
19
|
+
typeof current === "string" ||
|
|
20
|
+
typeof current === "number" ||
|
|
21
|
+
typeof current === "boolean") {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
// array → validate each item
|
|
25
|
+
if (Array.isArray(current)) {
|
|
26
|
+
for (const item of current) {
|
|
27
|
+
stack.push(item);
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// object → validate each value
|
|
32
|
+
if (isPlainObject(current)) {
|
|
33
|
+
for (const v of Object.values(current)) {
|
|
34
|
+
stack.push(v);
|
|
30
35
|
}
|
|
36
|
+
continue;
|
|
31
37
|
}
|
|
38
|
+
// everything else is invalid
|
|
39
|
+
return false;
|
|
32
40
|
}
|
|
33
41
|
return true;
|
|
34
42
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '../../core/error/intor-error.js';
|
|
2
|
-
import 'logry';
|
|
3
1
|
import { getLocaleFromPathname } from '../locale/get-locale-from-pathname.js';
|
|
4
2
|
import { getLocaleFromHost } from '../locale/get-locale-from-host.js';
|
|
5
3
|
import { getLocaleFromQuery } from '../locale/get-locale-from-query.js';
|
|
@@ -20,11 +18,13 @@ async function resolveInbound(config, rawPathname, hasRedirected, localeInputs)
|
|
|
20
18
|
// ------------------------------------------------------
|
|
21
19
|
// Resolve locale from inbound inputs
|
|
22
20
|
// ------------------------------------------------------
|
|
23
|
-
const pathLocale = getLocaleFromPathname(
|
|
21
|
+
const pathLocale = getLocaleFromPathname(rawPathname, config);
|
|
24
22
|
const { locale, localeSource } = resolveLocale(config, {
|
|
25
23
|
path: { locale: pathLocale },
|
|
26
|
-
host: { locale: getLocaleFromHost(
|
|
27
|
-
query: {
|
|
24
|
+
host: { locale: getLocaleFromHost(host) },
|
|
25
|
+
query: {
|
|
26
|
+
locale: getLocaleFromQuery(query, config.routing.inbound.queryKey),
|
|
27
|
+
},
|
|
28
28
|
cookie: { locale: cookie },
|
|
29
29
|
detected: { locale: detected },
|
|
30
30
|
});
|
|
@@ -1,23 +1,30 @@
|
|
|
1
|
+
import '../../../core/error/intor-error.js';
|
|
2
|
+
import { normalizeLocale } from '../../../core/utils/normalizers/normalize-locale.js';
|
|
3
|
+
import 'logry';
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
|
-
*
|
|
6
|
+
* Resolve the active locale from inbound routing configuration.
|
|
3
7
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
8
|
+
* Iterates through configured locale sources and returns the first
|
|
9
|
+
* normalized, supported locale. Falls back to the detected locale
|
|
10
|
+
* or the default locale if none match.
|
|
6
11
|
*/
|
|
7
12
|
function resolveLocale(config, context) {
|
|
8
13
|
const { localeSources } = config.routing.inbound;
|
|
9
14
|
for (const source of localeSources) {
|
|
10
15
|
const locale = context[source]?.locale;
|
|
11
|
-
|
|
16
|
+
const normalized = normalizeLocale(locale, config.supportedLocales);
|
|
17
|
+
if (!normalized)
|
|
12
18
|
continue;
|
|
13
19
|
return {
|
|
14
|
-
locale,
|
|
20
|
+
locale: normalized,
|
|
15
21
|
localeSource: source,
|
|
16
22
|
};
|
|
17
23
|
}
|
|
18
24
|
// Fallback: detected is always available
|
|
19
25
|
return {
|
|
20
|
-
locale: context.detected.locale,
|
|
26
|
+
locale: normalizeLocale(context.detected.locale, config.supportedLocales) ||
|
|
27
|
+
config.defaultLocale,
|
|
21
28
|
localeSource: "detected",
|
|
22
29
|
};
|
|
23
30
|
}
|
|
@@ -32,7 +32,7 @@ const resolvePathname = (config, rawPathname, context) => {
|
|
|
32
32
|
return assertNever(localePrefix);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
const { pathname } = localizePathname(
|
|
35
|
+
const { pathname } = localizePathname(rawPathname, config, context.locale);
|
|
36
36
|
return {
|
|
37
37
|
pathname,
|
|
38
38
|
shouldRedirect: directive.type === "redirect",
|