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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get locale candidate from the `Accept-Language` header.
|
|
3
|
+
*
|
|
4
|
+
* Parses language priorities and returns the highest-priority
|
|
5
|
+
* language present in `supportedLocales`, without normalization.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* getLocaleFromAcceptLanguage("en-US,en;q=0.8,zh-TW;q=0.9", ["en-US", "zh-TW"])
|
|
10
|
+
* // => "en-US"
|
|
11
|
+
*
|
|
12
|
+
* getLocaleFromAcceptLanguage("fr,ja;q=0.9", ["en", "zh-TW"])
|
|
13
|
+
* // => undefined
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
const getLocaleFromAcceptLanguage = (acceptLanguageHeader, supportedLocales) => {
|
|
17
|
+
if (!acceptLanguageHeader || supportedLocales.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const supportedLocalesSet = new Set(supportedLocales);
|
|
21
|
+
// 1. Parse Accept-Language header into language + priority pairs
|
|
22
|
+
const parsedLanguages = acceptLanguageHeader.split(",").map((part) => {
|
|
23
|
+
const [rawLang, rawQ] = part.split(";");
|
|
24
|
+
const lang = rawLang.trim();
|
|
25
|
+
const q = rawQ ? Number.parseFloat(rawQ.split("=")[1]) : 1;
|
|
26
|
+
return {
|
|
27
|
+
lang,
|
|
28
|
+
q: Number.isNaN(q) ? 0 : q, // Invalid q values have lowest priority
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
// 2. Sort by priority (highest first)
|
|
32
|
+
const sortedByPriority = parsedLanguages.toSorted((a, b) => b.q - a.q);
|
|
33
|
+
// 3. Pick the first language explicitly supported
|
|
34
|
+
const preferred = sortedByPriority.find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
|
|
35
|
+
return preferred;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { getLocaleFromAcceptLanguage };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get locale candidate from hostname.
|
|
3
|
+
*
|
|
4
|
+
* Returns the left-most hostname label, without validation or normalization.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* getLocaleFromHost("en.example.com")
|
|
9
|
+
* // => "en"
|
|
10
|
+
*
|
|
11
|
+
* getLocaleFromHost("example.com")
|
|
12
|
+
* // => "example"
|
|
13
|
+
*
|
|
14
|
+
* getLocaleFromHost("api.jp.example.com")
|
|
15
|
+
* // => "api"
|
|
16
|
+
*
|
|
17
|
+
* getLocaleFromHost("localhost")
|
|
18
|
+
* // => undefined
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function getLocaleFromHost(host) {
|
|
22
|
+
if (!host)
|
|
23
|
+
return;
|
|
24
|
+
// Remove port (e.g. localhost:3000)
|
|
25
|
+
const hostname = host.split(":")[0];
|
|
26
|
+
const parts = hostname.split(".");
|
|
27
|
+
if (parts.length < 2)
|
|
28
|
+
return;
|
|
29
|
+
return parts[0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { getLocaleFromHost };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import '../../core/error/intor-error.js';
|
|
2
|
+
import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
|
|
3
|
+
import 'logry';
|
|
4
|
+
import 'p-limit';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get locale from pathname.
|
|
8
|
+
*
|
|
9
|
+
* Extracts the first pathname segment (after basePath) as a locale
|
|
10
|
+
* if it exactly matches one of the supported locales.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* getLocaleFromPathname(config, "/en/about")
|
|
15
|
+
* // => "en"
|
|
16
|
+
* getLocaleFromPathname(config, "/zh-TW")
|
|
17
|
+
* // => "zh-TW"
|
|
18
|
+
* getLocaleFromPathname(config, "/about")
|
|
19
|
+
* // => undefined
|
|
20
|
+
*
|
|
21
|
+
* // config.routing.basePath: "/app"
|
|
22
|
+
* getLocaleFromPathname(config, "/app/en/dashboard")
|
|
23
|
+
* // => "en"
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function getLocaleFromPathname(pathname, config) {
|
|
27
|
+
const { routing, supportedLocales } = config;
|
|
28
|
+
const { basePath } = routing;
|
|
29
|
+
// 1. Normalize pathname
|
|
30
|
+
const normalizedPathname = normalizePathname(pathname);
|
|
31
|
+
// 2. Strip basePath
|
|
32
|
+
let prefixedPathname = normalizedPathname;
|
|
33
|
+
if (basePath && normalizedPathname === basePath) {
|
|
34
|
+
prefixedPathname = "/";
|
|
35
|
+
}
|
|
36
|
+
else if (basePath && normalizedPathname.startsWith(basePath + "/")) {
|
|
37
|
+
prefixedPathname = normalizedPathname.slice(basePath.length);
|
|
38
|
+
}
|
|
39
|
+
// 3. Detect locale segment
|
|
40
|
+
const firstSegment = prefixedPathname.split("/").find(Boolean);
|
|
41
|
+
return firstSegment && supportedLocales.includes(firstSegment)
|
|
42
|
+
? firstSegment
|
|
43
|
+
: undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { getLocaleFromPathname };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get locale candidate from URL query parameters.
|
|
3
|
+
*
|
|
4
|
+
* Extracts the value of the configured query key, without
|
|
5
|
+
* validation or normalization.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* getLocaleFromQuery({ locale: "en" }, "locale")
|
|
10
|
+
* // => "en"
|
|
11
|
+
*
|
|
12
|
+
* getLocaleFromQuery({}, "locale")
|
|
13
|
+
* // => undefined
|
|
14
|
+
*
|
|
15
|
+
* getLocaleFromQuery({ locale: ["zh-TW"] }, "locale")
|
|
16
|
+
* // => "zh-TW"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
function getLocaleFromQuery(query, queryKey) {
|
|
20
|
+
if (!query)
|
|
21
|
+
return;
|
|
22
|
+
const raw = query[queryKey];
|
|
23
|
+
if (!raw)
|
|
24
|
+
return;
|
|
25
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { getLocaleFromQuery };
|
|
@@ -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;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import '../../core/error/intor-error.js';
|
|
2
|
+
import { getLogger } from '../../core/logger/get-logger.js';
|
|
3
|
+
import 'p-limit';
|
|
4
|
+
import { initTranslator } from '../translator/init-translator.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Initializes Intor for the current execution context.
|
|
8
|
+
*
|
|
9
|
+
* Produces a server-side snapshot for SSR and
|
|
10
|
+
* full-stack rendering environments.
|
|
11
|
+
*/
|
|
12
|
+
async function intor(config, locale, options) {
|
|
13
|
+
const baseLogger = getLogger(config.logger);
|
|
14
|
+
const logger = baseLogger.child({ scope: "intor" });
|
|
15
|
+
logger.info("Start Intor initialization.");
|
|
16
|
+
logger.debug(`Initializing Intor with locale "${locale}".`);
|
|
17
|
+
// Initialize a locale-bound translator snapshot with messages loaded
|
|
18
|
+
const translator = await initTranslator(config, locale, {
|
|
19
|
+
readers: options?.readers,
|
|
20
|
+
allowCacheWrite: options?.allowCacheWrite,
|
|
21
|
+
fetch: options?.fetch || globalThis.fetch,
|
|
22
|
+
});
|
|
23
|
+
logger.info("Intor initialized.");
|
|
24
|
+
return {
|
|
25
|
+
config,
|
|
26
|
+
locale: translator.locale,
|
|
27
|
+
messages: translator.messages,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { intor };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import pLimit from 'p-limit';
|
|
3
|
+
import '../../../core/error/intor-error.js';
|
|
4
|
+
import { normalizeCacheKey } from '../../../core/utils/normalizers/normalize-cache-key.js';
|
|
5
|
+
import { getLogger } from '../../../core/logger/get-logger.js';
|
|
6
|
+
import { getMessagesPool } from './cache/messages-pool.js';
|
|
7
|
+
import { readLocaleMessages } from './read-locale-messages/read-locale-messages.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Load locale messages from the local file system.
|
|
11
|
+
*
|
|
12
|
+
* This function serves as the orchestration layer for local message loading.
|
|
13
|
+
* It coordinates:
|
|
14
|
+
*
|
|
15
|
+
* - Locale resolution with fallbacks
|
|
16
|
+
* - Concurrency control for file system access
|
|
17
|
+
* - Process-level memoization (read by default, write by ownership)
|
|
18
|
+
*
|
|
19
|
+
* Local messages are cached for the lifetime of the process.
|
|
20
|
+
* Cache writes are restricted to the primary initialization flow.
|
|
21
|
+
*
|
|
22
|
+
* File traversal, parsing, and validation are delegated to lower-level utilities.
|
|
23
|
+
*/
|
|
24
|
+
const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir = "messages", concurrency = 10, readers, pool = getMessagesPool(), allowCacheWrite = false, loggerOptions, }) => {
|
|
25
|
+
const baseLogger = getLogger(loggerOptions);
|
|
26
|
+
const logger = baseLogger.child({ scope: "load-local-messages" });
|
|
27
|
+
const start = performance.now();
|
|
28
|
+
logger.debug("Loading local messages.", {
|
|
29
|
+
rootDir,
|
|
30
|
+
resolvedRootDir: path.resolve(rootDir),
|
|
31
|
+
});
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Cache key resolution
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
const cacheKey = normalizeCacheKey([
|
|
36
|
+
id,
|
|
37
|
+
"loaderType:local",
|
|
38
|
+
rootDir,
|
|
39
|
+
locale,
|
|
40
|
+
fallbackLocales?.toSorted().join(","),
|
|
41
|
+
namespaces?.toSorted().join(","),
|
|
42
|
+
].filter(Boolean));
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Cache read
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
if (cacheKey) {
|
|
47
|
+
const cached = pool.get(cacheKey);
|
|
48
|
+
if (cached) {
|
|
49
|
+
logger.debug("Messages cache hit.", { key: cacheKey });
|
|
50
|
+
return cached;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Resolve locale messages with ordered fallback strategy
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
const limit = pLimit(concurrency);
|
|
57
|
+
const candidateLocales = [locale, ...(fallbackLocales || [])];
|
|
58
|
+
let messages;
|
|
59
|
+
for (let i = 0; i < candidateLocales.length; i++) {
|
|
60
|
+
const candidateLocale = candidateLocales[i];
|
|
61
|
+
const isLast = i === candidateLocales.length - 1;
|
|
62
|
+
try {
|
|
63
|
+
const result = await readLocaleMessages({
|
|
64
|
+
locale: candidateLocale,
|
|
65
|
+
namespaces,
|
|
66
|
+
rootDir,
|
|
67
|
+
limit,
|
|
68
|
+
readers,
|
|
69
|
+
loggerOptions,
|
|
70
|
+
});
|
|
71
|
+
// Stop at the first locale that yields non-empty messages
|
|
72
|
+
if (Object.values(result[candidateLocale] || {}).length > 0) {
|
|
73
|
+
messages = result;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
if (isLast) {
|
|
79
|
+
logger.warn("Failed to load messages for all candidate locales.", {
|
|
80
|
+
locale,
|
|
81
|
+
fallbackLocales,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
logger.warn(`Failed to load locale messages for "${candidateLocale}", trying next fallback.`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Cache write (explicitly permitted)
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
93
|
+
const canWriteCache = isProd && allowCacheWrite;
|
|
94
|
+
if (canWriteCache && cacheKey && messages) {
|
|
95
|
+
pool.set(cacheKey, messages);
|
|
96
|
+
}
|
|
97
|
+
// Final success log with resolved locale and timing
|
|
98
|
+
if (messages) {
|
|
99
|
+
logger.trace("Finished loading local messages.", {
|
|
100
|
+
loadedLocale: Object.keys(messages)[0],
|
|
101
|
+
duration: `${Math.round(performance.now() - start)} ms`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return messages;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export { loadLocalMessages };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import '../../../../../core/error/intor-error.js';
|
|
4
|
+
import { getLogger } from '../../../../../core/logger/get-logger.js';
|
|
5
|
+
import 'p-limit';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Recursively collects message file metadata under a given locale root.
|
|
9
|
+
*
|
|
10
|
+
* - Traverses directories and collects matching message files
|
|
11
|
+
* - Supports filtering by file extensions and optional namespaces
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* [{
|
|
16
|
+
* namespace: "auth", // If messages under locale root (no namespace) -> "index"
|
|
17
|
+
* fullPath: "/Users/john/my-app/messages/en-US/auth/login.json",
|
|
18
|
+
* relativePath: "auth/login.json",
|
|
19
|
+
* segments: ["auth", "login"],
|
|
20
|
+
* basename: "login",
|
|
21
|
+
* }, ... ];
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
async function collectFileEntries({ readdir = fs.readdir, namespaces, rootDir, limit, exts = [], loggerOptions, }) {
|
|
25
|
+
const baseLogger = getLogger(loggerOptions);
|
|
26
|
+
const logger = baseLogger.child({ scope: "collect-file-entries" });
|
|
27
|
+
const supportedExts = new Set(["json", ...exts]);
|
|
28
|
+
const fileEntries = [];
|
|
29
|
+
// Recursive directory walk
|
|
30
|
+
const walk = async (currentDir) => {
|
|
31
|
+
// -------------------------------------------------------------------------
|
|
32
|
+
// Read directory entries
|
|
33
|
+
// -------------------------------------------------------------------------
|
|
34
|
+
let entries = [];
|
|
35
|
+
try {
|
|
36
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
logger.debug("Locale directory not found, skipping locale.", {
|
|
40
|
+
localeDir: currentDir,
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// -------------------------------------------------------------------------
|
|
45
|
+
// 1. Recurse into sub-directories (control flow, no limit)
|
|
46
|
+
// -------------------------------------------------------------------------
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
if (!entry.isDirectory())
|
|
49
|
+
continue;
|
|
50
|
+
await walk(path.join(currentDir, entry.name));
|
|
51
|
+
}
|
|
52
|
+
// -------------------------------------------------------------------------
|
|
53
|
+
// 2. Process files (IO-bound, concurrency-limited)
|
|
54
|
+
// -------------------------------------------------------------------------
|
|
55
|
+
const tasks = entries
|
|
56
|
+
.filter((entry) => entry.isFile())
|
|
57
|
+
.map((entry) => limit(async () => {
|
|
58
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
59
|
+
const ext = path.extname(entry.name).slice(1);
|
|
60
|
+
if (!ext || !supportedExts.has(ext))
|
|
61
|
+
return;
|
|
62
|
+
const relativePath = path.relative(rootDir, fullPath);
|
|
63
|
+
const withoutExt = relativePath.slice(0, relativePath.length - (ext.length + 1));
|
|
64
|
+
const segments = withoutExt.split(path.sep).filter(Boolean);
|
|
65
|
+
const namespace = segments.at(0);
|
|
66
|
+
if (!namespace)
|
|
67
|
+
return;
|
|
68
|
+
// Apply namespace filter (always allow "index")
|
|
69
|
+
if (namespaces && namespace !== "index") {
|
|
70
|
+
if (!namespaces.includes(namespace))
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
fileEntries.push({
|
|
74
|
+
namespace,
|
|
75
|
+
fullPath,
|
|
76
|
+
relativePath,
|
|
77
|
+
segments,
|
|
78
|
+
basename: path.basename(entry.name, `.${ext}`),
|
|
79
|
+
});
|
|
80
|
+
}));
|
|
81
|
+
await Promise.all(tasks);
|
|
82
|
+
};
|
|
83
|
+
await walk(rootDir);
|
|
84
|
+
if (fileEntries.length > 0) {
|
|
85
|
+
logger.trace(`Collected ${fileEntries.length} local message files for locale "${path.basename(rootDir)}".`);
|
|
86
|
+
}
|
|
87
|
+
return fileEntries;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { collectFileEntries };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import '../../../../../core/error/intor-error.js';
|
|
3
|
+
import { deepMerge } from '../../../../../core/utils/deep-merge.js';
|
|
4
|
+
import { getLogger } from '../../../../../core/logger/get-logger.js';
|
|
5
|
+
import 'p-limit';
|
|
6
|
+
import { isValidMessages } from '../../../../../core/messages/utils/is-valid-messages.js';
|
|
7
|
+
import { nestObjectFromPath } from '../../../../../core/messages/utils/nest-object-from-path.js';
|
|
8
|
+
import { jsonReader } from './utils/json-reader.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse locale message files into a unified Messages object (single-locale).
|
|
12
|
+
*
|
|
13
|
+
* - Reads JSON or custom formats (via `messagesReader`)
|
|
14
|
+
* - Validates message structure
|
|
15
|
+
* - Builds nested objects based on file path segments
|
|
16
|
+
* - Deep-merges entries by namespace
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```plain
|
|
20
|
+
* File paths:
|
|
21
|
+
* - en/index.json = { a: "A" }
|
|
22
|
+
* - en/ui.json = { b: "B" }
|
|
23
|
+
* - en/auth/index.json = { c: "C" }
|
|
24
|
+
* - en/auth/verify.json = { d: "D" }
|
|
25
|
+
*```
|
|
26
|
+
|
|
27
|
+
* The final return value is a `Messages` object:
|
|
28
|
+
* ```ts
|
|
29
|
+
* {
|
|
30
|
+
* a: "A",
|
|
31
|
+
* ui: { b: "B" },
|
|
32
|
+
* auth: {
|
|
33
|
+
* c: "C",
|
|
34
|
+
* verify: { d: "D" },
|
|
35
|
+
* },
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
async function parseFileEntries({ fileEntries, limit, readers, loggerOptions, }) {
|
|
40
|
+
const baseLogger = getLogger(loggerOptions);
|
|
41
|
+
const logger = baseLogger.child({ scope: "parse-file-entries" });
|
|
42
|
+
// Read and parse all file entries
|
|
43
|
+
const parsedFileEntries = [];
|
|
44
|
+
const tasks = fileEntries.map(({ namespace, segments, basename, fullPath }) => limit(async () => {
|
|
45
|
+
try {
|
|
46
|
+
// -------------------------------------------------------------------
|
|
47
|
+
// Read and validate file content
|
|
48
|
+
// -------------------------------------------------------------------
|
|
49
|
+
const ext = path.extname(fullPath).slice(1); // remove dot
|
|
50
|
+
let raw;
|
|
51
|
+
if (ext === "json") {
|
|
52
|
+
raw = await jsonReader(fullPath);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const reader = readers?.[ext];
|
|
56
|
+
if (!reader) {
|
|
57
|
+
throw new Error(`No message reader registered for .${ext} files. ` +
|
|
58
|
+
`Please register a reader for the "${ext}" extension.`);
|
|
59
|
+
}
|
|
60
|
+
raw = await reader(fullPath);
|
|
61
|
+
}
|
|
62
|
+
// Validate messages structure
|
|
63
|
+
if (!isValidMessages(raw)) {
|
|
64
|
+
throw new Error("Parsed content does not match expected Messages structure");
|
|
65
|
+
}
|
|
66
|
+
// -------------------------------------------------------------------
|
|
67
|
+
// Build nested message object from path segments
|
|
68
|
+
// -------------------------------------------------------------------
|
|
69
|
+
const segmentsWithoutNamespace = segments.slice(1);
|
|
70
|
+
const isIndexFile = basename === "index";
|
|
71
|
+
const keyPath = isIndexFile
|
|
72
|
+
? segmentsWithoutNamespace.slice(0, -1)
|
|
73
|
+
: segmentsWithoutNamespace;
|
|
74
|
+
// Nest the parsed content based on the path segments
|
|
75
|
+
const nestedMessages = nestObjectFromPath(keyPath, raw);
|
|
76
|
+
parsedFileEntries.push({ namespace, messages: nestedMessages });
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
logger.warn("Failed to read or parse file.", {
|
|
80
|
+
path: fullPath,
|
|
81
|
+
error,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
await Promise.all(tasks);
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Merge parsed entries by namespace
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
const result = {};
|
|
90
|
+
for (const { namespace, messages } of parsedFileEntries) {
|
|
91
|
+
// Root-level namespace (e.g. [locale]/index.json)
|
|
92
|
+
if (namespace === "index") {
|
|
93
|
+
Object.assign(result, deepMerge(result, messages));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
result[namespace] = deepMerge(result[namespace], messages);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export { parseFileEntries };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read & parse a JSON file
|
|
5
|
+
*/
|
|
6
|
+
async function jsonReader(filePath, readFile = fs.readFile) {
|
|
7
|
+
const raw = await readFile(filePath, "utf8");
|
|
8
|
+
const parsed = JSON.parse(raw);
|
|
9
|
+
return parsed;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { jsonReader };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { collectFileEntries } from './collect-file-entries/collect-file-entries.js';
|
|
3
|
+
import { parseFileEntries } from './parse-file-entries/parse-file-entries.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Read and assemble messages for a single locale from the file system.
|
|
7
|
+
*
|
|
8
|
+
* This function acts as a thin orchestration layer:
|
|
9
|
+
* - Collects message file metadata for the locale
|
|
10
|
+
* - Parses files into a single Messages object
|
|
11
|
+
* - Wraps the result under the locale key
|
|
12
|
+
*
|
|
13
|
+
* It does not perform validation or transformation itself.
|
|
14
|
+
*/
|
|
15
|
+
const readLocaleMessages = async ({ locale, namespaces, rootDir = "messages", limit, readers, loggerOptions, }) => {
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Collect message file entries for the locale
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
const fileEntries = await collectFileEntries({
|
|
20
|
+
namespaces,
|
|
21
|
+
rootDir: path.resolve(rootDir, locale),
|
|
22
|
+
limit,
|
|
23
|
+
exts: Object.keys(readers || {}),
|
|
24
|
+
loggerOptions,
|
|
25
|
+
});
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Parse collected files into a Messages object (single-locale)
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
const messages = await parseFileEntries({
|
|
30
|
+
fileEntries,
|
|
31
|
+
limit,
|
|
32
|
+
readers,
|
|
33
|
+
loggerOptions,
|
|
34
|
+
});
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Wrap parsed messages under the locale key
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
const localeMessages = { [locale]: messages };
|
|
39
|
+
return localeMessages;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export { readLocaleMessages };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import '../../core/error/intor-error.js';
|
|
2
|
+
import { resolveLoaderOptions } from '../../core/utils/resolve-loader-options.js';
|
|
3
|
+
import { getLogger } from '../../core/logger/get-logger.js';
|
|
4
|
+
import { loadRemoteMessages } from '../../core/messages/load-remote-messages/load-remote-messages.js';
|
|
5
|
+
import { loadLocalMessages } from './load-local-messages/load-local-messages.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Load locale messages according to the resolved Intor loader configuration.
|
|
9
|
+
*
|
|
10
|
+
* This function is the top-level orchestration entry for message loading.
|
|
11
|
+
* It is responsible for:
|
|
12
|
+
*
|
|
13
|
+
* - Resolving the active loader strategy (local or remote)
|
|
14
|
+
* - Dispatching to the appropriate loader implementation
|
|
15
|
+
* - Passing through cache and read-related options
|
|
16
|
+
*
|
|
17
|
+
* Message traversal, parsing, fallback resolution, and caching logic
|
|
18
|
+
* are delegated to the selected loader.
|
|
19
|
+
*/
|
|
20
|
+
const loadMessages = async ({ config, locale, readers, allowCacheWrite = false, fetch, }) => {
|
|
21
|
+
const baseLogger = getLogger(config.logger);
|
|
22
|
+
const logger = baseLogger.child({ scope: "load-messages" });
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Resolve loader configuration
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
const loader = resolveLoaderOptions(config, "server");
|
|
27
|
+
if (!loader) {
|
|
28
|
+
logger.warn("No loader options have been configured in the current config.");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const { type, namespaces, concurrency } = loader;
|
|
32
|
+
const fallbackLocales = config.fallbackLocales[locale] || [];
|
|
33
|
+
logger.info(`Loading messages for locale "${locale}".`);
|
|
34
|
+
logger.trace("Starting to load messages with runtime context.", {
|
|
35
|
+
loaderType: type,
|
|
36
|
+
...(type === "local" ? { rootDir: loader.rootDir } : {}),
|
|
37
|
+
locale,
|
|
38
|
+
fallbackLocales: fallbackLocales.join(", "),
|
|
39
|
+
namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : "*",
|
|
40
|
+
});
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Dispatch to loader implementation
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
let loadedMessages;
|
|
45
|
+
if (type === "local") {
|
|
46
|
+
loadedMessages = await loadLocalMessages({
|
|
47
|
+
id: config.id,
|
|
48
|
+
locale,
|
|
49
|
+
fallbackLocales,
|
|
50
|
+
namespaces,
|
|
51
|
+
rootDir: loader.rootDir,
|
|
52
|
+
concurrency,
|
|
53
|
+
readers,
|
|
54
|
+
allowCacheWrite,
|
|
55
|
+
loggerOptions: config.logger,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else if (type === "remote") {
|
|
59
|
+
loadedMessages = await loadRemoteMessages({
|
|
60
|
+
locale,
|
|
61
|
+
fallbackLocales,
|
|
62
|
+
namespaces,
|
|
63
|
+
concurrency,
|
|
64
|
+
fetch,
|
|
65
|
+
url: loader.url,
|
|
66
|
+
headers: loader.headers,
|
|
67
|
+
loggerOptions: config.logger,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Final sanity check
|
|
71
|
+
if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
|
|
72
|
+
logger.warn("No messages found.", { locale, fallbackLocales, namespaces });
|
|
73
|
+
}
|
|
74
|
+
return loadedMessages;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { loadMessages };
|