@modern-js/main-doc 3.1.4 → 3.2.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.
Files changed (47) hide show
  1. package/docs/en/components/international/init-options-desc.mdx +1 -1
  2. package/docs/en/components/international/install-command.mdx +4 -17
  3. package/docs/en/components/international/instance-code.mdx +4 -14
  4. package/docs/en/components/international/introduce.mdx +4 -1
  5. package/docs/en/configure/app/source/enable-async-pre-entry.mdx +30 -0
  6. package/docs/en/configure/app/tools/dev-server.mdx +0 -4
  7. package/docs/en/guides/advanced-features/international/_meta.json +0 -1
  8. package/docs/en/guides/advanced-features/international/advanced.mdx +48 -109
  9. package/docs/en/guides/advanced-features/international/api.mdx +125 -290
  10. package/docs/en/guides/advanced-features/international/best-practices.mdx +203 -48
  11. package/docs/en/guides/advanced-features/international/configuration.mdx +108 -315
  12. package/docs/en/guides/advanced-features/international/locale-detection.mdx +62 -208
  13. package/docs/en/guides/advanced-features/international/quick-start.mdx +41 -55
  14. package/docs/en/guides/advanced-features/international/resource-loading.mdx +63 -322
  15. package/docs/en/guides/advanced-features/international/routing.mdx +60 -138
  16. package/docs/en/guides/advanced-features/international.mdx +19 -27
  17. package/docs/en/guides/basic-features/alias.mdx +1 -1
  18. package/docs/en/guides/basic-features/html.mdx +2 -2
  19. package/docs/en/guides/basic-features/static-assets.mdx +1 -2
  20. package/docs/en/guides/concept/entries.mdx +2 -2
  21. package/docs/zh/components/international/init-options-desc.mdx +1 -1
  22. package/docs/zh/components/international/install-command.mdx +4 -16
  23. package/docs/zh/components/international/instance-code.mdx +4 -14
  24. package/docs/zh/components/international/introduce.mdx +5 -2
  25. package/docs/zh/configure/app/source/enable-async-pre-entry.mdx +77 -0
  26. package/docs/zh/configure/app/tools/dev-server.mdx +0 -4
  27. package/docs/zh/guides/advanced-features/bff/function.mdx +2 -2
  28. package/docs/zh/guides/advanced-features/international/_meta.json +0 -1
  29. package/docs/zh/guides/advanced-features/international/advanced.mdx +48 -109
  30. package/docs/zh/guides/advanced-features/international/api.mdx +126 -292
  31. package/docs/zh/guides/advanced-features/international/best-practices.mdx +204 -49
  32. package/docs/zh/guides/advanced-features/international/configuration.mdx +105 -318
  33. package/docs/zh/guides/advanced-features/international/locale-detection.mdx +62 -236
  34. package/docs/zh/guides/advanced-features/international/quick-start.mdx +40 -54
  35. package/docs/zh/guides/advanced-features/international/resource-loading.mdx +62 -324
  36. package/docs/zh/guides/advanced-features/international/routing.mdx +58 -136
  37. package/docs/zh/guides/advanced-features/international.mdx +19 -26
  38. package/docs/zh/guides/basic-features/alias.mdx +1 -1
  39. package/docs/zh/guides/basic-features/html.mdx +2 -2
  40. package/docs/zh/guides/basic-features/static-assets.mdx +1 -2
  41. package/docs/zh/guides/concept/entries.mdx +2 -2
  42. package/package.json +5 -4
  43. package/rspress.config.ts +45 -26
  44. package/docs/en/components/rspackPrecautions.mdx +0 -6
  45. package/docs/en/guides/advanced-features/international/basic.mdx +0 -417
  46. package/docs/zh/components/rspackPrecautions.mdx +0 -6
  47. package/docs/zh/guides/advanced-features/international/basic.mdx +0 -416
@@ -6,23 +6,26 @@ title: 最佳实践
6
6
 
7
7
  ## 资源文件组织
8
8
 
9
- 建议按以下方式组织资源文件:
9
+ 命名空间(Namespace)用于按业务模块拆分翻译文件,默认命名空间为 `translation`。按模块拆分可以减少初始加载体积,只在用到时按需加载对应命名空间。
10
+
11
+ 推荐目录结构:
10
12
 
