@intlayer/docs 8.9.4 → 8.9.6-canary.0
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/docs/ar/benchmark/index.md +0 -3
- package/docs/ar/benchmark/nextjs.md +15 -6
- package/docs/ar/benchmark/solid.md +155 -0
- package/docs/ar/benchmark/svelte.md +148 -0
- package/docs/ar/benchmark/tanstack.md +12 -3
- package/docs/ar/benchmark/vue.md +160 -0
- package/docs/ar/configuration.md +16 -12
- package/docs/ar/dictionary/content_file.md +51 -1
- package/docs/ar/plugins/sync-po.md +0 -21
- package/docs/bn/configuration.md +16 -12
- package/docs/cs/configuration.md +16 -12
- package/docs/de/benchmark/index.md +0 -3
- package/docs/de/benchmark/nextjs.md +15 -6
- package/docs/de/benchmark/solid.md +155 -0
- package/docs/de/benchmark/svelte.md +148 -0
- package/docs/de/benchmark/tanstack.md +12 -3
- package/docs/de/benchmark/vue.md +160 -0
- package/docs/de/configuration.md +16 -12
- package/docs/de/dictionary/content_file.md +52 -2
- package/docs/de/plugins/sync-po.md +0 -22
- package/docs/en/benchmark/nextjs.md +11 -2
- package/docs/en/benchmark/solid.md +22 -4
- package/docs/en/benchmark/svelte.md +17 -5
- package/docs/en/benchmark/tanstack.md +18 -3
- package/docs/en/benchmark/vue.md +17 -11
- package/docs/en/configuration.md +16 -13
- package/docs/en/dictionary/content_file.md +51 -1
- package/docs/en/plugins/sync-po.md +0 -21
- package/docs/en-GB/benchmark/index.md +0 -3
- package/docs/en-GB/benchmark/nextjs.md +15 -6
- package/docs/en-GB/benchmark/solid.md +155 -0
- package/docs/en-GB/benchmark/svelte.md +148 -0
- package/docs/en-GB/benchmark/tanstack.md +12 -3
- package/docs/en-GB/benchmark/vue.md +160 -0
- package/docs/en-GB/configuration.md +15 -11
- package/docs/en-GB/dictionary/content_file.md +51 -1
- package/docs/en-GB/plugins/sync-po.md +0 -21
- package/docs/es/benchmark/index.md +0 -3
- package/docs/es/benchmark/nextjs.md +15 -6
- package/docs/es/benchmark/solid.md +155 -0
- package/docs/es/benchmark/svelte.md +148 -0
- package/docs/es/benchmark/tanstack.md +12 -3
- package/docs/es/benchmark/vue.md +160 -0
- package/docs/es/configuration.md +16 -12
- package/docs/es/dictionary/content_file.md +51 -1
- package/docs/es/plugins/sync-po.md +0 -21
- package/docs/fr/benchmark/index.md +0 -3
- package/docs/fr/benchmark/nextjs.md +15 -6
- package/docs/fr/benchmark/solid.md +155 -0
- package/docs/fr/benchmark/svelte.md +148 -0
- package/docs/fr/benchmark/tanstack.md +12 -3
- package/docs/fr/benchmark/vue.md +160 -0
- package/docs/fr/configuration.md +16 -12
- package/docs/fr/dictionary/content_file.md +51 -1
- package/docs/fr/plugins/sync-po.md +0 -21
- package/docs/hi/benchmark/nextjs.md +15 -6
- package/docs/hi/benchmark/solid.md +155 -0
- package/docs/hi/benchmark/svelte.md +148 -0
- package/docs/hi/benchmark/tanstack.md +12 -3
- package/docs/hi/benchmark/vue.md +160 -0
- package/docs/hi/configuration.md +16 -12
- package/docs/hi/dictionary/content_file.md +51 -1
- package/docs/hi/plugins/sync-po.md +0 -21
- package/docs/id/benchmark/index.md +0 -3
- package/docs/id/benchmark/nextjs.md +15 -6
- package/docs/id/benchmark/solid.md +155 -0
- package/docs/id/benchmark/svelte.md +148 -0
- package/docs/id/benchmark/tanstack.md +12 -3
- package/docs/id/benchmark/vue.md +160 -0
- package/docs/id/configuration.md +16 -12
- package/docs/id/dictionary/content_file.md +51 -1
- package/docs/id/plugins/sync-po.md +0 -21
- package/docs/it/benchmark/index.md +1 -4
- package/docs/it/benchmark/nextjs.md +15 -6
- package/docs/it/benchmark/solid.md +155 -0
- package/docs/it/benchmark/svelte.md +148 -0
- package/docs/it/benchmark/tanstack.md +12 -3
- package/docs/it/benchmark/vue.md +160 -0
- package/docs/it/configuration.md +16 -12
- package/docs/it/dictionary/content_file.md +51 -1
- package/docs/it/plugins/sync-po.md +0 -21
- package/docs/ja/benchmark/index.md +5 -5
- package/docs/ja/benchmark/nextjs.md +15 -6
- package/docs/ja/benchmark/solid.md +155 -0
- package/docs/ja/benchmark/svelte.md +148 -0
- package/docs/ja/benchmark/tanstack.md +12 -3
- package/docs/ja/benchmark/vue.md +160 -0
- package/docs/ja/configuration.md +16 -12
- package/docs/ja/dictionary/content_file.md +50 -2
- package/docs/ja/intlayer_with_nextjs_no_locale_path.md +4 -3
- package/docs/ja/plugins/sync-po.md +0 -21
- package/docs/ko/benchmark/nextjs.md +15 -6
- package/docs/ko/benchmark/solid.md +155 -0
- package/docs/ko/benchmark/svelte.md +148 -0
- package/docs/ko/benchmark/tanstack.md +12 -3
- package/docs/ko/benchmark/vue.md +160 -0
- package/docs/ko/configuration.md +16 -12
- package/docs/ko/dictionary/content_file.md +51 -1
- package/docs/ko/intlayer_with_nextjs_no_locale_path.md +3 -2
- package/docs/ko/plugins/sync-po.md +0 -21
- package/docs/nl/configuration.md +16 -12
- package/docs/pl/benchmark/index.md +0 -3
- package/docs/pl/benchmark/nextjs.md +15 -6
- package/docs/pl/benchmark/solid.md +155 -0
- package/docs/pl/benchmark/svelte.md +148 -0
- package/docs/pl/benchmark/tanstack.md +12 -3
- package/docs/pl/benchmark/vue.md +160 -0
- package/docs/pl/configuration.md +16 -12
- package/docs/pl/dictionary/content_file.md +51 -1
- package/docs/pl/plugins/sync-po.md +0 -21
- package/docs/pt/benchmark/index.md +0 -3
- package/docs/pt/benchmark/nextjs.md +16 -7
- package/docs/pt/benchmark/solid.md +155 -0
- package/docs/pt/benchmark/svelte.md +148 -0
- package/docs/pt/benchmark/tanstack.md +13 -4
- package/docs/pt/benchmark/vue.md +160 -0
- package/docs/pt/configuration.md +16 -12
- package/docs/pt/dictionary/content_file.md +51 -1
- package/docs/pt/plugins/sync-po.md +0 -21
- package/docs/ru/benchmark/nextjs.md +15 -6
- package/docs/ru/benchmark/solid.md +155 -0
- package/docs/ru/benchmark/svelte.md +148 -0
- package/docs/ru/benchmark/tanstack.md +12 -3
- package/docs/ru/benchmark/vue.md +160 -0
- package/docs/ru/configuration.md +16 -12
- package/docs/ru/dictionary/content_file.md +52 -2
- package/docs/ru/plugins/sync-po.md +0 -21
- package/docs/tr/benchmark/index.md +0 -3
- package/docs/tr/benchmark/nextjs.md +15 -6
- package/docs/tr/benchmark/solid.md +155 -0
- package/docs/tr/benchmark/svelte.md +148 -0
- package/docs/tr/benchmark/tanstack.md +12 -3
- package/docs/tr/benchmark/vue.md +160 -0
- package/docs/tr/configuration.md +16 -12
- package/docs/tr/dictionary/content_file.md +51 -1
- package/docs/tr/plugins/sync-po.md +0 -21
- package/docs/uk/benchmark/nextjs.md +15 -6
- package/docs/uk/benchmark/solid.md +155 -0
- package/docs/uk/benchmark/svelte.md +148 -0
- package/docs/uk/benchmark/tanstack.md +12 -3
- package/docs/uk/benchmark/vue.md +160 -0
- package/docs/uk/configuration.md +16 -12
- package/docs/uk/dictionary/content_file.md +51 -1
- package/docs/uk/plugins/sync-po.md +0 -21
- package/docs/ur/configuration.md +16 -12
- package/docs/vi/benchmark/index.md +0 -3
- package/docs/vi/benchmark/nextjs.md +15 -6
- package/docs/vi/benchmark/solid.md +155 -0
- package/docs/vi/benchmark/svelte.md +148 -0
- package/docs/vi/benchmark/tanstack.md +12 -3
- package/docs/vi/benchmark/vue.md +160 -0
- package/docs/vi/configuration.md +16 -12
- package/docs/vi/dictionary/content_file.md +51 -1
- package/docs/vi/intlayer_with_nextjs_15.md +10 -57
- package/docs/vi/plugins/sync-po.md +0 -21
- package/docs/zh/benchmark/nextjs.md +15 -6
- package/docs/zh/benchmark/solid.md +155 -0
- package/docs/zh/benchmark/svelte.md +148 -0
- package/docs/zh/benchmark/tanstack.md +12 -3
- package/docs/zh/benchmark/vue.md +160 -0
- package/docs/zh/configuration.md +16 -12
- package/docs/zh/dictionary/content_file.md +51 -3
- package/docs/zh/plugins/sync-po.md +0 -21
- package/frequent_questions/ar/intlayerNode.md +3 -3
- package/frequent_questions/de/intlayerNode.md +3 -3
- package/frequent_questions/en/intlayerNode.md +3 -3
- package/frequent_questions/en-GB/intlayerNode.md +3 -3
- package/frequent_questions/es/intlayerNode.md +3 -3
- package/frequent_questions/fr/intlayerNode.md +3 -3
- package/frequent_questions/hi/intlayerNode.md +3 -3
- package/frequent_questions/id/intlayerNode.md +3 -3
- package/frequent_questions/it/intlayerNode.md +3 -3
- package/frequent_questions/ja/intlayerNode.md +3 -3
- package/frequent_questions/ko/intlayerNode.md +3 -3
- package/frequent_questions/pl/intlayerNode.md +3 -3
- package/frequent_questions/pt/intlayerNode.md +3 -3
- package/frequent_questions/ru/intlayerNode.md +3 -3
- package/frequent_questions/tr/intlayerNode.md +3 -3
- package/frequent_questions/uk/intlayerNode.md +3 -3
- package/frequent_questions/vi/intlayerNode.md +3 -3
- package/frequent_questions/zh/intlayerNode.md +3 -3
- package/package.json +8 -8
|
@@ -61,6 +61,13 @@ Poiché il problema è difficile, esistono molte soluzioni, alcune focalizzate s
|
|
|
61
61
|
|
|
62
62
|
Intlayer cerca di ottimizzare tutte queste dimensioni.
|
|
63
63
|
|
|
64
|
+
## TL;DR
|
|
65
|
+
|
|
66
|
+
- **Intlayer** e **next-translate**: Le migliori scelte per le prestazioni di Next.js, con l'impronta minima e il miglior supporto al rendering statico.
|
|
67
|
+
- **next-intl**: L'opzione più di tendenza, ma pesante e complessa da ottimizzare per grandi applicazioni.
|
|
68
|
+
- **next-i18next**: Popolare e ricca di plugin, ma comporta un peso del bundle significativo (~3× Intlayer).
|
|
69
|
+
- **Da evitare**: **gt-next** e **lingo.dev** a causa di gravi problemi di prestazioni, vendor lock-in ed errori che bloccano la build.
|
|
70
|
+
|
|
64
71
|
## Testa la tua app
|
|
65
72
|
|
|
66
73
|
Per far emergere questi problemi, ho creato uno scanner gratuito che puoi provare [qui](https://intlayer.org/i18n-seo-scanner).
|
|
@@ -99,14 +106,14 @@ Infine, `Intlayer` applica un'ottimizzazione build-time in modo che `useIntlayer
|
|
|
99
106
|
Per questo benchmark, abbiamo confrontato le seguenti librerie:
|
|
100
107
|
|
|
101
108
|
- `Base App` (Nessuna libreria i18n)
|
|
102
|
-
- `next-intlayer` (v8.7.
|
|
109
|
+
- `next-intlayer` (v8.7.12)
|
|
103
110
|
- `next-i18next` (v16.0.5)
|
|
104
111
|
- `next-intl` (v4.9.1)
|
|
105
112
|
- `@lingui/core` (v5.3.0)
|
|
106
113
|
- `next-translate` (v3.1.2)
|
|
107
114
|
- `next-international` (v1.3.1)
|
|
108
115
|
- `@inlang/paraglide-js` (v2.15.1)
|
|
109
|
-
-
|
|
116
|
+
- `@tolgee/react` (v7.0.0)
|
|
110
117
|
- `@lingo.dev/compiler` (v0.4.0)
|
|
111
118
|
- `wuchale` (v0.22.11)
|
|
112
119
|
- `gt-next` (v6.16.5)
|
|
@@ -161,10 +168,10 @@ Problemi riscontrati:
|
|
|
161
168
|
|
|
162
169
|
**(General Translation)** (`gt-next@6.16.5`):
|
|
163
170
|
|
|
164
|
-
- Per un'app da 110kb, `gt-
|
|
171
|
+
- Per un'app da 110kb, `gt-next` aggiunge più di 440kb extra.
|
|
165
172
|
- `Quota Exceeded, please upgrade your plan` al primissimo build con General Translation.
|
|
166
173
|
- Le traduzioni non vengono renderizzate; ottengo l'errore `Error: <T> used on the client-side outside of <GTProvider>`, che sembra essere un bug nella libreria.
|
|
167
|
-
- Durante l'implementazione di **gt-
|
|
174
|
+
- Durante l'implementazione di **gt-next**, ho riscontrato anche un [problema](https://github.com/generaltranslation/gt/issues/1210#event-24510646961) con la libreria: `does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser`, che faceva fallire l'applicazione. Dopo aver segnalato questo problema, il manutentore lo ha risolto entro 24 ore.
|
|
168
175
|
- La libreria blocca il rendering statico delle pagine Next.js.
|
|
169
176
|
|
|
170
177
|
**(Lingo.dev)** (`@lingo.dev/compiler@0.4.0`):
|
|
@@ -186,9 +193,11 @@ L'idea alla base di `Wuchale` è interessante ma non ancora praticabile. Ho risc
|
|
|
186
193
|
Personalmente non mi piace dover rigenerare file JS prima di ogni push, il che crea un rischio costante di conflitti di merge tramite PR. Lo strumento sembra inoltre più focalizzato su Vite che su Next.js.
|
|
187
194
|
Infine, rispetto ad altre soluzioni, Paraglide non usa uno store (es. contesto React) per recuperare la lingua corrente per renderizzare il contenuto. Per ogni nodo analizzato, richiederà la lingua da localStorage / cookie ecc. Ciò porta all'esecuzione di logica non necessaria che impatta sulla reattività del componente.
|
|
188
195
|
|
|
196
|
+
> Nota su paraglide: la soluzione inietta codice nella tua codebase per l'importazione, di conseguenza la metrica 'dimensione della lib' nel report benchmark è quasi 0. La generazione del codice è un aspetto positivo, poiché la funzione utilizzata includerà solo la lógica necessaria (prefisso totale vs nessun prefisso, cookie vs storage, ecc.). In confronto, Intlayer esegue questo filtraggio tramite iniezioni di variabili d'ambiente nella build per forzare il bundler a scartare i contenuti in base alla logica. Grazie a ciò, paraglide e intlayer risultano soluzioni da 6 a 10 volte più leggere rispetto a i18next o next-intl.
|
|
197
|
+
|
|
189
198
|
### 3 — Soluzioni accettabili
|
|
190
199
|
|
|
191
|
-
**(Tolgee)** (
|
|
200
|
+
**(Tolgee)** (`@tolgee/react@7.0.0`):
|
|
192
201
|
|
|
193
202
|
`Tolgee` affronta molti dei problemi menzionati in precedenza. L'ho trovato più difficile da adottare rispetto a strumenti simili. Non fornisce type safety, il che rende anche più difficile individuare le chiavi mancanti a compile time. Ho dovuto avvolgere le funzioni di Tolgee con le mie per aggiungere il rilevamento delle chiavi mancanti.
|
|
194
203
|
|
|
@@ -216,7 +225,7 @@ I formati dei messaggi differiscono inoltre: `next-intl` usa ICU MessageFormat,
|
|
|
216
225
|
|
|
217
226
|
`next-translate` è la mia raccomandazione principale se ti piace un'API in stile `t()`. È elegante grazie a `next-translate-plugin`, caricando i namespace attraverso `getStaticProps` con un caricatore Webpack / Turbopack. È anche l'opzione più leggera qui (~2.5kb). Per il namespacing, la definizione dei namespace per pagina o percorso nella config è ben pensata e più facile da mantenere rispetto alle principali alternative come **next-intl** o **next-i18next**. Nella versione `3.1.2`, ho notato che il rendering statico non funzionava; Next.js ripiegava sul rendering dinamico.
|
|
218
227
|
|
|
219
|
-
**(Intlayer)** (`next-intlayer@8.7.
|
|
228
|
+
**(Intlayer)** (`next-intlayer@8.7.12`):
|
|
220
229
|
|
|
221
230
|
Non giudicherò personalmente `next-intlayer` per motivi di obiettività, essendo la mia propria soluzione.
|
|
222
231
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-04-20
|
|
3
|
+
updatedAt: 2026-04-21
|
|
4
|
+
title: Migliore soluzione i18n per Solid nel 2026 - Rapporto Benchmark
|
|
5
|
+
description: Confronta le librerie di internazionalizzazione (i18n) per Solid come solid-primitives, solid-i18next e Intlayer. Rapporto dettagliato sulle prestazioni in termini di dimensioni del bundle, leak e reattività.
|
|
6
|
+
keywords:
|
|
7
|
+
- benchmark
|
|
8
|
+
- i18n
|
|
9
|
+
- intl
|
|
10
|
+
- solid
|
|
11
|
+
- prestazioni
|
|
12
|
+
- intlayer
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- benchmark
|
|
16
|
+
- solid
|
|
17
|
+
author: Aymeric PINEAU
|
|
18
|
+
applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-solid-template
|
|
19
|
+
history:
|
|
20
|
+
- version: 8.7.12
|
|
21
|
+
date: 2026-01-06
|
|
22
|
+
changes: "Inizializzazione del benchmark"
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Librerie i18n per Solid — Rapporto Benchmark 2026
|
|
26
|
+
|
|
27
|
+
Questa pagina è un rapporto di benchmark per le soluzioni i18n su Solid.
|
|
28
|
+
|
|
29
|
+
## Sommario
|
|
30
|
+
|
|
31
|
+
<Toc/>
|
|
32
|
+
|
|
33
|
+
## Benchmark Interattivo
|
|
34
|
+
|
|
35
|
+
<I18nBenchmark framework="vite-solid" vertical/>
|
|
36
|
+
|
|
37
|
+
## Riferimento dei risultati:
|
|
38
|
+
|
|
39
|
+
<iframe
|
|
40
|
+
src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_solid.md"
|
|
41
|
+
width="100%"
|
|
42
|
+
height="600px"
|
|
43
|
+
style="border:none;">
|
|
44
|
+
</iframe>
|
|
45
|
+
|
|
46
|
+
> https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_solid.md
|
|
47
|
+
|
|
48
|
+
Vedi il repository completo del benchmark [qui](https://github.com/intlayer-org/benchmark-i18n/tree/main).
|
|
49
|
+
|
|
50
|
+
## Introduzione
|
|
51
|
+
|
|
52
|
+
Le soluzioni di internazionalizzazione sono tra le dipendenze più pesanti in un'app Solid. Il rischio principale è l'invio di contenuti non necessari: traduzioni per altre pagine e altre lingue nel bundle di una singola rotta.
|
|
53
|
+
|
|
54
|
+
Man mano che l'app cresce, questo problema può far esplodere rapidamente il JavaScript inviato al client e rallentare la navigazione.
|
|
55
|
+
|
|
56
|
+
In pratica, per le implementazioni meno ottimizzate, una pagina internazionalizzata può finire per essere diverse volte più pesante della versione senza i18n.
|
|
57
|
+
|
|
58
|
+
L'autre impatto riguarda l'esperienza dello sviluppatore (DX): come si dichiara il contenuto, i tipi, l'organizzazione dei namespace, il caricamento dinamico e la reattività al cambio di lingua.
|
|
59
|
+
|
|
60
|
+
## TL;DR
|
|
61
|
+
|
|
62
|
+
- **Intlayer**: Scelta consigliata per applicazioni Solid professionali che necessitano di funzionalità avanzate e ottimizzazione (v8.7.12).
|
|
63
|
+
- **@solid-primitives/i18n**: Eccellente alternativa leggera per progetti semplici, sebbene manchi di funzionalità avanzate come il lazy loading.
|
|
64
|
+
- **solid-i18next**: Opzione standard ma pesante (~4.7× Intlayer) con gli stessi svantaggi di React i18next.
|
|
65
|
+
- **Paraglide**: Approccio innovativo ma DX complessa e problemi di tree-shaking in alcune configurazioni.
|
|
66
|
+
|
|
67
|
+
## Testa la tua app
|
|
68
|
+
|
|
69
|
+
Per individuare rapidamente i problemi di leak i18n, ho configurato uno scanner gratuito disponibile [qui](https://intlayer.org/i18n-seo-scanner).
|
|
70
|
+
|
|
71
|
+
<iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
|
|
72
|
+
|
|
73
|
+
## Il problema
|
|
74
|
+
|
|
75
|
+
Due leve sono essenziali per limitare il costo di un'app multilingue:
|
|
76
|
+
|
|
77
|
+
- Dividere il contenuto per pagina / namespace per non caricare interi dizionari quando non servono.
|
|
78
|
+
- Caricare la lingua corretta in modo dinamico, solo quando necessario.
|
|
79
|
+
|
|
80
|
+
Comprendere i limiti tecnici di questi approcci:
|
|
81
|
+
|
|
82
|
+
**Caricamento dinamico**
|
|
83
|
+
|
|
84
|
+
Senza caricamento dinamico, la maggior parte delle soluzioni mantiene i messaggi in memoria fin dal primo render, aggiungendo un overhead significativo per le app con molte rotte e lingue.
|
|
85
|
+
|
|
86
|
+
Con il caricamento dinamico, si accetta un compromesso: meno JS iniziale, ma a volte una richiesta extra quando si cambia lingua.
|
|
87
|
+
|
|
88
|
+
**Divisione dei contenuti (Splitting)**
|
|
89
|
+
|
|
90
|
+
Le sintassi costruite attorno a `t('a.b.c')` sono molto comode ma spesso incoraggiano a mantenere grandi oggetti JSON a runtime. Questo modello rende difficile il tree-shaking a meno che la libreria non offra una reale strategia di divisione per pagina.
|
|
91
|
+
|
|
92
|
+
## Metodologia
|
|
93
|
+
|
|
94
|
+
Per questo benchmark, abbiamo confrontato le seguenti librerie:
|
|
95
|
+
|
|
96
|
+
- `Base App` (Nessuna libreria i18n)
|
|
97
|
+
- `solid-intlayer` (v8.7.12)
|
|
98
|
+
- `@solid-primitives/i18n` (v2.2.1)
|
|
99
|
+
- `solid-i18next` (v17.0.2)
|
|
100
|
+
- `@inlang/paraglide-js` (v2.17.0)
|
|
101
|
+
|
|
102
|
+
Il framework è `Solid` con un'app multilingue di **10 pagine** e **10 lingue**.
|
|
103
|
+
|
|
104
|
+
Abbiamo confrontato **quattro strategie di caricamento**:
|
|
105
|
+
|
|
106
|
+
| Strategia | Senza namespace (globale) | Con namespace (scoped) |
|
|
107
|
+
| :----------------------- | :--------------------------------------------- | :----------------------------------------------------------------- |
|
|
108
|
+
| **Caricamento statico** | **Static**: Tutto in memoria all'avvio. | **Scoped static**: Diviso per namespace; tutto caricato all'avvio. |
|
|
109
|
+
| **Caricamento dinamico** | **Dynamic**: Caricamento on-demand per lingua. | **Scoped dynamic**: Caricamento granulare per namespace e lingua. |
|
|
110
|
+
|
|
111
|
+
## Riepilogo delle strategie
|
|
112
|
+
|
|
113
|
+
- **Static**: Semplice; nessuna latenza di rete dopo il caricamento iniziale. Svantaggio: grandi dimensioni del bundle.
|
|
114
|
+
- **Dynamic**: Riduce il peso iniziale (lazy-loading). Ideale quando si hanno molte lingue.
|
|
115
|
+
- **Scoped static**: Mantiene il codice organizzato (separazione logica) senza complesse richieste di rete extra.
|
|
116
|
+
- **Scoped dynamic**: Il miglior approccio per il _code splitting_ e le prestazioni. Minimizza la memoria caricando solo ciò di cui la vista corrente e la lingua attiva hanno bisogno.
|
|
117
|
+
|
|
118
|
+
## Risultati in dettaglio
|
|
119
|
+
|
|
120
|
+
### 1 — Soluzioni da evitare
|
|
121
|
+
|
|
122
|
+
> Nessuna soluzione chiara da evitare nell'ecosistema Solid.
|
|
123
|
+
|
|
124
|
+
### 2 — Soluzioni accettabili
|
|
125
|
+
|
|
126
|
+
**(solid-i18next)** (`solid-i18next@17.0.2`):
|
|
127
|
+
|
|
128
|
+
`solid-i18next` è probabilmente l'opzione più popolare perché è stata tra le prime a soddisfare le esigenze i18n delle app JavaScript. Dispone inoltre di un ampio set di plugin della comunità per problemi specifici.
|
|
129
|
+
|
|
130
|
+
Il pacchetto è pesante (~14.6kb, circa 4.7 volte `solid-intlayer`).
|
|
131
|
+
|
|
132
|
+
Tuttavia, condivide gli stessi principali svantaggi degli stack costruiti su `t('a.b.c')`: le ottimizzazioni sono possibili ma richiedono molto tempo, e i grandi progetti rischiano cattive pratiche (namespace + caricamento dinamico + tipi).
|
|
133
|
+
|
|
134
|
+
**(@solid-primitives/i18n)** (`@solid-primitives/i18n@2.2.1`):
|
|
135
|
+
|
|
136
|
+
Solid primitive è estremamente leggero ed efficiente. Consiglio questa soluzione per progetti leggeri, ma può mancare rapidamente di funzionalità per soluzioni professionali incluse la gestione dei cookie, il reindirizzamento proxy, i formattatori ecc.
|
|
137
|
+
Manca inoltre del lazy loading e dello scoping dei namespace per l'ottimizzazione delle dimensioni della pagina.
|
|
138
|
+
|
|
139
|
+
**(Paraglide)** (`@inlang/paraglide-js@2.17.0`):
|
|
140
|
+
|
|
141
|
+
`Paraglide` offre un approccio innovativo e ben ponderato. Tuttavia, in questo benchmark il tree-shaking pubblicizzato dalla loro azienda non ha funzionato per la mia implementazione. Il workflow e la DX sono inoltre più complessi rispetto ad altre opzioni.
|
|
142
|
+
Personalmente, non amo dover rigenerare file JS prima di ogni push, il che crea un costante rischio di conflitti di merge tramite le PR.
|
|
143
|
+
Infine, rispetto ad altre soluzioni, Paraglide non utilizza uno store (es. Solid signal) per recuperare la lingua corrente per renderizzare il contenuto. Per ogni nodo analizzato, richiederà la lingua al localStorage / cookie ecc. Ciò porta all'esecuzione di logica non necessaria che impatta sulla reattività del componente.
|
|
144
|
+
|
|
145
|
+
### 3 — Raccomandazioni
|
|
146
|
+
|
|
147
|
+
**(Intlayer)** (`solid-intlayer@8.7.12`):
|
|
148
|
+
|
|
149
|
+
Non giudicherò personalmente `solid-intlayer` per motivi di obiettività, essendo la mia soluzione.
|
|
150
|
+
|
|
151
|
+
### Nota personale
|
|
152
|
+
|
|
153
|
+
Questa nota è personale e non influisce sui risultati del benchmark. Tuttavia, nel mondo i18n si vede spesso un consenso attorno a un pattern come `const t = useTranslation('xx')` + `<>{t('xx.xx')}</>` per i contenuti tradotti.
|
|
154
|
+
|
|
155
|
+
Nelle app Solid, iniettare una funzione come `JSX.Element` è, a mio avviso, un anti-pattern. Aggiunge inoltre una complessità evitabile e un overhead di esecuzione JavaScript (anche se appena percettibile).
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-04-20
|
|
3
|
+
updatedAt: 2026-04-21
|
|
4
|
+
title: Migliore soluzione i18n per Svelte nel 2026 - Rapporto Benchmark
|
|
5
|
+
description: Confronta le librerie di internazionalizzazione (i18n) per Svelte come svelte-i18n, Paraglide e Intlayer. Rapporto dettagliato sulle prestazioni in termini di dimensioni del bundle, leak e reattività.
|
|
6
|
+
keywords:
|
|
7
|
+
- benchmark
|
|
8
|
+
- i18n
|
|
9
|
+
- intl
|
|
10
|
+
- svelte
|
|
11
|
+
- prestazioni
|
|
12
|
+
- intlayer
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- benchmark
|
|
16
|
+
- svelte
|
|
17
|
+
author: Aymeric PINEAU
|
|
18
|
+
applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-svelte-template
|
|
19
|
+
history:
|
|
20
|
+
- version: 8.7.12
|
|
21
|
+
date: 2026-01-06
|
|
22
|
+
changes: "Inizializzazione del benchmark"
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Librerie i18n per Svelte — Rapporto Benchmark 2026
|
|
26
|
+
|
|
27
|
+
Questa pagina è un rapporto di benchmark per le soluzioni i18n su Svelte.
|
|
28
|
+
|
|
29
|
+
## Sommario
|
|
30
|
+
|
|
31
|
+
<Toc/>
|
|
32
|
+
|
|
33
|
+
## Benchmark Interattivo
|
|
34
|
+
|
|
35
|
+
<I18nBenchmark framework="vite-svelte" vertical/>
|
|
36
|
+
|
|
37
|
+
## Riferimento dei risultati:
|
|
38
|
+
|
|
39
|
+
<iframe
|
|
40
|
+
src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_svelte.md"
|
|
41
|
+
width="100%"
|
|
42
|
+
height="600px"
|
|
43
|
+
style="border:none;">
|
|
44
|
+
</iframe>
|
|
45
|
+
|
|
46
|
+
> https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_svelte.md
|
|
47
|
+
|
|
48
|
+
Vedi il repository completo del benchmark [qui](https://github.com/intlayer-org/benchmark-i18n/tree/main).
|
|
49
|
+
|
|
50
|
+
## Introduzione
|
|
51
|
+
|
|
52
|
+
Le soluzioni di internazionalizzazione sono tra le dipendenze più pesanti in un'app Svelte. Il rischio principale è l'invio di contenuti non necessari: traduzioni per altre pagine e altre lingue nel bundle di una singola rotta.
|
|
53
|
+
|
|
54
|
+
Man mano che l'app cresce, questo problema può far esplodere rapidamente il JavaScript inviato al client e rallentare la navigazione.
|
|
55
|
+
|
|
56
|
+
In pratica, per le implementazioni meno ottimizzate, una pagina internazionalizzata può finire per essere diverse volte più pesante della versione senza i18n.
|
|
57
|
+
|
|
58
|
+
L'altro impatto riguarda l'esperienza dello sviluppatore (DX): come si dichiara il contenuto, i tipi, l'organizzazione dei namespace, il caricamento dinamico e la reattività al cambio di lingua.
|
|
59
|
+
|
|
60
|
+
## TL;DR
|
|
61
|
+
|
|
62
|
+
- **Intlayer**: La scelta più efficiente in termini di prestazioni (v8.7.12) con il footprint più ridotto.
|
|
63
|
+
- **Paraglide**: Forte candidato per il tree-shaking ma presenta una DX più complessa e un overhead di reattività.
|
|
64
|
+
- **svelte-i18n**: Completo e standard per Svelte, ma comporta un peso del bundle molto maggiore (~7 volte Intlayer).
|
|
65
|
+
|
|
66
|
+
## Testa la tua app
|
|
67
|
+
|
|
68
|
+
Per individuare rapidamente i problemi di leak i18n, ho configurato uno scanner gratuito disponibile [qui](https://intlayer.org/i18n-seo-scanner).
|
|
69
|
+
|
|
70
|
+
<iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
|
|
71
|
+
|
|
72
|
+
## Il problema
|
|
73
|
+
|
|
74
|
+
Due leve sono essenziali per limitare il costo di un'app multilingue:
|
|
75
|
+
|
|
76
|
+
- Dividere il contenuto per pagina / namespace per non caricare interi dizionari quando non servono.
|
|
77
|
+
- Caricare la lingua corretta in modo dinamico, solo quando necessario.
|
|
78
|
+
|
|
79
|
+
Comprendere i limiti tecnici di questi approcci:
|
|
80
|
+
|
|
81
|
+
**Caricamento dinamico**
|
|
82
|
+
|
|
83
|
+
Senza caricamento dinamico, la maggior parte delle soluzioni mantiene i messaggi in memoria fin dal primo render, aggiungendo un overhead significativo per le app con molte rotte e lingue.
|
|
84
|
+
|
|
85
|
+
Con il caricamento dinamico, si accetta un compromesso: meno JS iniziale, ma a volte una richiesta extra quando si cambia lingua.
|
|
86
|
+
|
|
87
|
+
**Divisione dei contenuti (Splitting)**
|
|
88
|
+
|
|
89
|
+
Le sintassi costruite attorno a `t('a.b.c')` sono molto comode ma spesso incoraggiano a mantenere grandi oggetti JSON a runtime. Questo modello rende difficile il tree-shaking a meno che la libreria non offra una reale strategia di divisione per pagina.
|
|
90
|
+
|
|
91
|
+
## Metodologia
|
|
92
|
+
|
|
93
|
+
Per questo benchmark, abbiamo confrontato le seguenti librerie:
|
|
94
|
+
|
|
95
|
+
- `Base App` (Nessuna libreria i18n)
|
|
96
|
+
- `svelte-intlayer` (v8.7.12)
|
|
97
|
+
- `svelte-i18n` (v4.0.1)
|
|
98
|
+
- `@inlang/paraglide-js` (v2.17.0)
|
|
99
|
+
|
|
100
|
+
Il framework è `Svelte` con un'app multilingue di **10 pagine** e **10 lingue**.
|
|
101
|
+
|
|
102
|
+
Abbiamo confrontato **quattro strategie di caricamento**:
|
|
103
|
+
|
|
104
|
+
| Strategia | Senza namespace (globale) | Con namespace (scoped) |
|
|
105
|
+
| :----------------------- | :--------------------------------------------- | :----------------------------------------------------------------- |
|
|
106
|
+
| **Caricamento statico** | **Static**: Tutto in memoria all'avvio. | **Scoped static**: Diviso per namespace; tutto caricato all'avvio. |
|
|
107
|
+
| **Caricamento dinamico** | **Dynamic**: Caricamento on-demand per lingua. | **Scoped dynamic**: Caricamento granulare per namespace e lingua. |
|
|
108
|
+
|
|
109
|
+
## Riepilogo delle strategie
|
|
110
|
+
|
|
111
|
+
- **Static**: Semplice; nessuna latenza di rete dopo il caricamento iniziale. Svantaggio: grandi dimensioni del bundle.
|
|
112
|
+
- **Dynamic**: Riduce il peso iniziale (lazy-loading). Ideale quando si hanno molte lingue.
|
|
113
|
+
- **Scoped static**: Mantiene il codice organizzato (separazione logica) senza complesse richieste di rete extra.
|
|
114
|
+
- **Scoped dynamic**: Il miglior approccio per il _code splitting_ e le prestazioni. Minimizza la memoria caricando solo ciò di cui la vista corrente e la lingua attiva hanno bisogno.
|
|
115
|
+
|
|
116
|
+
## Risultati in dettaglio
|
|
117
|
+
|
|
118
|
+
### 1 — Soluzioni da evitare
|
|
119
|
+
|
|
120
|
+
> Nessuna soluzione chiara da evitare nell'ecosistema Svelte.
|
|
121
|
+
|
|
122
|
+
### 2 — Soluzioni accettabili
|
|
123
|
+
|
|
124
|
+
**(Paraglide)** (`@inlang/paraglide-js@2.17.0`):
|
|
125
|
+
|
|
126
|
+
`Paraglide` offre un approccio innovativo e ben ponderato. Nel contesto di un'app Vite + Svelte, il tree-shaking pubblicizzato dalla loro azienda funziona come previsto, il che è eccellente.
|
|
127
|
+
Ma nel caso di React + TanStack Start, il tree-shaking non ha funzionato come previsto, così come per Next.js. Detto questo, l'uso di Paraglide in un progetto Svelte e TanStack Start meriterebbe una verifica più approfondita.
|
|
128
|
+
Il workflow e la DX sono inoltre più complessi rispetto ad altre opzioni.
|
|
129
|
+
Personalmente, non amo dover rigenerare file JS prima di ogni push, il che crea un costante rischio di conflitti di merge tramite le PR. Lo strumento sembra inoltre più focalizzato su Vite che su Next.js.
|
|
130
|
+
Infine, rispetto ad altre soluzioni, Paraglide non utilizza uno store (es. Svelte store) per recuperare la lingua corrente per renderizzare il contenuto. Per ogni nodo analizzato, richiederà la lingua al localStorage / cookie ecc. Ciò porta all'esecuzione di logica non necessaria che impatta sulla reattività del componente.
|
|
131
|
+
|
|
132
|
+
> Nota su Paraglide: la soluzione inietta codice nella tua codebase per gli import; di conseguenza, la metrica 'lib size' nel rapporto benchmark è quasi 0. La generazione di codice è una cosa buona, perché la funzione utilizzata includerà solo la logica necessaria (prefisso ovunque vs nessun prefisso, cookie vs storage, ecc.). In confronto, Intlayer esegue questo filtraggio tramite iniezioni di variabili d'ambiente durante la build per forzare il bundler a fare tree-shaking del contenuto a seconda della logica. Grazie a ciò, Paraglide e Intlayer finiscono per essere soluzioni da 6 a 10 volte più leggere di i18next o next-intl.
|
|
133
|
+
|
|
134
|
+
**(svelte-i18n)** (`svelte-i18n@3.4.0`):
|
|
135
|
+
|
|
136
|
+
Questa soluzione soddisfa tutte le esigenze i18n in un progetto Svelte. Ma come per i18next o altre importanti soluzioni i18n, è un po' pesante (~15.9kb, circa 7 volte `svelte-intlayer`).
|
|
137
|
+
|
|
138
|
+
### 3 — Raccomandazioni
|
|
139
|
+
|
|
140
|
+
**(Intlayer)** (`svelte-intlayer@8.7.12`):
|
|
141
|
+
|
|
142
|
+
Non giudicherò personalmente `svelte-intlayer` per motivi di obiettività, essendo la mia soluzione.
|
|
143
|
+
|
|
144
|
+
### Nota personale
|
|
145
|
+
|
|
146
|
+
Questa nota è personale e non influisce sui risultati del benchmark. Tuttavia, nel mondo i18n si vede spesso un consenso attorno a un pattern come `const t = useTranslation('xx')` + `<>{t('xx.xx')}</>` per i contenuti tradotti.
|
|
147
|
+
|
|
148
|
+
Nelle app Svelte, iniettare una funzione come `Slot` è, a mio avviso, un anti-pattern. Aggiunge inoltre una complessità evitabile e un overhead di esecuzione JavaScript (anche se appena percettibile).
|
|
@@ -57,6 +57,13 @@ In pratica, per le implementazioni meno ottimizzate, una pagina internazionalizz
|
|
|
57
57
|
|
|
58
58
|
L'altro impatto riguarda la developer experience (DX): come si dichiara il contenuto, i tipi, l'organizzazione dei namespace, il caricamento dinamico e la reattività al cambio di lingua.
|
|
59
59
|
|
|
60
|
+
## TL;DR
|
|
61
|
+
|
|
62
|
+
- **Intlayer**: Fornisce le migliori prestazioni e la dimensione del bundle più ridotta (v8.7.12) per TanStack Start.
|
|
63
|
+
- **react-i18next** & **use-intl**: Alternative mature con ecosistemi ampi, ma significativamente più pesanti e complesse da ottimizzare.
|
|
64
|
+
- **Paraglide**: Idea innovativa di tree-shaking che non funziona nella pratica. DX complessa e sovraccarico di reattività in TanStack Start.
|
|
65
|
+
- **Da evitare**: **General Translation (GT)** e **Lingo.dev** a causa di gravi problemi di prestazioni, limiti di quota AI e vendor lock-in.
|
|
66
|
+
|
|
60
67
|
## Testa la tua app
|
|
61
68
|
|
|
62
69
|
Per individuare rapidamente problemi di leakage i18n, ho configurato uno scanner gratuito disponibile [qui](https://intlayer.org/i18n-seo-scanner).
|
|
@@ -87,12 +94,12 @@ Le sintassi costruite attorno a `const t = useTranslation()` + `t('a.b.c')` sono
|
|
|
87
94
|
Per questo benchmark, abbiamo confrontato le seguenti librerie:
|
|
88
95
|
|
|
89
96
|
- `Base App` (Nessuna libreria i18n)
|
|
90
|
-
- `react-intlayer` (v8.7.
|
|
97
|
+
- `react-intlayer` (v8.7.12)
|
|
91
98
|
- `react-i18next` (v17.0.2)
|
|
92
99
|
- `use-intl` (v4.9.1)
|
|
93
100
|
- `@lingui/core` (v5.3.0)
|
|
94
101
|
- `@inlang/paraglide-js` (v2.15.1)
|
|
95
|
-
-
|
|
102
|
+
- `@tolgee/react` (v7.0.0)
|
|
96
103
|
- `react-intl` (v10.1.1)
|
|
97
104
|
- `wuchale` (v0.22.11)
|
|
98
105
|
- `gt-react` (vlatest)
|
|
@@ -150,7 +157,9 @@ L'idea alla base di `Wuchale` è interessante ma non ancora una soluzione pratic
|
|
|
150
157
|
|
|
151
158
|
`Paraglide` offre un approccio innovativo e ben ponderato. Tuttavia, in questo benchmark il tree-shaking pubblicizzato non ha funzionato per la mia implementazione Next.js o per TanStack Start. Il workflow e la DX sono inoltre più complessi di altre opzioni. Personalmente non sono un fan del dover rigenerare file JS prima di ogni push, il che crea un rischio costante di conflitti di merge per gli sviluppatori tramite PR.
|
|
152
159
|
|
|
153
|
-
|
|
160
|
+
> Nota su paraglide: questa soluzione inietta codice nella tua base di codice per le importazioni; di conseguenza, la metrica 'lib size' nel rapporto benchmark è quasi 0. La generazione del codice è un aspetto positivo, poiché la funzione utilizzata includerà solo la logica necessaria (prefisso ovunque vs nessun prefisso, cookie vs storage, ecc.). Al contrario, Intlayer esegue questo filtraggio tramite iniezioni di variabili d'ambiente nella build per forzare il bundler a eseguire il tree-shaking del contenuto in base alla logica. Grazie a ciò, paraglide e intlayer risultano soluzioni da 6 a 10 volte più leggere rispetto a i18next o next-intl.
|
|
161
|
+
|
|
162
|
+
**(Tolgee)** (`@tolgee/react@7.0.0`):
|
|
154
163
|
|
|
155
164
|
`Tolgee` affronta molti dei problemi menzionati in precedenza. L'ho trovato più difficile da avviare rispetto ad altri strumenti con approcci simili. Non fornisce type safety, il che rende anche molto difficile individuare le chiavi mancanti a compile time. Ho dovuto avvolgere le API di Tolgee con le mie per aggiungere il rilevamento delle chiavi mancanti.
|
|
156
165
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-04-20
|
|
3
|
+
updatedAt: 2026-04-21
|
|
4
|
+
title: Migliore soluzione i18n per Vue nel 2026 - Rapporto Benchmark
|
|
5
|
+
description: Confronta le librerie di internazionalizzazione (i18n) per Vue come vue-i18n, fluent-vue e Intlayer. Rapporto dettagliato sulle prestazioni in termini di dimensioni del bundle, leak e reattività.
|
|
6
|
+
keywords:
|
|
7
|
+
- benchmark
|
|
8
|
+
- i18n
|
|
9
|
+
- intl
|
|
10
|
+
- vue
|
|
11
|
+
- prestazioni
|
|
12
|
+
- intlayer
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- benchmark
|
|
16
|
+
- vue
|
|
17
|
+
author: Aymeric PINEAU
|
|
18
|
+
applicationTemplate: https://github.com/intlayer-org/benchmark-i18n-vue-template
|
|
19
|
+
history:
|
|
20
|
+
- version: 8.7.12
|
|
21
|
+
date: 2026-01-06
|
|
22
|
+
changes: "Inizializzazione del benchmark"
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Librerie i18n per Vue — Rapporto Benchmark 2026
|
|
26
|
+
|
|
27
|
+
Questa pagina è un rapporto di benchmark per le soluzioni i18n su Vue.
|
|
28
|
+
|
|
29
|
+
## Sommario
|
|
30
|
+
|
|
31
|
+
<Toc/>
|
|
32
|
+
|
|
33
|
+
## Benchmark Interattivo
|
|
34
|
+
|
|
35
|
+
<I18nBenchmark framework="vite-vue" vertical/>
|
|
36
|
+
|
|
37
|
+
## Riferimento dei risultati:
|
|
38
|
+
|
|
39
|
+
<iframe
|
|
40
|
+
src="https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_vue.md"
|
|
41
|
+
width="100%"
|
|
42
|
+
height="600px"
|
|
43
|
+
style="border:none;">
|
|
44
|
+
</iframe>
|
|
45
|
+
|
|
46
|
+
> https://intlayer.org/markdown?url=https%3A%2F%2Fraw.githubusercontent.com%2Fintlayer-org%2Fbenchmark-i18n%2Fmain%2Freport%2Fscripts%2Fsummarize-vite_vue.md
|
|
47
|
+
|
|
48
|
+
Vedi il repository completo del benchmark [qui](https://github.com/intlayer-org/benchmark-i18n/tree/main).
|
|
49
|
+
|
|
50
|
+
## Introduzione
|
|
51
|
+
|
|
52
|
+
Le soluzioni di internazionalizzazione sono tra le dipendenze più pesanti in un'app Vue. Il rischio principale è l'invio di contenuti non necessari: traduzioni per altre pagine e altre lingue nel bundle di una singola rotta.
|
|
53
|
+
|
|
54
|
+
Man mano che l'app cresce, questo problema può far esplodere rapidamente il JavaScript inviato al client e rallentare la navigazione.
|
|
55
|
+
|
|
56
|
+
In pratica, per le implementazioni meno ottimizzate, una pagina internazionalizzata può finire per essere diverse volte più pesante della versione senza i18n.
|
|
57
|
+
|
|
58
|
+
L'altro impatto riguarda l'esperienza dello sviluppatore (DX): come si dichiara il contenuto, i tipi, l'organizzazione dei namespace, il caricamento dinamico e la reattività al cambio di lingua.
|
|
59
|
+
|
|
60
|
+
## TL;DR
|
|
61
|
+
|
|
62
|
+
- **Intlayer**: La soluzione più leggera (v8.7.12) con scoping e caricamento dinamico nativi.
|
|
63
|
+
- **vue-i18n**: Lo standard del settore con un ricco ecosistema, ma può essere significativamente più pesante e difficile da ottimizzare per il code-splitting in applicazioni di grandi dimensioni.
|
|
64
|
+
- **fluent-vue**: Organizzazione dei messaggi innovativa ma manca di type-safety e risulta essere una soluzione estremamente pesante.
|
|
65
|
+
|
|
66
|
+
## Testa la tua app
|
|
67
|
+
|
|
68
|
+
Per individuare rapidamente i problemi di leak i18n, ho configurato uno scanner gratuito disponibile [qui](https://intlayer.org/i18n-seo-scanner).
|
|
69
|
+
|
|
70
|
+
<iframe src="https://intlayer.org/i18n-seo-scanner" width="100%" height="600px" style="border:none;"/>
|
|
71
|
+
|
|
72
|
+
## Il problema
|
|
73
|
+
|
|
74
|
+
Due leve sono essenziali per limitare il costo di un'app multilingue:
|
|
75
|
+
|
|
76
|
+
- Dividere il contenuto per pagina / namespace per non caricare interi dizionari quando non servono.
|
|
77
|
+
- Caricare la lingua corretta in modo dinamico, solo quando necessario.
|
|
78
|
+
|
|
79
|
+
Comprendere i limiti tecnici di questi approcci:
|
|
80
|
+
|
|
81
|
+
**Caricamento dinamico**
|
|
82
|
+
|
|
83
|
+
Senza caricamento dinamico, la maggior parte delle soluzioni mantiene i messaggi in memoria fin dal primo render, aggiungendo un overhead significativo per le app con molte rotte e lingue.
|
|
84
|
+
|
|
85
|
+
Con il caricamento dinamico, si accetta un compromesso: meno JS iniziale, ma a volte una richiesta extra quando si cambia lingua.
|
|
86
|
+
|
|
87
|
+
**Divisione dei contenuti (Splitting)**
|
|
88
|
+
|
|
89
|
+
Le sintassi costruite attorno a `const { t } = useI18n()` + `t('a.b.c')` sono molto comode ma spesso incoraggiano a mantenere grandi oggetti JSON a runtime. Questo modello rende difficile il tree-shaking a meno che la libreria non offra una reale strategia di divisione per pagina.
|
|
90
|
+
|
|
91
|
+
## Metodologia
|
|
92
|
+
|
|
93
|
+
Per questo benchmark, abbiamo confrontato le seguenti librerie:
|
|
94
|
+
|
|
95
|
+
- `Base App` (Nessuna libreria i18n)
|
|
96
|
+
- `vue-intlayer` (v8.7.12)
|
|
97
|
+
- `vue-i18n` (v11.4.0)
|
|
98
|
+
- `fluent-vue` (v3.8.2)
|
|
99
|
+
|
|
100
|
+
Il framework è `Vue` con un'app multilingue di **10 pagine** e **10 lingue**.
|
|
101
|
+
|
|
102
|
+
Abbiamo confrontato **quattro strategie di caricamento**:
|
|
103
|
+
|
|
104
|
+
| Strategia | Senza namespace (globale) | Con namespace (scoped) |
|
|
105
|
+
| :----------------------- | :--------------------------------------------- | :----------------------------------------------------------------- |
|
|
106
|
+
| **Caricamento statico** | **Static**: Tutto in memoria all'avvio. | **Scoped static**: Diviso per namespace; tutto caricato all'avvio. |
|
|
107
|
+
| **Caricamento dinamico** | **Dynamic**: Caricamento on-demand per lingua. | **Scoped dynamic**: Caricamento granulare per namespace e lingua. |
|
|
108
|
+
|
|
109
|
+
## Riepilogo delle strategie
|
|
110
|
+
|
|
111
|
+
- **Static**: Semplice; nessuna latenza di rete dopo il caricamento iniziale. Svantaggio: grandi dimensioni del bundle.
|
|
112
|
+
- **Dynamic**: Riduce il peso iniziale (lazy-loading). Ideale quando si hanno molte lingue.
|
|
113
|
+
- **Scoped static**: Mantiene il codice organizzato (separazione logica) senza complesse richieste di rete extra.
|
|
114
|
+
- **Scoped dynamic**: Il miglior approccio per il _code splitting_ e le prestazioni. Minimizza la memoria caricando solo ciò di cui la vista corrente e la lingua attiva hanno bisogno.
|
|
115
|
+
|
|
116
|
+
### Cosa ho misurato:
|
|
117
|
+
|
|
118
|
+
Ho eseguito la stessa app multilingue in un browser reale per ogni stack, poi ho annotato cosa passava effettivamente sulla rete e quanto tempo richiedevano le operazioni. Le dimensioni sono riportate **dopo la normale compressione web**, perché è più vicino a ciò che le persone scaricano effettivamente.
|
|
119
|
+
|
|
120
|
+
- **Dimensioni della libreria di internazionalizzazione**: Dopo il bundling, il tree-shaking e la minificazione, la dimensione della libreria i18n è la dimensione del codice dei provider + composable in un componente vuoto. Non include il caricamento dei file di traduzione. Risponde a quanto è "costosa" la libreria prima che entri in gioco il tuo contenuto.
|
|
121
|
+
|
|
122
|
+
- **JavaScript per pagina**: Per ogni rotta del benchmark, quanto script viene scaricato dal browser per quella visita, mediato tra le pagine della suite (e tra le lingue). Le pagine pesanti sono pagine lente.
|
|
123
|
+
|
|
124
|
+
- **Leak da altre lingue (Leakage)**: È il contenuto della stessa pagina ma in un'altra lingua che verrebbe caricato per errore nella pagina verificata. Questo contenuto è inutile e dovrebbe essere evitato (es. contenuto della pagina `/fr/about` nel bundle della pagina `/en/about`).
|
|
125
|
+
|
|
126
|
+
- **Leak da altre rotte**: La stessa idea per **altre schermate** nell'app: se i loro testi vengono caricati quando hai aperto solo una pagina (es. contenuto della pagina `/en/about` nel bundle della pagina `/en/contact`). Un punteggio alto indica una divisione debole o bundle troppo ampi.
|
|
127
|
+
|
|
128
|
+
- **Dimensione media del bundle del componente**: Gli elementi UI comuni vengono misurati **uno alla volta**, invece di nascondersi all'interno di un unico numero gigante dell'app. Mostra se l'internazionalizzazione gonfia silenziosamente i componenti quotidiani. Ad esempio, se il tuo componente viene renderizzato di nuovo, caricherà tutti quei dati dalla memoria. Allegare un JSON gigante a qualsiasi componente è come collegare un grande magazzino di dati inutilizzati che rallenterà le prestazioni dei tuoi componenti.
|
|
129
|
+
|
|
130
|
+
- **Reattività al cambio lingua**: Cambio la lingua usando il controllo dell'app stessa e cronometro quanto tempo passa finché la pagina non è chiaramente cambiata, quello che un visitatore noterebbe.
|
|
131
|
+
|
|
132
|
+
- **Lavoro di rendering dopo un cambio di lingua**: Un follow-up più preciso: quanto sforzo ha impiegato l'interfaccia per ridisegnarsi per la nuova lingua una volta avviato il cambio. Utile quando il tempo "percepito" e il costo del framework divergono.
|
|
133
|
+
|
|
134
|
+
- **Tempo di caricamento iniziale della pagina**: Dalla navigazione fino a quando il browser considera la pagina completamente caricata per gli scenari testati. Utile per confrontare gli avvii a freddo.
|
|
135
|
+
|
|
136
|
+
- **Tempo di idratazione (Hydration)**: Il tempo che il client impiega per trasformare l'HTML del server in un'interfaccia interattiva. Un trattino nelle tabelle significa che quella implementazione non ha fornito una cifra di idratazione affidabile in questo benchmark.
|
|
137
|
+
|
|
138
|
+
## Risultati in dettaglio
|
|
139
|
+
|
|
140
|
+
### 1 — Soluzioni da evitare
|
|
141
|
+
|
|
142
|
+
> Nessuna soluzione chiara da evitare nell'ecosistema Vue.
|
|
143
|
+
|
|
144
|
+
### 2 — Soluzioni accettabili
|
|
145
|
+
|
|
146
|
+
**(vue-i18n)** (`vue-i18n@11.4.0`):
|
|
147
|
+
|
|
148
|
+
- **vue-i18n** è senza dubbio la libreria i18n più utilizzata per Vue, ha molte funzionalità e un ecosistema immenso. Ma sotto il cofano la soluzione è piuttosto pesante. Anche se vue-i18n integra il caricamento pigro dei messaggi, manca di una funzione di scoping. Nel caso di una classica app Vue SPA non ci sono problemi, ma per un'app Nuxt, utilizzando @nuxt/i18n, ciò porta a includere i messaggi di tutte le pagine in una sola. Per una grande app Nuxt con più di 10 pagine, può diventare davvero problematico.
|
|
149
|
+
|
|
150
|
+
Il pacchetto è molto pesante (~24.3kb, circa 9 volte `vue-intlayer`).
|
|
151
|
+
|
|
152
|
+
**(fluent-vue)** (`fluent-vue@0.5.0`):
|
|
153
|
+
|
|
154
|
+
- **fluent-vue** offre un tentativo di innovazione attraverso il formato .ftl. L'organizzazione dei messaggi è ottima, più facile per iniziare. Ma in pratica, la mancanza di type-safety aumenta il rischio di errore e può diventare rapidamente dispendioso in termini di tempo per il debug. Inoltre, questa soluzione carica i messaggi tramite un plugin vite che forza il caricamento di tutto il contenuto in tutte le lingue in ogni pagina. Inoltre, è una soluzione estremamente pesante (~92.7kb, circa 34 volte `vue-intlayer`).
|
|
155
|
+
|
|
156
|
+
### 3 — Raccomandazioni
|
|
157
|
+
|
|
158
|
+
**(Intlayer)** (`vue-intlayer@8.7.12`):
|
|
159
|
+
|
|
160
|
+
Non giudicherò personalmente `vue-intlayer` per motivi di obiettività, essendo la mia soluzione.
|