@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: Otimizando o tamanho e desempenho do bundle i18n
|
|
5
|
+
description: Reduza o tamanho do bundle da sua aplicação otimizando o conteúdo de internacionalização (i18n). Aprenda a aproveitar o tree shaking e o lazy loading para dicionários com Intlayer.
|
|
6
|
+
keywords:
|
|
7
|
+
- Otimização de bundle
|
|
8
|
+
- Automação de conteúdo
|
|
9
|
+
- Conteúdo dinâmico
|
|
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: "Adicionadas as opções `minify` e `purge` à configuração de build"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Otimizando o tamanho e desempenho do bundle i18n
|
|
25
|
+
|
|
26
|
+
Um dos desafios mais comuns com as soluções i18n tradicionais que dependem de arquivos JSON é o gerenciamento do tamanho do conteúdo. Se os desenvolvedores não separarem manualmente o conteúdo em namespaces, os usuários muitas vezes acabam baixando traduções para cada página e potencialmente para cada idioma apenas para visualizar uma única página.
|
|
27
|
+
|
|
28
|
+
Por exemplo, uma aplicação com 10 páginas traduzidas em 10 idiomas pode resultar em um usuário baixando o conteúdo de 100 páginas, embora precise apenas de **uma** (a página atual no idioma atual). Isso leva a desperdício de largura de banda e tempos de carregamento mais lentos.
|
|
29
|
+
|
|
30
|
+
**O Intlayer resolve esse problema através da otimização em tempo de build.** Ele analisa seu código para detectar quais dicionários são realmente usados por componente e reinjeta apenas o conteúdo necessário no seu bundle.
|
|
31
|
+
|
|
32
|
+
## Índice
|
|
33
|
+
|
|
34
|
+
<TOC />
|
|
35
|
+
|
|
36
|
+
## Escaneie seu bundle
|
|
37
|
+
|
|
38
|
+
Analisar seu bundle é o primeiro passo para identificar arquivos JSON "pesados" e oportunidades de code-splitting. Essas ferramentas geram um mapa de árvore (treemap) visual do código compilado da sua aplicação, permitindo que você veja exatamente quais bibliotecas estão consumindo mais espaço.
|
|
39
|
+
|
|
40
|
+
<Tabs>
|
|
41
|
+
<Tab value="vite">
|
|
42
|
+
|
|
43
|
+
### Vite / Rollup
|
|
44
|
+
|
|
45
|
+
O Vite usa o Rollup por baixo do pano. O plugin `rollup-plugin-visualizer` gera um arquivo HTML interativo que mostra o tamanho de cada módulo no seu gráfico.
|
|
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, // Abrir automaticamente o relatório no seu navegador
|
|
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
|
+
Para projetos que usam o App Router e o Turbopack, o Next.js fornece um analisador experimental integrado que não requer dependências extras.
|
|
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
|
+
Se você estiver usando o bundler Webpack padrão no Next.js, use o analisador de bundle oficial. Ative-o definindo uma variável de ambiente durante o build.
|
|
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
|
+
// Sua configuração do Next.js
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Uso:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ANALYZE=true npm run build
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
</Tab>
|
|
130
|
+
<Tab value="Webpack (CRA / Angular / etc)">
|
|
131
|
+
|
|
132
|
+
### Webpack Padrão
|
|
133
|
+
|
|
134
|
+
Para o Create React App (ejected), Angular ou configurações personalizadas do Webpack, use o padrão da indústria `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
|
+
## Como Funciona
|
|
170
|
+
|
|
171
|
+
O Intlayer usa uma **abordagem por componente**. Ao contrário dos arquivos JSON globais, seu conteúdo é definido ao lado ou dentro de seus componentes. Durante o processo de build, o Intlayer:
|
|
172
|
+
|
|
173
|
+
1. **Analisa** seu código para encontrar chamadas `useIntlayer`.
|
|
174
|
+
2. **Gera** o conteúdo do dicionário correspondente.
|
|
175
|
+
3. **Substitui** a chamada `useIntlayer` por código otimizado com base na sua configuração.
|
|
176
|
+
|
|
177
|
+
Isso garante que:
|
|
178
|
+
|
|
179
|
+
- Se um componente não for importado, seu conteúdo não será incluído no bundle (Dead Code Elimination).
|
|
180
|
+
- Se um componente for carregado via lazy-loading, seu conteúdo também será carregado dessa forma.
|
|
181
|
+
|
|
182
|
+
## Configuração por Plataforma
|
|
183
|
+
|
|
184
|
+
<Tabs>
|
|
185
|
+
<Tab value="nextjs">
|
|
186
|
+
|
|
187
|
+
### Next.js
|
|
188
|
+
|
|
189
|
+
O Next.js requer o plugin `@intlayer/swc` para lidar com a transformação, já que o Next.js usa SWC para builds.
|
|
190
|
+
|
|
191
|
+
> Este plugin não é instalado por padrão porque os plugins SWC ainda são experimentais para o Next.js. Isso pode mudar no futuro.
|
|
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
|
+
Uma vez instalado, o Intlayer detectará e usará automaticamente o plugin.
|
|
210
|
+
|
|
211
|
+
</Tab>
|
|
212
|
+
<Tab value="vite">
|
|
213
|
+
|
|
214
|
+
### Vite
|
|
215
|
+
|
|
216
|
+
O Vite usa o plugin `@intlayer/babel`, que está incluído como dependência do `vite-intlayer`. A otimização é habilitada por padrão. Nada mais precisa ser feito.
|
|
217
|
+
|
|
218
|
+
</Tab>
|
|
219
|
+
<Tab value="webpack">
|
|
220
|
+
|
|
221
|
+
### Webpack
|
|
222
|
+
|
|
223
|
+
Para habilitar a otimização de bundle com Intlayer no Webpack, você precisa instalar e configurar o plugin Babel (`@intlayer/babel`) ou SWC (`@intlayer/swc`) apropriado.
|
|
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
|
+
## Configuração
|
|
256
|
+
|
|
257
|
+
Você pode controlar como o Intlayer otimiza seu bundle através da propriedade `build` no seu `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
|
+
* Minificar os dicionários para reduzir o tamanho do bundle.
|
|
273
|
+
*/
|
|
274
|
+
minify: true;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Remover (purge) as chaves não utilizadas nos dicionários
|
|
278
|
+
*/
|
|
279
|
+
purge: true;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Indica se o build deve verificar tipos TypeScript
|
|
283
|
+
*/
|
|
284
|
+
checkTypes: false;
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export default config;
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
> Manter a opção padrão para `optimize` é recomendado na grande maioria dos casos.
|
|
292
|
+
|
|
293
|
+
> Veja a doc de configuração para mais detalhes: [Configuração](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/configuration.md)
|
|
294
|
+
|
|
295
|
+
### Opções de Build
|
|
296
|
+
|
|
297
|
+
As seguintes opções estão disponíveis sob o objeto de configuração `build`:
|
|
298
|
+
|
|
299
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
300
|
+
| :------------- | :-------- | :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
301
|
+
| **`optimize`** | `boolean` | `undefined` | Controla se a otimização de build está habilitada. Se `true`, o Intlayer substitui as chamadas de dicionário por injeções otimizadas. Se `false`, a otimização é desabilitada. Idealmente definido como `true` em prod. |
|
|
302
|
+
| **`minify`** | `boolean` | `false` | Se deve minificar os dicionários para reduzir o tamanho do bundle. |
|
|
303
|
+
| **`purge`** | `boolean` | `false` | Se deve remover as chaves não utilizadas nos dicionários. |
|
|
304
|
+
|
|
305
|
+
### Minificação
|
|
306
|
+
|
|
307
|
+
Minificar dicionários remove espaços em branco desnecessários, comentários e reduz o tamanho do conteúdo JSON. Isso é especialmente útil para dicionários grandes.
|
|
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
|
+
> Nota: A minificação é ignorada se `optimize` estiver desabilitado ou se o Visual Editor estiver habilitado (pois o editor precisa do conteúdo completo para permitir a edição).
|
|
322
|
+
|
|
323
|
+
### Purging (Remoção)
|
|
324
|
+
|
|
325
|
+
O purging garante que apenas as chaves realmente usadas no seu código sejam incluídas no bundle final de dicionários. Isso pode reduzir significativamente o tamanho do seu bundle se você tiver dicionários grandes com muitas chaves que não são usadas em todas as partes da sua aplicação.
|
|
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
|
+
> Nota: O purging é ignorado se `optimize` estiver desabilitado.
|
|
340
|
+
|
|
341
|
+
### Modo de Importação
|
|
342
|
+
|
|
343
|
+
Para grandes aplicações, incluindo várias páginas e locais, seus arquivos JSON podem representar uma parte significativa do tamanho do seu bundle. O Intlayer permite que você controle como os dicionários são carregados.
|
|
344
|
+
|
|
345
|
+
O modo de importação pode ser definido por padrão globalmente no seu arquivo `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
|
+
Assim como para cada dicionário nos seus arquivos `.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", // Sobrescrever o modo de importação padrão
|
|
367
|
+
content: {
|
|
368
|
+
// ...
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export default appContent;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
376
|
+
| :--------------- | :--------------------------------- | :--------- | :-------------------------------------------------------------------------------------------------------------------------- |
|
|
377
|
+
| **`importMode`** | `'static'`, `'dynamic'`, `'fetch'` | `'static'` | **Obsoleto**: Use `dictionary.importMode` em seu lugar. Determina como os dicionários são carregados (ver detalhes abaixo). |
|
|
378
|
+
|
|
379
|
+
A configuração `importMode` dita como o conteúdo do dicionário é injetado no seu componente.
|
|
380
|
+
Você pode defini-lo globalmente no arquivo `intlayer.config.ts` sob o objeto `dictionary`, ou pode sobrescrevê-lo para um dicionário específico em seu arquivo `.content.ts`.
|
|
381
|
+
|
|
382
|
+
### 1. Modo Estático (`default`)
|
|
383
|
+
|
|
384
|
+
No modo estático, o Intlayer substitui `useIntlayer` por `useDictionary` e injeta o dicionário diretamente no bundle JavaScript.
|
|
385
|
+
|
|
386
|
+
- **Prós:** Renderização instantânea (síncrona), zero solicitações de rede extras durante a hidratação.
|
|
387
|
+
- **Contras:** O bundle inclui traduções para **todos** os idiomas disponíveis para aquele componente específico.
|
|
388
|
+
- **Ideal para:** Single Page Applications (SPA).
|
|
389
|
+
|
|
390
|
+
**Exemplo de código transformado:**
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
// Seu código
|
|
394
|
+
const content = useIntlayer("my-key");
|
|
395
|
+
|
|
396
|
+
// Código otimizado (Estático)
|
|
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. Modo Dinâmico
|
|
410
|
+
|
|
411
|
+
No modo dinâmico, o Intlayer substitui `useIntlayer` por `useDictionaryAsync`. Isso usa `import()` (mecanismo semelhante ao Suspense) para carregar de forma assíncrona (lazy-load) especificamente o JSON para o local atual.
|
|
412
|
+
|
|
413
|
+
- **Prós:** **Tree shaking a nível de local.** Um usuário visualizando a versão em inglês baixará _apenas_ o dicionário em inglês. O dicionário em francês nunca é carregado.
|
|
414
|
+
- **Contras:** Dispara uma solicitação de rede (busca de asset) por componente durante a hidratação.
|
|
415
|
+
- **Ideal para:** Grandes blocos de texto, artigos ou aplicações que suportam muitos idiomas onde o tamanho do bundle é crítico.
|
|
416
|
+
|
|
417
|
+
**Exemplo de código transformado:**
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
// Seu código
|
|
421
|
+
const content = useIntlayer("my-key");
|
|
422
|
+
|
|
423
|
+
// Código otimizado (Dinâmico)
|
|
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
|
+
> Ao usar `importMode: 'dynamic'`, se você tiver 100 componentes usando `useIntlayer` em uma única página, o navegador tentará 100 buscas separadas. Para evitar essa "cascata" de solicitações, agrupe o conteúdo em menos arquivos `.content` (ex: um dicionário por seção da página) em vez de um por componente átomo.
|
|
437
|
+
|
|
438
|
+
### 3. Modo Fetch
|
|
439
|
+
|
|
440
|
+
Comporta-se de forma semelhante ao modo Dinâmico, mas tenta buscar dicionários da API Intlayer Live Sync primeiro. Se a chamada da API falhar ou o conteúdo não estiver marcado para atualizações ao vivo, ele recorre à importação dinâmica.
|
|
441
|
+
|
|
442
|
+
> Consulte a documentação do CMS para mais detalhes: [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/intlayer_CMS.md)
|
|
443
|
+
|
|
444
|
+
> No modo fetch, purge e minificação não podem ser usados.
|
|
445
|
+
|
|
446
|
+
## Resumo: Estático vs Dinâmico
|
|
447
|
+
|
|
448
|
+
| Recurso | Modo Estático | Modo Dinâmico |
|
|
449
|
+
| :-------------------- | :------------------------------------------------ | :-------------------------------------- |
|
|
450
|
+
| **Tamanho Bundle JS** | Maior (inclui todos os idiomas para o componente) | Menor (apenas código, sem conteúdo) |
|
|
451
|
+
| **Carga Inicial** | Instantânea (O conteúdo está no bundle) | Pequeno atraso (Busca o JSON) |
|
|
452
|
+
| **Solicitações Rede** | 0 solicitações extras | 1 solicitação por dicionário |
|
|
453
|
+
| **Tree Shaking** | Nível de componente | Nível de componente + Nível de local |
|
|
454
|
+
| **Melhor Caso Uso** | Componentes de UI, pequenas aplicações | Páginas com muito texto, muitos idiomas |
|
|
@@ -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),
|
|
@@ -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>
|
|
@@ -327,30 +325,20 @@ import { getPrefix } from "intlayer";
|
|
|
327
325
|
|
|
328
326
|
export const LOCALE_ROUTE = "{-$locale}" as const;
|
|
329
327
|
|
|
330
|
-
|
|
331
|
-
export type RemoveLocaleParam<T> = T extends string
|
|
332
|
-
? RemoveLocaleFromString<T>
|
|
333
|
-
: T;
|
|
328
|
+
export type To = StripLocalePrefix<LinkComponentProps["to"]>;
|
|
334
329
|
|
|
335
|
-
export type
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
330
|
+
export type StripLocalePrefix<T extends string | undefined> = T extends
|
|
331
|
+
| `/${typeof LOCALE_ROUTE}/`
|
|
332
|
+
| `/${typeof LOCALE_ROUTE}`
|
|
333
|
+
? "/"
|
|
334
|
+
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
335
|
+
? `/${Rest}`
|
|
336
|
+
: T;
|
|
339
337
|
|
|
340
338
|
type LocalizedLinkProps = {
|
|
341
339
|
to?: To;
|
|
342
340
|
} & Omit<LinkComponentProps, "to">;
|
|
343
341
|
|
|
344
|
-
// Auxiliares
|
|
345
|
-
type RemoveAll<
|
|
346
|
-
S extends string,
|
|
347
|
-
Sub extends string,
|
|
348
|
-
> = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
|
|
349
|
-
|
|
350
|
-
type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
|
|
351
|
-
RemoveAll<S, typeof LOCALE_ROUTE>
|
|
352
|
-
>;
|
|
353
|
-
|
|
354
342
|
export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
355
343
|
const { locale } = useLocale();
|
|
356
344
|
const { localePrefix } = getPrefix(locale);
|
|
@@ -379,26 +367,26 @@ Então, podemos criar um hook `useLocalizedNavigate` para navegação programát
|
|
|
379
367
|
import { useNavigate } from "@tanstack/react-router";
|
|
380
368
|
import { getPrefix } from "intlayer";
|
|
381
369
|
import { useLocale } from "react-intlayer";
|
|
382
|
-
import {
|
|
370
|
+
import type { StripLocalePrefix } from "@/components/localized-link";
|
|
383
371
|
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
384
372
|
|
|
385
|
-
type
|
|
386
|
-
|
|
387
|
-
| `/${typeof LOCALE_ROUTE}/`
|
|
388
|
-
? "/"
|
|
389
|
-
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
390
|
-
? `/${Rest}`
|
|
391
|
-
: never;
|
|
373
|
+
type NavigateFn = ReturnType<typeof useNavigate>;
|
|
374
|
+
type BaseNavigateOptions = Parameters<NavigateFn>[0];
|
|
392
375
|
|
|
393
376
|
type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
|
|
394
377
|
|
|
395
|
-
type
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
378
|
+
export type LocalizedNavigateOptions = Omit<
|
|
379
|
+
BaseNavigateOptions,
|
|
380
|
+
"to" | "params"
|
|
381
|
+
> & {
|
|
382
|
+
to: LocalizedTo;
|
|
383
|
+
params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
|
|
400
384
|
};
|
|
401
385
|
|
|
386
|
+
type LocalizedNavigate = (
|
|
387
|
+
options: LocalizedNavigateOptions
|
|
388
|
+
) => ReturnType<NavigateFn>;
|
|
389
|
+
|
|
402
390
|
export const useLocalizedNavigate = () => {
|
|
403
391
|
const navigate = useNavigate();
|
|
404
392
|
|
|
@@ -445,38 +433,6 @@ import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
|
|
|
445
433
|
|
|
446
434
|
export const Route = createFileRoute("/{-$locale}/")({
|
|
447
435
|
component: RouteComponent,
|
|
448
|
-
head: ({ params }) => {
|
|
449
|
-
const { locale } = params;
|
|
450
|
-
const path = "/"; // The path for this route
|
|
451
|
-
|
|
452
|
-
const metaContent = getIntlayer("app", locale);
|
|
453
|
-
|
|
454
|
-
return {
|
|
455
|
-
links: [
|
|
456
|
-
// Canonical link: Points to the current localized page
|
|
457
|
-
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
458
|
-
|
|
459
|
-
// Hreflang: Tell Google about all localized versions
|
|
460
|
-
...localeMap(({ locale: mapLocale }) => ({
|
|
461
|
-
rel: "alternate",
|
|
462
|
-
hrefLang: mapLocale,
|
|
463
|
-
href: getLocalizedUrl(path, mapLocale),
|
|
464
|
-
})),
|
|
465
|
-
|
|
466
|
-
// x-default: For users in unmatched languages
|
|
467
|
-
// Define the default fallback locale (usually your primary language)
|
|
468
|
-
{
|
|
469
|
-
rel: "alternate",
|
|
470
|
-
hrefLang: "x-default",
|
|
471
|
-
href: getLocalizedUrl(path, defaultLocale),
|
|
472
|
-
},
|
|
473
|
-
],
|
|
474
|
-
meta: [
|
|
475
|
-
{ title: metaContent.title },
|
|
476
|
-
{ name: "description", content: metaContent.meta.description },
|
|
477
|
-
],
|
|
478
|
-
};
|
|
479
|
-
},
|
|
480
436
|
});
|
|
481
437
|
|
|
482
438
|
function RouteComponent() {
|
|
@@ -631,12 +587,33 @@ export const Route = createFileRoute("/{-$locale}/")({
|
|
|
631
587
|
component: RouteComponent,
|
|
632
588
|
head: ({ params }) => {
|
|
633
589
|
const { locale } = params;
|
|
634
|
-
const
|
|
590
|
+
const path = "/"; // The path for this route
|
|
591
|
+
|
|
592
|
+
const metaContent = getIntlayer("app", locale);
|
|
635
593
|
|
|
636
594
|
return {
|
|
595
|
+
links: [
|
|
596
|
+
// Canonical link: Points to the current localized page
|
|
597
|
+
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
598
|
+
|
|
599
|
+
// Hreflang: Tell Google about all localized versions
|
|
600
|
+
...localeMap(({ locale: mapLocale }) => ({
|
|
601
|
+
rel: "alternate",
|
|
602
|
+
hrefLang: mapLocale,
|
|
603
|
+
href: getLocalizedUrl(path, mapLocale),
|
|
604
|
+
})),
|
|
605
|
+
|
|
606
|
+
// x-default: For users in unmatched languages
|
|
607
|
+
// Define the default fallback locale (usually your primary language)
|
|
608
|
+
{
|
|
609
|
+
rel: "alternate",
|
|
610
|
+
hrefLang: "x-default",
|
|
611
|
+
href: getLocalizedUrl(path, defaultLocale),
|
|
612
|
+
},
|
|
613
|
+
],
|
|
637
614
|
meta: [
|
|
638
615
|
{ title: metaContent.title },
|
|
639
|
-
{
|
|
616
|
+
{ name: "description", content: metaContent.meta.description },
|
|
640
617
|
],
|
|
641
618
|
};
|
|
642
619
|
},
|