@intlayer/docs 8.6.10 → 8.7.0-canary.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.
Files changed (129) hide show
  1. package/blog/ar/i18n_using_next-i18next.md +1 -1
  2. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  3. package/blog/de/i18n_using_next-i18next.md +1 -1
  4. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  5. package/blog/en/i18n_using_next-i18next.md +1 -1
  6. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  7. package/blog/en-GB/i18n_using_next-i18next.md +1 -1
  8. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  9. package/blog/es/i18n_using_next-i18next.md +1 -1
  10. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  11. package/blog/fr/i18n_using_next-i18next.md +1 -1
  12. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  13. package/blog/hi/i18n_using_next-i18next.md +1 -1
  14. package/blog/id/i18n_using_next-i18next.md +1 -1
  15. package/blog/id/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  16. package/blog/it/i18n_using_next-i18next.md +1 -1
  17. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  18. package/blog/ja/i18n_using_next-i18next.md +1 -1
  19. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  20. package/blog/ko/i18n_using_next-i18next.md +1 -1
  21. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  22. package/blog/pl/i18n_using_next-i18next.md +1 -1
  23. package/blog/pl/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  24. package/blog/pt/i18n_using_next-i18next.md +1 -1
  25. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  26. package/blog/ru/i18n_using_next-i18next.md +1 -1
  27. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  28. package/blog/tr/i18n_using_next-i18next.md +1 -1
  29. package/blog/uk/i18n_using_next-i18next.md +1 -1
  30. package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  31. package/blog/vi/i18n_using_next-i18next.md +1 -1
  32. package/blog/vi/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  33. package/blog/zh/i18n_using_next-i18next.md +1 -1
  34. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  35. package/docs/ar/bundle_optimization.md +454 -0
  36. package/docs/ar/intlayer_with_next-i18next.md +1 -1
  37. package/docs/ar/intlayer_with_next-intl.md +1 -1
  38. package/docs/ar/intlayer_with_tanstack+solid.md +24 -5
  39. package/docs/ar/intlayer_with_tanstack.md +45 -68
  40. package/docs/bn/bundle_optimization.md +454 -0
  41. package/docs/cs/bundle_optimization.md +454 -0
  42. package/docs/de/bundle_optimization.md +454 -0
  43. package/docs/de/intlayer_with_next-i18next.md +1 -1
  44. package/docs/de/intlayer_with_next-intl.md +1 -1
  45. package/docs/de/intlayer_with_tanstack+solid.md +24 -5
  46. package/docs/de/intlayer_with_tanstack.md +45 -68
  47. package/docs/en/bundle_optimization.md +36 -8
  48. package/docs/en/intlayer_with_next-i18next.md +1 -1
  49. package/docs/en/intlayer_with_next-intl.md +1 -1
  50. package/docs/en/intlayer_with_tanstack+solid.md +24 -5
  51. package/docs/en/intlayer_with_tanstack.md +45 -68
  52. package/docs/en-GB/bundle_optimization.md +454 -0
  53. package/docs/en-GB/intlayer_with_next-i18next.md +1 -1
  54. package/docs/en-GB/intlayer_with_next-intl.md +1 -1
  55. package/docs/en-GB/intlayer_with_tanstack+solid.md +24 -5
  56. package/docs/en-GB/intlayer_with_tanstack.md +47 -70
  57. package/docs/es/bundle_optimization.md +454 -0
  58. package/docs/es/intlayer_with_next-i18next.md +1 -1
  59. package/docs/es/intlayer_with_next-intl.md +1 -1
  60. package/docs/es/intlayer_with_tanstack+solid.md +24 -5
  61. package/docs/es/intlayer_with_tanstack.md +45 -68
  62. package/docs/fr/bundle_optimization.md +454 -0
  63. package/docs/fr/intlayer_with_next-i18next.md +1 -1
  64. package/docs/fr/intlayer_with_next-intl.md +1 -1
  65. package/docs/fr/intlayer_with_tanstack+solid.md +24 -5
  66. package/docs/fr/intlayer_with_tanstack.md +45 -68
  67. package/docs/hi/bundle_optimization.md +454 -0
  68. package/docs/hi/intlayer_with_next-i18next.md +1 -1
  69. package/docs/hi/intlayer_with_next-intl.md +1 -1
  70. package/docs/hi/intlayer_with_tanstack+solid.md +24 -5
  71. package/docs/hi/intlayer_with_tanstack.md +45 -68
  72. package/docs/id/bundle_optimization.md +454 -0
  73. package/docs/id/intlayer_with_next-i18next.md +1 -1
  74. package/docs/id/intlayer_with_next-intl.md +1 -1
  75. package/docs/id/intlayer_with_tanstack+solid.md +24 -5
  76. package/docs/id/intlayer_with_tanstack.md +45 -68
  77. package/docs/it/bundle_optimization.md +454 -0
  78. package/docs/it/intlayer_with_next-i18next.md +1 -1
  79. package/docs/it/intlayer_with_next-intl.md +1 -1
  80. package/docs/it/intlayer_with_tanstack+solid.md +24 -5
  81. package/docs/it/intlayer_with_tanstack.md +45 -68
  82. package/docs/ja/bundle_optimization.md +454 -0
  83. package/docs/ja/intlayer_with_next-i18next.md +1 -1
  84. package/docs/ja/intlayer_with_next-intl.md +1 -1
  85. package/docs/ja/intlayer_with_tanstack+solid.md +24 -5
  86. package/docs/ja/intlayer_with_tanstack.md +45 -36
  87. package/docs/ko/bundle_optimization.md +454 -0
  88. package/docs/ko/intlayer_with_next-i18next.md +1 -1
  89. package/docs/ko/intlayer_with_next-intl.md +1 -1
  90. package/docs/ko/intlayer_with_tanstack+solid.md +24 -5
  91. package/docs/ko/intlayer_with_tanstack.md +45 -68
  92. package/docs/nl/bundle_optimization.md +454 -0
  93. package/docs/pl/bundle_optimization.md +454 -0
  94. package/docs/pl/intlayer_with_next-i18next.md +1 -1
  95. package/docs/pl/intlayer_with_next-intl.md +1 -1
  96. package/docs/pl/intlayer_with_tanstack+solid.md +24 -5
  97. package/docs/pl/intlayer_with_tanstack.md +45 -68
  98. package/docs/pt/bundle_optimization.md +454 -0
  99. package/docs/pt/intlayer_with_next-i18next.md +1 -1
  100. package/docs/pt/intlayer_with_next-intl.md +1 -1
  101. package/docs/pt/intlayer_with_tanstack+solid.md +24 -5
  102. package/docs/pt/intlayer_with_tanstack.md +45 -68
  103. package/docs/ru/bundle_optimization.md +454 -0
  104. package/docs/ru/intlayer_with_next-i18next.md +1 -1
  105. package/docs/ru/intlayer_with_next-intl.md +1 -1
  106. package/docs/ru/intlayer_with_tanstack+solid.md +24 -5
  107. package/docs/ru/intlayer_with_tanstack.md +45 -68
  108. package/docs/tr/bundle_optimization.md +454 -0
  109. package/docs/tr/intlayer_with_next-i18next.md +1 -1
  110. package/docs/tr/intlayer_with_next-intl.md +1 -1
  111. package/docs/tr/intlayer_with_tanstack+solid.md +24 -5
  112. package/docs/tr/intlayer_with_tanstack.md +45 -68
  113. package/docs/uk/bundle_optimization.md +454 -0
  114. package/docs/uk/intlayer_with_next-i18next.md +1 -1
  115. package/docs/uk/intlayer_with_next-intl.md +1 -1
  116. package/docs/uk/intlayer_with_tanstack+solid.md +24 -5
  117. package/docs/uk/intlayer_with_tanstack.md +45 -68
  118. package/docs/ur/bundle_optimization.md +454 -0
  119. package/docs/vi/bundle_optimization.md +454 -0
  120. package/docs/vi/intlayer_with_next-i18next.md +1 -1
  121. package/docs/vi/intlayer_with_next-intl.md +1 -1
  122. package/docs/vi/intlayer_with_tanstack+solid.md +24 -5
  123. package/docs/vi/intlayer_with_tanstack.md +45 -68
  124. package/docs/zh/bundle_optimization.md +454 -0
  125. package/docs/zh/intlayer_with_next-i18next.md +1 -1
  126. package/docs/zh/intlayer_with_next-intl.md +1 -1
  127. package/docs/zh/intlayer_with_tanstack+solid.md +24 -5
  128. package/docs/zh/intlayer_with_tanstack.md +45 -68
  129. package/package.json +7 -7
