intor 2.4.14 → 2.4.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 +2 -1
- package/dist/core/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/core/src/core/locale/match-locale.js +57 -0
- package/dist/core/src/core/locale/parse-locale.js +27 -0
- package/dist/core/src/routing/inbound/resolve-locale/resolve-locale.js +3 -3
- package/dist/express/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/express/src/core/locale/match-locale.js +57 -0
- package/dist/express/src/core/locale/parse-locale.js +27 -0
- package/dist/express/src/routing/inbound/resolve-locale/resolve-locale.js +3 -3
- package/dist/fastify/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/fastify/src/core/locale/match-locale.js +57 -0
- package/dist/fastify/src/core/locale/parse-locale.js +27 -0
- package/dist/fastify/src/routing/inbound/resolve-locale/resolve-locale.js +3 -3
- package/dist/hono/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/hono/src/core/locale/match-locale.js +57 -0
- package/dist/hono/src/core/locale/parse-locale.js +27 -0
- package/dist/hono/src/routing/inbound/resolve-locale/resolve-locale.js +3 -3
- package/dist/next/src/adapters/next/server/get-locale.js +4 -4
- package/dist/next/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/next/src/core/locale/match-locale.js +57 -0
- package/dist/next/src/core/locale/parse-locale.js +27 -0
- package/dist/next/src/routing/inbound/resolve-locale/resolve-locale.js +3 -3
- package/dist/react/src/client/react/provider/intor-provider.js +6 -0
- package/dist/react/src/client/shared/helpers/get-client-locale.js +2 -2
- package/dist/react/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/react/src/core/locale/match-locale.js +57 -0
- package/dist/react/src/core/locale/parse-locale.js +27 -0
- package/dist/svelte/src/client/shared/helpers/get-client-locale.js +2 -2
- package/dist/svelte/src/client/svelte/provider/create-intor-store.js +3 -1
- package/dist/svelte/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/svelte/src/core/locale/match-locale.js +57 -0
- package/dist/svelte/src/core/locale/parse-locale.js +27 -0
- package/dist/svelte-kit/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/svelte-kit/src/core/locale/match-locale.js +57 -0
- package/dist/svelte-kit/src/core/locale/parse-locale.js +27 -0
- package/dist/svelte-kit/src/routing/inbound/resolve-locale/resolve-locale.js +3 -3
- package/dist/types/export/index.d.ts +2 -1
- package/dist/types/src/core/index.d.ts +2 -1
- package/dist/types/src/core/locale/canonicalize-locale.d.ts +11 -0
- package/dist/types/src/core/locale/index.d.ts +1 -0
- package/dist/types/src/core/locale/match-locale.d.ts +16 -0
- package/dist/types/src/core/locale/parse-locale.d.ts +14 -0
- package/dist/types/src/core/utils/index.d.ts +1 -1
- package/dist/types/src/core/utils/normalizers/index.d.ts +0 -1
- package/dist/vue/src/client/shared/helpers/get-client-locale.js +2 -2
- package/dist/vue/src/client/vue/provider/intor-provider.js +21 -14
- package/dist/vue/src/core/locale/canonicalize-locale.js +23 -0
- package/dist/vue/src/core/locale/match-locale.js +57 -0
- package/dist/vue/src/core/locale/parse-locale.js +27 -0
- package/package.json +27 -27
- package/dist/core/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/express/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/fastify/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/hono/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/next/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/react/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/svelte/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/svelte-kit/src/core/utils/normalizers/normalize-locale.js +0 -59
- package/dist/types/src/core/utils/normalizers/normalize-locale.d.ts +0 -23
- package/dist/vue/src/core/utils/normalizers/normalize-locale.js +0 -59
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonicalizes a BCP 47 locale string.
|
|
3
|
+
*
|
|
4
|
+
* - Uses `Intl.getCanonicalLocales` when available.
|
|
5
|
+
* - Returns the original input if `Intl` is unavailable.
|
|
6
|
+
* - Returns `undefined` for invalid locale input.
|
|
7
|
+
*
|
|
8
|
+
* This function performs normalization only.
|
|
9
|
+
* It does not perform matching or fallback.
|
|
10
|
+
*/
|
|
11
|
+
function canonicalizeLocale(input) {
|
|
12
|
+
try {
|
|
13
|
+
if (typeof Intl === "undefined" || !Intl.getCanonicalLocales) {
|
|
14
|
+
return input;
|
|
15
|
+
}
|
|
16
|
+
return Intl.getCanonicalLocales(input)[0];
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { canonicalizeLocale };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { canonicalizeLocale } from './canonicalize-locale.js';
|
|
2
|
+
import { parseLocale } from './parse-locale.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Matches a locale candidate against a list of supported locales.
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
* 1. Exact canonical match
|
|
9
|
+
* 2. Same language + same script
|
|
10
|
+
* 3. Same language only (only if candidate has no script)
|
|
11
|
+
*
|
|
12
|
+
* Returns `undefined` if no match is found.
|
|
13
|
+
*
|
|
14
|
+
* Notes:
|
|
15
|
+
* - Matching is deterministic and order-sensitive.
|
|
16
|
+
* - Does not perform automatic fallback to default locale.
|
|
17
|
+
* - Does not cross script boundaries.
|
|
18
|
+
*/
|
|
19
|
+
function matchLocale(locale, supportedLocales = []) {
|
|
20
|
+
if (!locale || supportedLocales.length === 0)
|
|
21
|
+
return;
|
|
22
|
+
const canonicalCandidate = canonicalizeLocale(locale);
|
|
23
|
+
if (!canonicalCandidate)
|
|
24
|
+
return;
|
|
25
|
+
const candidateParts = parseLocale(canonicalCandidate);
|
|
26
|
+
const supportedCanonical = supportedLocales.flatMap((original) => {
|
|
27
|
+
const canonical = canonicalizeLocale(original);
|
|
28
|
+
if (!canonical)
|
|
29
|
+
return [];
|
|
30
|
+
return [{ original, canonical, parts: parseLocale(canonical) }];
|
|
31
|
+
});
|
|
32
|
+
// 1. Exact match
|
|
33
|
+
for (const s of supportedCanonical) {
|
|
34
|
+
if (s.canonical === canonicalCandidate) {
|
|
35
|
+
return s.original;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 2. Same language + same script
|
|
39
|
+
if (candidateParts.script) {
|
|
40
|
+
for (const s of supportedCanonical) {
|
|
41
|
+
if (s.parts.language === candidateParts.language &&
|
|
42
|
+
s.parts.script === candidateParts.script) {
|
|
43
|
+
return s.original;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// 3. Same language only
|
|
49
|
+
for (const s of supportedCanonical) {
|
|
50
|
+
if (s.parts.language === candidateParts.language) {
|
|
51
|
+
return s.original;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { matchLocale };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a canonical locale tag into structural components.
|
|
3
|
+
*
|
|
4
|
+
* - Uses `Intl.Locale` when available.
|
|
5
|
+
* - Falls back to minimal parsing (language only) if unavailable.
|
|
6
|
+
*
|
|
7
|
+
* This function does not validate or match locales.
|
|
8
|
+
*/
|
|
9
|
+
function parseLocale(tag) {
|
|
10
|
+
try {
|
|
11
|
+
if (typeof Intl !== "undefined" && typeof Intl.Locale === "function") {
|
|
12
|
+
const locale = new Intl.Locale(tag);
|
|
13
|
+
return {
|
|
14
|
+
language: locale.language,
|
|
15
|
+
script: locale.script,
|
|
16
|
+
region: locale.region,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// fallback below
|
|
22
|
+
}
|
|
23
|
+
const parts = tag.split("-");
|
|
24
|
+
return { language: parts[0].toLowerCase() };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { parseLocale };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intor",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.16",
|
|
4
4
|
"description": "The i18n library for modern JavaScript",
|
|
5
5
|
"author": "Yiming Liao",
|
|
6
6
|
"homepage": "https://intor.dev",
|
|
@@ -31,57 +31,57 @@
|
|
|
31
31
|
"type": "module",
|
|
32
32
|
"exports": {
|
|
33
33
|
".": {
|
|
34
|
+
"types": "./dist/types/export/index.d.ts",
|
|
34
35
|
"import": "./dist/core/export/index.js",
|
|
35
|
-
"require": "./dist/core/export/index.js"
|
|
36
|
-
"types": "./dist/types/export/index.d.ts"
|
|
36
|
+
"require": "./dist/core/export/index.js"
|
|
37
37
|
},
|
|
38
38
|
"./internal": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"types": "./dist/types/export/internal/index.d.ts",
|
|
40
|
+
"import": "./dist/core/export/internal/index.js"
|
|
41
41
|
},
|
|
42
42
|
"./server": {
|
|
43
|
-
"
|
|
44
|
-
"
|
|
43
|
+
"types": "./dist/types/export/server/index.d.ts",
|
|
44
|
+
"import": "./dist/core/export/server/index.js"
|
|
45
45
|
},
|
|
46
46
|
"./edge": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
47
|
+
"types": "./dist/types/export/edge/index.d.ts",
|
|
48
|
+
"import": "./dist/core/export/edge/index.js"
|
|
49
49
|
},
|
|
50
50
|
"./react": {
|
|
51
|
-
"
|
|
52
|
-
"
|
|
51
|
+
"types": "./dist/types/export/react/index.d.ts",
|
|
52
|
+
"import": "./dist/react/export/react/index.js"
|
|
53
53
|
},
|
|
54
54
|
"./vue": {
|
|
55
|
-
"
|
|
56
|
-
"
|
|
55
|
+
"types": "./dist/types/export/vue/index.d.ts",
|
|
56
|
+
"import": "./dist/vue/export/vue/index.js"
|
|
57
57
|
},
|
|
58
58
|
"./svelte": {
|
|
59
|
-
"
|
|
60
|
-
"
|
|
59
|
+
"types": "./dist/types/export/svelte/index.d.ts",
|
|
60
|
+
"import": "./dist/svelte/export/svelte/index.js"
|
|
61
61
|
},
|
|
62
62
|
"./next": {
|
|
63
|
-
"
|
|
64
|
-
"
|
|
63
|
+
"types": "./dist/types/export/next/index.d.ts",
|
|
64
|
+
"import": "./dist/next/export/next/index.js"
|
|
65
65
|
},
|
|
66
66
|
"./next/server": {
|
|
67
|
-
"
|
|
68
|
-
"
|
|
67
|
+
"types": "./dist/types/export/next/server/index.d.ts",
|
|
68
|
+
"import": "./dist/next/export/next/server/index.js"
|
|
69
69
|
},
|
|
70
70
|
"./svelte-kit": {
|
|
71
|
-
"
|
|
72
|
-
"
|
|
71
|
+
"types": "./dist/types/export/svelte-kit/index.d.ts",
|
|
72
|
+
"import": "./dist/svelte-kit/export/svelte-kit/index.js"
|
|
73
73
|
},
|
|
74
74
|
"./express": {
|
|
75
|
-
"
|
|
76
|
-
"
|
|
75
|
+
"types": "./dist/types/export/express/index.d.ts",
|
|
76
|
+
"import": "./dist/express/export/express/index.js"
|
|
77
77
|
},
|
|
78
78
|
"./fastify": {
|
|
79
|
-
"
|
|
80
|
-
"
|
|
79
|
+
"types": "./dist/types/export/fastify/index.d.ts",
|
|
80
|
+
"import": "./dist/fastify/export/fastify/index.js"
|
|
81
81
|
},
|
|
82
82
|
"./hono": {
|
|
83
|
-
"
|
|
84
|
-
"
|
|
83
|
+
"types": "./dist/types/export/hono/index.d.ts",
|
|
84
|
+
"import": "./dist/hono/export/hono/index.js"
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
87
|
"main": "./dist/core/export/index.js",
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const toCanonical = (input) => {
|
|
2
|
-
try {
|
|
3
|
-
return Intl.getCanonicalLocales(input)[0];
|
|
4
|
-
}
|
|
5
|
-
catch {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Normalizes a locale string and resolves the best match
|
|
11
|
-
* from a list of supported locales.
|
|
12
|
-
*
|
|
13
|
-
* Resolution strategy:
|
|
14
|
-
*
|
|
15
|
-
* 1. Exact canonical match (BCP 47)
|
|
16
|
-
* 2. Base language fallback
|
|
17
|
-
* - Falls back by base language when no exact match is found
|
|
18
|
-
* (e.g. `"en"` → `"en-US"`).
|
|
19
|
-
* - Script and region subtags are ignored during this step
|
|
20
|
-
* (e.g. `"zh-Hans"` → `"zh-Hant-TW"`)
|
|
21
|
-
* - Preference is determined by the order of `supportedLocales`.
|
|
22
|
-
*
|
|
23
|
-
* Returns `undefined` if no suitable match is found.
|
|
24
|
-
*
|
|
25
|
-
* Notes:
|
|
26
|
-
* - Invalid locale inputs are ignored gracefully.
|
|
27
|
-
* - Always returns an original entry from `supportedLocales`.
|
|
28
|
-
* - Requires `Intl` locale support in the runtime.
|
|
29
|
-
*/
|
|
30
|
-
const normalizeLocale = (locale, supportedLocales = []) => {
|
|
31
|
-
if (!locale || supportedLocales.length === 0)
|
|
32
|
-
return;
|
|
33
|
-
const canonicalLocale = toCanonical(locale);
|
|
34
|
-
if (!canonicalLocale)
|
|
35
|
-
return;
|
|
36
|
-
const supportedCanonicalMap = new Map();
|
|
37
|
-
for (const l of supportedLocales) {
|
|
38
|
-
const normalized = toCanonical(l);
|
|
39
|
-
if (normalized) {
|
|
40
|
-
supportedCanonicalMap.set(normalized, l);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// 1. Exact match
|
|
44
|
-
if (supportedCanonicalMap.has(canonicalLocale)) {
|
|
45
|
-
return supportedCanonicalMap.get(canonicalLocale);
|
|
46
|
-
}
|
|
47
|
-
const baseLang = canonicalLocale.split("-")[0];
|
|
48
|
-
// 2. Match by same base language (e.g., "en" matches "en-US" or "en-GB")
|
|
49
|
-
for (const [key, original] of supportedCanonicalMap) {
|
|
50
|
-
const supportedBase = key.split("-")[0];
|
|
51
|
-
if (supportedBase === baseLang) {
|
|
52
|
-
return original;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// 3. No match
|
|
56
|
-
return;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export { normalizeLocale };
|