@modern-js/main-doc 3.1.5 → 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 (46) 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 +4 -4
  43. package/docs/en/components/rspackPrecautions.mdx +0 -6
  44. package/docs/en/guides/advanced-features/international/basic.mdx +0 -417
  45. package/docs/zh/components/rspackPrecautions.mdx +0 -6
  46. package/docs/zh/guides/advanced-features/international/basic.mdx +0 -416
@@ -6,23 +6,26 @@ title: Best Practices
6
6
 
7
7
  ## Resource File Organization
8
8
 
9
- It's recommended to organize resource files as follows:
9
+ 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.
10
+
11
+ Recommended directory structure:
10
12
 
11
13
  ```
12
14
  locales/
13
15
  ├── en/
14
- │ ├── translation.json # Default namespace
15
- │ ├── common.json # Common translations
16
- │ └── errors.json # Error messages
16
+ │ ├── translation.json <- Default namespace for common text
17
+ │ ├── common.json <- Common UI text such as buttons and labels
18
+ │ └── errors.json <- Error messages
17
19
  └── zh/
18
20
  ├── translation.json
19
21
  ├── common.json
20
22
  └── errors.json
21
23
  ```
22
24
 
23
- **Configure Multiple Namespaces**:
25
+ Declare multiple namespaces:
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
- **Using Namespaces**:
39
+ Use a specific namespace in components:
37
40
 
