@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/ru/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/ru/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-компоненты, небольшие приложения | Страницы с большим количеством текста, много языков |
|
|
@@ -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>
|
|
@@ -326,30 +324,20 @@ import { getPrefix } from "intlayer";
|
|
|
326
324
|
|
|
327
325
|
export const LOCALE_ROUTE = "{-$locale}" as const;
|
|
328
326
|
|
|
329
|
-
|
|
330
|
-
export type RemoveLocaleParam<T> = T extends string
|
|
331
|
-
? RemoveLocaleFromString<T>
|
|
332
|
-
: T;
|
|
327
|
+
export type To = StripLocalePrefix<LinkComponentProps["to"]>;
|
|
333
328
|
|
|
334
|
-
export type
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
329
|
+
export type StripLocalePrefix<T extends string | undefined> = T extends
|
|
330
|
+
| `/${typeof LOCALE_ROUTE}/`
|
|
331
|
+
| `/${typeof LOCALE_ROUTE}`
|
|
332
|
+
? "/"
|
|
333
|
+
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
334
|
+
? `/${Rest}`
|
|
335
|
+
: T;
|
|
338
336
|
|
|
339
337
|
type LocalizedLinkProps = {
|
|
340
338
|
to?: To;
|
|
341
339
|
} & Omit<LinkComponentProps, "to">;
|
|
342
340
|
|
|
343
|
-
// Вспомогательные типы
|
|
344
|
-
type RemoveAll<
|
|
345
|
-
S extends string,
|
|
346
|
-
Sub extends string,
|
|
347
|
-
> = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
|
|
348
|
-
|
|
349
|
-
type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
|
|
350
|
-
RemoveAll<S, typeof LOCALE_ROUTE>
|
|
351
|
-
>;
|
|
352
|
-
|
|
353
341
|
export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
354
342
|
const { locale } = useLocale();
|
|
355
343
|
const { localePrefix } = getPrefix(locale);
|
|
@@ -378,26 +366,26 @@ export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
|
378
366
|
import { useNavigate } from "@tanstack/react-router";
|
|
379
367
|
import { getPrefix } from "intlayer";
|
|
380
368
|
import { useLocale } from "react-intlayer";
|
|
381
|
-
import {
|
|
369
|
+
import type { StripLocalePrefix } from "@/components/localized-link";
|
|
382
370
|
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
383
371
|
|
|
384
|
-
type
|
|
385
|
-
|
|
386
|
-
| `/${typeof LOCALE_ROUTE}/`
|
|
387
|
-
? "/"
|
|
388
|
-
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
389
|
-
? `/${Rest}`
|
|
390
|
-
: never;
|
|
372
|
+
type NavigateFn = ReturnType<typeof useNavigate>;
|
|
373
|
+
type BaseNavigateOptions = Parameters<NavigateFn>[0];
|
|
391
374
|
|
|
392
375
|
type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
|
|
393
376
|
|
|
394
|
-
type
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
377
|
+
export type LocalizedNavigateOptions = Omit<
|
|
378
|
+
BaseNavigateOptions,
|
|
379
|
+
"to" | "params"
|
|
380
|
+
> & {
|
|
381
|
+
to: LocalizedTo;
|
|
382
|
+
params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
|
|
399
383
|
};
|
|
400
384
|
|
|
385
|
+
type LocalizedNavigate = (
|
|
386
|
+
options: LocalizedNavigateOptions
|
|
387
|
+
) => ReturnType<NavigateFn>;
|
|
388
|
+
|
|
401
389
|
export const useLocalizedNavigate = () => {
|
|
402
390
|
const navigate = useNavigate();
|
|
403
391
|
|
|
@@ -444,38 +432,6 @@ import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
|
|
|
444
432
|
|
|
445
433
|
export const Route = createFileRoute("/{-$locale}/")({
|
|
446
434
|
component: RouteComponent,
|
|
447
|
-
head: ({ params }) => {
|
|
448
|
-
const { locale } = params;
|
|
449
|
-
const path = "/"; // The path for this route
|
|
450
|
-
|
|
451
|
-
const metaContent = getIntlayer("app", locale);
|
|
452
|
-
|
|
453
|
-
return {
|
|
454
|
-
links: [
|
|
455
|
-
// Canonical link: Points to the current localized page
|
|
456
|
-
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
457
|
-
|
|
458
|
-
// Hreflang: Tell Google about all localized versions
|
|
459
|
-
...localeMap(({ locale: mapLocale }) => ({
|
|
460
|
-
rel: "alternate",
|
|
461
|
-
hrefLang: mapLocale,
|
|
462
|
-
href: getLocalizedUrl(path, mapLocale),
|
|
463
|
-
})),
|
|
464
|
-
|
|
465
|
-
// x-default: For users in unmatched languages
|
|
466
|
-
// Define the default fallback locale (usually your primary language)
|
|
467
|
-
{
|
|
468
|
-
rel: "alternate",
|
|
469
|
-
hrefLang: "x-default",
|
|
470
|
-
href: getLocalizedUrl(path, defaultLocale),
|
|
471
|
-
},
|
|
472
|
-
],
|
|
473
|
-
meta: [
|
|
474
|
-
{ title: metaContent.title },
|
|
475
|
-
{ name: "description", content: metaContent.meta.description },
|
|
476
|
-
],
|
|
477
|
-
};
|
|
478
|
-
},
|
|
479
435
|
});
|
|
480
436
|
|
|
481
437
|
function RouteComponent() {
|
|
@@ -630,12 +586,33 @@ export const Route = createFileRoute("/{-$locale}/")({
|
|
|
630
586
|
component: RouteComponent,
|
|
631
587
|
head: ({ params }) => {
|
|
632
588
|
const { locale } = params;
|
|
633
|
-
const
|
|
589
|
+
const path = "/"; // The path for this route
|
|
590
|
+
|
|
591
|
+
const metaContent = getIntlayer("app", locale);
|
|
634
592
|
|
|
635
593
|
return {
|
|
594
|
+
links: [
|
|
595
|
+
// Canonical link: Points to the current localized page
|
|
596
|
+
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
597
|
+
|
|
598
|
+
// Hreflang: Tell Google about all localized versions
|
|
599
|
+
...localeMap(({ locale: mapLocale }) => ({
|
|
600
|
+
rel: "alternate",
|
|
601
|
+
hrefLang: mapLocale,
|
|
602
|
+
href: getLocalizedUrl(path, mapLocale),
|
|
603
|
+
})),
|
|
604
|
+
|
|
605
|
+
// x-default: For users in unmatched languages
|
|
606
|
+
// Define the default fallback locale (usually your primary language)
|
|
607
|
+
{
|
|
608
|
+
rel: "alternate",
|
|
609
|
+
hrefLang: "x-default",
|
|
610
|
+
href: getLocalizedUrl(path, defaultLocale),
|
|
611
|
+
},
|
|
612
|
+
],
|
|
636
613
|
meta: [
|
|
637
614
|
{ title: metaContent.title },
|
|
638
|
-
{
|
|
615
|
+
{ name: "description", content: metaContent.meta.description },
|
|
639
616
|
],
|
|
640
617
|
};
|
|
641
618
|
},
|