@@ -0,0 +1,454 @@
1
+ ---
2
+ createdAt: 2025-11-25
3
+ updatedAt: 2026-04-08
4
+ title: 优化 i18n 构建包大小与性能
5
+ description: 通过优化国际化 (i18n) 内容来减小应用程序构建包的大小。学习如何利用 Intlayer 对字典进行 Tree Shaking 和延迟加载。
6
+ keywords:
7
+ - 构建包优化
8
+ - 内容自动化
9
+ - 动态内容
10
+ - Intlayer
11
+ - Next.js
12
+ - JavaScript
13
+ - React
14
+ slugs:
15
+ - doc
16
+ - concept
17
+ - bundle-optimization
18
+ history:
19
+ - version: 8.7.0
20
+ date: 2026-04-08
21
+ changes: "在构建配置中增加了 `minify` 和 `purge` 选项"
22
+ ---
23
+
24
+ # 优化 i18n 构建包大小与性能
25
+
26
+ 依赖 JSON 文件的传统 i18n 解决方案面临的最常见挑战之一是管理内容大小。如果开发人员不手动将内容分离到命名空间中,用户往往为了查看单个页面而下载每个页面甚至每种语言的翻译。
27
+
28
+ 例如,一个包含 10 个页面并翻译成 10 种语言的应用程序,可能会导致用户下载 100 个页面的内容,即使他们只需要**一页**(当前语言的当前页面)。这会导致带宽浪费和加载速度变慢。
29
+
30
+ **Intlayer 通过构建时优化解决了这个问题。** 它分析您的代码以检测每个组件实际使用的字典,并仅将必要的内容重新注入到您的构建包中。
31
+
32
+ ## 目录
33
+
34
+ <TOC />
35
+
36
+ ## 扫描您的构建包
37
+
38
+ 分析构建包是识别“重型”JSON 文件和代码分割机会的第一步。这些工具可以生成应用程序编译后代码的可视化树图 (treemap),让您确切地看到哪些库占用了最多的空间。
39
+
40
+ <Tabs>
41
+ <Tab value="vite">
42
+
43
+ ### Vite / Rollup
44
+
45
+ Vite 底层使用 Rollup。`rollup-plugin-visualizer` 插件生成一个交互式 HTML 文件,显示图中每个模块的大小。
46
+
47
+ ```bash
48
+ npm install -D rollup-plugin-visualizer
49
+ ```
50
+
51
+ ```typescript fileName="vite.config.ts"
52
+ import { defineConfig } from "vite";
53
+ import { visualizer } from "rollup-plugin-visualizer";
54
+
55
+ export default defineConfig({
56
+ plugins: [
57
+ visualizer({
58
+ open: true, // 在浏览器中自动打开报告
59
+ filename: "stats.html",
60
+ gzipSize: true,
61
+ brotliSize: true,
62
+ }),
63
+ ],
64
+ });
65
+ ```
66
+
67
+ </Tab>
68
+ <Tab value="nextjs (turbopack)">
69
+
70
+ ### Next.js (Turbopack)
71
+
72
+ 对于使用 App Router 和 Turbopack 的项目,Next.js 提供了一个内置的实验性分析器,不需要额外的依赖。
73
+
74
+ ```bash packageManager='npm'
75
+ npx next experimental-analyze
76
+ ```
77
+
78
+ ```bash packageManager='yarn'
79
+ yarn next experimental-analyze
80
+ ```
81
+
82
+ ```bash packageManager='pnpm'
83
+ pnpm next experimental-analyze
84
+ ```
85
+
86
+ ```bash packageManager='bun'
87
+ bun next experimental-analyze
88
+ ```
89
+
90
+ </Tab>
91
+ <Tab value="nextjs (Webpack)">
92
+
93
+ ### Next.js (Webpack)
94
+
95
+ 如果您在 Next.js 中使用默认的 Webpack 打包器,请使用官方的构建包分析器。通过在构建期间设置环境变量来触发它。
96
+
97
+ ```bash packageManager='npm'
98
+ npm install -D @next/bundle-analyzer
99
+ ```
100
+
101
+ ```bash packageManager='yarn'
102
+ yarn add -D @next/bundle-analyzer
103
+ ```
104
+
105
+ ```bash packageManager='pnpm'
106
+ pnpm add -D @next/bundle-analyzer
107
+ ```
108
+
109
+ ```bash packageManager='bun'
110
+ bun add -d @next/bundle-analyzer
111
+ ```
112
+
113
+ ```javascript fileName="next.config.js"
114
+ const withBundleAnalyzer = require("@next/bundle-analyzer")({
115
+ enabled: process.env.ANALYZE === "true",
116
+ });
117
+
118
+ module.exports = withBundleAnalyzer({
119
+ // 您的 Next.js 配置
120
+ });
121
+ ```
122
+
123
+ **用法:**
124
+
125
+ ```bash
126
+ ANALYZE=true npm run build
127
+ ```
128
+
129
+ </Tab>
130
+ <Tab value="Webpack (CRA / Angular / etc)">
131
+
132
+ ### 标准 Webpack
133
+
134
+ 对于 Create React App (ejected)、Angular 或自定义 Webpack 设置,请使用行业标准的 `webpack-bundle-analyzer`。
135
+
136
+ ```bash packageManager='npm'
137
+ npm install -D webpack-bundle-analyzer
138
+ ```
139
+
140
+ ```bash packageManager='yarn'
141
+ yarn add -D webpack-bundle-analyzer
142
+ ```
143
+
144
+ ```bash packageManager='pnpm'
145
+ pnpm add -D webpack-bundle-analyzer
146
+ ```
147
+
148
+ ```bash packageManager='bun'
149
+ bun add -d webpack-bundle-analyzer
150
+ ```
151
+
152
+ ```typescript fileName="webpack.config.ts
153
+ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
154
+
155
+ export default {
156
+ plugins: [
157
+ new BundleAnalyzerPlugin({
158
+ analyzerMode: "static",
159
+ reportFilename: "bundle-analyzer.html",
160
+ openAnalyzer: false,
161
+ }),
162
+ ],
163
+ };
164
+ ```
165
+
166
+ </Tab>
167
+ </Tabs>
168
+
169
+ ## 工作原理
170
+
171
+ Intlayer 使用**按组件优化**的方法。与全局 JSON 文件不同,您的内容定义在组件旁边或内部。在构建过程中,Intlayer 会:
172
+
173
+ 1. **分析**您的代码以查找 `useIntlayer` 调用。
174
+ 2. **构建**相应的字典内容。
175
+ 3. 根据您的配置用优化后的代码**替换** `useIntlayer` 调用。
176
+
177
+ 这确保了:
178
+
179
+ - 如果一个组件没有被引入,它的内容就不会包含在构建包中(Dead Code Elimination)。
180
+ - 如果一个组件是延迟加载的,它的内容也会被延迟加载。
181
+
182
+ ## 按平台设置
183
+
184
+ <Tabs>
185
+ <Tab value="nextjs">
186
+
187
+ ### Next.js
188
+
189
+ Next.js 需要 `@intlayer/swc` 插件来处理转换,因为 Next.js 使用 SWC 进行构建。
190
+
191
+ > 该插件默认不安装,因为 SWC 插件对 Next.js 仍处于实验阶段。未来可能会发生变化。
192
+
193
+ ```bash packageManager="npm"
194
+ npm install -D @intlayer/swc
195
+ ```
196
+
197
+ ```bash packageManager="yarn"
198
+ yarn add -D @intlayer/swc
199
+ ```
200
+
201
+ ```bash packageManager="pnpm"
202
+ pnpm add -D @intlayer/swc
203
+ ```
204
+
205
+ ```bash packageManager="bun"
206
+ bun add -d @intlayer/swc
207
+ ```
208
+
209
+ 安装后,Intlayer 将自动检测并使用该插件。
210
+
211
+ </Tab>
212
+ <Tab value="vite">
213
+
214
+ ### Vite
215
+
216
+ Vite 使用 `@intlayer/babel` 插件,该插件作为 `vite-intlayer` 的依赖项包含在内。优化默认启用。无需额外操作。
217
+
218
+ </Tab>
219
+ <Tab value="webpack">
220
+
221
+ ### Webpack
222
+
223
+ 要在 Webpack 上启用 Intlayer 构建包优化,您需要安装并配置相应的 Babel (`@intlayer/babel`) 或 SWC (`@intlayer/swc`) 插件。
224
+
225
+ ```bash packageManager="npm"
226
+ npm install -D @intlayer/babel
227
+ ```
228
+
229
+ ```bash packageManager="yarn"
230
+ yarn add -D @intlayer/babel
231
+ ```
232
+
233
+ ```bash packageManager="pnpm"
234
+ pnpm add -D @intlayer/babel
235
+ ```
236
+
237
+ ```bash packageManager="bun"
238
+ bun add -d @intlayer/babel
239
+ ```
240
+
241
+ ```typescript fileName="babel.config.js"
242
+ const {
243
+ getOptimizePluginOptions,
244
+ intlayerOptimizeBabelPlugin,
245
+ } = require("@intlayer/babel");
246
+
247
+ module.exports = {
248
+ plugins: [[intlayerOptimizeBabelPlugin, getOptimizePluginOptions()]],
249
+ };
250
+ ```
251
+
252
+ </Tab>
253
+ </Tabs>
254
+
255
+ ## 配置
256
+
257
+ 您可以通过 `intlayer.config.ts` 中的 `build` 属性来控制 Intlayer 如何优化您的构建包。
258
+
259
+ ```typescript fileName="intlayer.config.ts"
260
+ import { Locales, type IntlayerConfig } from "intlayer";
261
+
262
+ const config: IntlayerConfig = {
263
+ internationalization: {
264
+ locales: [Locales.ENGLISH, Locales.FRENCH],
265
+ defaultLocale: Locales.ENGLISH,
266
+ },
267
+ dictionary: {
268
+ importMode: "dynamic",
269
+ },
270
+ build: {
271
+ /**
272
+ * 压缩字典以减小构建包大小。
273
+ */
274
+ minify: true;
275
+
276
+ /**
277
+ * 清除字典中未使用的键
278
+ */
279
+ purge: true;
280
+
281
+ /**
282
+ * 指示构建是否应检查 TypeScript 类型
283
+ */
284
+ checkTypes: false;
285
+ },
286
+ };
287
+
288
+ export default config;
289
+ ```
290
+
291
+ > 在绝大多数情况下,建议保持 `optimize` 的默认选项。
292
+
293
+ > 有关更多详情,请参见配置文档:[配置](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/configuration.md)
294
+
295
+ ### 构建选项
296
+
297
+ `build` 配置对象下提供以下选项:
298
+
299
+ | 属性 | 类型 | 默认值 | 描述 |
300
+ | :------------- | :-------- | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------- |
301
+ | **`optimize`** | `boolean` | `undefined` | 控制是否启用构建优化。如果为 `true`,Intlayer 使用优化后的注入替换字典调用。如果为 `false`,则禁用优化。建议在生产环境中设置为 `true`。 |
302
+ | **`minify`** | `boolean` | `false` | 是否压缩字典以减小构建包大小。 |
303
+ | **`purge`** | `boolean` | `false` | 是否清除字典中未使用的键。 |
304
+
305
+ ### 压缩 (Minification)
306
+
307
+ 压缩字典可以删除不必要的空格、注释,并减小 JSON 内容的大小。这对于大型字典尤其有用。
308
+
309
+ ```typescript fileName="intlayer.config.ts"
310
+ import type { IntlayerConfig } from "intlayer";
311
+
312
+ const config: IntlayerConfig = {
313
+ build: {
314
+ minify: true,
315
+ },
316
+ };
317
+
318
+ export default config;
319
+ ```
320
+
321
+ > 注意:如果禁用 `optimize` 或启用了可视化编辑器(因为编辑器需要完整内容以便编辑),则会忽略压缩。
322
+
323
+ ### 清除 (Purging)
324
+
325
+ 清除确保只有代码中实际使用的键才会包含在最终的字典构建包中。如果您的大型字典包含许多并非在应用程序每个部分都使用的键,这可以显著减小构建包的大小。
326
+
327
+ ```typescript fileName="intlayer.config.ts"
328
+ import type { IntlayerConfig } from "intlayer";
329
+
330
+ const config: IntlayerConfig = {
331
+ build: {
332
+ purge: true,
333
+ },
334
+ };
335
+
336
+ export default config;
337
+ ```
338
+
339
+ > 注意:如果禁用 `optimize`,则会忽略清除。
340
+
341
+ ### 导入模式 (Import Mode)
342
+
343
+ 对于包含多个页面和语言的大型应用程序,您的 JSON 文件可能占用构建包大小的很大一部分。Intlayer 允许您控制字典的加载方式。
344
+
345
+ 导入模式可以在 `intlayer.config.ts` 文件中全局定义默认值。
346
+
347
+ ```typescript fileName="intlayer.config.ts"
348
+ import type { IntlayerConfig } from "intlayer";
349
+
350
+ const config: IntlayerConfig = {
351
+ build: {
352
+ minify: true,
353
+ },
354
+ };
355
+
356
+ export default config;
357
+ ```
358
+
359
+ 也可以在您的 `.content.{{ts|tsx|js|jsx|mjs|cjs|json|jsonc|json5}}` 文件中为每个字典定义。
360
+
361
+ ```ts
362
+ import { type Dictionary, t } from "intlayer";
363
+
364
+ const appContent: Dictionary = {
365
+ key: "app",
366
+ importMode: "dynamic", // 覆盖默认导入模式
367
+ content: {
368
+ // ...
369
+ },
370
+ };
371
+
372
+ export default appContent;
373
+ ```
374
+
375
+ | 属性 | 类型 | 默认值 | 描述 |
376
+ | :--------------- | :--------------------------------- | :--------- | :--------------------------------------------------------------------------- |
377
+ | **`importMode`** | `'static'`, `'dynamic'`, `'fetch'` | `'static'` | **已弃用**:请改用 `dictionary.importMode`。确定字典的加载方式(详见下文)。 |
378
+
379
+ `importMode` 设置规定了字典内容如何注入到您的组件中。
380
+ 您可以在 `intlayer.config.ts` 文件的 `dictionary` 对象下全局定义它,或者在字典的 `.content.ts` 文件中覆盖它。
381
+
382
+ ### 1. 静态模式 (`default`)
383
+
384
+ 在静态模式下,Intlayer 将 `useIntlayer` 替换为 `useDictionary`,并将字典直接注入 JavaScript 构建包中。
385
+
386
+ - **优点:** 即时渲染(同步),水合过程中零额外网络请求。
387
+ - **缺点:** 构建包包含该特定组件**所有**可用语言的翻译。
388
+ - **最适合:** 单页应用程序 (SPA)。
389
+
390
+ **转换后的代码示例:**
391
+
392
+ ```tsx
393
+ // 您的代码
394
+ const content = useIntlayer("my-key");
395
+
396
+ // 优化后的代码(静态)
397
+ const content = useDictionary({
398
+ key: "my-key",
399
+ content: {
400
+ nodeType: "translation",
401
+ translation: {
402
+ en: "My title",
403
+ fr: "Mon titre",
404
+ },
405
+ },
406
+ });
407
+ ```
408
+
409
+ ### 2. 动态模式
410
+
411
+ 在动态模式下,Intlayer 将 `useIntlayer` 替换为 `useDictionaryAsync`。这使用 `import()`(类似于 Suspense 的机制)来延迟加载当前语言特定的 JSON。
412
+
413
+ - **优点:** **语言级别的 Tree Shaking。** 查看英文版本的用户将*仅*下载英文字典,永远不会加载法文字典。
414
+ - **缺点:** 在水合过程中每个组件都会触发一次网络请求(资源获取)。
415
+ - **最适合:** 大型文本块、文章或支持多种语言且构建包大小至关重要的应用程序。
416
+
417
+ **转换后的代码示例:**
418
+
419
+ ```tsx
420
+ // 您的代码
421
+ const content = useIntlayer("my-key");
422
+
423
+ // 优化后的代码(动态)
424
+ const content = useDictionaryAsync({
425
+ en: () =>
426
+ import(".intlayer/dynamic_dictionary/my-key/en.json").then(
427
+ (mod) => mod.default
428
+ ),
429
+ fr: () =>
430
+ import(".intlayer/dynamic_dictionary/my-key/fr.json").then(
431
+ (mod) => mod.default
432
+ ),
433
+ });
434
+ ```
435
+
436
+ > 使用 `importMode: 'dynamic'` 时,如果您在单个页面上有 100 个组件使用 `useIntlayer`,浏览器将尝试 100 次单独的获取。为了避免这种请求“瀑布”,请将内容分组到更少的 `.content` 文件中(例如,每个页面部分一个字典),而不是每个原子组件一个。
437
+
438
+ ### 3. 获取模式 (Fetch Mode)
439
+
440
+ 行为类似于动态模式,但首先尝试从 Intlayer Live Sync API 获取字典。如果 API 调用失败或内容未标记为实时更新,则会回退到动态导入。
441
+
442
+ > 有关更多详情,请参见 CMS 文档:[CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/intlayer_CMS.md)
443
+
444
+ > 在获取模式下,不能使用清除和压缩。
445
+
446
+ ## 总结:静态 vs 动态
447
+
448
+ | 特性 | 静态模式 | 动态模式 |
449
+ | :---------------- | :------------------------- | :----------------------- |
450
+ | **JS 构建包大小** | 较大(包含组件的所有语言) | 最小(仅代码,无内容) |
451
+ | **初始加载** | 即时(内容在构建包中) | 轻微延迟(获取 JSON) |
452
+ | **网络请求** | 0 次额外请求 | 每个字典 1 次请求 |
453
+ | **Tree Shaking** | 组件级 | 组件级 + 语言级 |
454
+ | **最佳用例** | UI 组件、小型应用 | 文本较多的页面、多种语言 |
@@ -257,7 +257,7 @@ export default function LocaleLayout({
257
257
  params: { locale: string };
258
258
  }) {
259
259
  const locale: Locale = (locales as readonly string[]).includes(params.locale)
260
- ? (params.locale as any)
260
+ ? params.locale
261
261
  : defaultLocale;
262
262
 
263
263
  const dir = isRtl(locale) ? "rtl" : "ltr";
@@ -103,7 +103,7 @@ async function loadMessages(locale: string) {
103
103
  }
104
104
 
105
105
  export default getRequestConfig(async ({ locale }) => {
106
- if (!locales.includes(locale as any)) notFound();
106
+ if (!locales.includes(locale)) notFound();
107
107
 
108
108
  return {
109
109
  messages: await loadMessages(locale),
@@ -194,9 +194,7 @@ const RootComponent: ParentComponent = (props) => {
194
194
  </head>
195
195
  <body>
196
196
  <IntlayerProvider locale={locale}>
197
- <Suspense>
198
- {props.children}
199
- </Suspense>
197
+ <Suspense>{props.children}</Suspense>
200
198
  </IntlayerProvider>
201
199
  <Scripts />
202
200
  </body>
@@ -506,12 +504,33 @@ export const Route = createFileRoute("/{-$locale}/")({
506
504
  component: RouteComponent,
507
505
  head: ({ params }) => {
508
506
  const { locale } = params;
509
- const metaContent = getIntlayer("page-metadata", locale);
507
+ const path = "/"; // The path for this route
508
+
509
+ const metaContent = getIntlayer("app", locale);
510
510
 
511
511
  return {
512
+ links: [
513
+ // Canonical link: Points to the current localized page
514
+ { rel: "canonical", href: getLocalizedUrl(path, locale) },
515
+
516
+ // Hreflang: Tell Google about all localized versions
517
+ ...localeMap(({ locale: mapLocale }) => ({
518
+ rel: "alternate",
519
+ hrefLang: mapLocale,
520
+ href: getLocalizedUrl(path, mapLocale),
521
+ })),
522
+
523
+ // x-default: For users in unmatched languages
524
+ // Define the default fallback locale (usually your primary language)
525
+ {
526
+ rel: "alternate",
527
+ hrefLang: "x-default",
528
+ href: getLocalizedUrl(path, defaultLocale),
529
+ },
530
+ ],
512
531
  meta: [
513
532
  { title: metaContent.title },
514
- { content: metaContent.description, name: "description" },
533
+ { name: "description", content: metaContent.meta.description },
515
534
  ],
516
535
  };
517
536
  },
@@ -225,9 +225,7 @@ function RootDocument({ children }: { children: ReactNode }) {
225
225
  <HeadContent />
226
226
  </head>
227
227
  <body>
228
- <IntlayerProvider locale={locale}>
229
- {children}
230
- </IntlayerProvider>
228
+ <IntlayerProvider locale={locale}>{children}</IntlayerProvider>
231
229
  <Scripts />
232
230
  </body>
233
231
  </html>
@@ -330,30 +328,20 @@ import { getPrefix } from "intlayer";
330
328
 
331
329
  export const LOCALE_ROUTE = "{-$locale}" as const;
332
330
 
333
- // 主要工具类型
334
- export type RemoveLocaleParam<T> = T extends string
335
- ? RemoveLocaleFromString<T>
336
- : T;
331
+ export type To = StripLocalePrefix<LinkComponentProps["to"]>;
337
332
 
338
- export type To = RemoveLocaleParam<LinkComponentProps["to"]>;
339
-
340
- type CollapseDoubleSlashes<S extends string> =
341
- S extends `${infer H}//${infer T}` ? CollapseDoubleSlashes<`${H}/${T}`> : S;
333
+ export type StripLocalePrefix<T extends string | undefined> = T extends
334
+ | `/${typeof LOCALE_ROUTE}/`
335
+ | `/${typeof LOCALE_ROUTE}`
336
+ ? "/"
337
+ : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
338
+ ? `/${Rest}`
339
+ : T;
342
340
 
343
341
  type LocalizedLinkProps = {
344
342
  to?: To;
345
343
  } & Omit<LinkComponentProps, "to">;
346
344
 
347
- // 辅助类型
348
- type RemoveAll<
349
- S extends string,
350
- Sub extends string,
351
- > = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
352
-
353
- type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
354
- RemoveAll<S, typeof LOCALE_ROUTE>
355
- >;
356
-
357
345
  export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
358
346
  const { locale } = useLocale();
359
347
  const { localePrefix } = getPrefix(locale);
@@ -382,26 +370,26 @@ export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
382
370
  import { useNavigate } from "@tanstack/react-router";
383
371
  import { getPrefix } from "intlayer";
384
372
  import { useLocale } from "react-intlayer";
385
- import { LOCALE_ROUTE } from "@/components/localized-link";
373
+ import type { StripLocalePrefix } from "@/components/localized-link";
386
374
  import type { FileRouteTypes } from "@/routeTree.gen";
387
375
 
388
- type StripLocalePrefix<T extends string> = T extends
389
- | `/${typeof LOCALE_ROUTE}`
390
- | `/${typeof LOCALE_ROUTE}/`
391
- ? "/"
392
- : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
393
- ? `/${Rest}`
394
- : never;
376
+ type NavigateFn = ReturnType<typeof useNavigate>;
377
+ type BaseNavigateOptions = Parameters<NavigateFn>[0];
395
378
 
396
379
  type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
397
380
 
398
- type LocalizedNavigate = {
399
- (to: LocalizedTo): ReturnType<ReturnType<typeof useNavigate>>;
400
- (
401
- opts: { to: LocalizedTo } & Record<string, unknown>
402
- ): ReturnType<ReturnType<typeof useNavigate>>;
381
+ export type LocalizedNavigateOptions = Omit<
382
+ BaseNavigateOptions,
383
+ "to" | "params"
384
+ > & {
385
+ to: LocalizedTo;
386
+ params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
403
387
  };
404
388
 
389
+ type LocalizedNavigate = (
390
+ options: LocalizedNavigateOptions
391
+ ) => ReturnType<NavigateFn>;
392
+
405
393
  export const useLocalizedNavigate = () => {
406
394
  const navigate = useNavigate();
407
395
 
@@ -448,38 +436,6 @@ import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
448
436
 
449
437
  export const Route = createFileRoute("/{-$locale}/")({
450
438
  component: RouteComponent,
451
- head: ({ params }) => {
452
- const { locale } = params;
453
- const path = "/"; // The path for this route
454
-
455
- const metaContent = getIntlayer("app", locale);
456
-
457
- return {
458
- links: [
459
- // Canonical link: Points to the current localized page
460
- { rel: "canonical", href: getLocalizedUrl(path, locale) },
461
-
462
- // Hreflang: Tell Google about all localized versions
463
- ...localeMap(({ locale: mapLocale }) => ({
464
- rel: "alternate",
465
- hrefLang: mapLocale,
466
- href: getLocalizedUrl(path, mapLocale),
467
- })),
468
-
469
- // x-default: For users in unmatched languages
470
- // Define the default fallback locale (usually your primary language)
471
- {
472
- rel: "alternate",
473
- hrefLang: "x-default",
474
- href: getLocalizedUrl(path, defaultLocale),
475
- },
476
- ],
477
- meta: [
478
- { title: metaContent.title },
479
- { name: "description", content: metaContent.meta.description },
480
- ],
481
- };
482
- },
483
439
  });
484
440
 
485
441
  function RouteComponent() {
@@ -634,12 +590,33 @@ export const Route = createFileRoute("/{-$locale}/")({
634
590
  component: RouteComponent,
635
591
  head: ({ params }) => {
636
592
  const { locale } = params;
637
- const metaContent = getIntlayer("page-metadata", locale);
593
+ const path = "/"; // The path for this route
594
+
595
+ const metaContent = getIntlayer("app", locale);
638
596
 
639
597
  return {
598
+ links: [
599
+ // Canonical link: Points to the current localized page
600
+ { rel: "canonical", href: getLocalizedUrl(path, locale) },
601
+
602
+ // Hreflang: Tell Google about all localized versions
603
+ ...localeMap(({ locale: mapLocale }) => ({
604
+ rel: "alternate",
605
+ hrefLang: mapLocale,
606
+ href: getLocalizedUrl(path, mapLocale),
607
+ })),
608
+
609
+ // x-default: For users in unmatched languages
610
+ // Define the default fallback locale (usually your primary language)
611
+ {
612
+ rel: "alternate",
613
+ hrefLang: "x-default",
614
+ href: getLocalizedUrl(path, defaultLocale),
615
+ },
616
+ ],
640
617
  meta: [
641
618
  { title: metaContent.title },
642
- { content: metaContent.description, name: "description" },
619
+ { name: "description", content: metaContent.meta.description },
643
620
  ],
644
621
  };
645
622
  },