38
41
  ```tsx
39
42
  import { useTranslation } from 'react-i18next';
40
43
 
41
- function MyComponent() {
44
+ // Use a single namespace.
45
+ function MyButton() {
42
46
  const { t } = useTranslation('common');
47
+ return <button>{t('submit')}</button>;
48
+ }
49
+
50
+ // Use multiple namespaces and access keys with 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 can omit repeated prefixes.
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
- ## Error Handling
73
+ ## Translation Key Naming
74
+
75
+ The quality of translation key names directly affects maintenance cost. Recommendations:
76
+
77
+ - **Use semantic words** and avoid abbreviations: `button.submit` is better than `btn.sbm`.
78
+ - **Use module-based prefixes**: `dashboard.table.header`, `auth.login.title`.
79
+ - **Do not use complete source-language text as keys**: Keys should be stable identifiers, not the translation content itself.
80
+ - **Use dots for hierarchy**: Match the nested JSON structure.
81
+
82
+ ```json
83
+ // Recommended
84
+ {
85
+ "page": {
86
+ "title": "User Settings",
87
+ "description": "Manage your account information"
88
+ },
89
+ "form": {
90
+ "username": { "label": "Username", "placeholder": "Enter username" },
91
+ "submit": "Save changes"
92
+ }
93
+ }
94
+ ```
95
+
96
+ ```tsx
97
+ t('page.title')
98
+ t('form.username.label')
99
+ t('form.submit', { defaultValue: 'Save' }) // defaultValue prevents showing the key string when the key is missing.
100
+ ```
101
+
102
+ ## Plurals
103
+
104
+ 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.
105
+
106
+ ```json
107
+ // locales/en/translation.json (JSON v4 format)
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" (Chinese usually has only one form)
126
+ t('item', { count: 5 }) // "5 个条目" / "5 items"
127
+ ```
128
+
129
+ 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.
130
+
131
+ :::tip
132
+ Use the `_plural` format only if your project uses an old i18next version or explicitly configures `compatibilityJSON: 'v3'`. New projects should use v4 format.
133
+ :::
134
+
135
+ ## Nested Keys
136
+
137
+ Nested structures can clearly reflect UI hierarchy and are accessed with dots:
138
+
139
+ ```json
140
+ {
141
+ "modal": {
142
+ "confirm": {
143
+ "title": "Confirm deletion",
144
+ "message": "This action cannot be undone. Continue?",
145
+ "actions": {
146
+ "ok": "Confirm",
147
+ "cancel": "Cancel"
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ```tsx
155
+ t('modal.confirm.title')
156
+ t('modal.confirm.actions.ok')
157
+ ```
158
+
159
+ Avoid nesting deeper than 4 levels. Excessive depth makes key names long and hard to maintain.
160
+
161
+ ## Formatting Interpolation
49
162
 
50
- It's recommended to provide fallback options when resource loading fails:
163
+ Use the `interpolation.format` function to handle number, date, currency, and other formatting consistently, instead of calling `Intl` APIs separately in each component:
51
164
 
52
- ### Check Resource Loading State
165
+ ```ts
166
+ // src/modern.runtime.ts
167
+ export default defineRuntimeConfig({
168
+ i18n: {
169
+ initOptions: {
170
+ interpolation: {
171
+ escapeValue: false, // React already escapes text. Disable this to avoid double escaping.
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
+ Use `, format` in translation files to specify the formatting type:
193
+
194
+ ```json
195
+ {
196
+ "price": "Current price: {{value, currency}}",
197
+ "expiry": "Expiry date: {{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
+ ```
53
205
 
54
- When using SDK backend or need to ensure resources are loaded, use `isResourcesReady` from `useModernI18n`:
206
+ ## Error Handling
207
+
208
+ ### Loading State Handling
209
+
210
+ When using a custom backend, translation resources are loaded asynchronously. Use `isResourcesReady` to handle the loading state:
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
- // Check if resources are loaded and ready
65
220
  if (!isResourcesReady) {
66
- return <div>Loading translation resources...</div>;
221
+ return <div>Loading translations...</div>;
67
222
  }
68
223
 
69
224
  return <div>{t('content', { defaultValue: 'Default content' })}</div>;
70
225
  }
71
226
  ```
72
227
 
73
- ### Alternative: Check i18next Initialization
74
-
75
- For simpler cases, you can also check i18next initialization status:
228
+ Static resources (HTTP/FS backend) load quickly, so loading state handling is usually unnecessary. If you need to check, use `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
+ ### Missing Translation Keys
82
236
 
83
- // Check if i18n is initialized
84
- if (!i18n.isInitialized) {
85
- return <div>Loading...</div>;
86
- }
237
+ Provide fallback text with `defaultValue` to prevent key strings from appearing in the UI:
87
238
 
88
- return <div>{t('content', { defaultValue: 'Default content' })}</div>;
239
+ ```tsx
240
+ t('missing.key', { defaultValue: 'Default text' })
241
+ ```
242
+
243
+ In development, you can enable `saveMissing` to output missing keys to the console for debugging:
244
+
245
+ ```ts
246
+ initOptions: {
247
+ fallbackLng: 'en',
248
+ saveMissing: true, // Recommended only in development.
89
249
  }
90
250
  ```
91
251
 
92
- :::tip
93
- `isResourcesReady` is more accurate for SDK backend scenarios as it checks if all required resources are actually loaded, not just if the instance is initialized.
252
+ ### Network Failures and Resource 404s
94
253
 
95
- :::
254
+ When translation file loading fails, i18next falls back to the resources for `fallbackLng`. If those also fail, `t()` returns the key string directly. Recommendations:
255
+
256
+ - In production, make sure the translation files for `fallbackLng`, usually `en`, are complete and stable.
257
+ - When using chained backend, use local files as the fallback to reduce dependency on remote services.
96
258
 
97
259
  ## Type Safety
98
260
 
99
- Add type definitions for translation keys to improve development experience:
261
+ Extend react-i18next type definitions so TypeScript can check whether translation keys exist and provide autocomplete:
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
- Using type-safe translations:
280
+ Referencing JSON file types directly is less likely to drift from actual translation files than manually written interfaces:
124
281
 
125
282
  ```tsx
126
- import { useTranslation } from 'react-i18next';
127
-
128
- function MyComponent() {
129
- const { t } = useTranslation();
130
-
131
- // TypeScript will check if the key exists
132
- return <div>{t('hello')}</div>; // ✅ Type safe
133
- // return <div>{t('invalid')}</div>; // ❌ TypeScript error
134
- }
283
+ const { t } = useTranslation();
284
+ t('welcome'); // TypeScript autocomplete
285
+ t('nonExistent'); // TypeScript error
135
286
  ```
287
+
288
+ :::tip Large projects
289
+ 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.
290
+ :::