@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.
- package/blog/ar/i18n_using_next-i18next.md +1 -1
- package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/de/i18n_using_next-i18next.md +1 -1
- package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/en/i18n_using_next-i18next.md +1 -1
- package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/en-GB/i18n_using_next-i18next.md +1 -1
- package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/es/i18n_using_next-i18next.md +1 -1
- package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/fr/i18n_using_next-i18next.md +1 -1
- package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/hi/i18n_using_next-i18next.md +1 -1
- package/blog/id/i18n_using_next-i18next.md +1 -1
- package/blog/id/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/it/i18n_using_next-i18next.md +1 -1
- package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ja/i18n_using_next-i18next.md +1 -1
- package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ko/i18n_using_next-i18next.md +1 -1
- package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/pl/i18n_using_next-i18next.md +1 -1
- package/blog/pl/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/pt/i18n_using_next-i18next.md +1 -1
- package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ru/i18n_using_next-i18next.md +1 -1
- package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/tr/i18n_using_next-i18next.md +1 -1
- package/blog/uk/i18n_using_next-i18next.md +1 -1
- package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/vi/i18n_using_next-i18next.md +1 -1
- package/blog/vi/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/zh/i18n_using_next-i18next.md +1 -1
- package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/docs/ar/bundle_optimization.md +454 -0
- package/docs/ar/intlayer_with_next-i18next.md +1 -1
- package/docs/ar/intlayer_with_next-intl.md +1 -1
- package/docs/ar/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ar/intlayer_with_tanstack.md +45 -68
- package/docs/bn/bundle_optimization.md +454 -0
- package/docs/cs/bundle_optimization.md +454 -0
- package/docs/de/bundle_optimization.md +454 -0
- package/docs/de/intlayer_with_next-i18next.md +1 -1
- package/docs/de/intlayer_with_next-intl.md +1 -1
- package/docs/de/intlayer_with_tanstack+solid.md +24 -5
- package/docs/de/intlayer_with_tanstack.md +45 -68
- package/docs/en/bundle_optimization.md +36 -8
- package/docs/en/intlayer_with_next-i18next.md +1 -1
- package/docs/en/intlayer_with_next-intl.md +1 -1
- package/docs/en/intlayer_with_tanstack+solid.md +24 -5
- package/docs/en/intlayer_with_tanstack.md +45 -68
- package/docs/en-GB/bundle_optimization.md +454 -0
- package/docs/en-GB/intlayer_with_next-i18next.md +1 -1
- package/docs/en-GB/intlayer_with_next-intl.md +1 -1
- package/docs/en-GB/intlayer_with_tanstack+solid.md +24 -5
- package/docs/en-GB/intlayer_with_tanstack.md +47 -70
- package/docs/es/bundle_optimization.md +454 -0
- package/docs/es/intlayer_with_next-i18next.md +1 -1
- package/docs/es/intlayer_with_next-intl.md +1 -1
- package/docs/es/intlayer_with_tanstack+solid.md +24 -5
- package/docs/es/intlayer_with_tanstack.md +45 -68
- package/docs/fr/bundle_optimization.md +454 -0
- package/docs/fr/intlayer_with_next-i18next.md +1 -1
- package/docs/fr/intlayer_with_next-intl.md +1 -1
- package/docs/fr/intlayer_with_tanstack+solid.md +24 -5
- package/docs/fr/intlayer_with_tanstack.md +45 -68
- package/docs/hi/bundle_optimization.md +454 -0
- package/docs/hi/intlayer_with_next-i18next.md +1 -1
- package/docs/hi/intlayer_with_next-intl.md +1 -1
- package/docs/hi/intlayer_with_tanstack+solid.md +24 -5
- package/docs/hi/intlayer_with_tanstack.md +45 -68
- package/docs/id/bundle_optimization.md +454 -0
- package/docs/id/intlayer_with_next-i18next.md +1 -1
- package/docs/id/intlayer_with_next-intl.md +1 -1
- package/docs/id/intlayer_with_tanstack+solid.md +24 -5
- package/docs/id/intlayer_with_tanstack.md +45 -68
- package/docs/it/bundle_optimization.md +454 -0
- package/docs/it/intlayer_with_next-i18next.md +1 -1
- package/docs/it/intlayer_with_next-intl.md +1 -1
- package/docs/it/intlayer_with_tanstack+solid.md +24 -5
- package/docs/it/intlayer_with_tanstack.md +45 -68
- package/docs/ja/bundle_optimization.md +454 -0
- package/docs/ja/intlayer_with_next-i18next.md +1 -1
- package/docs/ja/intlayer_with_next-intl.md +1 -1
- package/docs/ja/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ja/intlayer_with_tanstack.md +45 -36
- package/docs/ko/bundle_optimization.md +454 -0
- package/docs/ko/intlayer_with_next-i18next.md +1 -1
- package/docs/ko/intlayer_with_next-intl.md +1 -1
- package/docs/ko/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ko/intlayer_with_tanstack.md +45 -68
- package/docs/nl/bundle_optimization.md +454 -0
- package/docs/pl/bundle_optimization.md +454 -0
- package/docs/pl/intlayer_with_next-i18next.md +1 -1
- package/docs/pl/intlayer_with_next-intl.md +1 -1
- package/docs/pl/intlayer_with_tanstack+solid.md +24 -5
- package/docs/pl/intlayer_with_tanstack.md +45 -68
- package/docs/pt/bundle_optimization.md +454 -0
- package/docs/pt/intlayer_with_next-i18next.md +1 -1
- package/docs/pt/intlayer_with_next-intl.md +1 -1
- package/docs/pt/intlayer_with_tanstack+solid.md +24 -5
- package/docs/pt/intlayer_with_tanstack.md +45 -68
- package/docs/ru/bundle_optimization.md +454 -0
- package/docs/ru/intlayer_with_next-i18next.md +1 -1
- package/docs/ru/intlayer_with_next-intl.md +1 -1
- package/docs/ru/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ru/intlayer_with_tanstack.md +45 -68
- package/docs/tr/bundle_optimization.md +454 -0
- package/docs/tr/intlayer_with_next-i18next.md +1 -1
- package/docs/tr/intlayer_with_next-intl.md +1 -1
- package/docs/tr/intlayer_with_tanstack+solid.md +24 -5
- package/docs/tr/intlayer_with_tanstack.md +45 -68
- package/docs/uk/bundle_optimization.md +454 -0
- package/docs/uk/intlayer_with_next-i18next.md +1 -1
- package/docs/uk/intlayer_with_next-intl.md +1 -1
- package/docs/uk/intlayer_with_tanstack+solid.md +24 -5
- package/docs/uk/intlayer_with_tanstack.md +45 -68
- package/docs/ur/bundle_optimization.md +454 -0
- package/docs/vi/bundle_optimization.md +454 -0
- package/docs/vi/intlayer_with_next-i18next.md +1 -1
- package/docs/vi/intlayer_with_next-intl.md +1 -1
- package/docs/vi/intlayer_with_tanstack+solid.md +24 -5
- package/docs/vi/intlayer_with_tanstack.md +45 -68
- package/docs/zh/bundle_optimization.md +454 -0
- package/docs/zh/intlayer_with_next-i18next.md +1 -1
- package/docs/zh/intlayer_with_next-intl.md +1 -1
- package/docs/zh/intlayer_with_tanstack+solid.md +24 -5
- package/docs/zh/intlayer_with_tanstack.md +45 -68
- 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
|
-
?
|
|
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
|
|
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
|
|
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
|
-
{
|
|
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
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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 {
|
|
373
|
+
import type { StripLocalePrefix } from "@/components/localized-link";
|
|
386
374
|
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
387
375
|
|
|
388
|
-
type
|
|
389
|
-
|
|
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
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
|
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
|
-
{
|
|
619
|
+
{ name: "description", content: metaContent.meta.description },
|
|
643
620
|
],
|
|
644
621
|
};
|
|
645
622
|
},
|