@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). Дізнайтеся, як використовувати tree shaking та lazy loading для словників за допомогою Intlayer.
|
|
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
|
+
Однією з найпоширеніших проблем традиційних i18n-рішень, що покладаються на JSON-файли, є управління розміром контенту. Якщо розробники вручну не розділяють контент на простори імен (namespaces), користувачі часто завантажують переклади для кожної сторінки і, можливо, для кожної мови лише для того, щоб переглянути одну сторінку.
|
|
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
|
+
Якщо ви використовуєте стандартний збирач Webpack у Next.js, використовуйте офіційний аналізатор бандлів. Запустіть його, встановивши змінну оточення під час збірки.
|
|
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
|
+
- Якщо компонент не імпортовано, його контент не включається в бандл (видалення мертвого коду).
|
|
180
|
+
- Якщо компонент завантажується ліниво (lazy-loading), його контент також завантажується ліниво.
|
|
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
|
+
Щоб увімкнути оптимізацію бандла за допомогою Intlayer у Webpack, вам потрібно встановити та налаштувати відповідний плагін 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 оптимізує ваш бандл, за допомогою властивості `build` у вашому `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
|
+
* Мініфікувати словники для зменшення розміру бандла.
|
|
273
|
+
*/
|
|
274
|
+
minify: true;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Видаляти невикористані ключі в словниках (purge)
|
|
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/uk/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
|
+
### Мініфікація
|
|
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
|
|
439
|
+
|
|
440
|
+
Поводиться аналогічно динамічному режиму, але спочатку намагається отримати словники з API Intlayer Live Sync. Якщо виклик API завершується невдало або контент не позначено для живих оновлень, він перемикається на динамічний імпорт.
|
|
441
|
+
|
|
442
|
+
> Подробиці дивіться у документації CMS: [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/uk/intlayer_CMS.md)
|
|
443
|
+
|
|
444
|
+
> У режимі fetch не можна використовувати очищення (purge) та мініфікацію.
|
|
445
|
+
|
|
446
|
+
## Резюме: Статичний vs Динамічний
|
|
447
|
+
|
|
448
|
+
| Функція | Статичний режим | Динамічний режим |
|
|
449
|
+
| :--------------------------------- | :---------------------------------------- | :---------------------------------------------- |
|
|
450
|
+
| **Розмір JS бандла** | Більший (включає всі мови для компонента) | Найменший (лише код, контенту немає) |
|
|
451
|
+
| **Початкове завантаження** | Миттєво (контент уже в бандлі) | Невелика затримка (завантаження JSON) |
|
|
452
|
+
| **Мережеві запити** | 0 додаткових запитів | 1 запит на кожен словник |
|
|
453
|
+
| **Tree Shaking** | На рівні компонентів | На рівні компонентів + на рівні локалей |
|
|
454
|
+
| **Найкращий варіант використання** | UI-компоненти, невеликі застосунки | Сторінки з великою кількістю тексту, багато мов |
|
|
@@ -256,7 +256,7 @@ export default function LocaleLayout({
|
|
|
256
256
|
params: { locale: string };
|
|
257
257
|
}) {
|
|
258
258
|
const locale: Locale = (locales as readonly string[]).includes(params.locale)
|
|
259
|
-
?
|
|
259
|
+
? params.locale
|
|
260
260
|
: defaultLocale;
|
|
261
261
|
|
|
262
262
|
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),
|
|
@@ -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
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
// Допоміжні типи
|
|
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 @@ export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
|
377
365
|
import { useNavigate } from "@tanstack/react-router";
|
|
378
366
|
import { getPrefix } from "intlayer";
|
|
379
367
|
import { useLocale } from "react-intlayer";
|
|
380
|
-
import {
|
|
368
|
+
import type { StripLocalePrefix } from "@/components/localized-link";
|
|
381
369
|
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
382
370
|
|
|
383
|
-
type
|
|
384
|
-
|
|
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
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
|
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
|
-
{
|
|
614
|
+
{ name: "description", content: metaContent.meta.description },
|
|
638
615
|
],
|
|
639
616
|
};
|
|
640
617
|
},
|