@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: Tối ưu hóa kích thước Bundle i18n & Hiệu suất
5
+ description: Giảm kích thước bundle ứng dụng bằng cách tối ưu hóa nội dung quốc tế hóa (i18n). Tìm hiểu cách tận dụng tree shaking và lazy loading cho từ điển với Intlayer.
6
+ keywords:
7
+ - Tối ưu hóa Bundle
8
+ - Tự động hóa nội dung
9
+ - Nội dung động
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: "Thêm các tùy chọn `minify` và `purge` vào cấu hình build"
22
+ ---
23
+
24
+ # Tối ưu hóa kích thước Bundle i18n & Hiệu suất
25
+
26
+ Một trong những thách thức phổ biến nhất với các giải pháp i18n truyền thống dựa trên tệp JSON là quản lý kích thước nội dung. Nếu các nhà phát triển không tách nội dung thành các không gian tên (namespaces) theo cách thủ công, người dùng thường phải tải xuống các bản dịch cho mọi trang và có thể là mọi ngôn ngữ chỉ để xem một trang duy nhất.
27
+
28
+ Ví dụ, một ứng dụng có 10 trang được dịch sang 10 ngôn ngữ có thể dẫn đến việc người dùng tải xuống nội dung của 100 trang, mặc dù họ chỉ cần **một** trang (trang hiện tại bằng ngôn ngữ hiện tại). Điều này dẫn đến lãng phí băng thông và thời gian tải chậm hơn.
29
+
30
+ **Intlayer giải quyết vấn đề này thông qua tối ưu hóa tại thời điểm build.** Nó phân tích mã của bạn để phát hiện từ điển nào thực sự được sử dụng cho mỗi thành phần và chỉ chèn lại nội dung cần thiết vào bundle của bạn.
31
+
32
+ ## Mục lục
33
+
34
+ <TOC />
35
+
36
+ ## Quét bundle của bạn
37
+
38
+ Phân tích bundle là bước đầu tiên để xác định các tệp JSON "nặng" và cơ hội tách mã (code-splitting). Các công cụ này tạo ra một sơ đồ cây (treemap) trực quan về mã đã biên dịch của ứng dụng, cho phép bạn thấy chính xác thư viện nào đang tiêu tốn nhiều không gian nhất.
39
+
40
+ <Tabs>
41
+ <Tab value="vite">
42
+
43
+ ### Vite / Rollup
44
+
45
+ Vite sử dụng Rollup dưới nền t cả. Plugin `rollup-plugin-visualizer` tạo ra một tệp HTML tương tác hiển thị kích thước của mọi mô-đun trong biểu đồ của bạn.
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, // Tự động mở báo cáo trong trình duyệt của bạn
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
+ Đối với các dự án sử dụng App Router và Turbopack, Next.js cung cấp một trình phân tích thử nghiệm tích hợp sẵn mà không cần thêm phụ thuộc.
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
+ Nếu bạn đang sử dụng trình đóng gói Webpack mặc định trong Next.js, hãy sử dụng trình phân tích bundle chính thức. Kích hoạt nó bằng cách đặt biến môi trường trong quá trình build của bạn.
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
+ // Cấu hình Next.js của bạn
120
+ });
121
+ ```
122
+
123
+ **Sử dụng:**
124
+
125
+ ```bash
126
+ ANALYZE=true npm run build
127
+ ```
128
+
129
+ </Tab>
130
+ <Tab value="Webpack (CRA / Angular / etc)">
131
+
132
+ ### Webpack Tiêu chuẩn
133
+
134
+ Đối với Create React App (ejected), Angular hoặc các thiết lập Webpack tùy chỉnh, hãy sử dụng tiêu chuẩn ngành `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
+ ## Cách thức hoạt động
170
+
171
+ Intlayer sử dụng **cách tiếp cận theo từng thành phần**. Không giống như các tệp JSON toàn cục, nội dung của bạn được xác định bên cạnh hoặc bên trong các thành phần của bạn. Trong quá trình build, Intlayer sẽ:
172
+
173
+ 1. **Phân tích** mã của bạn để tìm các cuộc gọi `useIntlayer`.
174
+ 2. **Xây dựng** nội dung từ điển tương ứng.
175
+ 3. **Thay thế** cuộc gọi `useIntlayer` bằng mã được tối ưu hóa dựa trên cấu hình của bạn.
176
+
177
+ Điều này đảm bảo rằng:
178
+
179
+ - Nếu một thành phần không được nhập, nội dung của nó sẽ không được bao gồm trong bundle (Loại bỏ mã chết - Dead Code Elimination).
180
+ - Nếu một thành phần được tải chậm (lazy-loaded), nội dung của nó cũng được tải chậm.
181
+
182
+ ## Thiết lập theo Nền tảng
183
+
184
+ <Tabs>
185
+ <Tab value="nextjs">
186
+
187
+ ### Next.js
188
+
189
+ Next.js yêu cầu plugin `@intlayer/swc` để xử lý quá trình chuyển đổi, vì Next.js sử dụng SWC cho các lần build.
190
+
191
+ > Plugin này không được cài đặt theo mặc định vì các plugin SWC vẫn đang được thử nghiệm cho Next.js. Nó có thể thay đổi trong tương lai.
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
+ Sau khi cài đặt, Intlayer sẽ tự động phát hiện và sử dụng plugin này.
210
+
211
+ </Tab>
212
+ <Tab value="vite">
213
+
214
+ ### Vite
215
+
216
+ Vite sử dụng plugin `@intlayer/babel` được bao gồm như một phụ thuộc của `vite-intlayer`. Quá trình tối ưu hóa được bật theo mặc định. Không cần làm gì thêm.
217
+
218
+ </Tab>
219
+ <Tab value="webpack">
220
+
221
+ ### Webpack
222
+
223
+ Để bật tối ưu hóa bundle với Intlayer trên Webpack, bạn cần cài đặt và cấu hình plugin Babel (`@intlayer/babel`) hoặc SWC (`@intlayer/swc`) thích hợp.
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
+ ## Cấu hình
256
+
257
+ Bạn có thể kiểm soát cách Intlayer tối ưu hóa bundle của mình thông qua thuộc tính `build` trong tệp `intlayer.config.ts`.
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
+ * Nén (minify) các từ điển để giảm kích thước bundle.
273
+ */
274
+ minify: true;
275
+
276
+ /**
277
+ * Loại bỏ (purge) các khóa không sử dụng trong từ điển
278
+ */
279
+ purge: true;
280
+
281
+ /**
282
+ * Cho biết liệu quá trình build có nên kiểm tra các kiểu TypeScript hay không
283
+ */
284
+ checkTypes: false;
285
+ },
286
+ };
287
+
288
+ export default config;
289
+ ```
290
+
291
+ > Giữ tùy chọn mặc định cho `optimize` được khuyến nghị trong hầu hết các trường hợp.
292
+
293
+ > Xem cấu hình tài liệu để biết thêm chi tiết: [Cấu hình](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/configuration.md)
294
+
295
+ ### Tùy chọn Build
296
+
297
+ Các tùy chọn sau đây có sẵn trong đối tượng cấu hình `build`:
298
+
299
+ | Thuộc tính | Kiểu | Mặc định | Mô tả |
300
+ | :------------- | :-------- | :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
301
+ | **`optimize`** | `boolean` | `undefined` | Kiểm soát xem có bật tối ưu hóa build hay không. Nếu là `true`, Intlayer thay thế các cuộc gọi từ điển bằng các lệnh chèn được tối ưu hóa. Nếu là `false`, tối ưu hóa bị tắt. Tốt nhất nên đặt thành `true` trong production. |
302
+ | **`minify`** | `boolean` | `false` | Liệu có nén các từ điển để giảm kích thước bundle hay không. |
303
+ | **`purge`** | `boolean` | `false` | Liệu có loại bỏ các khóa không sử dụng trong từ điển hay không. |
304
+
305
+ ### Nén (Minification)
306
+
307
+ Nén từ điển giúp loại bỏ các khoảng trắng, nhận xét không cần thiết và giảm kích thước nội dung JSON. Điều này đặc biệt hữu ích cho các từ điển lớn.
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
+ > Lưu ý: Nén bị bỏ qua nếu `optimize` bị tắt hoặc nếu Trình chỉnh sửa trực quan (Visual Editor) được bật (vì trình chỉnh sửa cần nội dung đầy đủ để cho phép chỉnh sửa).
322
+
323
+ ### Loại bỏ (Purging)
324
+
325
+ Loại bỏ đảm bảo rằng chỉ những khóa thực sự được sử dụng trong mã của bạn mới được đưa vào bundle từ điển cuối cùng. Điều này có thể giảm đáng kể kích thước bundle của bạn nếu bạn có các từ điển lớn với nhiều khóa không được sử dụng trong mọi phần của ứng dụng.
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
+ > Lưu ý: Loại bỏ bị bỏ qua nếu `optimize` bị tắt.
340
+
341
+ ### Chế độ Nhập (Import Mode)
342
+
343
+ Đối với các ứng dụng lớn, bao gồm nhiều trang và ngôn ngữ, tệp JSON của bạn có thể chiếm một phần đáng kể trong kích thước bundle. Intlayer cho phép bạn kiểm soát cách tải từ điển.
344
+
345
+ Chế độ nhập có thể được xác định mặc định trên toàn cầu trong tệp `intlayer.config.ts` của bạn.
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
+ Cũng như đối với mỗi từ điển trong các tệp `.content.{{ts|tsx|js|jsx|mjs|cjs|json|jsonc|json5}}` của bạn.
360
+
361
+ ```ts
362
+ import { type Dictionary, t } from "intlayer";
363
+
364
+ const appContent: Dictionary = {
365
+ key: "app",
366
+ importMode: "dynamic", // Ghi đè chế độ nhập mặc định
367
+ content: {
368
+ // ...
369
+ },
370
+ };
371
+
372
+ export default appContent;
373
+ ```
374
+
375
+ | Thuộc tính | Kiểu | Mặc định | Mô tả |
376
+ | :--------------- | :--------------------------------- | :--------- | :------------------------------------------------------------------------------------------------------------ |
377
+ | **`importMode`** | `'static'`, `'dynamic'`, `'fetch'` | `'static'` | **Đã lỗi thời**: Sử dụng `dictionary.importMode` thay thế. Xác định cách tải từ điển (xem chi tiết bên dưới). |
378
+
379
+ Cài đặt `importMode` quy định cách nội dung từ điển được chèn vào thành phần của bạn.
380
+ Bạn có thể xác định nó trên toàn cầu trong tệp `intlayer.config.ts` dưới đối tượng `dictionary`, hoặc bạn có thể ghi đè nó cho một từ điển cụ thể trong tệp `.content.ts` của nó.
381
+
382
+ ### 1. Chế độ Tĩnh (`default`)
383
+
384
+ Trong chế độ tĩnh, Intlayer thay thế `useIntlayer` bằng `useDictionary` và chèn từ điển trực tiếp vào bundle JavaScript.
385
+
386
+ - **Ưu điểm:** Hiển thị tức thì (đồng bộ), không có yêu cầu mạng bổ sung trong quá trình hydrat hóa.
387
+ - **Nhược điểm:** Bundle bao gồm các bản dịch cho **tất cả** các ngôn ngữ có sẵn cho thành phần cụ thể đó.
388
+ - **Tốt nhất cho:** Ứng dụng trang đơn (SPA).
389
+
390
+ **Ví dụ mã đã chuyển đổi:**
391
+
392
+ ```tsx
393
+ // Mã của bạn
394
+ const content = useIntlayer("my-key");
395
+
396
+ // Mã được tối ưu hóa (Tĩnh)
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. Chế độ Động
410
+
411
+ Trong chế độ động, Intlayer thay thế `useIntlayer` bằng `useDictionaryAsync`. Điều này sử dụng `import()` (cơ chế giống như Suspense) để tải chậm (lazy-load) cụ thể JSON cho ngôn ngữ hiện tại.
412
+
413
+ - **Ưu điểm:** **Tree shaking cấp độ ngôn ngữ.** Người dùng xem phiên bản tiếng Anh sẽ _chỉ_ tải xuống từ điển tiếng Anh. Từ điển tiếng Pháp không bao giờ được tải.
414
+ - **Nhược điểm:** Kích hoạt một yêu cầu mạng (lấy tài nguyên) cho mỗi thành phần trong quá trình hydrat hóa.
415
+ - **Tốt nhất cho:** Các khối văn bản lớn, bài báo hoặc ứng dụng hỗ trợ nhiều ngôn ngữ nơi kích thước bundle là quan trọng.
416
+
417
+ **Ví dụ mã đã chuyển đổi:**
418
+
419
+ ```tsx
420
+ // Mã của bạn
421
+ const content = useIntlayer("my-key");
422
+
423
+ // Mã được tối ưu hóa (Động)
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
+ > Khi sử dụng `importMode: 'dynamic'`, nếu bạn có 100 thành phần sử dụng `useIntlayer` trên một trang duy nhất, trình duyệt sẽ thử 100 lần lấy riêng biệt. Để tránh tình trạng "thác nước" (waterfall) của các yêu cầu này, hãy nhóm nội dung vào ít tệp `.content` hơn (ví dụ: một từ điển cho mỗi phần trang) thay vì một từ điển cho mỗi thành phần nguyên tử.
437
+
438
+ ### 3. Chế độ Fetch
439
+
440
+ Hoạt động tương tự như chế độ Động nhưng cố gắng lấy các từ điển từ Intlayer Live Sync API trước. Nếu cuộc gọi API thất bại hoặc nội dung không được đánh dấu để cập nhật trực tiếp, nó sẽ quay lại chế độ nhập động.
441
+
442
+ > Xem tài liệu CMS để biết thêm chi tiết: [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/intlayer_CMS.md)
443
+
444
+ > Trong chế độ fetch, không thể sử dụng loại bỏ (purge) và nén (minification).
445
+
446
+ ## Tóm tắt: Tĩnh vs Động
447
+
448
+ | Tính năng | Chế độ Tĩnh | Chế độ Động |
449
+ | :------------------------------ | :--------------------------------------------------- | :----------------------------------------- |
450
+ | **Kích thước Bundle JS** | Lớn hơn (bao gồm tất cả các ngôn ngữ cho thành phần) | Nhỏ nhất (chỉ mã, không có nội dung) |
451
+ | **Tải ban đầu** | Tức thì (Nội dung có trong bundle) | Độ trễ nhẹ (Lấy JSON) |
452
+ | **Yêu cầu mạng** | 0 yêu cầu bổ sung | 1 yêu cầu trên mỗi từ điển |
453
+ | **Tree Shaking** | Cấp độ thành phần | Cấp độ thành phần + Cấp độ ngôn ngữ |
454
+ | **Trường hợp sử dụng tốt nhất** | Các thành phần giao diện người dùng, Ứng dụng nhỏ | Các trang có nhiều văn bản, Nhiều ngôn ngữ |
@@ -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),
@@ -193,9 +193,7 @@ const RootComponent: ParentComponent = (props) => {
193
193
  </head>
194
194
  <body>
195
195
  <IntlayerProvider locale={locale}>
196
- <Suspense>
197
- {props.children}
198
- </Suspense>
196
+ <Suspense>{props.children}</Suspense>
199
197
  </IntlayerProvider>
200
198
  <Scripts />
201
199
  </body>
@@ -505,12 +503,33 @@ export const Route = createFileRoute("/{-$locale}/")({
505
503
  component: RouteComponent,
506
504
  head: ({ params }) => {
507
505
  const { locale } = params;
508
- const metaContent = getIntlayer("page-metadata", locale);
506
+ const path = "/"; // The path for this route
507
+
508
+ const metaContent = getIntlayer("app", locale);
509
509
 
510
510
  return {
511
+ links: [
512
+ // Canonical link: Points to the current localized page
513
+ { rel: "canonical", href: getLocalizedUrl(path, locale) },
514
+
515
+ // Hreflang: Tell Google about all localized versions
516
+ ...localeMap(({ locale: mapLocale }) => ({
517
+ rel: "alternate",
518
+ hrefLang: mapLocale,
519
+ href: getLocalizedUrl(path, mapLocale),
520
+ })),
521
+
522
+ // x-default: For users in unmatched languages
523
+ // Define the default fallback locale (usually your primary language)
524
+ {
525
+ rel: "alternate",
526
+ hrefLang: "x-default",
527
+ href: getLocalizedUrl(path, defaultLocale),
528
+ },
529
+ ],
511
530
  meta: [
512
531
  { title: metaContent.title },
513
- { content: metaContent.description, name: "description" },
532
+ { name: "description", content: metaContent.meta.description },
514
533
  ],
515
534
  };
516
535
  },
@@ -224,9 +224,7 @@ function RootDocument({ children }: { children: ReactNode }) {
224
224
  <HeadContent />
225
225
  </head>
226
226
  <body>
227
- <IntlayerProvider locale={locale}>
228
- {children}
229
- </IntlayerProvider>
227
+ <IntlayerProvider locale={locale}>{children}</IntlayerProvider>
230
228
  <Scripts />
231
229
  </body>
232
230
  </html>
@@ -325,30 +323,20 @@ import { getPrefix } from "intlayer";
325
323
 
326
324
  export const LOCALE_ROUTE = "{-$locale}" as const;
327
325
 
328
- // Tiện ích chính
329
- export type RemoveLocaleParam<T> = T extends string
330
- ? RemoveLocaleFromString<T>
331
- : T;
326
+ export type To = StripLocalePrefix<LinkComponentProps["to"]>;
332
327
 
333
- export type To = RemoveLocaleParam<LinkComponentProps["to"]>;
334
-
335
- type CollapseDoubleSlashes<S extends string> =
336
- S extends `${infer H}//${infer T}` ? CollapseDoubleSlashes<`${H}/${T}`> : S;
328
+ export type StripLocalePrefix<T extends string | undefined> = T extends
329
+ | `/${typeof LOCALE_ROUTE}/`
330
+ | `/${typeof LOCALE_ROUTE}`
331
+ ? "/"
332
+ : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
333
+ ? `/${Rest}`
334
+ : T;
337
335
 
