@modern-js/app-tools 3.7.0 → 3.8.1
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/bin/modern-bundle-docs.js +7 -0
- package/dist/cjs/builder/generator/createBuilderProviderConfig.js +5 -1
- package/dist/cjs/bundleDocs.js +114 -0
- package/dist/cjs/commands/build.js +2 -1
- package/dist/cjs/commands/deploy.js +4 -2
- package/dist/cjs/commands/index.js +0 -4
- package/dist/cjs/config/default.js +2 -0
- package/dist/cjs/index.js +52 -14
- package/dist/cjs/plugins/analyze/index.js +2 -2
- package/dist/cjs/plugins/analyze/utils.js +3 -2
- package/dist/cjs/plugins/initialize/index.js +4 -3
- package/dist/cjs/plugins/serverBuild.js +3 -2
- package/dist/esm/builder/generator/createBuilderProviderConfig.mjs +5 -1
- package/dist/esm/bundleDocs.mjs +62 -0
- package/dist/esm/commands/build.mjs +2 -1
- package/dist/esm/commands/deploy.mjs +4 -2
- package/dist/esm/commands/index.mjs +0 -4
- package/dist/esm/config/default.mjs +2 -0
- package/dist/esm/index.mjs +15 -1
- package/dist/esm/plugins/analyze/index.mjs +2 -2
- package/dist/esm/plugins/analyze/utils.mjs +3 -2
- package/dist/esm/plugins/initialize/index.mjs +4 -3
- package/dist/esm/plugins/serverBuild.mjs +3 -2
- package/dist/esm-node/builder/generator/createBuilderProviderConfig.mjs +5 -1
- package/dist/esm-node/bundleDocs.mjs +63 -0
- package/dist/esm-node/commands/build.mjs +2 -1
- package/dist/esm-node/commands/deploy.mjs +4 -2
- package/dist/esm-node/commands/index.mjs +0 -4
- package/dist/esm-node/config/default.mjs +2 -0
- package/dist/esm-node/index.mjs +15 -1
- package/dist/esm-node/plugins/analyze/index.mjs +2 -2
- package/dist/esm-node/plugins/analyze/utils.mjs +3 -2
- package/dist/esm-node/plugins/initialize/index.mjs +4 -3
- package/dist/esm-node/plugins/serverBuild.mjs +3 -2
- package/dist/types/bundleDocs.d.ts +20 -0
- package/dist/types/commands/deploy.d.ts +2 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/plugins/analyze/utils.d.ts +1 -1
- package/dist/types/types/config/dev.d.ts +6 -0
- package/docs/apis/app/commands.md +241 -0
- package/docs/apis/app/hooks/api/lambda.md +9 -0
- package/docs/apis/app/hooks/config/favicon.md +24 -0
- package/docs/apis/app/hooks/config/html.md +5 -0
- package/docs/apis/app/hooks/config/icon.md +24 -0
- package/docs/apis/app/hooks/config/mock.md +6 -0
- package/docs/apis/app/hooks/config/public.md +26 -0
- package/docs/apis/app/hooks/config/upload.md +50 -0
- package/docs/apis/app/hooks/modern-config.md +5 -0
- package/docs/apis/app/hooks/server/server.md +5 -0
- package/docs/apis/app/hooks/shared.md +3 -0
- package/docs/apis/app/hooks/src/app.md +30 -0
- package/docs/apis/app/hooks/src/entry.md +34 -0
- package/docs/apis/app/hooks/src/entry.server.md +51 -0
- package/docs/apis/app/hooks/src/modern.runtime.md +5 -0
- package/docs/apis/app/hooks/src/routes.md +86 -0
- package/docs/apis/app/hooks/src/server.md +3 -0
- package/docs/apis/app/runtime/bff/use-hono-context.md +27 -0
- package/docs/apis/app/runtime/core/create-root.md +19 -0
- package/docs/apis/app/runtime/core/render.md +39 -0
- package/docs/apis/app/runtime/core/runtime-context.md +156 -0
- package/docs/apis/app/runtime/router/router.md +280 -0
- package/docs/apis/app/runtime/ssr/no-ssr.md +35 -0
- package/docs/apis/app/runtime/ssr/renderStreaming.md +67 -0
- package/docs/apis/app/runtime/ssr/renderString.md +60 -0
- package/docs/apis/app/runtime/ssr/requestHandler.md +44 -0
- package/docs/apis/app/runtime/utility/css-in-js.md +40 -0
- package/docs/apis/app/runtime/utility/head.md +35 -0
- package/docs/apis/app/runtime/utility/loadable.md +82 -0
- package/docs/community/blog/2022-0708-updates.md +91 -0
- package/docs/community/blog/2022-0910-updates.md +76 -0
- package/docs/community/blog/overview.md +63 -0
- package/docs/community/blog/v2-release-note.md +238 -0
- package/docs/community/blog/v3-release-note.md +622 -0
- package/docs/community/contributing-guide.md +253 -0
- package/docs/community/releases.md +27 -0
- package/docs/community/showcase.md +34 -0
- package/docs/community/team.md +14 -0
- package/docs/configure/app/bff/cross-project.md +20 -0
- package/docs/configure/app/bff/prefix.md +29 -0
- package/docs/configure/app/builder-plugins.md +66 -0
- package/docs/configure/app/dev/asset-prefix.md +13 -0
- package/docs/configure/app/dev/before-start-url.md +17 -0
- package/docs/configure/app/dev/client.md +41 -0
- package/docs/configure/app/dev/hmr.md +10 -0
- package/docs/configure/app/dev/host.md +18 -0
- package/docs/configure/app/dev/https.md +77 -0
- package/docs/configure/app/dev/lazy-compilation.md +39 -0
- package/docs/configure/app/dev/live-reload.md +10 -0
- package/docs/configure/app/dev/mock-dir.md +31 -0
- package/docs/configure/app/dev/progress-bar.md +19 -0
- package/docs/configure/app/dev/server.md +124 -0
- package/docs/configure/app/dev/setup-middlewares.md +32 -0
- package/docs/configure/app/dev/start-url.md +48 -0
- package/docs/configure/app/dev/watch-files.md +27 -0
- package/docs/configure/app/dev/write-to-disk.md +10 -0
- package/docs/configure/app/experiments/source-build.md +31 -0
- package/docs/configure/app/html/app-icon.md +28 -0
- package/docs/configure/app/html/crossorigin.md +10 -0
- package/docs/configure/app/html/favicon.md +16 -0
- package/docs/configure/app/html/inject.md +10 -0
- package/docs/configure/app/html/meta.md +24 -0
- package/docs/configure/app/html/mount-id.md +10 -0
- package/docs/configure/app/html/output-structure.md +10 -0
- package/docs/configure/app/html/script-loading.md +10 -0
- package/docs/configure/app/html/tags.md +15 -0
- package/docs/configure/app/html/template-parameters.md +33 -0
- package/docs/configure/app/html/template.md +10 -0
- package/docs/configure/app/html/title.md +18 -0
- package/docs/configure/app/output/asset-prefix.md +11 -0
- package/docs/configure/app/output/assets-retry.md +77 -0
- package/docs/configure/app/output/charset.md +10 -0
- package/docs/configure/app/output/clean-dist-path.md +16 -0
- package/docs/configure/app/output/convert-to-rem.md +79 -0
- package/docs/configure/app/output/copy.md +10 -0
- package/docs/configure/app/output/css-modules.md +48 -0
- package/docs/configure/app/output/data-uri-limit.md +26 -0
- package/docs/configure/app/output/disable-css-module-extension.md +55 -0
- package/docs/configure/app/output/disable-inline-runtime-chunk.md +41 -0
- package/docs/configure/app/output/disable-svgr.md +16 -0
- package/docs/configure/app/output/disable-ts-checker.md +49 -0
- package/docs/configure/app/output/dist-path.md +43 -0
- package/docs/configure/app/output/enable-asset-manifest.md +36 -0
- package/docs/configure/app/output/enable-css-module-tsdeclaration.md +28 -0
- package/docs/configure/app/output/enable-inline-route-manifests.md +16 -0
- package/docs/configure/app/output/externals.md +20 -0
- package/docs/configure/app/output/filename-hash.md +10 -0
- package/docs/configure/app/output/filename.md +55 -0
- package/docs/configure/app/output/inject-styles.md +10 -0
- package/docs/configure/app/output/inline-scripts.md +29 -0
- package/docs/configure/app/output/inline-styles.md +29 -0
- package/docs/configure/app/output/legal-comments.md +18 -0
- package/docs/configure/app/output/minify.md +22 -0
- package/docs/configure/app/output/override-browserslist.md +22 -0
- package/docs/configure/app/output/polyfill.md +12 -0
- package/docs/configure/app/output/source-map.md +30 -0
- package/docs/configure/app/output/split-route-chunks.md +16 -0
- package/docs/configure/app/output/ssg.md +81 -0
- package/docs/configure/app/output/ssgByEntries.md +90 -0
- package/docs/configure/app/output/svg-default-export.md +30 -0
- package/docs/configure/app/output/temp-dir.md +20 -0
- package/docs/configure/app/performance/build-cache.md +39 -0
- package/docs/configure/app/performance/chunk-split.md +40 -0
- package/docs/configure/app/performance/dns-prefetch.md +15 -0
- package/docs/configure/app/performance/preconnect.md +16 -0
- package/docs/configure/app/performance/prefetch.md +21 -0
- package/docs/configure/app/performance/preload.md +23 -0
- package/docs/configure/app/performance/print-file-size.md +40 -0
- package/docs/configure/app/performance/profile.md +10 -0
- package/docs/configure/app/performance/remove-console.md +10 -0
- package/docs/configure/app/performance/remove-moment-locale.md +10 -0
- package/docs/configure/app/plugins.md +59 -0
- package/docs/configure/app/resolve/alias-strategy.md +10 -0
- package/docs/configure/app/resolve/alias.md +9 -0
- package/docs/configure/app/resolve/condition-names.md +13 -0
- package/docs/configure/app/resolve/dedupe.md +9 -0
- package/docs/configure/app/resolve/extensions.md +13 -0
- package/docs/configure/app/runtime/0-intro.md +58 -0
- package/docs/configure/app/runtime/plugins.md +58 -0
- package/docs/configure/app/runtime/router.md +35 -0
- package/docs/configure/app/security/check-syntax.md +69 -0
- package/docs/configure/app/security/nonce.md +15 -0
- package/docs/configure/app/security/sri.md +20 -0
- package/docs/configure/app/server/base-url.md +26 -0
- package/docs/configure/app/server/port.md +18 -0
- package/docs/configure/app/server/public-routes.md +22 -0
- package/docs/configure/app/server/routes.md +86 -0
- package/docs/configure/app/server/rsc.md +26 -0
- package/docs/configure/app/server/ssr-by-entries.md +25 -0
- package/docs/configure/app/server/ssr.md +78 -0
- package/docs/configure/app/server/tsconfig-path.md +59 -0
- package/docs/configure/app/source/alias-strategy.md +14 -0
- package/docs/configure/app/source/alias.md +23 -0
- package/docs/configure/app/source/config-dir.md +20 -0
- package/docs/configure/app/source/decorators.md +25 -0
- package/docs/configure/app/source/define.md +16 -0
- package/docs/configure/app/source/disable-default-entries.md +28 -0
- package/docs/configure/app/source/enable-async-entry.md +54 -0
- package/docs/configure/app/source/enable-async-pre-entry.md +26 -0
- package/docs/configure/app/source/entries-dir.md +35 -0
- package/docs/configure/app/source/entries.md +179 -0
- package/docs/configure/app/source/exclude.md +10 -0
- package/docs/configure/app/source/global-vars.md +106 -0
- package/docs/configure/app/source/include.md +36 -0
- package/docs/configure/app/source/main-entry-name.md +24 -0
- package/docs/configure/app/source/pre-entry.md +10 -0
- package/docs/configure/app/source/react-compiler.md +68 -0
- package/docs/configure/app/source/transform-import.md +27 -0
- package/docs/configure/app/split-chunks.md +17 -0
- package/docs/configure/app/tools/autoprefixer.md +44 -0
- package/docs/configure/app/tools/bundler-chain.md +26 -0
- package/docs/configure/app/tools/css-extract.md +33 -0
- package/docs/configure/app/tools/css-loader.md +17 -0
- package/docs/configure/app/tools/dev-server.md +113 -0
- package/docs/configure/app/tools/html-plugin.md +41 -0
- package/docs/configure/app/tools/less.md +81 -0
- package/docs/configure/app/tools/lightningcss-loader.md +35 -0
- package/docs/configure/app/tools/minify-css.md +53 -0
- package/docs/configure/app/tools/postcss.md +34 -0
- package/docs/configure/app/tools/rspack.md +10 -0
- package/docs/configure/app/tools/sass.md +78 -0
- package/docs/configure/app/tools/style-loader.md +10 -0
- package/docs/configure/app/tools/swc.md +65 -0
- package/docs/configure/app/tools/ts-checker.md +109 -0
- package/docs/configure/app/usage.md +276 -0
- package/docs/guides/advanced-features/bff/cross-project.md +109 -0
- package/docs/guides/advanced-features/bff/extend-server.md +120 -0
- package/docs/guides/advanced-features/bff/frameworks.md +124 -0
- package/docs/guides/advanced-features/bff/function.md +314 -0
- package/docs/guides/advanced-features/bff/operators.md +554 -0
- package/docs/guides/advanced-features/bff/sdk.md +116 -0
- package/docs/guides/advanced-features/bff/upload.md +101 -0
- package/docs/guides/advanced-features/bff.md +18 -0
- package/docs/guides/advanced-features/build-performance.md +130 -0
- package/docs/guides/advanced-features/compatibility.md +120 -0
- package/docs/guides/advanced-features/international/advanced.md +128 -0
- package/docs/guides/advanced-features/international/api.md +231 -0
- package/docs/guides/advanced-features/international/best-practices.md +286 -0
- package/docs/guides/advanced-features/international/configuration.md +227 -0
- package/docs/guides/advanced-features/international/locale-detection.md +126 -0
- package/docs/guides/advanced-features/international/quick-start.md +128 -0
- package/docs/guides/advanced-features/international/resource-loading.md +154 -0
- package/docs/guides/advanced-features/international/routing.md +130 -0
- package/docs/guides/advanced-features/international.md +27 -0
- package/docs/guides/advanced-features/low-level.md +46 -0
- package/docs/guides/advanced-features/page-performance/code-split.md +77 -0
- package/docs/guides/advanced-features/page-performance/inline-assets.md +159 -0
- package/docs/guides/advanced-features/page-performance/optimize-bundle.md +97 -0
- package/docs/guides/advanced-features/page-performance/react-compiler.md +69 -0
- package/docs/guides/advanced-features/server-monitor/logger.md +41 -0
- package/docs/guides/advanced-features/server-monitor/metrics.md +58 -0
- package/docs/guides/advanced-features/server-monitor/monitors.md +242 -0
- package/docs/guides/advanced-features/source-build.md +164 -0
- package/docs/guides/advanced-features/web-server.md +288 -0
- package/docs/guides/basic-features/alias.md +102 -0
- package/docs/guides/basic-features/css/css-in-js.md +72 -0
- package/docs/guides/basic-features/css/css-modules.md +212 -0
- package/docs/guides/basic-features/css/css.md +27 -0
- package/docs/guides/basic-features/css/tailwindcss.md +27 -0
- package/docs/guides/basic-features/data/data-cache.md +510 -0
- package/docs/guides/basic-features/data/data-fetch.md +415 -0
- package/docs/guides/basic-features/data/data-write.md +227 -0
- package/docs/guides/basic-features/debug/mock.md +109 -0
- package/docs/guides/basic-features/debug/proxy.md +21 -0
- package/docs/guides/basic-features/debug/rsdoctor.md +62 -0
- package/docs/guides/basic-features/debug/using-storybook.md +112 -0
- package/docs/guides/basic-features/deploy.md +458 -0
- package/docs/guides/basic-features/env-vars.md +177 -0
- package/docs/guides/basic-features/html.md +255 -0
- package/docs/guides/basic-features/output-files.md +141 -0
- package/docs/guides/basic-features/render/before-render.md +108 -0
- package/docs/guides/basic-features/render/overview.md +47 -0
- package/docs/guides/basic-features/render/rsc.md +525 -0
- package/docs/guides/basic-features/render/ssg.md +228 -0
- package/docs/guides/basic-features/render/ssr-cache.md +201 -0
- package/docs/guides/basic-features/render/ssr.md +321 -0
- package/docs/guides/basic-features/render/streaming-ssr.md +264 -0
- package/docs/guides/basic-features/routes/config-routes.md +426 -0
- package/docs/guides/basic-features/routes/routes.md +498 -0
- package/docs/guides/basic-features/static-assets/json-files.md +120 -0
- package/docs/guides/basic-features/static-assets/svg-assets.md +168 -0
- package/docs/guides/basic-features/static-assets/wasm-assets.md +62 -0
- package/docs/guides/basic-features/static-assets.md +160 -0
- package/docs/guides/basic-features/testing/playwright.md +120 -0
- package/docs/guides/basic-features/testing/rstest.md +251 -0
- package/docs/guides/concept/builder.md +37 -0
- package/docs/guides/concept/entries.md +319 -0
- package/docs/guides/concept/server.md +35 -0
- package/docs/guides/get-started/ai-coding-agents.md +58 -0
- package/docs/guides/get-started/glossary.md +63 -0
- package/docs/guides/get-started/introduction.md +36 -0
- package/docs/guides/get-started/quick-start.md +236 -0
- package/docs/guides/get-started/tech-stack.md +82 -0
- package/docs/guides/get-started/upgrade.md +123 -0
- package/docs/guides/topic-detail/module-federation/application.md +116 -0
- package/docs/guides/topic-detail/module-federation/deploy.md +104 -0
- package/docs/guides/topic-detail/module-federation/i18n.md +670 -0
- package/docs/guides/topic-detail/module-federation/introduce.md +35 -0
- package/docs/guides/topic-detail/module-federation/ssr.md +118 -0
- package/docs/guides/topic-detail/module-federation/usage.md +219 -0
- package/docs/guides/troubleshooting/builder.md +110 -0
- package/docs/guides/troubleshooting/cli.md +35 -0
- package/docs/guides/troubleshooting/dependencies.md +119 -0
- package/docs/guides/troubleshooting/hmr.md +144 -0
- package/docs/guides/upgrade/config.md +963 -0
- package/docs/guides/upgrade/entry.md +463 -0
- package/docs/guides/upgrade/other.md +183 -0
- package/docs/guides/upgrade/overview.md +33 -0
- package/docs/guides/upgrade/tailwindcss.md +91 -0
- package/docs/guides/upgrade/web-server.md +109 -0
- package/docs/index.md +33 -0
- package/docs/llms.txt +285 -0
- package/docs/plugin/cli-plugins/api.md +573 -0
- package/docs/plugin/cli-plugins/life-cycle.md +2 -0
- package/docs/plugin/introduction.md +152 -0
- package/docs/plugin/official/cli-plugins/plugin-bff.md +5 -0
- package/docs/plugin/official/cli-plugins/plugin-ssg.md +5 -0
- package/docs/plugin/official/cli-plugins/plugin-styled-components.md +5 -0
- package/docs/plugin/official/cli-plugins.md +4 -0
- package/docs/plugin/plugin-system.md +238 -0
- package/docs/plugin/runtime-plugins/api.md +194 -0
- package/docs/plugin/runtime-plugins/life-cycle.md +2 -0
- package/docs/plugin/server-plugins/api.md +209 -0
- package/docs/plugin/server-plugins/life-cycle.md +13 -0
- package/docs/tutorials/examples/csr-auth.md +9 -0
- package/docs/tutorials/foundations/introduction.md +16 -0
- package/package.json +46 -13
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## useModernI18n
|
|
4
|
+
|
|
5
|
+
`useModernI18n` is a React Hook provided by the plugin. Use it to access internationalization state and actions in components.
|
|
6
|
+
|
|
7
|
+
### Return Value
|
|
8
|
+
|
|
9
|
+
| Field | Type | Description |
|
|
10
|
+
| --------------------- | --------------------------------- | ---------------------------------------------------------------------------- |
|
|
11
|
+
| `language` | `string` | Current language code |
|
|
12
|
+
| `changeLanguage` | `(lang: string) => Promise<void>` | Switches language |
|
|
13
|
+
| `supportedLanguages` | `string[]` | Supported language list, from `localeDetection.languages` |
|
|
14
|
+
| `isLanguageSupported` | `(lang: string) => boolean` | Checks whether a language is in the supported list |
|
|
15
|
+
| `isResourcesReady` | `boolean` | Whether translation resources for the current language have finished loading |
|
|
16
|
+
| `i18nInstance` | `I18nInstance` | i18next instance for advanced scenarios |
|
|
17
|
+
|
|
18
|
+
### Basic Usage
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
22
|
+
import { useTranslation } from 'react-i18next';
|
|
23
|
+
|
|
24
|
+
function LanguageSwitcher() {
|
|
25
|
+
const { language, changeLanguage, supportedLanguages } = useModernI18n();
|
|
26
|
+
const { t } = useTranslation();
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div>
|
|
30
|
+
<p>{t('welcome')}</p>
|
|
31
|
+
{supportedLanguages.map(lang => (
|
|
32
|
+
<button
|
|
33
|
+
key={lang}
|
|
34
|
+
onClick={() => changeLanguage(lang)}
|
|
35
|
+
disabled={lang === language}
|
|
36
|
+
>
|
|
37
|
+
{lang}
|
|
38
|
+
</button>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### changeLanguage
|
|
46
|
+
|
|
47
|
+
When switching language, the following happens in order:
|
|
48
|
+
|
|
49
|
+
1. The language of the i18next instance is updated.
|
|
50
|
+
2. Browser cache is updated (Cookie / LocalStorage, depending on the `caches` configuration).
|
|
51
|
+
3. The URL path prefix is updated if `localePathRedirect` is enabled.
|
|
52
|
+
|
|
53
|
+
`changeLanguage` is asynchronous, so use `await` when calling it:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
await changeLanguage('zh');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### isResourcesReady
|
|
60
|
+
|
|
61
|
+
In custom backend scenarios, translation resources are loaded asynchronously. Use `isResourcesReady` to avoid rendering before resources are ready:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
function MyComponent() {
|
|
65
|
+
const { isResourcesReady } = useModernI18n();
|
|
66
|
+
const { t } = useTranslation();
|
|
67
|
+
|
|
68
|
+
if (!isResourcesReady) {
|
|
69
|
+
return <div>Loading translations...</div>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return <div>{t('content')}</div>;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`isResourcesReady` checks whether the i18n instance is initialized, whether resources are currently loading for the current language, and whether all required namespaces have finished loading.
|
|
77
|
+
|
|
78
|
+
## I18nLink Component
|
|
79
|
+
|
|
80
|
+
A route link component with a locale prefix.
|
|
81
|
+
|
|
82
|
+
### Props
|
|
83
|
+
|
|
84
|
+
| Prop | Type | Required | Description |
|
|
85
|
+
| ---------------- | ----------------- | -------- | ------------------------------------------------------------------ |
|
|
86
|
+
| `to` | `string` | Yes | Target path. Do not include the locale prefix |
|
|
87
|
+
| `children` | `React.ReactNode` | Yes | Link content |
|
|
88
|
+
| `replace` | `boolean` | No | Uses `history.replace` instead of `push` |
|
|
89
|
+
| `state` | `any` | No | State passed to the target route |
|
|
90
|
+
| Other Link props | - | No | Inherited from the `Link` component in `@modern-js/runtime/router` |
|
|
91
|
+
|
|
92
|
+
### Usage
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { I18nLink } from '@modern-js/plugin-i18n/runtime';
|
|
96
|
+
|
|
97
|
+
<I18nLink to="/about">About</I18nLink>
|
|
98
|
+
<I18nLink to="/contact" replace>Contact</I18nLink>
|
|
99
|
+
<I18nLink to="/profile" state={{ from: 'home' }}>Profile</I18nLink>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Runtime Plugin API
|
|
103
|
+
|
|
104
|
+
The i18n plugin mounts `changeLanguage` and `i18nInstance` on the `context` of the `onBeforeRender` hook for other runtime plugins:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import type { RuntimePlugin } from '@modern-js/runtime';
|
|
108
|
+
|
|
109
|
+
const myPlugin = (): RuntimePlugin => ({
|
|
110
|
+
name: 'my-plugin',
|
|
111
|
+
setup: api => {
|
|
112
|
+
api.onBeforeRender(async context => {
|
|
113
|
+
if (!context.changeLanguage) return; // Make sure the i18n plugin is loaded.
|
|
114
|
+
|
|
115
|
+
const lang = detectLangFromRequest(context);
|
|
116
|
+
const supported = context.i18nInstance?.options?.supportedLngs ?? [];
|
|
117
|
+
|
|
118
|
+
if (supported.includes(lang)) {
|
|
119
|
+
try {
|
|
120
|
+
await context.changeLanguage(lang);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
console.error('Language change failed:', e);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Notes:**
|
|
131
|
+
|
|
132
|
+
- Make sure the i18n plugin is registered before plugins that use `context.changeLanguage`.
|
|
133
|
+
- `changeLanguage` does not update the URL path on the server. Use it together with the routing plugin or handle the path manually.
|
|
134
|
+
|
|
135
|
+
## Type Definitions
|
|
136
|
+
|
|
137
|
+
### I18nInstance
|
|
138
|
+
|
|
139
|
+
The i18next instance type used by the plugin. It is a subset of the i18next `i18n` type and only lists fields actually used by the plugin:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
interface I18nInstance {
|
|
143
|
+
language: string;
|
|
144
|
+
isInitialized: boolean;
|
|
145
|
+
init: (options?: I18nInitOptions) => void | Promise<void>;
|
|
146
|
+
changeLanguage: (lang: string) => void | Promise<void>;
|
|
147
|
+
use: (plugin: any) => void;
|
|
148
|
+
createInstance: (options?: I18nInitOptions) => I18nInstance;
|
|
149
|
+
options?: {
|
|
150
|
+
backend?: BackendOptions;
|
|
151
|
+
supportedLngs?: string[];
|
|
152
|
+
[key: string]: any;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### I18nSdkLoader
|
|
158
|
+
|
|
159
|
+
Type of the custom backend loader function:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
type I18nSdkLoader = (options: I18nSdkLoadOptions) => Promise<Resources>;
|
|
163
|
+
|
|
164
|
+
interface I18nSdkLoadOptions {
|
|
165
|
+
lng?: string; // Single language code
|
|
166
|
+
ns?: string; // Single namespace
|
|
167
|
+
lngs?: string[]; // Multiple language codes
|
|
168
|
+
nss?: string[]; // Multiple namespaces
|
|
169
|
+
all?: boolean; // Load all resources
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
type Resources = {
|
|
173
|
+
[lng: string]: {
|
|
174
|
+
[ns: string]: Record<string, any>;
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### LanguageDetectorOptions
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
interface LanguageDetectorOptions {
|
|
183
|
+
order?: string[];
|
|
184
|
+
lookupQuerystring?: string; // Default: 'lng'
|
|
185
|
+
lookupCookie?: string; // Default: 'i18next'
|
|
186
|
+
lookupLocalStorage?: string; // Default: 'i18nextLng'
|
|
187
|
+
lookupSession?: string;
|
|
188
|
+
lookupHeader?: string; // Default: 'accept-language'
|
|
189
|
+
lookupFromPathIndex?: number; // Default: 0
|
|
190
|
+
caches?: false | string[];
|
|
191
|
+
cookieMinutes?: number; // Default: 525600 (1 year)
|
|
192
|
+
cookieExpirationDate?: Date;
|
|
193
|
+
cookieDomain?: string;
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Integration with react-i18next
|
|
198
|
+
|
|
199
|
+
The plugin is fully compatible with react-i18next, and the two API sets can be used together:
|
|
200
|
+
|
|
201
|
+
- **`useTranslation`** (react-i18next): Gets the `t()` function for translation. This is the most commonly used Hook.
|
|
202
|
+
- **`useModernI18n`** (provided by the plugin): Gets plugin-level state such as language switching, supported language list, and resource loading status.
|
|
203
|
+
|
|
204
|
+
Most components only need `useTranslation`. Use `useModernI18n` only when you need language switching or resource loading status:
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
import { useTranslation } from 'react-i18next';
|
|
208
|
+
import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
209
|
+
|
|
210
|
+
function App() {
|
|
211
|
+
const { t } = useTranslation(); // Translate text
|
|
212
|
+
const { language, changeLanguage } = useModernI18n(); // Switch language
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div>
|
|
216
|
+
<h1>{t('welcome')}</h1>
|
|
217
|
+
<button onClick={() => changeLanguage('zh')}>Chinese</button>
|
|
218
|
+
<button onClick={() => changeLanguage('en')}>English</button>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
You can also get the i18next instance from `useModernI18n` and call capabilities outside react-i18next directly:
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
const { i18nInstance } = useModernI18n();
|
|
228
|
+
|
|
229
|
+
// Dynamically add translation resources.
|
|
230
|
+
i18nInstance.addResourceBundle('zh', 'translation', { key: 'Value' }, true, true);
|
|
231
|
+
```
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Best Practices
|
|
2
|
+
|
|
3
|
+
## Resource File Organization
|
|
4
|
+
|
|
5
|
+
Namespaces are used to split translation files by business module. The default namespace is `translation`. Splitting by module can reduce the initial loading size and load only the namespaces that are actually used.
|
|
6
|
+
|
|
7
|
+
Recommended directory structure:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
locales/
|
|
11
|
+
├── en/
|
|
12
|
+
│ ├── translation.json <- Default namespace for common text
|
|
13
|
+
│ ├── common.json <- Common UI text such as buttons and labels
|
|
14
|
+
│ └── errors.json <- Error messages
|
|
15
|
+
└── zh/
|
|
16
|
+
├── translation.json
|
|
17
|
+
├── common.json
|
|
18
|
+
└── errors.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Declare multiple namespaces:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// src/modern.runtime.ts
|
|
25
|
+
export default defineRuntimeConfig({
|
|
26
|
+
i18n: {
|
|
27
|
+
initOptions: {
|
|
28
|
+
ns: ['translation', 'common', 'errors'],
|
|
29
|
+
defaultNS: 'translation',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use a specific namespace in components:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useTranslation } from 'react-i18next';
|
|
39
|
+
|
|
40
|
+
// Use a single namespace.
|
|
41
|
+
function MyButton() {
|
|
42
|
+
const { t } = useTranslation('common');
|
|
43
|
+
return <button>{t('submit')}</button>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Use multiple namespaces and access keys with namespace:key.
|
|
47
|
+
function Dashboard() {
|
|
48
|
+
const { t } = useTranslation(['dashboard', 'common']);
|
|
49
|
+
return (
|
|
50
|
+
<header>
|
|
51
|
+
<h1>{t('dashboard:title')}</h1>
|
|
52
|
+
<button>{t('common:button.refresh')}</button>
|
|
53
|
+
</header>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// keyPrefix can omit repeated prefixes.
|
|
58
|
+
function ButtonGroup() {
|
|
59
|
+
const { t } = useTranslation('common', { keyPrefix: 'button' });
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
<button>{t('submit')}</button> {/* common:button.submit */}
|
|
63
|
+
<button>{t('cancel')}</button> {/* common:button.cancel */}
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Translation Key Naming
|
|
70
|
+
|
|
71
|
+
The quality of translation key names directly affects maintenance cost. Recommendations:
|
|
72
|
+
|
|
73
|
+
- **Use semantic words** and avoid abbreviations: `button.submit` is better than `btn.sbm`.
|
|
74
|
+
- **Use module-based prefixes**: `dashboard.table.header`, `auth.login.title`.
|
|
75
|
+
- **Do not use complete source-language text as keys**: Keys should be stable identifiers, not the translation content itself.
|
|
76
|
+
- **Use dots for hierarchy**: Match the nested JSON structure.
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
// Recommended
|
|
80
|
+
{
|
|
81
|
+
"page": {
|
|
82
|
+
"title": "User Settings",
|
|
83
|
+
"description": "Manage your account information"
|
|
84
|
+
},
|
|
85
|
+
"form": {
|
|
86
|
+
"username": { "label": "Username", "placeholder": "Enter username" },
|
|
87
|
+
"submit": "Save changes"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
t('page.title')
|
|
94
|
+
t('form.username.label')
|
|
95
|
+
t('form.submit', { defaultValue: 'Save' }) // defaultValue prevents showing the key string when the key is missing.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Plurals
|
|
99
|
+
|
|
100
|
+
i18next automatically selects the correct plural form based on the `count` parameter. **Note: since i18next v21, JSON v4 format is used by default**. Plural suffixes changed to CLDR standards such as `_zero`, `_one`, and `_other`; the old `_plural` format is deprecated.
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
// locales/en/translation.json (JSON v4 format)
|
|
104
|
+
{
|
|
105
|
+
"item_zero": "No items",
|
|
106
|
+
"item_one": "{{count}} item",
|
|
107
|
+
"item_other": "{{count}} items"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
// locales/zh/translation.json
|
|
113
|
+
{
|
|
114
|
+
"item_zero": "没有条目",
|
|
115
|
+
"item_other": "{{count}} 个条目"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
t('item', { count: 0 }) // "没有条目" / "No items"
|
|
121
|
+
t('item', { count: 1 }) // "没有条目" / "1 item" (Chinese usually has only one form)
|
|
122
|
+
t('item', { count: 5 }) // "5 个条目" / "5 items"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Different languages have different plural rules. English has singular and plural, while Russian has one/few/many forms. i18next automatically matches CLDR rules based on the language code.
|
|
126
|
+
|
|
127
|
+
:::tip
|
|
128
|
+
Use the `_plural` format only if your project uses an old i18next version or explicitly configures `compatibilityJSON: 'v3'`. New projects should use v4 format.
|
|
129
|
+
:::
|
|
130
|
+
|
|
131
|
+
## Nested Keys
|
|
132
|
+
|
|
133
|
+
Nested structures can clearly reflect UI hierarchy and are accessed with dots:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"modal": {
|
|
138
|
+
"confirm": {
|
|
139
|
+
"title": "Confirm deletion",
|
|
140
|
+
"message": "This action cannot be undone. Continue?",
|
|
141
|
+
"actions": {
|
|
142
|
+
"ok": "Confirm",
|
|
143
|
+
"cancel": "Cancel"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
t('modal.confirm.title')
|
|
152
|
+
t('modal.confirm.actions.ok')
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Avoid nesting deeper than 4 levels. Excessive depth makes key names long and hard to maintain.
|
|
156
|
+
|
|
157
|
+
## Formatting Interpolation
|
|
158
|
+
|
|
159
|
+
Use the `interpolation.format` function to handle number, date, currency, and other formatting consistently, instead of calling `Intl` APIs separately in each component:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
// src/modern.runtime.ts
|
|
163
|
+
export default defineRuntimeConfig({
|
|
164
|
+
i18n: {
|
|
165
|
+
initOptions: {
|
|
166
|
+
interpolation: {
|
|
167
|
+
escapeValue: false, // React already escapes text. Disable this to avoid double escaping.
|
|
168
|
+
format(value, format, lng) {
|
|
169
|
+
if (format === 'currency') {
|
|
170
|
+
return new Intl.NumberFormat(lng, {
|
|
171
|
+
style: 'currency',
|
|
172
|
+
currency: lng === 'zh' ? 'CNY' : 'USD',
|
|
173
|
+
}).format(Number(value));
|
|
174
|
+
}
|
|
175
|
+
if (format === 'date') {
|
|
176
|
+
return new Intl.DateTimeFormat(lng, { dateStyle: 'medium' }).format(
|
|
177
|
+
value instanceof Date ? value : new Date(value),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Use `, format` in translation files to specify the formatting type:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"price": "Current price: {{value, currency}}",
|
|
193
|
+
"expiry": "Expiry date: {{date, date}}"
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
t('price', { value: 99.5 }) // "当前价格:¥99.50" (zh) / "$99.50" (en)
|
|
199
|
+
t('expiry', { date: new Date() }) // "到期日:2025年5月12日" (zh)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Error Handling
|
|
203
|
+
|
|
204
|
+
### Loading State Handling
|
|
205
|
+
|
|
206
|
+
When using a custom backend, translation resources are loaded asynchronously. Use `isResourcesReady` to handle the loading state:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
210
|
+
import { useTranslation } from 'react-i18next';
|
|
211
|
+
|
|
212
|
+
function MyComponent() {
|
|
213
|
+
const { isResourcesReady } = useModernI18n();
|
|
214
|
+
const { t } = useTranslation();
|
|
215
|
+
|
|
216
|
+
if (!isResourcesReady) {
|
|
217
|
+
return <div>Loading translations...</div>;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return <div>{t('content', { defaultValue: 'Default content' })}</div>;
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Static resources (HTTP/FS backend) load quickly, so loading state handling is usually unnecessary. If you need to check, use `i18n.isInitialized`:
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
const { t, i18n } = useTranslation();
|
|
228
|
+
if (!i18n.isInitialized) return null;
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Missing Translation Keys
|
|
232
|
+
|
|
233
|
+
Provide fallback text with `defaultValue` to prevent key strings from appearing in the UI:
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
t('missing.key', { defaultValue: 'Default text' })
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
In development, you can enable `saveMissing` to output missing keys to the console for debugging:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
initOptions: {
|
|
243
|
+
fallbackLng: 'en',
|
|
244
|
+
saveMissing: true, // Recommended only in development.
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Network Failures and Resource 404s
|
|
249
|
+
|
|
250
|
+
When translation file loading fails, i18next falls back to the resources for `fallbackLng`. If those also fail, `t()` returns the key string directly. Recommendations:
|
|
251
|
+
|
|
252
|
+
- In production, make sure the translation files for `fallbackLng`, usually `en`, are complete and stable.
|
|
253
|
+
- When using chained backend, use local files as the fallback to reduce dependency on remote services.
|
|
254
|
+
|
|
255
|
+
## Type Safety
|
|
256
|
+
|
|
257
|
+
Extend react-i18next type definitions so TypeScript can check whether translation keys exist and provide autocomplete:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
// types/i18n.d.ts
|
|
261
|
+
import 'react-i18next';
|
|
262
|
+
import type translation from '../locales/en/translation.json';
|
|
263
|
+
import type common from '../locales/en/common.json';
|
|
264
|
+
|
|
265
|
+
declare module 'react-i18next' {
|
|
266
|
+
interface CustomTypeOptions {
|
|
267
|
+
defaultNS: 'translation';
|
|
268
|
+
resources: {
|
|
269
|
+
translation: typeof translation;
|
|
270
|
+
common: typeof common;
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Referencing JSON file types directly is less likely to drift from actual translation files than manually written interfaces:
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
const { t } = useTranslation();
|
|
280
|
+
t('welcome'); // TypeScript autocomplete
|
|
281
|
+
t('nonExistent'); // TypeScript error
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
:::tip Large projects
|
|
285
|
+
When there are many translation files, manually maintaining type files is costly. You can use [i18next-parser](https://github.com/i18next/i18next-parser) or [i18next-resources-for-ts](https://github.com/i18next/i18next-resources-for-ts) to generate TypeScript types from translation files automatically.
|
|
286
|
+
:::
|