11
13
  ```
12
14
  locales/
13
15
  ├── en/
14
- │ ├── translation.json # 默认命名空间
15
- │ ├── common.json # 通用翻译
16
- │ └── errors.json # 错误信息
16
+ │ ├── translation.json 默认命名空间,放通用文案
17
+ │ ├── common.json 按钮、标签等公共 UI 文案
18
+ │ └── errors.json 错误信息
17
19
  └── zh/
18
20
  ├── translation.json
19
21
  ├── common.json
20
22
  └── errors.json
21
23
  ```
22
24
 
23
- **配置多个命名空间**:
25
+ 声明多个命名空间:
24
26
 
25
27
  ```ts
28
+ // src/modern.runtime.ts
26
29
  export default defineRuntimeConfig({
27
30
  i18n: {
28
31
  initOptions: {
@@ -33,25 +36,178 @@ export default defineRuntimeConfig({
33
36
  });
34
37
  ```
35
38
 
36
- **使用命名空间**:
39
+ 在组件中使用指定命名空间:
37
40
 
38
41
  ```tsx
39
42
  import { useTranslation } from 'react-i18next';
40
43
 
41
- function MyComponent() {
44
+ // 指定单个命名空间
45
+ function MyButton() {
42
46
  const { t } = useTranslation('common');
47
+ return <button>{t('submit')}</button>;
48
+ }
49
+
50
+ // 同时使用多个命名空间,用 namespace:key 格式访问
51
+ function Dashboard() {
52
+ const { t } = useTranslation(['dashboard', 'common']);
53
+ return (
54
+ <header>
55
+ <h1>{t('dashboard:title')}</h1>
56
+ <button>{t('common:button.refresh')}</button>
57
+ </header>
58
+ );
59
+ }
43
60
 
44
- return <div>{t('welcome')}</div>;
61
+ // keyPrefix 可以省略重复的前缀
62
+ function ButtonGroup() {
63
+ const { t } = useTranslation('common', { keyPrefix: 'button' });
64
+ return (
65
+ <>
66
+ <button>{t('submit')}</button> {/* → common:button.submit */}
67
+ <button>{t('cancel')}</button> {/* → common:button.cancel */}
68
+ </>
69
+ );
45
70
  }
46
71
  ```
47
72
 
48
- ## 错误处理
73
+ ## 翻译键命名规范
74
+
75
+ 翻译键的命名质量直接影响后续维护成本,建议:
76
+
77
+ - **用语义化词语**,避免缩写:`button.submit` 优于 `btn.sbm`
78
+ - **按模块划分前缀**:`dashboard.table.header`、`auth.login.title`
79
+ - **不要用完整中文文案当键名**:键名应是稳定的标识符,而不是翻译内容本身
80
+ - **用点号表示层级**:与 JSON 嵌套结构一一对应
81
+
82
+ ```json
83
+ // ✅ 推荐
84
+ {
85
+ "page": {
86
+ "title": "用户设置",
87
+ "description": "管理你的账号信息"
88
+ },
89
+ "form": {
90
+ "username": { "label": "用户名", "placeholder": "请输入用户名" },
91
+ "submit": "保存更改"
92
+ }
93
+ }
94
+ ```
95
+
96
+ ```tsx
97
+ t('page.title')
98
+ t('form.username.label')
99
+ t('form.submit', { defaultValue: '保存' }) // defaultValue 防止键缺失时显示 key 字符串
100
+ ```
101
+
102
+ ## 复数
103
+
104
+ i18next 根据 `count` 参数自动选择对应的复数形式。**注意:i18next v21 之后默认使用 JSON v4 格式**,复数后缀改为 CLDR 标准(`_zero`、`_one`、`_other` 等),旧的 `_plural` 写法已废弃。
105
+
106
+ ```json
107
+ // locales/en/translation.json(JSON v4 格式)
108
+ {
109
+ "item_zero": "No items",
110
+ "item_one": "{{count}} item",
111
+ "item_other": "{{count}} items"
112
+ }
113
+ ```
114
+
115
+ ```json
116
+ // locales/zh/translation.json
117
+ {
118
+ "item_zero": "没有条目",
119
+ "item_other": "{{count}} 个条目"
120
+ }
121
+ ```
122
+
123
+ ```tsx
124
+ t('item', { count: 0 }) // → "没有条目" / "No items"
125
+ t('item', { count: 1 }) // → "没有条目" / "1 item"(中文通常只有一种形式)
126
+ t('item', { count: 5 }) // → "5 个条目" / "5 items"
127
+ ```
49
128
 