338
336
  type LocalizedLinkProps = {
339
337
  to?: To;
340
338
  } & Omit<LinkComponentProps, "to">;
341
339
 
342
- // Các hàm trợ giúp
343
- type RemoveAll<
344
- S extends string,
345
- Sub extends string,
346
- > = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
347
-
348
- type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
349
- RemoveAll<S, typeof LOCALE_ROUTE>
350
- >;
351
-
352
340
  export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
353
341
  const { locale } = useLocale();
354
342
  const { localePrefix } = getPrefix(locale);
@@ -377,26 +365,26 @@ Sau đó, chúng ta có thể tạo một hook `useLocalizedNavigate` để đi
377
365
  import { useNavigate } from "@tanstack/react-router";
378
366
  import { getPrefix } from "intlayer";
379
367
  import { useLocale } from "react-intlayer";
380
- import { LOCALE_ROUTE } from "@/components/localized-link";
368
+ import type { StripLocalePrefix } from "@/components/localized-link";
381
369
  import type { FileRouteTypes } from "@/routeTree.gen";
382
370
 
383
- type StripLocalePrefix<T extends string> = T extends
384
- | `/${typeof LOCALE_ROUTE}`
385
- | `/${typeof LOCALE_ROUTE}/`
386
- ? "/"
387
- : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
388
- ? `/${Rest}`
389
- : never;
371
+ type NavigateFn = ReturnType<typeof useNavigate>;
372
+ type BaseNavigateOptions = Parameters<NavigateFn>[0];
390
373
 
