@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: Optimización del tamaño y rendimiento del bundle i18n
|
|
5
|
+
description: Reduzca el tamaño del bundle de su aplicación optimizando el contenido de internacionalización (i18n). Aprenda a aprovechar el tree shaking y la carga diferida para los diccionarios con Intlayer.
|
|
6
|
+
keywords:
|
|
7
|
+
- Optimización de bundle
|
|
8
|
+
- Automatización de contenido
|
|
9
|
+
- Contenido 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: "Se añadieron las opciones `minify` y `purge` a la configuración de construcción"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Optimización del tamaño y rendimiento del bundle i18n
|
|
25
|
+
|
|
26
|
+
Uno de los desafíos más comunes con las soluciones i18n tradicionales que dependen de archivos JSON es la gestión del tamaño del contenido. Si los desarrolladores no separan manualmente el contenido en namespaces, los usuarios a menudo terminan descargando traducciones para cada página y potencialmente para cada idioma solo para ver una sola página.
|
|
27
|
+
|
|
28
|
+
Por ejemplo, una aplicación con 10 páginas traducidas a 10 idiomas podría resultar en que un usuario descargue el contenido de 100 páginas, aunque solo necesite **una** (la página actual en el idioma actual). Esto conduce a un desperdicio de ancho de banda y tiempos de carga más lentos.
|
|
29
|
+
|
|
30
|
+
**Intlayer resuelve este problema a través de la optimización en tiempo de compilación.** Analiza su código para detectar qué diccionarios se usan realmente por componente y reinyecta solo el contenido necesario en su bundle.
|
|
31
|
+
|
|
32
|
+
## Tabla de contenidos
|
|
33
|
+
|
|
34
|
+
<TOC />
|
|
35
|
+
|
|
36
|
+
## Escanee su bundle
|
|
37
|
+
|
|
38
|
+
Analizar su bundle es el primer paso para identificar archivos JSON "pesados" y oportunidades de división de código (code-splitting). Estas herramientas generan un mapa de árbol visual del código compilado de su aplicación, lo que le permite ver exactamente qué librerías están consumiendo más espacio.
|
|
39
|
+
|
|
40
|
+
<Tabs>
|
|
41
|
+
<Tab value="vite">
|
|
42
|
+
|
|
43
|
+
### Vite / Rollup
|
|
44
|
+
|
|
45
|
+
Vite utiliza Rollup internamente. El complemento `rollup-plugin-visualizer` genera un archivo HTML interactivo que muestra el tamaño de cada módulo en su 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 automáticamente el informe en su 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 proyectos que utilizan App Router y Turbopack, Next.js proporciona un analizador experimental integrado que no requiere dependencias adicionales.
|
|
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
|
+
Si está utilizando el empaquetador Webpack predeterminado en Next.js, use el analizador de bundle oficial. Actívelo configurando una variable de entorno durante su construcción.
|
|
96
|
+
|
|
97
|
+
```bash packageManager='npm'
|
|
98
|
+
npm install -D @next/bundle-analyzer
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```bash packageManager='yarn'
|
|
102
|
+
yarn add -D @next/bundle-analyzer
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```bash packageManager='pnpm'
|
|
106
|
+
pnpm add -D @next/bundle-analyzer
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```bash packageManager='bun'
|
|
110
|
+
bun add -d @next/bundle-analyzer
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```javascript fileName="next.config.js"
|
|
114
|
+
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
|
115
|
+
enabled: process.env.ANALYZE === "true",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
module.exports = withBundleAnalyzer({
|
|
119
|
+
// Su configuración de 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 estándar
|
|
133
|
+
|
|
134
|
+
Para Create React App (ejected), Angular o configuraciones personalizadas de Webpack, use el estándar de la industria `webpack-bundle-analyzer`.
|
|
135
|
+
|
|
136
|
+
```bash packageManager='npm'
|
|
137
|
+
npm install -D webpack-bundle-analyzer
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```bash packageManager='yarn'
|
|
141
|
+
yarn add -D webpack-bundle-analyzer
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```bash packageManager='pnpm'
|
|
145
|
+
pnpm add -D webpack-bundle-analyzer
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```bash packageManager='bun'
|
|
149
|
+
bun add -d webpack-bundle-analyzer
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```typescript fileName="webpack.config.ts
|
|
153
|
+
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
|
154
|
+
|
|
155
|
+
export default {
|
|
156
|
+
plugins: [
|
|
157
|
+
new BundleAnalyzerPlugin({
|
|
158
|
+
analyzerMode: "static",
|
|
159
|
+
reportFilename: "bundle-analyzer.html",
|
|
160
|
+
openAnalyzer: false,
|
|
161
|
+
}),
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
</Tab>
|
|
167
|
+
</Tabs>
|
|
168
|
+
|
|
169
|
+
## Cómo funciona
|
|
170
|
+
|
|
171
|
+
Intlayer utiliza un **enfoque por componente**. A diferencia de los archivos JSON globales, su contenido se define junto o dentro de sus componentes. Durante el proceso de construcción, Intlayer:
|
|
172
|
+
|
|
173
|
+
1. **Analiza** su código para encontrar llamadas a `useIntlayer`.
|
|
174
|
+
2. **Construye** el contenido del diccionario correspondiente.
|
|
175
|
+
3. **Reemplaza** la llamada a `useIntlayer` con código optimizado basado en su configuración.
|
|
176
|
+
|
|
177
|
+
Esto garantiza que:
|
|
178
|
+
|
|
179
|
+
- Si un componente no se importa, su contenido no se incluye en el bundle (eliminación de código muerto).
|
|
180
|
+
- Si un componente se carga de forma diferida (lazy-loading), su contenido también se carga de forma diferida.
|
|
181
|
+
|
|
182
|
+
## Configuración por plataforma
|
|
183
|
+
|
|
184
|
+
<Tabs>
|
|
185
|
+
<Tab value="nextjs">
|
|
186
|
+
|
|
187
|
+
### Next.js
|
|
188
|
+
|
|
189
|
+
Next.js requiere el complemento `@intlayer/swc` para manejar la transformación, ya que Next.js utiliza SWC para las construcciones.
|
|
190
|
+
|
|
191
|
+
> Este complemento no se instala de forma predeterminada porque los complementos de SWC todavía son experimentales para Next.js. Podría cambiar en el 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
|
+
Una vez instalado, Intlayer detectará y utilizará automáticamente el complemento.
|
|
210
|
+
|
|
211
|
+
</Tab>
|
|
212
|
+
<Tab value="vite">
|
|
213
|
+
|
|
214
|
+
### Vite
|
|
215
|
+
|
|
216
|
+
Vite utiliza el complemento `@intlayer/babel`, que se incluye como dependencia de `vite-intlayer`. La optimización está habilitada de forma predeterminada. No hay nada más que hacer.
|
|
217
|
+
|
|
218
|
+
</Tab>
|
|
219
|
+
<Tab value="webpack">
|
|
220
|
+
|
|
221
|
+
### Webpack
|
|
222
|
+
|
|
223
|
+
Para habilitar la optimización del bundle con Intlayer en Webpack, debe instalar y configurar el complemento Babel (`@intlayer/babel`) o SWC (`@intlayer/swc`) adecuado.
|
|
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
|
+
## Configuración
|
|
256
|
+
|
|
257
|
+
Puede controlar cómo Intlayer optimiza su bundle a través de la propiedad `build` en su `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 los diccionarios para reducir el tamaño del bundle.
|
|
273
|
+
*/
|
|
274
|
+
minify: true;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Purgar las claves no utilizadas en los diccionarios
|
|
278
|
+
*/
|
|
279
|
+
purge: true;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Indica si la construcción debe verificar los tipos de TypeScript
|
|
283
|
+
*/
|
|
284
|
+
checkTypes: false;
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export default config;
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
> Se recomienda mantener la opción predeterminada para `optimize` en la gran mayoría de los casos.
|
|
292
|
+
|
|
293
|
+
> Consulte la documentación de configuración para más detalles: [Configuración](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/configuration.md)
|
|
294
|
+
|
|
295
|
+
### Opciones de construcción
|
|
296
|
+
|
|
297
|
+
Las siguientes opciones están disponibles bajo el objeto de configuración `build`:
|
|
298
|
+
|
|
299
|
+
| Propiedad | Tipo | Predeterminado | Descripción |
|
|
300
|
+
| :------------- | :-------- | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
301
|
+
| **`optimize`** | `boolean` | `undefined` | Controla si la optimización de la construcción está habilitada. Si es `true`, Intlayer reemplaza las llamadas a diccionarios con inyecciones optimizadas. Si es `false`, la optimización se deshabilita. Ideal en prod. |
|
|
302
|
+
| **`minify`** | `boolean` | `false` | Si se deben minificar los diccionarios para reducir el tamaño del bundle. |
|
|
303
|
+
| **`purge`** | `boolean` | `false` | Si se deben purgar las claves no utilizadas en los diccionarios. |
|
|
304
|
+
|
|
305
|
+
### Minificación
|
|
306
|
+
|
|
307
|
+
Minificar diccionarios elimina espacios en blanco innecesarios, comentarios y reduce el tamaño del contenido JSON. Esto es especialmente útil para diccionarios 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: La minificación se ignora si `optimize` está deshabilitado o si el Editor Visual está habilitado (ya que el editor necesita el contenido completo para permitir la edición).
|
|
322
|
+
|
|
323
|
+
### Purgado
|
|
324
|
+
|
|
325
|
+
El purgado garantiza que solo las claves realmente utilizadas en su código se incluyan en el bundle final de diccionarios. Esto puede reducir significativamente el tamaño de su bundle si tiene diccionarios grandes con muchas claves que no se usan en todas las partes de su aplicación.
|
|
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: El purgado se ignora si `optimize` está deshabilitado.
|
|
340
|
+
|
|
341
|
+
### Modo de importación
|
|
342
|
+
|
|
343
|
+
Para aplicaciones grandes, que incluyen varias páginas y configuraciones regionales, sus archivos JSON pueden representar una parte significativa del tamaño de su bundle. Intlayer le permite controlar cómo se cargan los diccionarios.
|
|
344
|
+
|
|
345
|
+
El modo de importación puede definirse por defecto globalmente en su archivo `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
|
+
Así como para cada diccionario en sus archivos `.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", // Sobrescribir el modo de importación predeterminado
|
|
367
|
+
content: {
|
|
368
|
+
// ...
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export default appContent;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
| Propiedad | Tipo | Predeterminado | Descripción |
|
|
376
|
+
| :--------------- | :--------------------------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------ |
|
|
377
|
+
| **`importMode`** | `'static'`, `'dynamic'`, `'fetch'` | `'static'` | **Obsoleto**: Use `dictionary.importMode` en su lugar. Determina cómo se cargan los diccionarios (ver detalles a continuación). |
|
|
378
|
+
|
|
379
|
+
La configuración `importMode` dicta cómo se inyecta el contenido del diccionario en su componente.
|
|
380
|
+
Puede definirlo globalmente en el archivo `intlayer.config.ts` bajo el objeto `dictionary`, o puede sobrescribirlo para un diccionario específico en su archivo `.content.ts`.
|
|
381
|
+
|
|
382
|
+
### 1. Modo estático (`default`)
|
|
383
|
+
|
|
384
|
+
En modo estático, Intlayer reemplaza `useIntlayer` con `useDictionary` e inyecta el diccionario directamente en el bundle de JavaScript.
|
|
385
|
+
|
|
386
|
+
- **Pros:** Renderizado instantáneo (síncrono), cero solicitudes de red adicionales durante la hidratación.
|
|
387
|
+
- **Contras:** El bundle incluye traducciones para **todos** los idiomas disponibles para ese componente específico.
|
|
388
|
+
- **Ideal para:** Aplicaciones de una sola página (SPA).
|
|
389
|
+
|
|
390
|
+
**Ejemplo de código transformado:**
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
// Su código
|
|
394
|
+
const content = useIntlayer("my-key");
|
|
395
|
+
|
|
396
|
+
// Código optimizado (Estatico)
|
|
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
|
+
En modo dinámico, Intlayer reemplaza `useIntlayer` con `useDictionaryAsync`. Esto utiliza `import()` (mecanismo similar a Suspense) para cargar de forma diferida específicamente el JSON para la configuración regional actual.
|
|
412
|
+
|
|
413
|
+
- **Pros:** **Tree shaking a nivel de locale.** Un usuario que vea la versión en inglés _solo_ descargará el diccionario en inglés. El diccionario en francés nunca se carga.
|
|
414
|
+
- **Contras:** Activa una solicitud de red (obtención de activos) por componente durante la hidratación.
|
|
415
|
+
- **Ideal para:** Bloques de texto grandes, artículos o aplicaciones que admiten muchos idiomas donde el tamaño del bundle es crítico.
|
|
416
|
+
|
|
417
|
+
**Ejemplo de código transformado:**
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
// Su código
|
|
421
|
+
const content = useIntlayer("my-key");
|
|
422
|
+
|
|
423
|
+
// Código optimizado (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
|
+
> Al usar `importMode: 'dynamic'`, si tiene 100 componentes que usan `useIntlayer` en una sola página, el navegador intentará 100 descargas separadas. Para evitar esta "cascada" de solicitudes, agrupe el contenido en menos archivos `.content` (por ejemplo, un diccionario por sección de la página) en lugar de uno por componente átomo.
|
|
437
|
+
|
|
438
|
+
### 3. Modo Fetch
|
|
439
|
+
|
|
440
|
+
Se comporta de manera similar al modo dinámico, pero intenta obtener diccionarios de la API de Live Sync de Intlayer primero. Si la llamada a la API falla o el contenido no está marcado para actualizaciones en vivo, recurre a la importación dinámica.
|
|
441
|
+
|
|
442
|
+
> Consulte la documentación del CMS para más detalles: [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/intlayer_CMS.md)
|
|
443
|
+
|
|
444
|
+
> En modo fetch, no se pueden usar el purgado y la minificación.
|
|
445
|
+
|
|
446
|
+
## Resumen: Estático vs Dinámico
|
|
447
|
+
|
|
448
|
+
| Característica | Modo estático | Modo dinámico |
|
|
449
|
+
| :----------------------- | :-------------------------------------------------------- | :---------------------------------------- |
|
|
450
|
+
| **Tamaño del bundle JS** | Más grande (incluye todos los idiomas para el comp.) | Más pequeño (solo código, sin contenido) |
|
|
451
|
+
| **Carga inicial** | Instantánea (El contenido está en el bundle) | Ligero retraso (Obtiene el JSON) |
|
|
452
|
+
| **Solicitudes de red** | 0 solicitudes adicionales | 1 solicitud por diccionario |
|
|
453
|
+
| **Tree Shaking** | A nivel de componente | A nivel de componente + A nivel de locale |
|
|
454
|
+
| **Mejor caso de uso** | Componentes de interfaz de usuario, aplicaciones pequeñas | Páginas con mucho texto, muchos idiomas |
|
|
@@ -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
|
-
// Ayudantes
|
|
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 @@ Luego podemos crear un hook `useLocalizedNavigate` para la navegación programá
|
|
|
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
|
},
|