@intlayer/docs 6.1.4 → 6.1.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.
Files changed (75) hide show
  1. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1366 -75
  2. package/blog/ar/nextjs-multilingual-seo-comparison.md +364 -0
  3. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1288 -72
  4. package/blog/de/nextjs-multilingual-seo-comparison.md +362 -0
  5. package/blog/en/intlayer_with_next-i18next.mdx +431 -0
  6. package/blog/en/intlayer_with_next-intl.mdx +335 -0
  7. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +583 -336
  8. package/blog/en/nextjs-multilingual-seo-comparison.md +360 -0
  9. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1144 -37
  10. package/blog/en-GB/nextjs-multilingual-seo-comparison.md +360 -0
  11. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1236 -64
  12. package/blog/es/nextjs-multilingual-seo-comparison.md +363 -0
  13. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1142 -75
  14. package/blog/fr/nextjs-multilingual-seo-comparison.md +362 -0
  15. package/blog/hi/nextjs-multilingual-seo-comparison.md +363 -0
  16. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1130 -55
  17. package/blog/it/nextjs-multilingual-seo-comparison.md +363 -0
  18. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1150 -76
  19. package/blog/ja/nextjs-multilingual-seo-comparison.md +362 -0
  20. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1139 -73
  21. package/blog/ko/nextjs-multilingual-seo-comparison.md +362 -0
  22. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1143 -76
  23. package/blog/pt/nextjs-multilingual-seo-comparison.md +362 -0
  24. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1150 -74
  25. package/blog/ru/nextjs-multilingual-seo-comparison.md +370 -0
  26. package/blog/tr/next-i18next_vs_next-intl_vs_intlayer.md +2 -0
  27. package/blog/tr/nextjs-multilingual-seo-comparison.md +362 -0
  28. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1152 -75
  29. package/blog/zh/nextjs-multilingual-seo-comparison.md +394 -0
  30. package/dist/cjs/generated/blog.entry.cjs +16 -0
  31. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  32. package/dist/cjs/generated/docs.entry.cjs +16 -0
  33. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  34. package/dist/esm/generated/blog.entry.mjs +16 -0
  35. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  36. package/dist/esm/generated/docs.entry.mjs +16 -0
  37. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  38. package/dist/types/generated/blog.entry.d.ts +1 -0
  39. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  40. package/dist/types/generated/docs.entry.d.ts +1 -0
  41. package/dist/types/generated/docs.entry.d.ts.map +1 -1
  42. package/docs/ar/component_i18n.md +186 -0
  43. package/docs/ar/vs_code_extension.md +48 -109
  44. package/docs/de/component_i18n.md +186 -0
  45. package/docs/de/vs_code_extension.md +46 -107
  46. package/docs/en/component_i18n.md +186 -0
  47. package/docs/en/interest_of_intlayer.md +2 -2
  48. package/docs/en/intlayer_with_nextjs_14.md +18 -1
  49. package/docs/en/intlayer_with_nextjs_15.md +18 -1
  50. package/docs/en/vs_code_extension.md +24 -114
  51. package/docs/en-GB/component_i18n.md +186 -0
  52. package/docs/en-GB/vs_code_extension.md +42 -103
  53. package/docs/es/component_i18n.md +182 -0
  54. package/docs/es/vs_code_extension.md +53 -114
  55. package/docs/fr/component_i18n.md +186 -0
  56. package/docs/fr/vs_code_extension.md +50 -111
  57. package/docs/hi/component_i18n.md +186 -0
  58. package/docs/hi/vs_code_extension.md +49 -110
  59. package/docs/it/component_i18n.md +186 -0
  60. package/docs/it/vs_code_extension.md +50 -111
  61. package/docs/ja/component_i18n.md +186 -0
  62. package/docs/ja/vs_code_extension.md +50 -111
  63. package/docs/ko/component_i18n.md +186 -0
  64. package/docs/ko/vs_code_extension.md +48 -109
  65. package/docs/pt/component_i18n.md +186 -0
  66. package/docs/pt/vs_code_extension.md +46 -107
  67. package/docs/ru/component_i18n.md +186 -0
  68. package/docs/ru/vs_code_extension.md +48 -109
  69. package/docs/tr/component_i18n.md +186 -0
  70. package/docs/tr/vs_code_extension.md +54 -115
  71. package/docs/zh/component_i18n.md +186 -0
  72. package/docs/zh/vs_code_extension.md +51 -105
  73. package/package.json +11 -11
  74. package/src/generated/blog.entry.ts +16 -0
  75. package/src/generated/docs.entry.ts +16 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  createdAt: 2025-08-23
3
- updatedAt: 2025-08-23
3
+ updatedAt: 2025-09-29
4
4
  title: next-i18next vs next-intl vs Intlayer
5
5
  description: Confronta next-i18next con next-intl e Intlayer per l'internazionalizzazione (i18n) di un'app Next.js
6
6
  keywords:
@@ -19,7 +19,12 @@ slugs:
19
19
 
20
20
  # next-i18next VS next-intl VS intlayer | Internazionalizzazione (i18n) in Next.js
21
21
 
