@modern-js/plugin-i18n 2.69.5 → 3.0.0-alpha.0
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/README.md +6 -0
- package/dist/cjs/cli/index.cjs +154 -0
- package/dist/cjs/runtime/I18nLink.cjs +68 -0
- package/dist/cjs/runtime/context.cjs +138 -0
- package/dist/cjs/runtime/hooks.cjs +189 -0
- package/dist/cjs/runtime/i18n/backend/config.cjs +39 -0
- package/dist/cjs/runtime/i18n/backend/defaults.cjs +56 -0
- package/dist/cjs/runtime/i18n/backend/defaults.node.cjs +56 -0
- package/dist/cjs/runtime/i18n/backend/index.cjs +108 -0
- package/dist/cjs/runtime/i18n/backend/middleware.cjs +54 -0
- package/dist/cjs/runtime/i18n/backend/middleware.common.cjs +105 -0
- package/dist/cjs/runtime/i18n/backend/middleware.node.cjs +58 -0
- package/dist/cjs/runtime/i18n/backend/sdk-backend.cjs +171 -0
- package/dist/cjs/runtime/i18n/detection/config.cjs +63 -0
- package/dist/cjs/runtime/i18n/detection/index.cjs +309 -0
- package/dist/cjs/runtime/i18n/detection/middleware.cjs +185 -0
- package/dist/cjs/runtime/i18n/detection/middleware.node.cjs +74 -0
- package/dist/cjs/runtime/i18n/index.cjs +43 -0
- package/dist/cjs/runtime/i18n/instance.cjs +132 -0
- package/dist/cjs/runtime/i18n/utils.cjs +185 -0
- package/dist/cjs/runtime/index.cjs +162 -0
- package/dist/cjs/runtime/types.cjs +18 -0
- package/dist/cjs/runtime/utils.cjs +134 -0
- package/dist/cjs/server/index.cjs +178 -0
- package/dist/cjs/shared/deepMerge.cjs +54 -0
- package/dist/cjs/shared/detection.cjs +105 -0
- package/dist/cjs/shared/type.cjs +18 -0
- package/dist/cjs/shared/utils.cjs +78 -0
- package/dist/esm/cli/index.js +106 -0
- package/dist/esm/runtime/I18nLink.js +31 -0
- package/dist/esm/runtime/context.js +101 -0
- package/dist/esm/runtime/hooks.js +146 -0
- package/dist/esm/runtime/i18n/backend/config.js +5 -0
- package/dist/esm/runtime/i18n/backend/defaults.js +19 -0
- package/dist/esm/runtime/i18n/backend/defaults.node.js +19 -0
- package/dist/esm/runtime/i18n/backend/index.js +74 -0
- package/dist/esm/runtime/i18n/backend/middleware.common.js +61 -0
- package/dist/esm/runtime/i18n/backend/middleware.js +7 -0
- package/dist/esm/runtime/i18n/backend/middleware.node.js +8 -0
- package/dist/esm/runtime/i18n/backend/sdk-backend.js +137 -0
- package/dist/esm/runtime/i18n/detection/config.js +26 -0
- package/dist/esm/runtime/i18n/detection/index.js +260 -0
- package/dist/esm/runtime/i18n/detection/middleware.js +132 -0
- package/dist/esm/runtime/i18n/detection/middleware.node.js +31 -0
- package/dist/esm/runtime/i18n/index.js +3 -0
- package/dist/esm/runtime/i18n/instance.js +77 -0
- package/dist/esm/runtime/i18n/utils.js +136 -0
- package/dist/esm/runtime/index.js +119 -0
- package/dist/esm/runtime/types.js +0 -0
- package/dist/esm/runtime/utils.js +82 -0
- package/dist/esm/server/index.js +168 -0
- package/dist/esm/shared/deepMerge.js +20 -0
- package/dist/esm/shared/detection.js +71 -0
- package/dist/esm/shared/type.js +0 -0
- package/dist/esm/shared/utils.js +35 -0
- package/dist/esm-node/cli/index.js +106 -0
- package/dist/esm-node/runtime/I18nLink.js +31 -0
- package/dist/esm-node/runtime/context.js +101 -0
- package/dist/esm-node/runtime/hooks.js +146 -0
- package/dist/esm-node/runtime/i18n/backend/config.js +5 -0
- package/dist/esm-node/runtime/i18n/backend/defaults.js +19 -0
- package/dist/esm-node/runtime/i18n/backend/defaults.node.js +19 -0
- package/dist/esm-node/runtime/i18n/backend/index.js +74 -0
- package/dist/esm-node/runtime/i18n/backend/middleware.common.js +61 -0
- package/dist/esm-node/runtime/i18n/backend/middleware.js +7 -0
- package/dist/esm-node/runtime/i18n/backend/middleware.node.js +8 -0
- package/dist/esm-node/runtime/i18n/backend/sdk-backend.js +137 -0
- package/dist/esm-node/runtime/i18n/detection/config.js +26 -0
- package/dist/esm-node/runtime/i18n/detection/index.js +260 -0
- package/dist/esm-node/runtime/i18n/detection/middleware.js +132 -0
- package/dist/esm-node/runtime/i18n/detection/middleware.node.js +31 -0
- package/dist/esm-node/runtime/i18n/index.js +3 -0
- package/dist/esm-node/runtime/i18n/instance.js +77 -0
- package/dist/esm-node/runtime/i18n/utils.js +136 -0
- package/dist/esm-node/runtime/index.js +119 -0
- package/dist/esm-node/runtime/types.js +0 -0
- package/dist/esm-node/runtime/utils.js +82 -0
- package/dist/esm-node/server/index.js +168 -0
- package/dist/esm-node/shared/deepMerge.js +20 -0
- package/dist/esm-node/shared/detection.js +71 -0
- package/dist/esm-node/shared/type.js +0 -0
- package/dist/esm-node/shared/utils.js +35 -0
- package/dist/types/cli/index.d.ts +21 -0
- package/dist/types/runtime/I18nLink.d.ts +8 -0
- package/dist/types/runtime/context.d.ts +38 -0
- package/dist/types/runtime/hooks.d.ts +28 -0
- package/dist/types/runtime/i18n/backend/config.d.ts +2 -0
- package/dist/types/runtime/i18n/backend/defaults.d.ts +13 -0
- package/dist/types/runtime/i18n/backend/defaults.node.d.ts +8 -0
- package/dist/types/runtime/i18n/backend/index.d.ts +3 -0
- package/dist/types/runtime/i18n/backend/middleware.common.d.ts +14 -0
- package/dist/types/runtime/i18n/backend/middleware.d.ts +12 -0
- package/dist/types/runtime/i18n/backend/middleware.node.d.ts +13 -0
- package/dist/types/runtime/i18n/backend/sdk-backend.d.ts +52 -0
- package/dist/types/runtime/i18n/detection/config.d.ts +11 -0
- package/dist/types/runtime/i18n/detection/index.d.ts +50 -0
- package/dist/types/runtime/i18n/detection/middleware.d.ts +24 -0
- package/dist/types/runtime/i18n/detection/middleware.node.d.ts +17 -0
- package/dist/types/runtime/i18n/index.d.ts +3 -0
- package/dist/types/runtime/i18n/instance.d.ts +93 -0
- package/dist/types/runtime/i18n/utils.d.ts +29 -0
- package/dist/types/runtime/index.d.ts +19 -0
- package/dist/types/runtime/types.d.ts +15 -0
- package/dist/types/runtime/utils.d.ts +33 -0
- package/dist/types/server/index.d.ts +8 -0
- package/dist/types/shared/deepMerge.d.ts +1 -0
- package/dist/types/shared/detection.d.ts +11 -0
- package/dist/types/shared/type.d.ts +156 -0
- package/dist/types/shared/utils.d.ts +5 -0
- package/package.json +100 -34
- package/rslib.config.mts +4 -0
- package/src/cli/index.ts +245 -0
- package/src/runtime/I18nLink.tsx +76 -0
- package/src/runtime/context.tsx +256 -0
- package/src/runtime/hooks.ts +274 -0
- package/src/runtime/i18n/backend/config.ts +10 -0
- package/src/runtime/i18n/backend/defaults.node.ts +31 -0
- package/src/runtime/i18n/backend/defaults.ts +37 -0
- package/src/runtime/i18n/backend/index.ts +181 -0
- package/src/runtime/i18n/backend/middleware.common.ts +116 -0
- package/src/runtime/i18n/backend/middleware.node.ts +32 -0
- package/src/runtime/i18n/backend/middleware.ts +28 -0
- package/src/runtime/i18n/backend/sdk-backend.ts +292 -0
- package/src/runtime/i18n/detection/config.ts +32 -0
- package/src/runtime/i18n/detection/index.ts +641 -0
- package/src/runtime/i18n/detection/middleware.node.ts +84 -0
- package/src/runtime/i18n/detection/middleware.ts +251 -0
- package/src/runtime/i18n/index.ts +8 -0
- package/src/runtime/i18n/instance.ts +227 -0
- package/src/runtime/i18n/utils.ts +333 -0
- package/src/runtime/index.tsx +275 -0
- package/src/runtime/types.ts +17 -0
- package/src/runtime/utils.ts +151 -0
- package/src/server/index.ts +336 -0
- package/src/shared/deepMerge.ts +38 -0
- package/src/shared/detection.ts +131 -0
- package/src/shared/type.ts +170 -0
- package/src/shared/utils.ts +82 -0
- package/tsconfig.json +12 -0
- package/dist/cjs/index.js +0 -73
- package/dist/cjs/languageDetector.js +0 -51
- package/dist/cjs/utils/index.js +0 -39
- package/dist/esm/index.js +0 -61
- package/dist/esm/languageDetector.js +0 -33
- package/dist/esm/utils/index.js +0 -16
- package/dist/esm-node/index.js +0 -49
- package/dist/esm-node/languageDetector.js +0 -26
- package/dist/esm-node/utils/index.js +0 -15
- package/dist/types/index.d.ts +0 -34
- package/dist/types/languageDetector.d.ts +0 -6
- package/dist/types/utils/index.d.ts +0 -5
package/package.json
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
"name": "@modern-js/plugin-i18n",
|
|
3
3
|
"description": "A Progressive React Framework for modern web development.",
|
|
4
4
|
"homepage": "https://modernjs.dev",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bugs": "https://github.com/web-infra-dev/modern.js/issues",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "https://github.com/web-infra-dev/modern.js",
|
|
9
|
-
"directory": "packages/
|
|
10
|
+
"directory": "packages/runtime/plugin-i18n"
|
|
10
11
|
},
|
|
11
12
|
"license": "MIT",
|
|
12
13
|
"keywords": [
|
|
@@ -15,47 +16,112 @@
|
|
|
15
16
|
"modern",
|
|
16
17
|
"modern.js"
|
|
17
18
|
],
|
|
18
|
-
"version": "
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"main": "./dist/cjs/index.js",
|
|
22
|
-
"module": "./dist/esm/index.js",
|
|
23
|
-
"typesVersions": {
|
|
24
|
-
"*": {
|
|
25
|
-
"language-detector": [
|
|
26
|
-
"./dist/types/languageDetector.d.ts"
|
|
27
|
-
]
|
|
28
|
-
}
|
|
19
|
+
"version": "3.0.0-alpha.0",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
29
22
|
},
|
|
23
|
+
"jsnext:source": "./src/cli/index.ts",
|
|
24
|
+
"types": "./dist/types/cli/index.d.ts",
|
|
25
|
+
"main": "./dist/cjs/cli/index.cjs",
|
|
26
|
+
"module": "./dist/esm/cli/index.js",
|
|
30
27
|
"exports": {
|
|
31
28
|
".": {
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
"types": "./dist/types/index.d.ts",
|
|
37
|
-
"default": "./dist/esm/index.js"
|
|
29
|
+
"types": "./dist/types/cli/index.d.ts",
|
|
30
|
+
"jsnext:source": "./src/cli/index.ts",
|
|
31
|
+
"default": "./dist/cjs/cli/index.cjs"
|
|
38
32
|
},
|
|
39
|
-
"./
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
"./package.json": "./package.json",
|
|
34
|
+
"./cli": {
|
|
35
|
+
"types": "./dist/types/cli/index.d.ts",
|
|
36
|
+
"jsnext:source": "./src/cli/index.ts",
|
|
37
|
+
"default": "./dist/cjs/cli/index.cjs"
|
|
38
|
+
},
|
|
39
|
+
"./runtime": {
|
|
40
|
+
"types": "./dist/types/runtime/index.d.ts",
|
|
41
|
+
"jsnext:source": "./src/runtime/index.ts",
|
|
42
|
+
"import": "./dist/esm/runtime/index.js",
|
|
43
|
+
"node": "./dist/esm-node/runtime/index.js",
|
|
44
|
+
"default": "./dist/esm/runtime/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./server": {
|
|
47
|
+
"types": "./dist/types/server/index.d.ts",
|
|
48
|
+
"jsnext:source": "./src/server/index.ts",
|
|
49
|
+
"import": "./dist/esm/server/index.js",
|
|
50
|
+
"node": "./dist/esm-node/server/index.js",
|
|
51
|
+
"default": "./dist/esm/server/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./i18n": {
|
|
54
|
+
"types": "./dist/types/runtime/i18n/index.d.ts",
|
|
55
|
+
"jsnext:source": "./src/runtime/i18n/index.ts",
|
|
56
|
+
"import": "./dist/esm/runtime/i18n/index.js",
|
|
57
|
+
"node": "./dist/esm-node/runtime/i18n/index.js",
|
|
58
|
+
"default": "./dist/esm/runtime/i18n/index.js"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"typesVersions": {
|
|
62
|
+
"*": {
|
|
63
|
+
".": [
|
|
64
|
+
"./dist/types/cli/index.d.ts"
|
|
65
|
+
],
|
|
66
|
+
"cli": [
|
|
67
|
+
"./dist/types/cli/index.d.ts"
|
|
68
|
+
],
|
|
69
|
+
"runtime": [
|
|
70
|
+
"./dist/types/runtime/index.d.ts"
|
|
71
|
+
],
|
|
72
|
+
"server": [
|
|
73
|
+
"./dist/types/server/index.d.ts"
|
|
74
|
+
],
|
|
75
|
+
"i18n": [
|
|
76
|
+
"./dist/types/runtime/i18n/index.d.ts"
|
|
77
|
+
]
|
|
46
78
|
}
|
|
47
79
|
},
|
|
48
80
|
"dependencies": {
|
|
49
81
|
"@swc/helpers": "^0.5.17",
|
|
50
|
-
"
|
|
82
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
83
|
+
"i18next-http-middleware": "^3.9.1",
|
|
84
|
+
"i18next-fs-backend": "^2.6.1",
|
|
85
|
+
"i18next-http-backend": "^3.0.2",
|
|
86
|
+
"i18next-chained-backend": "^4.6.2",
|
|
87
|
+
"@modern-js/plugin": "3.0.0-alpha.0",
|
|
88
|
+
"@modern-js/server-core": "3.0.0-alpha.0",
|
|
89
|
+
"@modern-js/server-runtime": "3.0.0-alpha.0",
|
|
90
|
+
"@modern-js/runtime-utils": "3.0.0-alpha.0",
|
|
91
|
+
"@modern-js/types": "3.0.0-alpha.0",
|
|
92
|
+
"@modern-js/utils": "3.0.0-alpha.0"
|
|
93
|
+
},
|
|
94
|
+
"peerDependencies": {
|
|
95
|
+
"react": ">=17.0.2",
|
|
96
|
+
"react-dom": ">=17.0.2",
|
|
97
|
+
"i18next": ">=25.7.3",
|
|
98
|
+
"react-i18next": ">=15.7.4",
|
|
99
|
+
"@modern-js/runtime": "^3.0.0-alpha.0"
|
|
100
|
+
},
|
|
101
|
+
"peerDependenciesMeta": {
|
|
102
|
+
"i18next": {
|
|
103
|
+
"optional": true
|
|
104
|
+
},
|
|
105
|
+
"react-i18next": {
|
|
106
|
+
"optional": true
|
|
107
|
+
}
|
|
51
108
|
},
|
|
52
109
|
"devDependencies": {
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
110
|
+
"i18next": "25.7.3",
|
|
111
|
+
"react-i18next": "15.7.4",
|
|
112
|
+
"@rslib/core": "0.18.5",
|
|
113
|
+
"@types/jest": "^29.5.14",
|
|
114
|
+
"@types/node": "^20",
|
|
115
|
+
"jest": "^29.7.0",
|
|
116
|
+
"react": "^19.2.3",
|
|
117
|
+
"react-dom": "^19.2.3",
|
|
118
|
+
"ts-jest": "^29.4.6",
|
|
119
|
+
"ts-node": "^10.9.2",
|
|
56
120
|
"typescript": "^5",
|
|
57
|
-
"@
|
|
58
|
-
"@
|
|
121
|
+
"@modern-js/runtime": "3.0.0-alpha.0",
|
|
122
|
+
"@modern-js/app-tools": "3.0.0-alpha.0",
|
|
123
|
+
"@scripts/jest-config": "2.66.0",
|
|
124
|
+
"@modern-js/rslib": "2.68.10"
|
|
59
125
|
},
|
|
60
126
|
"sideEffects": false,
|
|
61
127
|
"publishConfig": {
|
|
@@ -63,8 +129,8 @@
|
|
|
63
129
|
"access": "public"
|
|
64
130
|
},
|
|
65
131
|
"scripts": {
|
|
66
|
-
"
|
|
67
|
-
"build": "
|
|
68
|
-
"test": "jest
|
|
132
|
+
"dev": "rslib build --watch",
|
|
133
|
+
"build": "rslib build",
|
|
134
|
+
"test": "jest"
|
|
69
135
|
}
|
|
70
136
|
}
|
package/rslib.config.mts
ADDED
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import type { AppTools, CliPlugin } from '@modern-js/app-tools';
|
|
4
|
+
import { getPublicDirRoutePrefixes } from '@modern-js/server-core';
|
|
5
|
+
import type { Entrypoint } from '@modern-js/types';
|
|
6
|
+
import type { BackendOptions, LocaleDetectionOptions } from '../shared/type';
|
|
7
|
+
import { getBackendOptions, getLocaleDetectionOptions } from '../shared/utils';
|
|
8
|
+
|
|
9
|
+
export type TransformRuntimeConfigFn = (
|
|
10
|
+
extendedConfig: Record<string, any>,
|
|
11
|
+
entrypoint: Entrypoint,
|
|
12
|
+
) => Record<string, any>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if a directory exists and contains JSON files
|
|
16
|
+
*/
|
|
17
|
+
function hasJsonFiles(dirPath: string): boolean {
|
|
18
|
+
try {
|
|
19
|
+
if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const entries = fs.readdirSync(dirPath);
|
|
23
|
+
// Check if there are any JSON files in the directory or subdirectories
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const entryPath = path.join(dirPath, entry);
|
|
26
|
+
const stat = fs.statSync(entryPath);
|
|
27
|
+
if (stat.isFile() && entry.endsWith('.json')) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (stat.isDirectory()) {
|
|
31
|
+
// Recursively check subdirectories (e.g., locales/en/, locales/zh/)
|
|
32
|
+
if (hasJsonFiles(entryPath)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Auto-detect if locales directory exists with JSON files
|
|
45
|
+
* Checks both project root and config/public directory
|
|
46
|
+
*/
|
|
47
|
+
function detectLocalesDirectory(
|
|
48
|
+
appDirectory: string,
|
|
49
|
+
normalizedConfig?: any,
|
|
50
|
+
): boolean {
|
|
51
|
+
// Check project root directory
|
|
52
|
+
const rootLocalesPath = path.join(appDirectory, 'locales');
|
|
53
|
+
if (hasJsonFiles(rootLocalesPath)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check config/public directory
|
|
58
|
+
const configPublicPath = path.join(
|
|
59
|
+
appDirectory,
|
|
60
|
+
'config',
|
|
61
|
+
'public',
|
|
62
|
+
'locales',
|
|
63
|
+
);
|
|
64
|
+
if (hasJsonFiles(configPublicPath)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check publicDir if configured
|
|
69
|
+
const publicDir = normalizedConfig?.server?.publicDir;
|
|
70
|
+
if (publicDir) {
|
|
71
|
+
const publicDirPath = Array.isArray(publicDir) ? publicDir[0] : publicDir;
|
|
72
|
+
const localesPath = path.isAbsolute(publicDirPath)
|
|
73
|
+
? path.join(publicDirPath, 'locales')
|
|
74
|
+
: path.join(appDirectory, publicDirPath, 'locales');
|
|
75
|
+
if (hasJsonFiles(localesPath)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface I18nPluginOptions {
|
|
84
|
+
localeDetection?: LocaleDetectionOptions;
|
|
85
|
+
backend?: BackendOptions;
|
|
86
|
+
transformRuntimeConfig?: TransformRuntimeConfigFn;
|
|
87
|
+
customPlugin?: {
|
|
88
|
+
runtime?: {
|
|
89
|
+
name?: string;
|
|
90
|
+
path?: string;
|
|
91
|
+
};
|
|
92
|
+
server?: {
|
|
93
|
+
name?: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
[key: string]: any;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const i18nPlugin = (
|
|
100
|
+
options: I18nPluginOptions = {},
|
|
101
|
+
): CliPlugin<AppTools> => ({
|
|
102
|
+
name: '@modern-js/plugin-i18n',
|
|
103
|
+
setup: api => {
|
|
104
|
+
const {
|
|
105
|
+
localeDetection,
|
|
106
|
+
backend,
|
|
107
|
+
transformRuntimeConfig,
|
|
108
|
+
customPlugin,
|
|
109
|
+
...restOptions
|
|
110
|
+
} = options;
|
|
111
|
+
|
|
112
|
+
api._internalRuntimePlugins(({ entrypoint, plugins }) => {
|
|
113
|
+
const localeDetectionOptions = localeDetection
|
|
114
|
+
? getLocaleDetectionOptions(entrypoint.entryName, localeDetection)
|
|
115
|
+
: undefined;
|
|
116
|
+
|
|
117
|
+
// Auto-detect locales directory and enable backend if:
|
|
118
|
+
// 1. User didn't explicitly set backend.enabled to false
|
|
119
|
+
// 2. Locales directory exists with JSON files
|
|
120
|
+
// 3. If user configured loadPath or addPath, auto-enable backend without detection
|
|
121
|
+
let backendOptions: BackendOptions | undefined;
|
|
122
|
+
const { appDirectory } = api.getAppContext();
|
|
123
|
+
const normalizedConfig = api.getNormalizedConfig();
|
|
124
|
+
|
|
125
|
+
if (backend) {
|
|
126
|
+
const entryBackendOptions = getBackendOptions(
|
|
127
|
+
entrypoint.entryName,
|
|
128
|
+
backend,
|
|
129
|
+
);
|
|
130
|
+
// If user explicitly set enabled to false, don't auto-detect
|
|
131
|
+
if (entryBackendOptions?.enabled === false) {
|
|
132
|
+
backendOptions = entryBackendOptions;
|
|
133
|
+
} else {
|
|
134
|
+
// If user configured loadPath or addPath, auto-enable backend
|
|
135
|
+
// No need to detect locales directory since user has specified the path
|
|
136
|
+
if (entryBackendOptions?.loadPath || entryBackendOptions?.addPath) {
|
|
137
|
+
backendOptions = {
|
|
138
|
+
...entryBackendOptions,
|
|
139
|
+
enabled: true,
|
|
140
|
+
};
|
|
141
|
+
} else if (entryBackendOptions?.enabled !== true) {
|
|
142
|
+
// Auto-detect if enabled is not explicitly true and no loadPath/addPath configured
|
|
143
|
+
const hasLocales = detectLocalesDirectory(
|
|
144
|
+
appDirectory,
|
|
145
|
+
normalizedConfig,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (hasLocales) {
|
|
149
|
+
// Auto-enable backend if locales directory is detected
|
|
150
|
+
backendOptions = {
|
|
151
|
+
...entryBackendOptions,
|
|
152
|
+
enabled: true,
|
|
153
|
+
};
|
|
154
|
+
} else {
|
|
155
|
+
backendOptions = entryBackendOptions;
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
backendOptions = entryBackendOptions;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
// No backend config provided, try auto-detection
|
|
163
|
+
const hasLocales = detectLocalesDirectory(
|
|
164
|
+
appDirectory,
|
|
165
|
+
normalizedConfig,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (hasLocales) {
|
|
169
|
+
// Auto-enable backend if locales directory is detected
|
|
170
|
+
backendOptions = getBackendOptions(entrypoint.entryName, {
|
|
171
|
+
enabled: true,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const { metaName } = api.getAppContext();
|
|
177
|
+
|
|
178
|
+
// Transform extended config if transform function is provided
|
|
179
|
+
let extendedConfig = restOptions;
|
|
180
|
+
if (transformRuntimeConfig) {
|
|
181
|
+
extendedConfig = transformRuntimeConfig(
|
|
182
|
+
restOptions,
|
|
183
|
+
entrypoint as Entrypoint,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Build final config with base config and transformed extended config
|
|
188
|
+
const config = {
|
|
189
|
+
entryName: entrypoint.entryName,
|
|
190
|
+
localeDetection: localeDetectionOptions,
|
|
191
|
+
backend: backendOptions,
|
|
192
|
+
...extendedConfig,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
plugins.push({
|
|
196
|
+
name: customPlugin?.runtime?.name || 'i18n',
|
|
197
|
+
path: customPlugin?.runtime?.path || `@${metaName}/plugin-i18n/runtime`,
|
|
198
|
+
config,
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
entrypoint,
|
|
202
|
+
plugins,
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
api._internalServerPlugins(({ plugins }) => {
|
|
207
|
+
const { serverRoutes, metaName } = api.getAppContext();
|
|
208
|
+
const normalizedConfig = api.getNormalizedConfig();
|
|
209
|
+
|
|
210
|
+
let staticRoutePrefixes: string[] = [];
|
|
211
|
+
if (serverRoutes && Array.isArray(serverRoutes)) {
|
|
212
|
+
// Get static route prefixes from 'public' directories
|
|
213
|
+
// 'public' routes are handled by static plugin
|
|
214
|
+
staticRoutePrefixes = serverRoutes
|
|
215
|
+
.filter(
|
|
216
|
+
route => !route.entryName && route.entryPath.startsWith('public'),
|
|
217
|
+
)
|
|
218
|
+
.map(route => route.urlPath)
|
|
219
|
+
.filter(Boolean);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Also include publicDir configuration paths
|
|
223
|
+
// publicDir files are copied to dist/{dir}/ and should be treated as static resources
|
|
224
|
+
const publicDirPrefixes = getPublicDirRoutePrefixes(
|
|
225
|
+
normalizedConfig?.server?.publicDir,
|
|
226
|
+
);
|
|
227
|
+
publicDirPrefixes.forEach(prefix => {
|
|
228
|
+
if (!staticRoutePrefixes.includes(prefix)) {
|
|
229
|
+
staticRoutePrefixes.push(prefix);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
plugins.push({
|
|
234
|
+
name: customPlugin?.server?.name || `@${metaName}/plugin-i18n/server`,
|
|
235
|
+
options: {
|
|
236
|
+
localeDetection,
|
|
237
|
+
staticRoutePrefixes,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
return { plugins };
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
export default i18nPlugin;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Link, useInRouterContext, useParams } from '@modern-js/runtime/router';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
import { useModernI18n } from './context';
|
|
4
|
+
import { buildLocalizedUrl } from './utils';
|
|
5
|
+
|
|
6
|
+
export interface I18nLinkProps {
|
|
7
|
+
to: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
[key: string]: any; // Allow other props to be passed through
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* I18nLink component that automatically adds language prefix to navigation links.
|
|
14
|
+
* This component should be used within a :lang dynamic route context.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* // When current language is 'en' and to="/about"
|
|
19
|
+
* // The actual link will be "/en/about"
|
|
20
|
+
* <I18nLink to="/about">About</I18nLink>
|
|
21
|
+
*
|
|
22
|
+
* // When current language is 'zh' and to="/"
|
|
23
|
+
* // The actual link will be "/zh"
|
|
24
|
+
* <I18nLink to="/">Home</I18nLink>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
// Use static imports to avoid breaking router tree-shaking. Detect router context via useInRouterContext.
|
|
28
|
+
const useRouterHooks = () => {
|
|
29
|
+
const inRouter = useInRouterContext();
|
|
30
|
+
return {
|
|
31
|
+
Link: inRouter ? Link : null,
|
|
32
|
+
params: inRouter ? useParams() : ({} as any),
|
|
33
|
+
hasRouter: inRouter,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const I18nLink: React.FC<I18nLinkProps> = ({
|
|
38
|
+
to,
|
|
39
|
+
children,
|
|
40
|
+
...props
|
|
41
|
+
}) => {
|
|
42
|
+
const { Link, params, hasRouter } = useRouterHooks();
|
|
43
|
+
const { language, supportedLanguages } = useModernI18n();
|
|
44
|
+
|
|
45
|
+
// Get the current language from context (which reflects the actual current language)
|
|
46
|
+
// URL params might be stale after language changes, so we prioritize the context language
|
|
47
|
+
const currentLang = language;
|
|
48
|
+
|
|
49
|
+
// Build the localized URL by adding language prefix
|
|
50
|
+
const localizedTo = buildLocalizedUrl(to, currentLang, supportedLanguages);
|
|
51
|
+
|
|
52
|
+
// In development mode, warn if used outside of :lang route context
|
|
53
|
+
if (process.env.NODE_ENV === 'development' && hasRouter && !params.lang) {
|
|
54
|
+
console.warn(
|
|
55
|
+
'I18nLink is being used outside of a :lang dynamic route context. ' +
|
|
56
|
+
'This may cause unexpected behavior. Please ensure I18nLink is used within a route that has a :lang parameter.',
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// If router is not available, render as a regular anchor tag
|
|
61
|
+
if (!hasRouter || !Link) {
|
|
62
|
+
return (
|
|
63
|
+
<a href={localizedTo} {...props}>
|
|
64
|
+
{children}
|
|
65
|
+
</a>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<Link to={localizedTo} {...props}>
|
|
71
|
+
{children}
|
|
72
|
+
</Link>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export default I18nLink;
|