391
374
  type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
392
375
 
393
- type LocalizedNavigate = {
394
- (to: LocalizedTo): ReturnType<ReturnType<typeof useNavigate>>;
395
- (
396
- opts: { to: LocalizedTo } & Record<string, unknown>
397
- ): ReturnType<ReturnType<typeof useNavigate>>;
376
+ export type LocalizedNavigateOptions = Omit<
377
+ BaseNavigateOptions,
378
+ "to" | "params"
379
+ > & {
380
+ to: LocalizedTo;
381
+ params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
398
382
  };
399
383
 
384
+ type LocalizedNavigate = (
385
+ options: LocalizedNavigateOptions
386
+ ) => ReturnType<NavigateFn>;
387
+
400
388
  export const useLocalizedNavigate = () => {
401
389
  const navigate = useNavigate();
402
390
 
@@ -443,38 +431,6 @@ import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
443
431
 
444
432
  export const Route = createFileRoute("/{-$locale}/")({
445
433
  component: RouteComponent,
446
- head: ({ params }) => {
447
- const { locale } = params;
448
- const path = "/"; // The path for this route
449
-
450
- const metaContent = getIntlayer("app", locale);
451
-
452
- return {
453
- links: [
454
- // Canonical link: Points to the current localized page
455
- { rel: "canonical", href: getLocalizedUrl(path, locale) },
456
-
457
- // Hreflang: Tell Google about all localized versions
458
- ...localeMap(({ locale: mapLocale }) => ({
459
- rel: "alternate",
460
- hrefLang: mapLocale,
461
- href: getLocalizedUrl(path, mapLocale),
462
- })),
463
-
464
- // x-default: For users in unmatched languages
465
- // Define the default fallback locale (usually your primary language)
466
- {
467
- rel: "alternate",
468
- hrefLang: "x-default",
469
- href: getLocalizedUrl(path, defaultLocale),
470
- },
471
- ],
472
- meta: [
473
- { title: metaContent.title },
474
- { name: "description", content: metaContent.meta.description },
475
- ],
476
- };
477
- },
478
434
  });
479
435
 
480
436
  function RouteComponent() {
@@ -629,12 +585,33 @@ export const Route = createFileRoute("/{-$locale}/")({
629
585
  component: RouteComponent,
630
586
  head: ({ params }) => {
631
587
  const { locale } = params;
632
- const metaContent = getIntlayer("page-metadata", locale);
588
+ const path = "/"; // The path for this route
589
+
590
+ const metaContent = getIntlayer("app", locale);
633
591
 
634
592
  return {
593
+ links: [
594
+ // Canonical link: Points to the current localized page
595
+ { rel: "canonical", href: getLocalizedUrl(path, locale) },
596
+
597
+ // Hreflang: Tell Google about all localized versions
598
+ ...localeMap(({ locale: mapLocale }) => ({
599
+ rel: "alternate",
600
+ hrefLang: mapLocale,
601
+ href: getLocalizedUrl(path, mapLocale),
602
+ })),
603
+
604
+ // x-default: For users in unmatched languages
605
+ // Define the default fallback locale (usually your primary language)
606
+ {
607
+ rel: "alternate",
608
+ hrefLang: "x-default",
609
+ href: getLocalizedUrl(path, defaultLocale),
610
+ },
611
+ ],
635
612
  meta: [
636
613
  { title: metaContent.title },
637
- { content: metaContent.description, name: "description" },
614
+ { name: "description", content: metaContent.meta.description },
638
615
  ],
639
616
  };
640
617
  },