22
- Questa guida confronta tre opzioni i18n ampiamente utilizzate per **Next.js**: **next-intl**, **next-i18next** e **Intlayer**.
22
+ ![next-i18next VS next-intl VS intlayer](https://github.com/aymericzip/intlayer/blob/main/docs/assets/i18next-next-intl-intlayer.png?raw=true)
23
+
24
+ Diamo un'occhiata alle somiglianze e differenze tra tre opzioni i18n per Next.js: next-i18next, next-intl e Intlayer.
25
+
26
+ Questo non è un tutorial completo. È un confronto per aiutarti a scegliere.
27
+
23
28
  Ci concentriamo su **Next.js 13+ App Router** (con **React Server Components**) e valutiamo:
24
29
 
25
30
  1. **Architettura e organizzazione dei contenuti**
@@ -30,125 +35,1195 @@ Ci concentriamo su **Next.js 13+ App Router** (con **React Server Components**)
30
35
  6. **Esperienza sviluppatore (DX), strumenti e manutenzione**
31
36
  7. **SEO e scalabilità per progetti di grandi dimensioni**
32
37
 
33
- > **tl;dr**: Tutti e tre possono localizzare un'app Next.js. Se desideri **contenuti a livello di componente**, **tipi TypeScript rigorosi**, **controlli delle chiavi mancanti in fase di build**, **dizionari ottimizzati con tree-shaking** e **helper di prima classe per App Router e SEO**, **Intlayer** è la scelta più completa e moderna.
38
+ > **In breve**: Tutti e tre possono localizzare un'app Next.js. Se desideri **contenuti a livello di componente**, **tipi TypeScript rigorosi**, **controlli delle chiavi mancanti in fase di build**, **dizionari ottimizzati tramite tree-shaking** e **supporto di prima classe per App Router + helper SEO**, **Intlayer** è la scelta più completa e moderna.
39
+
40
+ > Una confusione comune tra gli sviluppatori è pensare che `next-intl` sia la versione Next.js di `react-intl`. Non è così: `next-intl` è mantenuto da [Amann](https://github.com/amannn), mentre `react-intl` è mantenuto da [FormatJS](https://github.com/formatjs/formatjs).
34
41
 
35
42
  ---
36
43
 
37
- ## Posizionamento ad alto livello
44
+ ## In breve
38
45
 
39
- - **next-intl** - Formattazione dei messaggi leggera e semplice con un solido supporto per Next.js. I cataloghi centralizzati sono comuni; lesperienza sviluppatore (DX) è semplice, ma la sicurezza e la manutenzione su larga scala rimangono per lo più a tuo carico.
40
- - **next-i18next** - i18next vestito per Next.js. Ecosistema maturo e funzionalità tramite plugin (ad esempio, ICU), ma la configurazione può essere verbosa e i cataloghi tendono a centralizzarsi con la crescita dei progetti.
41
- - **Intlayer** - Modello di contenuto incentrato sui componenti per Next.js, **tipizzazione TS rigorosa**, **controlli in fase di build**, **tree-shaking**, **middleware integrati e helper SEO**, **Editor Visivo/CMS** opzionale e **traduzioni assistite da AI**.
46
+ - **next-intl** - Formattazione dei messaggi leggera e semplice con un solido supporto per Next.js. I cataloghi centralizzati sono comuni; l'esperienza sviluppatore (DX) è semplice, ma la sicurezza e la manutenzione su larga scala rimangono per lo più a tuo carico.
47
+ - **next-i18next** - i18next in versione Next.js. Ecosistema maturo e funzionalità tramite plugin (ad esempio, ICU), ma la configurazione può essere verbosa e i cataloghi tendono a centralizzarsi con la crescita dei progetti.
48
+ - **Intlayer** - Modello di contenuto incentrato sui componenti per Next.js, **tipizzazione TS rigorosa**, **controlli a tempo di compilazione**, **tree-shaking**, **middleware integrati e helper SEO**, **Editor Visivo/CMS** opzionale e **traduzioni assistite da AI**.
49
+
50
+ ---
51
+
52
+ | Library | GitHub Stars | Total Commits | Last Commit | First Version | NPM Version | NPM Downloads |
53
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
54
+ | `aymericzip/intlayer` | [![GitHub Repo stars](https://img.shields.io/github/stars/aymericzip/intlayer?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/aymericzip/intlayer/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/aymericzip/intlayer?style=for-the-badge&label=commits)](https://github.com/aymericzip/intlayer/commits) | [![Last Commit](https://img.shields.io/github/last-commit/aymericzip/intlayer?style=for-the-badge)](https://github.com/aymericzip/intlayer/commits) | April 2024 | [![npm](https://img.shields.io/npm/v/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) | [![npm downloads](https://img.shields.io/npm/dm/intlayer?style=for-the-badge)](https://www.npmjs.com/package/intlayer) |
55
+ | `amannn/next-intl` | [![GitHub Repo stars](https://img.shields.io/github/stars/amannn/next-intl?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/amannn/next-intl/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/amannn/next-intl?style=for-the-badge&label=commits)](https://github.com/amannn/next-intl/commits) | [![Last Commit](https://img.shields.io/github/last-commit/amannn/next-intl?style=for-the-badge)](https://github.com/amannn/next-intl/commits) | Nov 2020 | [![npm](https://img.shields.io/npm/v/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) | [![npm downloads](https://img.shields.io/npm/dm/next-intl?style=for-the-badge)](https://www.npmjs.com/package/next-intl) |
56
+ | `i18next/i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/i18next?style=for-the-badge&label=commits)](https://github.com/i18next/i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/i18next?style=for-the-badge)](https://github.com/i18next/i18next/commits) | Jan 2012 | [![npm](https://img.shields.io/npm/v/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) | [![npm downloads](https://img.shields.io/npm/dm/i18next?style=for-the-badge)](https://www.npmjs.com/package/i18next) |
57
+ | `i18next/next-i18next` | [![GitHub Repo stars](https://img.shields.io/github/stars/i18next/next-i18next?style=for-the-badge&label=%E2%AD%90%20stars)](https://github.com/i18next/next-i18next/stargazers) | [![GitHub commit activity](https://img.shields.io/github/commit-activity/t/i18next/next-i18next?style=for-the-badge&label=commits)](https://github.com/i18next/next-i18next/commits) | [![Last Commit](https://img.shields.io/github/last-commit/i18next/next-i18next?style=for-the-badge)](https://github.com/i18next/next-i18next/commits) | Nov 2018 | [![npm](https://img.shields.io/npm/v/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) | [![npm downloads](https://img.shields.io/npm/dm/next-i18next?style=for-the-badge)](https://www.npmjs.com/package/next-i18next) |
58
+
59
+ > I badge si aggiornano automaticamente. Gli snapshot varieranno nel tempo.
42
60
 
43
61
  ---
44
62
 
45
63
  ## Confronto delle funzionalità affiancate (focalizzato su Next.js)
46
64
 
65
+ | Funzionalità | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
66
+
67
+ > I badge si aggiornano automaticamente. Gli snapshot varieranno nel tempo.
68
+
69
+ ---
70
+
71
+ ## Confronto delle Funzionalità Affiancate (focalizzato su Next.js)
72
+
47
73
  | Funzionalità | `next-intlayer` (Intlayer) | `next-intl` | `next-i18next` |
48
74
  | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
49
- | **Traduzioni Vicino ai Componenti** | ✅ Sì, contenuto collocato con ogni componente | ❌ No | ❌ No |
75
+ | **Traduzioni Vicino ai Componenti** | ✅ Sì, contenuto collocato insieme a ogni componente | ❌ No | ❌ No |
50
76
  | **Integrazione TypeScript** | ✅ Avanzata, tipi rigorosi generati automaticamente | ✅ Buona | ⚠️ Base |
51
- | **Rilevamento Traduzioni Mancanti** | ✅ Evidenziazione errori TypeScript e errore/avviso in fase di compilazione | ⚠️ Ripiego a runtime | ⚠️ Ripiego a runtime |
52
- | **Contenuto Ricco (JSX/Markdown/componenti)** | ✅ Supporto diretto | ❌ Non progettato per nodi ricchi | ⚠️ Limitato |
77
+ | **Rilevamento Traduzioni Mancanti** | ✅ Evidenziazione errori TypeScript e errori/avvisi in fase di compilazione | ⚠️ Fallback a runtime | ⚠️ Fallback a runtime |
78
+ | **Contenuti Ricchi (JSX/Markdown/componenti)** | ✅ Supporto diretto | ❌ Non progettato per nodi ricchi | ⚠️ Limitato |
53
79
  | **Traduzione basata su AI** | ✅ Sì, supporta più provider AI. Utilizzabile con le proprie chiavi API. Considera il contesto della tua applicazione e l'ambito del contenuto | ❌ No | ❌ No |
54
- | **Editor Visuale** | ✅ Sì, Editor Visuale locale + CMS opzionale; può esternalizzare il contenuto del codice; integrabile | ❌ No / disponibile tramite piattaforme di localizzazione esterne | ❌ No / disponibile tramite piattaforme di localizzazione esterne |
55
- | **Routing Localizzato** | ✅ Sì, supporta percorsi localizzati nativamente (funziona con Next.js e Vite) | ✅ Integrato, App Router supporta il segmento `[locale]` | ✅ Integrato |
80
+ | **Editor Visivo** | ✅ Sì, Editor Visivo locale + CMS opzionale; può esternalizzare il contenuto della codebase; integrabile | ❌ No / disponibile tramite piattaforme di localizzazione esterne | ❌ No / disponibile tramite piattaforme di localizzazione esterne |
81
+ | **Routing Localizzato** | ✅ Sì, supporta percorsi localizzati nativamente (funziona con Next.js & Vite) | ✅ Integrato, App Router supporta il segmento `[locale]` | ✅ Integrato |
56
82
  | **Generazione Dinamica delle Rotte** | ✅ Sì | ✅ Sì | ✅ Sì |
57
83
  | **Pluralizzazione** | ✅ Modelli basati su enumerazioni | ✅ Buono | ✅ Buono |
58
84
  | **Formattazione (date, numeri, valute)** | ✅ Formatter ottimizzati (Intl sotto il cofano) | ✅ Buono (helper Intl) | ✅ Buono (helper Intl) |
59
85
  | **Formato del contenuto** | ✅ .tsx, .ts, .js, .json, .md, .txt, (.yaml WIP) | ✅ .json, .js, .ts | ⚠️ .json |
60
86
  | **Supporto ICU** | ⚠️ In lavorazione | ✅ Sì | ⚠️ Tramite plugin (`i18next-icu`) |
61
87
  | **Helper SEO (hreflang, sitemap)** | ✅ Strumenti integrati: helper per sitemap, robots.txt, metadata | ✅ Buono | ✅ Buono |
62
- | **Ecosistema / Comunità** | ⚠️ Più piccolo ma in rapida crescita e reattivo | ✅ Di medie dimensioni, focalizzato su Next.js | ✅ Di medie dimensioni, focalizzato su Next.js |
88
+ | **Ecosistema / Comunità** | ⚠️ Più piccolo ma in rapida crescita e reattivo | ✅ Buono | ✅ Buono |
63
89
  | **Rendering lato server & Componenti Server** | ✅ Sì, ottimizzato per SSR / Componenti Server React | ⚠️ Supportato a livello di pagina ma è necessario passare le funzioni t sull'albero dei componenti per i componenti server figli | ⚠️ Supportato a livello di pagina ma è necessario passare le funzioni t sull'albero dei componenti per i componenti server figli |
64
90
  | **Tree-shaking (caricamento solo del contenuto usato)** | ✅ Sì, per componente al momento della build tramite plugin Babel/SWC | ⚠️ Parziale | ⚠️ Parziale |
65
- | **Lazy loading** | ✅ Sì, per locale / per dizionario | ✅ Sì (per rotta / per locale), necessita gestione dei namespace | ✅ Sì (per rotta / per locale), necessita gestione dei namespace |
91
+ | **Caricamento lazy** | ✅ Sì, per locale / per dizionario | ✅ Sì (per percorso/per locale), necessita gestione dei namespace | ✅ Sì (per percorso/per locale), necessita gestione dei namespace |
66
92
  | **Rimozione dei contenuti non utilizzati** | ✅ Sì, per dizionario al momento della build | ❌ No, può essere gestito manualmente con la gestione dei namespace | ❌ No, può essere gestito manualmente con la gestione dei namespace |
67
- | **Gestione di Grandi Progetti** | ✅ Favorisce un approccio modulare, adatto a design-system | ✅ Modulare con configurazione | ✅ Modulare con configurazione |
93
+ | **Gestione di progetti di grandi dimensioni** | ✅ Favorisce la modularità, adatto per sistemi di design | ✅ Modulare con configurazione | ✅ Modulare con configurazione |
94
+ | **Test delle traduzioni mancanti (CLI/CI)** | ✅ CLI: `npx intlayer content test` (audit compatibile con CI) | ⚠️ Non integrato; la documentazione suggerisce `npx @lingual/i18n-check` | ⚠️ Non integrato; si fa affidamento sugli strumenti i18next / runtime `saveMissing` |
68
95
 
69
96
  ---
70
97
 
71
- ## Confronto Approfondito
98
+ ## Introduzione
72
99
 
73
- ### 1) Architettura e scalabilità
100
+ Next.js offre supporto integrato per il routing internazionalizzato (es. segmenti di localizzazione). Ma questa funzionalità non gestisce le traduzioni da sola. Serve comunque una libreria per rendere contenuti localizzati agli utenti.
74
101
 
75
- - **next-intl / next-i18next**: Predefinito a **cataloghi centralizzati** per locale (più **namespace** in i18next). Funziona bene all'inizio, ma spesso diventa una grande superficie condivisa con un aumento del coupling e del churn delle chiavi.
76
- - **Intlayer**: Favorisce dizionari **per componente** (o per funzionalità) **co-localizzati** con il codice che servono. Questo riduce il carico cognitivo, facilita la duplicazione/migrazione delle parti dell’interfaccia utente e riduce i conflitti tra team. Il contenuto non utilizzato è naturalmente più facile da individuare ed eliminare.
102
+ Esistono molte librerie i18n, ma nel mondo Next.js oggi tre stanno guadagnando popolarità: next-i18next, next-intl e Intlayer.
77
103
 
78
- **Perché è importante:** In grandi codebase o configurazioni di design-system, il **contenuto modulare** scala meglio rispetto a cataloghi monolitici.
104
+ ---
105
+
106
+ ## Architettura e scalabilità
107
+
108
+ - **next-intl / next-i18next**: Predefinito a **cataloghi centralizzati** per locale (più **namespace** in i18next). Funziona bene all'inizio, ma spesso diventa una grande area condivisa con un aumento del coupling e del churn delle chiavi.
109
+ - **Intlayer**: Favorisce dizionari **per componente** (o per funzionalità) **co-locati** con il codice che servono. Questo riduce il carico cognitivo, facilita la duplicazione/migrazione delle parti dell'interfaccia utente e riduce i conflitti tra team. Il contenuto non utilizzato è naturalmente più facile da individuare ed eliminare.
110
+
111
+ **Perché è importante:** In grandi codebase o configurazioni di design system, il **contenuto modulare** scala meglio rispetto ai cataloghi monolitici.
79
112
 
80
113
  ---
81
114
 
82
- ### 2) TypeScript e sicurezza
115
+ ## Dimensioni del bundle e dipendenze
116
+
117
+ Dopo la compilazione dell'applicazione, il bundle è il JavaScript che il browser caricherà per rendere la pagina. La dimensione del bundle è quindi importante per le prestazioni dell'applicazione.
118
+
119
+ Due componenti sono importanti nel contesto di un bundle per applicazioni multilingue:
120
+
121
+ - Il codice dell'applicazione
122
+ - Il contenuto caricato dal browser
123
+
124
+ ## Codice dell'applicazione
125
+
126
+ L'importanza del codice dell'applicazione è minima in questo caso. Tutte e tre le soluzioni sono tree-shakable, il che significa che le parti di codice non utilizzate non vengono incluse nel bundle.
127
+
128
+ Ecco un confronto della dimensione del bundle JavaScript caricato dal browser per un'applicazione multilingue con le tre soluzioni.
129
+
130
+ Se non abbiamo bisogno di alcun formatter nell'applicazione, l'elenco delle funzioni esportate dopo il tree-shaking sarà:
131
+
132
+ - **next-intlayer**: `useIntlayer`, `useLocale`, `NextIntlClientProvider`, (La dimensione del bundle è 180.6 kB -> 78.6 kB (gzip))
133
+ - **next-intl**: `useTranslations`, `useLocale`, `NextIntlClientProvider`, (La dimensione del bundle è 101.3 kB -> 31.4 kB (gzip))
134
+ - **next-i18next**: `useTranslation`, `useI18n`, `I18nextProvider`, (La dimensione del bundle è 80.7 kB -> 25.5 kB (gzip))
135
+
136
+ Queste funzioni sono solo wrapper attorno al contesto/stato di React, quindi l'impatto totale della libreria i18n sulla dimensione del bundle è minimo.
137
+
138
+ > Intlayer è leggermente più grande di `next-intl` e `next-i18next` perché include più logica nella funzione `useIntlayer`. Questo è legato all'integrazione con markdown e `intlayer-editor`.
139
+
140
+ ## Contenuti e Traduzioni
141
+
142
+ Questa parte è spesso ignorata dagli sviluppatori, ma consideriamo il caso di un'applicazione composta da 10 pagine in 10 lingue. Supponiamo che ogni pagina integri contenuti unici al 100% per semplificare il calcolo (in realtà, molti contenuti sono ridondanti tra le pagine, ad esempio titolo della pagina, intestazione, piè di pagina, ecc.).
143
+
144
+ Un utente che desidera visitare la pagina `/fr/about` caricherà il contenuto di una pagina in una determinata lingua. Ignorare l'ottimizzazione dei contenuti significherebbe caricare inutilmente l'8.200% `((1 + (((10 pagine - 1) × (10 lingue - 1)))) × 100)` del contenuto dell'applicazione. Vedi il problema? Anche se questo contenuto rimane testo, e mentre probabilmente preferisci pensare a ottimizzare le immagini del tuo sito, stai inviando contenuti inutili in tutto il mondo e facendo elaborare i computer degli utenti per nulla.
145
+
146
+ Due questioni importanti:
147
+
148
+ - **Suddivisione per percorso:**
149
+
150
+ > Se sono nella pagina `/about`, non voglio caricare il contenuto della pagina `/home`
151
+
152
+ - **Suddivisione per locale:**
153
+
154
+ > Se sono nella pagina `/fr/about`, non voglio caricare il contenuto della pagina `/en/about`
83
155
 
84
- - **next-intl**: Supporto solido per TypeScript, ma le **chiavi non sono tipizzate rigorosamente di default**; dovrai mantenere manualmente i pattern di sicurezza.
85
- - **next-i18next**: Tipizzazioni base per gli hook; la **tipizzazione rigorosa delle chiavi richiede strumenti/configurazioni aggiuntive**.
86
- - **Intlayer**: **Genera tipi rigorosi** dal tuo contenuto. **Completamento automatico nell’IDE** e **errori a tempo di compilazione** individuano errori di battitura e chiavi mancanti prima del deploy.
156
+ Ancora una volta, tutte e tre le soluzioni sono consapevoli di queste problematiche e permettono di gestire queste ottimizzazioni. La differenza tra le tre soluzioni è l'esperienza dello sviluppatore (DX).
87
157
 
88
- **Perché è importante:** Il typing forte sposta i fallimenti a sinistra (CI/build) invece che a destra (runtime).
158
+ `next-intl` e `next-i18next` utilizzano un approccio centralizzato per gestire le traduzioni, permettendo di suddividere i file JSON per locale e per sotto-file. In `next-i18next`, chiamiamo i file JSON 'namespaces'; `next-intl` permette di dichiarare i messaggi. In `intlayer`, chiamiamo i file JSON 'dictionaries'.
159
+
160
+ - Nel caso di `next-intl`, come in `next-i18next`, il contenuto viene caricato a livello di pagina/layout, quindi questo contenuto viene caricato in un context provider. Ciò significa che lo sviluppatore deve gestire manualmente i file JSON che verranno caricati per ogni pagina.
161
+
162
+ > In pratica, questo implica che gli sviluppatori spesso saltano questa ottimizzazione, preferendo caricare tutto il contenuto nel context provider della pagina per semplicità.
163
+
164
+ - Nel caso di `intlayer`, tutto il contenuto viene caricato nell'applicazione. Successivamente un plugin (`@intlayer/babel` / `@intlayer/swc`) si occupa di ottimizzare il bundle caricando solo il contenuto utilizzato nella pagina. Lo sviluppatore quindi non deve gestire manualmente i dizionari che verranno caricati. Questo permette una migliore ottimizzazione, una migliore manutenibilità e riduce i tempi di sviluppo.
165
+
166
+ Man mano che l'applicazione cresce (soprattutto quando più sviluppatori lavorano sull'applicazione), è comune dimenticare di rimuovere il contenuto che non viene più utilizzato dai file JSON.
167
+
168
+ > Nota che tutti i file JSON vengono caricati in tutti i casi (next-intl, next-i18next, intlayer).
169
+
170
+ Ecco perché l'approccio di Intlayer è più performante: se un componente non viene più utilizzato, il suo dizionario non viene caricato nel bundle.
171
+
172
+ È anche importante come la libreria gestisce i fallback. Consideriamo che l'applicazione sia in inglese di default e che l'utente visiti la pagina `/fr/about`. Se le traduzioni in francese mancano, considereremo il fallback in inglese.
173
+
174
+ Nel caso di `next-intl` e `next-i18next`, la libreria richiede il caricamento del JSON relativo alla locale corrente, ma anche a quella di fallback. Quindi, considerando che tutto il contenuto è stato tradotto, ogni pagina caricherà il 100% di contenuto non necessario. **In confronto, `intlayer` elabora il fallback al momento della costruzione del dizionario. Pertanto, ogni pagina caricherà solo il contenuto utilizzato.**
175
+
176
+ Ecco un esempio dell'impatto dell'ottimizzazione della dimensione del bundle utilizzando `intlayer` in un'applicazione vite + react:
177
+
178
+ | Bundle ottimizzato | Bundle non ottimizzato |
179
+ | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
180
+ | ![bundle ottimizzato](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true) | ![bundle non ottimizzato](https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle_no_optimization.png?raw=true) |
89
181
 
90
182
  ---
91
183
 
92
- ### 3) Gestione delle traduzioni mancanti
184
+ ## TypeScript e sicurezza
185
+
186
+ <Columns>
187
+ <Column>
188
+
189
+ **next-intl**
190
+
191
+ - Supporto solido per TypeScript, ma **le chiavi non sono tipizzate rigorosamente di default**; dovrai mantenere manualmente i pattern di sicurezza.
192
+
193
+ </Column>
194
+ <Column>
195
+
196
+ **next-i18next**
197
+
198
+ - Tipizzazioni di base per gli hook; **la tipizzazione rigorosa delle chiavi richiede strumenti/configurazioni aggiuntive**.
199
+
200
+ </Column>
201
+ <Column>
93
202
 
94
- - **next-intl / next-i18next**: Si affidano a **fallback a runtime** (es. mostrare la chiave o la lingua predefinita). La build non fallisce.
95
- - **Intlayer**: **Rilevamento a tempo di build** con **avvisi/errori** per lingue o chiavi mancanti.
203
+ **intlayer**
96
204
 
97
- **Perché è importante:** Catturare le lacune durante la build previene “stringhe misteriose” in produzione e si allinea a rigide regole di rilascio.
205
+ - **Genera tipi rigorosi** dal tuo contenuto. **Completamento automatico nell’IDE** e **errori a tempo di compilazione** individuano errori di battitura e chiavi mancanti prima del deploy.
206
+
207
+ </Column>
208
+ </Columns>
209
+
210
+ **Perché è importante:** La tipizzazione forte sposta i fallimenti a sinistra (CI/build) invece che a destra (runtime).
98
211
 
99
212
  ---
100
213
 
101
- ### 4) Routing, middleware e strategia URL
214
+ ## Gestione delle traduzioni mancanti
215
+
216
+ **next-intl**
217
+
218
+ - Si basa su **fallback a runtime** (es. mostra la chiave o la locale di default). La build non fallisce.
219
+
220
+ **next-i18next**
102
221
 
103
- - Tutti e tre funzionano con il **routing localizzato di Next.js** sull’App Router.
104
- - **Intlayer** va oltre con il **middleware i18n** (rilevamento della lingua tramite header/cookie) e **helper** per generare URL localizzati e tag `<link rel="alternate" hreflang="…">`.
222
+ - Si basa su **fallback a runtime** (es. mostra la chiave o la locale di default). La build non fallisce.
105
223
 
106
- **Perché è importante:** Meno livelli di collegamento personalizzati; **UX coerente** e **SEO pulita** tra le diverse localizzazioni.
224
+ **intlayer**
225
+
226
+ - **Rilevamento a tempo di build** con **avvisi/errori** per locali o chiavi mancanti.
227
+
228
+ **Perché è importante:** Individuare le lacune durante la build previene “stringhe misteriose” in produzione e si allinea con rigide regole di rilascio.
107
229
 
108
230
  ---
109
231
 
110
- ### 5) Allineamento con i Server Components (RSC)
232
+ ## Routing, middleware e strategia URL
233
+
234
+ <Columns>
235
+ <Column>
236
+
237
+ **next-intl**
111
238
 
112
- - **Tutti** supportano Next.js 13+.
113
- - **Intlayer** semplifica il **confine server/client** con un’API coerente e provider progettati per RSC, così non devi passare formatter o funzioni di traduzione attraverso gli alberi dei componenti.
239
+ - Funziona con il **routing localizzato di Next.js** sull'App Router.
114
240
 
115
- **Perché è importante:** Modello mentale più chiaro e meno casi limite negli alberi ibridi.
241
+ </Column>
242
+ <Column>
243
+
244
+ **next-i18next**
245
+
246
+ - Funziona con il **routing localizzato di Next.js** sull'App Router.
247
+
248
+ </Column>
249
+ <Column>
250
+
251
+ **intlayer**
252
+
253
+ - Tutto quanto sopra, più **middleware i18n** (rilevamento della localizzazione tramite header/cookie) e **helper** per generare URL localizzati e tag `<link rel="alternate" hreflang="…">`.
254
+
255
+ </Column>
256
+ </Columns>
257
+
258
+ **Perché è importante:** Meno livelli di collegamento personalizzati; **UX coerente** e **SEO pulito** tra le diverse localizzazioni.
116
259
 
117
260
  ---
118
261
 
119
- ### 6) Prestazioni e comportamento di caricamento
262
+ ## Allineamento con Server Components (RSC)
263
+
264
+ <Columns>
265
+ <Column>
120
266
 
121
- - **next-intl / next-i18next**: Controllo parziale tramite **namespaces** e **divisioni a livello di route**; rischio di includere stringhe inutilizzate se non si mantiene la disciplina.
122
- - **Intlayer**: Effettua **tree-shaking** durante la build e **caricamento lazy per dizionario/locale**. Il contenuto non utilizzato non viene incluso.
267
+ **next-intl**
123
268
 
124
- **Perché è importante:** Bundle più piccoli e avvio più veloce, specialmente su siti multilingua.
269
+ - Supporta Next.js 13+. Spesso richiede di passare funzioni t/formatter attraverso gli alberi dei componenti in configurazioni ibride.
270
+
271
+ </Column>
272
+ <Column>
273
+
274
+ **next-i18next**
275
+
276
+ - Supporta Next.js 13+. Vincoli simili nel passaggio degli strumenti di traduzione attraverso i confini.
277
+
278
+ </Column>
279
+ <Column>
280
+
281
+ **intlayer**
282
+
283
+ - Supporta Next.js 13+ e facilita il **confine server/client** con un'API coerente e provider orientati a RSC, evitando il passaggio continuo di formatter o funzioni t.
284
+
285
+ </Column>
286
+ </Columns>
287
+
288
+ **Perché è importante:** Modello mentale più pulito e meno casi limite negli alberi ibridi.
125
289
 
126
290
  ---
127
291
 
128
- ### 7) DX, strumenti e manutenzione
292
+ ## DX, strumenti e manutenzione
293
+
294
+ <Columns>
295
+ <Column>
129
296
 
130
- - **next-intl / next-i18next**: Di solito si collegano piattaforme esterne per traduzioni e flussi editoriali.
131
- - **Intlayer**: Include un **Editor Visivo gratuito** e un **CMS opzionale** (compatibile con Git o esternalizzato). Inoltre, una **estensione VSCode** per la creazione di contenuti e **traduzioni assistite da AI** utilizzando le tue chiavi provider.
297
+ **next-intl**
298
+
299
+ - Comunemente abbinato a piattaforme di localizzazione esterne e flussi editoriali.
300
+
301
+ </Column>
302
+ <Column>
303
+
304
+ **next-i18next**
305
+
306
+ - Comunemente abbinato a piattaforme di localizzazione esterne e flussi editoriali.
307
+
308
+ </Column>
309
+ <Column>
310
+
311
+ **intlayer**
312
+
313
+ - Fornisce un **Editor Visivo gratuito** e un **CMS opzionale** (compatibile con Git o esternalizzato), oltre a un’**estensione VSCode** e **traduzioni assistite da AI** utilizzando le tue chiavi provider.
314
+
315
+ </Column>
316
+ </Columns>
132
317
 
133
318
  **Perché è importante:** Riduce i costi operativi e accorcia il ciclo tra sviluppatori e autori dei contenuti.
134
319
 
320
+ ## Integrazione con piattaforme di localizzazione (TMS)
321
+
322
+ Le grandi organizzazioni spesso si affidano a Sistemi di Gestione delle Traduzioni (TMS) come **Crowdin**, **Phrase**, **Lokalise**, **Localizely** o **Localazy**.
323
+
324
+ - **Perché interessa alle aziende**
325
+ - **Collaborazione e ruoli**: Sono coinvolti più attori: sviluppatori, product manager, traduttori, revisori, team marketing.
326
+ - **Scala ed efficienza**: localizzazione continua, revisione in contesto.
327
+
328
+ - **next-intl / next-i18next**
329
+ - Tipicamente utilizzano **cataloghi JSON centralizzati**, quindi l'esportazione/importazione con TMS è semplice.
330
+ - Ecosistemi maturi ed esempi/integrazioni per le piattaforme sopra menzionate.
331
+
332
+ - **Intlayer**
333
+ - Favorisce **dizionari decentralizzati per componente** e supporta contenuti in **TypeScript/TSX/JS/JSON/MD**.
334
+ - Questo migliora la modularità nel codice, ma può rendere più difficile l'integrazione plug-and-play con TMS quando uno strumento si aspetta file JSON centralizzati e piatti.
335
+ - Intlayer offre alternative: **traduzioni assistite da AI** (usando le tue chiavi provider), un **Editor Visivo/CMS** e workflow **CLI/CI** per rilevare e precompilare le lacune.
336
+
337
+ > Nota: `next-intl` e `i18next` accettano anche cataloghi TypeScript. Se il tuo team memorizza i messaggi in file `.ts` o li decentralizza per funzionalità, potresti incontrare attriti simili con il TMS. Tuttavia, molte configurazioni di `next-intl` rimangono centralizzate in una cartella `locales/`, che è un po' più facile da rifattorizzare in JSON per il TMS.
338
+
339
+ ## Esperienza dello Sviluppatore
340
+
341
+ Questa parte fa un confronto approfondito tra le tre soluzioni. Piuttosto che considerare casi semplici, come descritto nella documentazione 'getting started' per ogni soluzione, considereremo un caso d'uso reale, più simile a un progetto reale.
342
+
343
+ ### Struttura dell'app
344
+
345
+ La struttura dell'app è importante per garantire una buona manutenibilità del tuo codice.
346
+
347
+ <Tab defaultTab="next-intl" group='techno'>
348
+
349
+ <TabItem label="next-i18next" value="next-i18next">
350
+
351
+ ```bash
352
+ .
353
+ ├── i18n.config.ts
354
+ └── src
355
+ ├── locales
356
+ │ ├── en
357
+ │ │ ├── common.json
358
+ │ │ └── about.json
359
+ │ └── fr
360
+ │ ├── common.json
361
+ │ └── about.json
362
+ ├── app
363
+ │ ├── i18n
364
+ │ │ └── server.ts
365
+ │ └── [locale]
366
+ │ ├── layout.tsx
367
+ │ └── about.tsx
368
+ └── components
369
+ ├── I18nProvider.tsx
370
+ ├── ClientComponent.tsx
371
+ └── ServerComponent.tsx
372
+ ```
373
+
374
+ </TabItem>
375
+ <TabItem label="next-intl" value="next-intl">
376
+
377
+ ```bash
378
+ .
379
+ ├── i18n.ts
380
+ ├── locales
381
+ │ ├── en
382
+ │ │ ├── home.json
383
+ │ │ └── navbar.json
384
+ │ ├── fr
385
+ │ │ ├── home.json
386
+ │ │ └── navbar.json
387
+ │ └── es
388
+ │ ├── home.json
389
+ │ └── navbar.json
390
+ └── src
391
+ ├── middleware.ts
392
+ ├── app
393
+ │ ├── i18n
394
+ │ │ └── server.ts
395
+ │ └── [locale]
396
+ │ └── home.tsx
397
+ └── components
398
+ └── Navbar
399
+ └── index.tsx
400
+ ```
401
+
402
+ </TabItem>
403
+ <TabItem label="intlayer" value="intlayer">
404
+
405
+ ```bash
406
+ .
407
+ ├── intlayer.config.ts
408
+ └── src
409
+ ├── middleware.ts
410
+ ├── app
411
+ │ └── [locale]
412
+ │ ├── layout.tsx
413
+ │ └── home
414
+ │ ├── index.tsx
415
+ │ └── index.content.ts
416
+ └── components
417
+ └── Navbar
418
+ ├── index.tsx
419
+ └── index.content.ts
420
+ ```
421
+
422
+ </TabItem>
423
+ </Tab>
424
+
425
+ #### Confronto
426
+
427
+ - **next-intl / next-i18next**: Cataloghi centralizzati (JSON; namespace/messaggi). Struttura chiara, si integra bene con le piattaforme di traduzione, ma può portare a modifiche incrociate tra file man mano che le app crescono.
428
+ - **Intlayer**: Dizionari `.content.{ts|js|json}` per componente co-localizzati con i componenti. Facilita il riuso dei componenti e il ragionamento locale; aggiunge file e si basa su strumenti di build-time.
429
+
430
+ #### Configurazione e caricamento del contenuto
431
+
432
+ Come accennato in precedenza, è necessario ottimizzare il modo in cui ogni file JSON viene importato nel tuo codice.
433
+ Il modo in cui la libreria gestisce il caricamento dei contenuti è importante.
434
+
435
+ <Tab defaultTab="next-intl" group='techno'>
436
+ <TabItem label="next-i18next" value="next-i18next">
437
+
438
+ ```tsx fileName="next-i18next.config.js"
439
+ module.exports = {
440
+ i18n: {
441
+ locales: ["en", "fr", "es"],
442
+ defaultLocale: "en",
443
+ },
444
+ };
445
+ ```
446
+
447
+ ```tsx fileName="src/app/_app.tsx"
448
+ import { appWithTranslation } from "next-i18next";
449
+
450
+ const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />;
451
+
452
+ export default appWithTranslation(MyApp);
453
+ ```
454
+
455
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
456
+ import type { GetStaticProps } from "next";
457
+ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
458
+ import { useTranslation } from "next-i18next";
459
+ import { I18nextProvider, initReactI18next } from "react-i18next";
460
+ import { createInstance } from "i18next";
461
+ import { ClientComponent, ServerComponent } from "@components";
462
+
463
+ export default function HomePage({ locale }: { locale: string }) {
464
+ // Dichiarare esplicitamente il namespace utilizzato da questo componente
465
+ const resources = await loadMessagesFor(locale); // il tuo loader (JSON, ecc.)
466
+
467
+ const i18n = createInstance();
468
+ i18n.use(initReactI18next).init({
469
+ lng: locale,
470
+ fallbackLng: "en",
471
+ resources,
472
+ ns: ["common", "about"],
473
+ defaultNS: "common",
474
+ interpolation: { escapeValue: false },
475
+ });
476
+
477
+ const { t } = useTranslation("about");
478
+
479
+ return (
480
+ <I18nextProvider i18n={i18n}>
481
+ <main>
482
+ <h1>{t("title")}</h1>
483
+ <ClientComponent />
484
+ <ServerComponent />
485
+ </main>
486
+ </I18nextProvider>
487
+ );
488
+ }
489
+
490
+ export const getStaticProps: GetStaticProps = async ({ locale }) => {
491
+ // Carica solo i namespace necessari per QUESTA pagina
492
+ return {
493
+ props: {
494
+ ...(await serverSideTranslations(locale ?? "en", ["common", "about"])),
495
+ },
496
+ };
497
+ };
498
+ ```
499
+
500
+ </TabItem>
501
+ <TabItem label="next-intl" value="next-intl">
502
+
503
+ ```tsx fileName="i18n.ts"
504
+ import { getRequestConfig } from "next-intl/server";
505
+ import { notFound } from "next/navigation";
506
+
507
+ // Può essere importato da una configurazione condivisa
508
+ const locales = ["en", "fr", "es"];
509
+
510
+ export default getRequestConfig(async ({ locale }) => {
511
+ // Verifica che il parametro `locale` in ingresso sia valido
512
+ if (!locales.includes(locale as any)) notFound();
513
+
514
+ return {
515
+ messages: (await import(`../messages/${locale}.json`)).default,
516
+ };
517
+ });
518
+ ```
519
+
520
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
521
+ import { NextIntlClientProvider } from "next-intl";
522
+ import { getMessages, unstable_setRequestLocale } from "next-intl/server";
523
+ import pick from "lodash/pick";
524
+
525
+ export default async function LocaleLayout({
526
+ children,
527
+ params,
528
+ }: {
529
+ children: React.ReactNode;
530
+ params: { locale: string };
531
+ }) {
532
+ const { locale } = params;
533
+
534
+ // Imposta la locale attiva per questa richiesta di rendering server (RSC)
535
+ unstable_setRequestLocale(locale);
536
+
537
+ // I messaggi sono caricati lato server tramite src/i18n/request.ts
538
+ // (vedi documentazione next-intl). Qui inviamo solo un sottoinsieme al client
539
+ // necessario per i componenti client (ottimizzazione del payload).
540
+ const messages = await getMessages();
541
+ const clientMessages = pick(messages, ["common", "about"]);
542
+
543
+ const rtlLocales = ["ar", "he", "fa", "ur"];
544
+
545
+ return (
546
+ <html lang={locale} dir={rtlLocales.includes(locale) ? "rtl" : "ltr"}>
547
+ <body>
548
+ <NextIntlClientProvider locale={locale} messages={clientMessages}>
549
+ {children}
550
+ </NextIntlClientProvider>
551
+ </body>
552
+ </html>
553
+ );
554
+ }
555
+ ```
556
+
557
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
558
+ import { getTranslations } from "next-intl/server";
559
+ import { ClientComponent, ServerComponent } from "@components";
560
+
561
+ export default async function LandingPage({
562
+ params,
563
+ }: {
564
+ params: { locale: string };
565
+ }) {
566
+ // Caricamento strettamente lato server (non idratato sul client)
567
+ const t = await getTranslations("about");
568
+
569
+ return (
570
+ <main>
571
+ <h1>{t("title")}</h1>
572
+ <ClientComponent />
573
+ <ServerComponent />
574
+ </main>
575
+ );
576
+ }
577
+ ```
578
+
579
+ </TabItem>
580
+ <TabItem label="intlayer" value="intlayer">
581
+
582
+ ```tsx fileName="intlayer.config.ts"
583
+ export default {
584
+ internationalization: {
585
+ locales: ["en", "fr", "es"],
586
+ defaultLocale: "en",
587
+ },
588
+ };
589
+ ```
590
+
591
+ ```tsx fileName="src/app/[locale]/layout.tsx"
592
+ import { getHTMLTextDir } from "intlayer";
593
+ import {
594
+ IntlayerClientProvider,
595
+ generateStaticParams,
596
+ type NextLayoutIntlayer,
597
+ } from "next-intlayer";
598
+
599
+ export const dynamic = "force-static";
600
+
601
+ const LandingLayout: NextLayoutIntlayer = async ({ children, params }) => {
602
+ const { locale } = await params;
603
+
604
+ return (
605
+ <html lang={locale} dir={getHTMLTextDir(locale)}>
606
+ <IntlayerClientProvider locale={locale}>
607
+ {children}
608
+ </IntlayerClientProvider>
609
+ </html>
610
+ );
611
+ };
612
+
613
+ export default LandingLayout;
614
+ ```
615
+
616
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
617
+ import { PageContent } from "@components/PageContent";
618
+ import type { NextPageIntlayer } from "next-intlayer";
619
+ import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
620
+ import { ClientComponent, ServerComponent } from "@components";
621
+
622
+ const LandingPage: NextPageIntlayer = async ({ params }) => {
623
+ const { locale } = await params;
624
+ const { title } = useIntlayer("about", locale);
625
+
626
+ return (
627
+ <IntlayerServerProvider locale={locale}>
628
+ <main>
629
+ <h1>{title}</h1>
630
+ <ClientComponent />
631
+ <ServerComponent />
632
+ </main>
633
+ </IntlayerServerProvider>
634
+ );
635
+ };
636
+
637
+ export default LandingPage;
638
+ ```
639
+
640
+ </TabItem>
641
+ </Tab>
642
+
643
+ #### Confronto
644
+
645
+ Tutti e tre supportano il caricamento di contenuti e provider per locale.
646
+
647
+ - Con **next-intl/next-i18next**, di solito carichi i messaggi/namespace selezionati per ogni percorso e posizioni i provider dove necessario.
648
+
649
+ - Con **Intlayer**, viene aggiunta un'analisi in fase di build per dedurre l'uso, il che può ridurre la configurazione manuale e permettere un singolo provider radice.
650
+
651
+ Scegli tra controllo esplicito e automazione in base alle preferenze del team.
652
+
653
+ ### Uso in un componente client
654
+
655
+ Prendiamo un esempio di un componente client che rende un contatore.
656
+
657
+ <Tab defaultTab="next-intl" group='techno'>
658
+ <TabItem label="next-i18next" value="next-i18next">
659
+
660
+ **Traduzioni (devono essere veri JSON in `public/locales/...`)**
661
+
662
+ ```json fileName="public/locales/en/about.json"
663
+ {
664
+ "counter": {
665
+ "label": "Contatore",
666
+ "increment": "Incrementa"
667
+ }
668
+ }
669
+ ```
670
+
671
+ ```json fileName="public/locales/fr/about.json"
672
+ {
673
+ "counter": {
674
+ "label": "Contatore",
675
+ "increment": "Incrementa"
676
+ }
677
+ }
678
+ ```
679
+
680
+ **Componente client**
681
+
682
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
683
+ "use client";
684
+
685
+ import React, { useMemo, useState } from "react";
686
+ import { useTranslation } from "next-i18next";
687
+
688
+ const ClientComponentExample = () => {
689
+ const { t, i18n } = useTranslation("about");
690
+ const [count, setCount] = useState(0);
691
+
692
+ // next-i18next non espone useNumber; usa Intl.NumberFormat
693
+ const numberFormat = new Intl.NumberFormat(i18n.language);
694
+
695
+ return (
696
+ <div>
697
+ <p>{numberFormat.format(count)}</p>
698
+ <button
699
+ aria-label={t("counter.label")}
700
+ onClick={() => setCount((count) => count + 1)}
701
+ >
702
+ {t("counter.increment")}
703
+ </button>
704
+ </div>
705
+ );
706
+ };
707
+ ```
708
+
709
+ > Non dimenticare di aggiungere il namespace "about" nelle serverSideTranslations della pagina
710
+ > Qui utilizziamo la versione di React 19.x.x, ma per versioni inferiori sarà necessario usare useMemo per memorizzare l'istanza del formatter poiché è una funzione pesante
711
+
712
+ </TabItem>
713
+ <TabItem label="next-intl" value="next-intl">
714
+
715
+ **Traduzioni (struttura riutilizzata; caricale nei messaggi di next-intl come preferisci)**
716
+
717
+ ```json fileName="locales/en/about.json"
718
+ {
719
+ "counter": {
720
+ "label": "Counter",
721
+ "increment": "Increment"
722
+ }
723
+ }
724
+ ```
725
+
726
+ ```json fileName="locales/fr/about.json"
727
+ {
728
+ "counter": {
729
+ "label": "Compteur",
730
+ "increment": "Incrémenter"
731
+ }
732
+ }
733
+ ```
734
+
735
+ **Componente client**
736
+
737
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
738
+ "use client";
739
+
740
+ import React, { useState } from "react";
741
+ import { useTranslations, useFormatter } from "next-intl";
742
+
743
+ const ClientComponentExample = () => {
744
+ // Ambito direttamente sull'oggetto annidato
745
+ const t = useTranslations("about.counter");
746
+ const format = useFormatter();
747
+ const [count, setCount] = useState(0);
748
+
749
+ return (
750
+ <div>
751
+ <p>{format.number(count)}</p>
752
+ <button
753
+ aria-label={t("label")}
754
+ onClick={() => setCount((count) => count + 1)}
755
+ >
756
+ {t("increment")}
757
+ </button>
758
+ </div>
759
+ );
760
+ };
761
+ ```
762
+
763
+ > Non dimenticare di aggiungere il messaggio "about" nel messaggio client della pagina
764
+
765
+ </TabItem>
766
+ <TabItem label="intlayer" value="intlayer">
767
+
768
+ **Contenuto**
769
+
770
+ ```ts fileName="src/components/ClientComponentExample/index.content.ts"
771
+ import { t, type Dictionary } from "intlayer";
772
+
773
+ const counterContent = {
774
+ key: "counter",
775
+ content: {
776
+ label: t({ it: "Contatore", en: "Counter", fr: "Compteur" }),
777
+ increment: t({ it: "Incrementa", en: "Increment", fr: "Incrémenter" }),
778
+ },
779
+ } satisfies Dictionary;
780
+
781
+ export default counterContent;
782
+ ```
783
+
784
+ **Componente client**
785
+
786
+ ```tsx fileName="src/components/ClientComponentExample/index.tsx"
787
+ "use client";
788
+
789
+ import React, { useState } from "react";
790
+ import { useNumber, useIntlayer } from "next-intlayer";
791
+
792
+ const ClientComponentExample = () => {
793
+ const [count, setCount] = useState(0);
794
+ const { label, increment } = useIntlayer("counter"); // restituisce stringhe
795
+ const { number } = useNumber();
796
+
797
+ return (
798
+ <div>
799
+ <p>{number(count)}</p>
800
+ <button aria-label={label} onClick={() => setCount((count) => count + 1)}>
801
+ {increment}
802
+ </button>
803
+ </div>
804
+ );
805
+ };
806
+ ```
807
+
808
+ </TabItem>
809
+ </Tab>
810
+
811
+ #### Confronto
812
+
813
+ - **Formattazione dei numeri**
814
+ - **next-i18next**: nessun `useNumber`; utilizza `Intl.NumberFormat` (o i18next-icu).
815
+ - **next-intl**: `useFormatter().number(value)`.
816
+ - **Intlayer**: `useNumber()` integrato.
817
+
818
+ - **Chiavi**
819
+ - Mantieni una struttura annidata (`about.counter.label`) e definisci l’ambito del tuo hook di conseguenza (`useTranslation("about")` + `t("counter.label")` oppure `useTranslations("about.counter")` + `t("label")`).
820
+
821
+ - **Posizione dei file**
822
+ - **next-i18next** si aspetta JSON in `public/locales/{lng}/{ns}.json`.
823
+ - **next-intl** è flessibile; carica i messaggi come preferisci.
824
+ - **Intlayer** memorizza i contenuti in dizionari TS/JS e risolve tramite chiave.
825
+
135
826
  ---
136
827
 
137
- ## Quando scegliere quale?
828
+ ### Utilizzo in un componente server
829
+
830
+ Prendiamo il caso di un componente UI. Questo componente è un componente server e dovrebbe poter essere inserito come figlio di un componente client. (pagina (componente server) -> componente client -> componente server). Poiché questo componente può essere inserito come figlio di un componente client, non può essere async.
831
+
832
+ <Tab defaultTab="next-intl" group='techno'>
833
+ <TabItem label="next-i18next" value="next-i18next">
834
+
835
+ ```tsx fileName="src/pages/about.tsx"
836
+ import type { GetStaticProps } from "next";
837
+ import { useTranslation } from "next-i18next";
838
+
839
+ type ServerComponentProps = {
840
+ count: number;
841
+ };
842
+
843
+ const ServerComponent = ({ count }: ServerComponentProps) => {
844
+ const { t, i18n } = useTranslation("about");
845
+ const formatted = new Intl.NumberFormat(i18n.language).format(count);
846
+
847
+ return (
848
+ <div>
849
+ <p>{formatted}</p>
850
+ <button aria-label={t("counter.label")}>{t("counter.increment")}</button>
851
+ </div>
852
+ );
853
+ };
854
+ ```
855
+
856
+ </TabItem>
857
+ <TabItem label="next-intl" value="next-intl">
858
+
859
+ ```tsx fileName="src/components/ServerComponent.tsx"
860
+ type ServerComponentProps = {
861
+ count: number;
862
+ t: (key: string) => string;
863
+ formatter: Intl.NumberFormat;
864
+ };
865
+
866
+ const ServerComponent = ({ t, count, formatter }: ServerComponentProps) => {
867
+ const formatted = formatter.format(count);
868
+
869
+ return (
870
+ <div>
871
+ <p>{formatted}</p>
872
+ <button aria-label={t("label")}>{t("increment")}</button>
873
+ </div>
874
+ );
875
+ };
876
+ ```
877
+
878
+ > Poiché il componente server non può essere asincrono, è necessario passare le traduzioni e la funzione di formattazione come props.
879
+ >
880
+ > - `const t = await getTranslations("about.counter");`
881
+ > - `const formatter = await getFormatter().then((formatter) => formatter.number());`
882
+
883
+ </TabItem>
884
+ <TabItem label="intlayer" value="intlayer">
885
+
886
+ ```tsx fileName="src/components/ServerComponent.tsx"
887
+ import { useIntlayer, useNumber } from "next-intlayer/server";
888
+
889
+ const ServerComponent = ({ count }: { count: number }) => {
890
+ const { label, increment } = useIntlayer("counter");
891
+ const { number } = useNumber();
892
+
893
+ return (
894
+ <div>
895
+ <p>{number(count)}</p>
896
+ <button aria-label={label}>{increment}</button>
897
+ </div>
898
+ );
899
+ };
900
+ ```
901
+
902
+ </TabItem>
903
+ </Tab>
904
+
905
+ > Intlayer espone hook **sicuri per il server** tramite `next-intlayer/server`. Per funzionare, `useIntlayer` e `useNumber` utilizzano una sintassi simile agli hook client, ma dipendono internamente dal contesto server (`IntlayerServerProvider`).
906
+
907
+ ### Metadata / Sitemap / Robots
908
+
909
+ Tradurre i contenuti è ottimo. Ma spesso si dimentica che l'obiettivo principale dell'internazionalizzazione è rendere il tuo sito web più visibile al mondo. L'i18n è una leva incredibile per migliorare la visibilità del tuo sito web.
138
910
 
139
- - **Scegli next-intl** se desideri una soluzione **minimale**, ti senti a tuo agio con cataloghi centralizzati e la tua app è di **dimensioni piccole o medie**.
140
- - **Scegli next-i18next** se hai bisogno dell’**ecosistema di plugin di i18next** (ad esempio, regole ICU avanzate tramite plugin) e il tuo team conosce già i18next, accettando una **maggiore configurazione** per flessibilità.
141
- - **Scegli Intlayer** se apprezzi il **contenuto a livello di componente**, il **TypeScript rigoroso**, le **garanzie a build-time**, il **tree-shaking** e gli strumenti **batteries-included** per routing/SEO/editor - specialmente per **Next.js App Router** e **codebase grandi e modulari**.
911
+ Ecco una lista di buone pratiche riguardanti la SEO multilingue.
912
+
913
+ - impostare i meta tag hreflang nel tag `<head>`
914
+ > Aiuta i motori di ricerca a capire quali lingue sono disponibili nella pagina
915
+ - elencare tutte le traduzioni delle pagine nel sitemap.xml utilizzando lo schema XML `http://www.w3.org/1999/xhtml`
916
+ >
917
+ - non dimenticare di escludere le pagine con prefisso dal robots.txt (es. `/dashboard`, e `/fr/dashboard`, `/es/dashboard`)
918
+ >
919
+ - utilizzare un componente Link personalizzato per reindirizzare alla pagina più localizzata (es. in francese `<a href="/fr/about">A propos</a>`)
920
+ >
921
+
922
+ Gli sviluppatori spesso dimenticano di referenziare correttamente le loro pagine tra le diverse localizzazioni.
923
+
924
+ <Tab defaultTab="next-intl" group='techno'>
925
+
926
+ <TabItem label="next-i18next" value="next-i18next">
927
+
928
+ ```ts fileName="i18n.config.ts"
929
+ export const locales = ["en", "fr"] as const;
930
+ export type Locale = (typeof locales)[number];
931
+ export const defaultLocale: Locale = "en";
932
+
933
+ export function localizedPath(locale: string, path: string) {
934
+ return locale === defaultLocale ? path : "/" + locale + path;
935
+ }
936
+
937
+ const ORIGIN = "https://example.com";
938
+ export function abs(locale: string, path: string) {
939
+ return ORIGIN + localizedPath(locale, path);
940
+ }
941
+ ```
942
+
943
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
944
+ import type { Metadata } from "next";
945
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
946
+
947
+ export async function generateMetadata({
948
+ params,
949
+ }: {
950
+ params: { locale: string };
951
+ }): Promise<Metadata> {
952
+ const { locale } = params;
953
+
954
+ // Importa dinamicamente il file JSON corretto
955
+ const messages = (
956
+ await import("@/../public/locales/" + locale + "/about.json")
957
+ ).default;
958
+
959
+ const languages = Object.fromEntries(
960
+ locales.map((locale) => [locale, localizedPath(locale, "/about")])
961
+ );
962
+
963
+ return {
964
+ title: messages.title,
965
+ description: messages.description,
966
+ alternates: {
967
+ canonical: localizedPath(locale, "/about"),
968
+ languages: { ...languages, "x-default": "/about" },
969
+ },
970
+ };
971
+ }
972
+
973
+ export default async function AboutPage() {
974
+ return <h1>Informazioni</h1>;
975
+ }
976
+ ```
977
+
978
+ ```ts fileName="src/app/sitemap.ts"
979
+ import type { MetadataRoute } from "next";
980
+ import { locales, defaultLocale, abs } from "@/i18n.config";
981
+
982
+ export default function sitemap(): MetadataRoute.Sitemap {
983
+ const languages = Object.fromEntries(
984
+ locales.map((locale) => [locale, abs(locale, "/about")])
985
+ );
986
+ return [
987
+ {
988
+ url: abs(defaultLocale, "/about"),
989
+ lastModified: new Date(),
990
+ changeFrequency: "monthly",
991
+ priority: 0.7,
992
+ alternates: { languages },
993
+ },
994
+ ];
995
+ }
996
+ ```
997
+
998
+ ```ts fileName="src/app/robots.ts"
999
+ import type { MetadataRoute } from "next";
1000
+ import { locales, defaultLocale, localizedPath } from "@/i18n.config";
1001
+
1002
+ const ORIGIN = "https://example.com";
1003
+
1004
+ const expandAllLocales = (path: string) => [
1005
+ localizedPath(defaultLocale, path),
1006
+ ...locales
1007
+ .filter((locale) => locale !== defaultLocale)
1008
+ .map((locale) => localizedPath(locale, path)),
1009
+ ];
1010
+
1011
+ export default function robots(): MetadataRoute.Robots {
1012
+ const disallow = [
1013
+ ...expandAllLocales("/dashboard"),
1014
+ ...expandAllLocales("/admin"),
1015
+ ];
1016
+
1017
+ return {
1018
+ rules: { userAgent: "*", allow: ["/"], disallow },
1019
+ host: ORIGIN,
1020
+ sitemap: ORIGIN + "/sitemap.xml",
1021
+ };
1022
+ }
1023
+ ```
1024
+
1025
+ </TabItem>
1026
+ <TabItem label="next-intl" value="next-intl">
1027
+
1028
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
1029
+ import type { Metadata } from "next";
1030
+ import { locales, defaultLocale } from "@/i18n";
1031
+ import { getTranslations } from "next-intl/server";
1032
+
1033
+ function localizedPath(locale: string, path: string) {
1034
+ return locale === defaultLocale ? path : "/" + locale + path;
1035
+ }
1036
+
1037
+ export async function generateMetadata({
1038
+ params,
1039
+ }: {
1040
+ params: { locale: string };
1041
+ }): Promise<Metadata> {
1042
+ const { locale } = params;
1043
+ const t = await getTranslations({ locale, namespace: "about" });
1044
+
1045
+ const url = "/about";
1046
+ const languages = Object.fromEntries(
1047
+ locales.map((locale) => [locale, localizedPath(locale, url)])
1048
+ );
1049
+
1050
+ return {
1051
+ title: t("title"),
1052
+ description: t("description"),
1053
+ alternates: {
1054
+ canonical: localizedPath(locale, url),
1055
+ languages: { ...languages, "x-default": url },
1056
+ },
1057
+ };
1058
+ }
1059
+
1060
+ // ... Resto del codice della pagina
1061
+ ```
1062
+
1063
+ ```tsx fileName="src/app/sitemap.ts"
1064
+ import type { MetadataRoute } from "next";
1065
+ import { locales, defaultLocale } from "@/i18n";
1066
+
1067
+ const origin = "https://example.com";
1068
+
1069
+ const formatterLocalizedPath = (locale: string, path: string) =>
1070
+ locale === defaultLocale ? origin + path : origin + "/" + locale + path;
1071
+
1072
+ export default function sitemap(): MetadataRoute.Sitemap {
1073
+ const aboutLanguages = Object.fromEntries(
1074
+ locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
1075
+ );
1076
+
1077
+ return [
1078
+ {
1079
+ url: formatterLocalizedPath(defaultLocale, "/about"),
1080
+ lastModified: new Date(),
1081
+ changeFrequency: "monthly",
1082
+ priority: 0.7,
1083
+ alternates: { languages: aboutLanguages },
1084
+ },
1085
+ ];
1086
+ }
1087
+ ```
1088
+
1089
+ ```tsx fileName="src/app/robots.ts"
1090
+ import type { MetadataRoute } from "next";
1091
+ import { locales, defaultLocale } from "@/i18n";
1092
+
1093
+ const origin = "https://example.com";
1094
+ const withAllLocales = (path: string) => [
1095
+ path,
1096
+ ...locales
1097
+ .filter((locale) => locale !== defaultLocale)
1098
+ .map((locale) => "/" + locale + path),
1099
+ ];
1100
+
1101
+ export default function robots(): MetadataRoute.Robots {
1102
+ const disallow = [
1103
+ ...withAllLocales("/dashboard"),
1104
+ ...withAllLocales("/admin"),
1105
+ ];
1106
+
1107
+ return {
1108
+ rules: { userAgent: "*", allow: ["/"], disallow },
1109
+ host: origin,
1110
+ sitemap: origin + "/sitemap.xml",
1111
+ };
1112
+ }
1113
+ ```
1114
+
1115
+ </TabItem>
1116
+ <TabItem label="intlayer" value="intlayer">
1117
+
1118
+ ```typescript fileName="src/app/[locale]/about/layout.tsx"
1119
+ import { getIntlayer, getMultilingualUrls } from "intlayer";
1120
+ import type { Metadata } from "next";
1121
+ import type { LocalPromiseParams } from "next-intlayer";
1122
+
1123
+ export const generateMetadata = async ({
1124
+ params,
1125
+ }: LocalPromiseParams): Promise<Metadata> => {
1126
+ const { locale } = await params;
1127
+
1128
+ const metadata = getIntlayer("page-metadata", locale);
1129
+
1130
+ const multilingualUrls = getMultilingualUrls("/about");
1131
+
1132
+ return {
1133
+ ...metadata,
1134
+ alternates: {
1135
+ canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
1136
+ languages: { ...multilingualUrls, "x-default": "/about" },
1137
+ },
1138
+ };
1139
+ };
1140
+
1141
+ // ... Resto del codice della pagina
1142
+ ```
1143
+
1144
+ ```tsx fileName="src/app/sitemap.ts"
1145
+ import { getMultilingualUrls } from "intlayer";
1146
+ import type { MetadataRoute } from "next";
1147
+
1148
+ const sitemap = (): MetadataRoute.Sitemap => [
1149
+ {
1150
+ url: "https://example.com/about",
1151
+ alternates: {
1152
+ languages: { ...getMultilingualUrls("https://example.com/about") },
1153
+ },
1154
+ },
1155
+ ];
1156
+ ```
1157
+
1158
+ ```tsx fileName="src/app/robots.ts"
1159
+ import { getMultilingualUrls } from "intlayer";
1160
+ import type { MetadataRoute } from "next";
1161
+
1162
+ const getAllMultilingualUrls = (urls: string[]) =>
1163
+ urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
1164
+
1165
+ // Regole per il file robots.txt
1166
+ const robots = (): MetadataRoute.Robots => ({
1167
+ rules: {
1168
+ userAgent: "*",
1169
+ allow: ["/"],
1170
+ disallow: getAllMultilingualUrls(["/dashboard"]), // Blocca l'accesso a tutte le versioni multilingue di /dashboard
1171
+ },
1172
+ host: "https://example.com",
1173
+ sitemap: "https://example.com/sitemap.xml",
1174
+ });
1175
+
1176
+ export default robots;
1177
+ ```
1178
+
1179
+ </TabItem>
1180
+ </Tab>
1181
+
1182
+ > Intlayer fornisce una funzione `getMultilingualUrls` per generare URL multilingue per la tua sitemap.
1183
+
1184
+ ---
142
1185
 
143
1186
  ---
144
1187
 
145
- ## Note pratiche per la migrazione (next-intl / next-i18next → Intlayer)
1188
+ ## E il vincitore è…
1189
+
1190
+ Non è semplice. Ogni opzione ha dei compromessi. Ecco come la vedo:
1191
+
1192
+ <Columns>
1193
+ <Column>
1194
+
1195
+ **next-intl**
1196
+
1197
+ - la più semplice, leggera, con meno decisioni imposte. Se vuoi una soluzione **minimale**, ti senti a tuo agio con cataloghi centralizzati e la tua app è di dimensioni **piccole o medie**.
1198
+
1199
+ </Column>
1200
+ <Column>
1201
+
1202
+ **next-i18next**
1203
+
1204
+ - matura, ricca di funzionalità, molti plugin della community, ma con un costo di configurazione più elevato. Se hai bisogno dell’**ecosistema di plugin di i18next** (ad esempio, regole ICU avanzate tramite plugin) e il tuo team conosce già i18next, accettando **più configurazione** per maggiore flessibilità.
1205
+
1206
+ </Column>
1207
+ <Column>
1208
+
1209
+ **Intlayer**
1210
+
1211
+ - costruito per Next.js moderno, con contenuti modulari, sicurezza dei tipi, strumenti e meno codice boilerplate. Se apprezzi **contenuti a livello di componente**, **TypeScript rigoroso**, **garanzie a tempo di build**, **tree-shaking** e strumenti **completi** per routing/SEO/editor - specialmente per **Next.js App Router**, sistemi di design e **codebase grandi e modulari**.
1212
+
1213
+ </Column>
1214
+ </Columns>
1215
+
1216
+ Se preferisci una configurazione minima e accetti un po' di collegamenti manuali, next-intl è una buona scelta. Se hai bisogno di tutte le funzionalità e non ti dispiace la complessità, next-i18next funziona. Ma se vuoi una soluzione moderna, scalabile, modulare con strumenti integrati, Intlayer mira a offrirti tutto questo pronto all'uso.
1217
+
1218
+ > **Alternativa per team aziendali**: Se avete bisogno di una soluzione ben collaudata che funzioni perfettamente con piattaforme di localizzazione consolidate come **Crowdin**, **Phrase** o altri sistemi professionali di gestione delle traduzioni, considerate **next-intl** o **next-i18next** per il loro ecosistema maturo e le integrazioni comprovate.
1219
+
1220
+ > **Roadmap futura**: Intlayer prevede anche di sviluppare plugin che lavorino sopra le soluzioni **i18next** e **next-intl**. Questo vi offrirà i vantaggi di Intlayer per l'automazione, la sintassi e la gestione dei contenuti, mantenendo al contempo la sicurezza e la stabilità fornite da queste soluzioni consolidate nel codice della vostra applicazione.
1221
+
1222
+ ## Stelle su GitHub
1223
+
1224
+ Le stelle di GitHub sono un forte indicatore della popolarità di un progetto, della fiducia della comunità e della rilevanza a lungo termine. Sebbene non siano una misura diretta della qualità tecnica, riflettono quanti sviluppatori trovano il progetto utile, ne seguono i progressi e sono propensi ad adottarlo. Per stimare il valore di un progetto, le stelle aiutano a confrontare l'attrattiva tra le alternative e forniscono informazioni sulla crescita dell'ecosistema.
146
1225
 
147
- - **Inizia per funzionalità**: Sposta una rotta o un componente alla volta verso i **dizionari locali**.
148
- - **Mantieni i vecchi cataloghi in parallelo**: Usa un ponte durante la migrazione; evita un cambiamento drastico tutto in una volta.
149
- - **Attiva i controlli rigorosi**: Permetti che la rilevazione a build-time evidenzi presto le lacune.
150
- - **Adotta middleware e helper**: Standardizza il rilevamento della locale e i tag SEO su tutto il sito.
151
- - **Misura i bundle**: Aspettati **riduzioni nella dimensione del bundle** man mano che il contenuto non utilizzato viene eliminato.
1226
+ [![Grafico della Storia delle Stelle](https://api.star-history.com/svg?repos=i18next/next-i18next&repos=amannn/next-intl&repos=aymericzip/intlayer&type=Date)](https://www.star-history.com/#i18next/next-i18next&amannn/next-intl&aymericzip/intlayer)
152
1227
 
153
1228
  ---
154
1229
 
@@ -156,7 +1231,7 @@ Ci concentriamo su **Next.js 13+ App Router** (con **React Server Components**)
156
1231
 
157
1232
  Tutte e tre le librerie riescono nella localizzazione di base. La differenza è **quanto lavoro devi fare** per ottenere una configurazione robusta e scalabile in **Next.js moderno**:
158
1233
 
159
- - Con **Intlayer**, **contenuti modulari**, **TS rigoroso**, **sicurezza a tempo di build**, **bundle ottimizzati con tree-shaking** e **App Router di prima classe + strumenti SEO** sono **impostazioni predefinite**, non incombenze.
1234
+ - Con **Intlayer**, **contenuti modulari**, **TS rigoroso**, **sicurezza a tempo di build**, **bundle tree-shaken** e **App Router di prima classe + strumenti SEO** sono **impostazioni predefinite**, non incombenze.
160
1235
  - Se il tuo team valorizza la **manutenibilità e la velocità** in un'app multi-locale guidata da componenti, Intlayer offre l'esperienza **più completa** oggi disponibile.
161
1236
 
162
1237
  Consulta il documento ['Perché Intlayer?'](https://intlayer.org/doc/why) per maggiori dettagli.