50
- 建议在资源加载失败时提供回退方案:
129
+ 不同语言的复数规则不同(英语有单复数,俄语有 one/few/many 等多种形式),i18next 会根据语言代码自动匹配 CLDR 规则。
51
130
 
52
- ### 检查资源加载状态
131
+ :::tip
132
+ 如果项目使用了旧版 i18next 或显式配置了 `compatibilityJSON: 'v3'`,才需要使用 `_plural` 写法。新项目统一使用 v4 格式。
133
+ :::
53
134
 
54
- 当使用 SDK 后端或需要确保资源已加载时,使用 `useModernI18n` 中的 `isResourcesReady`:
135
+ ## 嵌套键
136
+
137
+ 嵌套结构可以直观反映 UI 层级,用点号访问:
138
+
139
+ ```json
140
+ {
141
+ "modal": {
142
+ "confirm": {
143
+ "title": "确认删除",
144
+ "message": "此操作不可撤销,是否继续?",
145
+ "actions": {
146
+ "ok": "确认",
147
+ "cancel": "取消"
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ```tsx
155
+ t('modal.confirm.title')
156
+ t('modal.confirm.actions.ok')
157
+ ```
158
+
159
+ 嵌套层级不建议超过 4 层,过深的结构会让键名变得冗长难维护。
160
+
161
+ ## 格式化插值
162
+
163
+ 通过 `interpolation.format` 函数统一处理数字、日期、货币等格式化,避免在每个组件里各自调用 `Intl` API:
164
+
165
+ ```ts
166
+ // src/modern.runtime.ts
167
+ export default defineRuntimeConfig({
168
+ i18n: {
169
+ initOptions: {
170
+ interpolation: {
171
+ escapeValue: false, // React 已默认转义文本,关闭避免重复转义
172
+ format(value, format, lng) {
173
+ if (format === 'currency') {
174
+ return new Intl.NumberFormat(lng, {
175
+ style: 'currency',
176
+ currency: lng === 'zh' ? 'CNY' : 'USD',
177
+ }).format(Number(value));
178
+ }
179
+ if (format === 'date') {
180
+ return new Intl.DateTimeFormat(lng, { dateStyle: 'medium' }).format(
181
+ value instanceof Date ? value : new Date(value),
182
+ );
183
+ }
184
+ return value;
185
+ },
186
+ },
187
+ },
188
+ },
189
+ });
190
+ ```
191
+
192
+ 翻译文件中用 `, format` 指定格式化类型:
193
+
194
+ ```json
195
+ {
196
+ "price": "当前价格:{{value, currency}}",
197
+ "expiry": "到期日:{{date, date}}"
198
+ }
199
+ ```
200
+
201
+ ```tsx
202
+ t('price', { value: 99.5 }) // → "当前价格:¥99.50"(zh)/ "$99.50"(en)
203
+ t('expiry', { date: new Date() }) // → "到期日:2025年5月12日"(zh)
204
+ ```
205
+
206
+ ## 错误处理
207
+
208
+ ### 加载状态处理
209
+
210
+ 使用自定义后端时,翻译资源需要异步加载,推荐用 `isResourcesReady` 处理加载中状态:
55
211
 
56
212
  ```tsx
57
213
  import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
@@ -61,75 +217,74 @@ function MyComponent() {
61
217
  const { isResourcesReady } = useModernI18n();
62
218
  const { t } = useTranslation();
63
219
 
64
- // 检查资源是否已加载并准备好
65
220
  if (!isResourcesReady) {
66
- return <div>正在加载翻译资源...</div>;
221
+ return <div>加载翻译中...</div>;
67
222
  }
68
223
 
69
- return <div>{t('content', { defaultValue: 'Default content' })}</div>;
224
+ return <div>{t('content', { defaultValue: '默认内容' })}</div>;
70
225
  }
71
226
  ```
72
227
 
73
- ### 替代方案:检查 i18next 初始化状态
74
-
75
- 对于简单场景,也可以检查 i18next 的初始化状态:
228
+ 静态资源(HTTP/FS 后端)加载较快,通常不需要处理加载状态;如需检查,可以用 `i18n.isInitialized`:
76
229
 
77
230
  ```tsx
78
- import { useTranslation } from 'react-i18next';
231
+ const { t, i18n } = useTranslation();
232
+ if (!i18n.isInitialized) return null;
233
+ ```
79
234
 
80
- function MyComponent() {
81
- const { t, i18n } = useTranslation();
235
+ ### 翻译键缺失处理
82
236
 
83
- // 检查 i18n 是否已初始化
84
- if (!i18n.isInitialized) {
85
- return <div>Loading...</div>;
86
- }
237
+ 通过 `defaultValue` 提供兜底文案,防止界面显示 key 字符串:
238
+
239
+ ```tsx
240
+ t('missing.key', { defaultValue: '默认文案' })
241
+ ```
242
+
243
+ 开发环境可以开启 `saveMissing`,将缺失的键输出到控制台方便排查:
87
244
 
88
- return <div>{t('content', { defaultValue: 'Default content' })}</div>;
245
+ ```ts
246
+ initOptions: {
247
+ fallbackLng: 'en',
248
+ saveMissing: true, // 仅建议开发环境开启
89
249
  }
90
250
  ```
91
251
 
92
- :::tip
93
- 对于 SDK 后端场景,`isResourcesReady` 更准确,因为它会检查所有必需的资源是否实际已加载,而不仅仅是检查实例是否已初始化。
252
+ ### 网络失败与资源 404
94
253
 
95
- :::
254
+ 翻译文件加载失败时,i18next 会回退到 `fallbackLng` 对应的资源;若仍失败,`t()` 直接返回 key 字符串。建议:
255
+
256
+ - 生产环境确保 `fallbackLng`(通常是 `en`)对应的翻译文件完整且稳定
257
+ - 使用链式后端时,本地文件作为兜底,减少对远程服务的依赖
96
258
 
97
259
  ## 类型安全
98
260
 
99
- 为翻译键添加类型定义,提高开发体验:
261
+ 通过扩展 react-i18next 的类型定义,让 TypeScript 检查翻译键是否存在并提供自动补全:
100
262
 
101
263
  ```ts
102
264
  // types/i18n.d.ts
103
265
  import 'react-i18next';
266
+ import type translation from '../locales/en/translation.json';
267
+ import type common from '../locales/en/common.json';
104
268
 
105
269
  declare module 'react-i18next' {
106
270
  interface CustomTypeOptions {
107
271
  defaultNS: 'translation';
108
272
  resources: {
109
- translation: {
110
- hello: string;
111
- world: string;
112
- welcome: string;
113
- };
114
- common: {
115
- submit: string;
116
- cancel: string;
117
- };
273
+ translation: typeof translation;
274
+ common: typeof common;
118
275
  };
119
276
  }
120
277
  }
121
278
  ```
122
279
 
123
- 使用类型安全的翻译:
280
+ 直接引用 JSON 文件的类型,比手写 interface 更不容易与实际翻译文件漂移:
124
281
 
125
282
  ```tsx
126
- import { useTranslation } from 'react-i18next';
127
-
128
- function MyComponent() {
129
- const { t } = useTranslation();
130
-
131
- // TypeScript 会检查键是否存在
132
- return <div>{t('hello')}</div>; // ✅ 类型安全
133
- // return <div>{t('invalid')}</div>; // ❌ TypeScript 错误
134
- }
283
+ const { t } = useTranslation();
284
+ t('welcome'); // ✅ TypeScript 自动补全
285
+ t('nonExistent'); // ❌ TypeScript 报错
135
286
  ```
287
+
288
+ :::tip 大型项目
289
+ 翻译文件较多时,手动维护类型文件成本较高。可以使用 [i18next-parser](https://github.com/i18next/i18next-parser) 或 [i18next-resources-for-ts](https://github.com/i18next/i18next-resources-for-ts) 从翻译文件自动生成 TypeScript 类型。
290
+ :::