HomeAmbiente di testVetrinaAppDocBlog
    • Englishinglese
      EN
    • русскийrusso
      RU
    • 日本語giapponese
      JA
    • françaisfrancese
      FR
    • 한국어coreano
      KO
    • 中文cinese
      ZH
    • españolspagnolo
      ES
    • Deutschtedesco
      DE
    • العربيةarabo
      AR
    • italianoitaliano
      IT
    • British Englishinglese britannico
      EN-GB
    • portuguêsportoghese
      PT
    • हिन्दीhindi
      HI
    • Türkçeturco
      TR
    • polskipolacco
      PL
    • Indonesiaindonesiano
      ID
    • Tiếng Việtvietnamita
      VI
    • українськаucraino
      UK
    /
    Filtra la documentazione per framework
    Alt+←
    Perché Intlayer?
    Iniziare
    Concetto
    • Come funziona Intlayer
    • Configurazione
    • TestFillBuildWatchExtractLoginPushPullConfigurationListVersionEditorLiveDebugDoc ReviewDoc TranslateSDK
    • Editor visuale
    • CMS
    • Integrazione CI/CD
    • TraduzionePluraleEnumerazioneCondizioneGenereInserimentoFileAnnidamentoMarkdownHTMLRecupero funzione
    • File per locale
    • Compilatore
    • Compilazione automatica
    • Test
    • Ottimizzazione del bundle
    Ambiente
    • Next.js 14 e App Router
      Next.js 15
      Next.js senza locale URL
      Next.js e Page Router
      Compiler
    • Tanstack Start Solid
    • Astro e React
      Astro e Svelte
      Astro e Vue
      Astro e Solid
      Astro e Preact
      Astro e Lit
      Astro e Vanilla JS
    • React Router v7
      React Router v7 (fs-routes)
      Compiler
    • Nuxt e Vue
    • Vite e Solid
    • SvelteKit
    • Vite e Preact
    • Vite e Vanilla JS
    • Vite e Lit
    • Angular 19 (Webpack)
      Analog
    • React CRA
    • React Native e Expo
    • Express.js
      NestJS
      Fastify
      Hono
      Adonis
    • Lynx e React
    Plugins
    • JSON
    • gettext (.po)
    Estensione VS Code
    Agente
    • Server MCP
    • Abilità dell’agente
    Versioni
    • v8
    • v7
    • v6
    Benchmark
    • Next.js
    • TanStack
    • Vue
    • Solid
    • Svelte
    Blog
    Fai una domanda
    1. Documentation
    2. Benchmark
    3. Next.js
    Autore: Aymeric PINEAU
    Creazione:2026-04-20Ultimo aggiornamento:2026-05-18
    Visualizza il modello di applicazione su GitHub

    Questa pagina ha un modello di applicazione disponibile.

    Riferimento a questa documentazione al tuo assistente AI preferito
    ChatGPT
    Claude
    DeepSeek
    Google AI mode
    Gemini
    Perplexity
    Mistral
    Grok

    Pose una domanda e ottieni un riassunto del documento facendo riferimento a questa pagina e al provider AI di tua scelta

    Cronologia delle versioni

    1. "Aggiungi comparazione delle stelle di GitHub"
      v8.9.818/05/2026
    2. "Inizializzazione benchmark"
      v8.7.506/01/2026

    Il contenuto di questa pagina è stato tradotto con un'IA.

    Vedi l'ultima versione del contenuto originale in inglese
    Modifica questa documentazione

    Se hai un’idea per migliorare questa documentazione, non esitare a contribuire inviando una pull request su GitHub.

    Collegamento GitHub alla documentazione
    Copia

    Copia il Markdown del documento nella porta-documenti

    Librerie i18n per Next.js - Rapporto Benchmark 2026

    Questa pagina è un rapporto benchmark per le soluzioni i18n su Next.js.

    Sommario

    Benchmark Interattivo

    Riferimento risultati:

    intlayer.org
    Visualizza i dati completi del benchmark

    Vedi il repository completo del benchmark qui.

    Introduzione

    Le librerie di internazionalizzazione hanno un impatto pesante sulla tua applicazione. Il rischio principale è caricare contenuti per ogni pagina e ogni lingua quando l'utente visita solo una pagina.

    Man mano che l'app cresce, le dimensioni del bundle possono aumentare esponenzialmente, il che può compromettere notevolmente le prestazioni.

    Ad esempio, nei casi peggiori, una volta internazionalizzata, la tua pagina può finire per essere quasi 4 volte più grande.

    Un altro impatto delle librerie i18n è il rallentamento dello sviluppo. Trasformare i componenti in contenuti multilingue in diverse lingue richiede tempo.

    Poiché il problema è difficile, esistono molte soluzioni, alcune focalizzate sulla DX (Developer Experience), altre sulle prestazioni o sulla scalabilità, e così via.

    Intlayer cerca di ottimizzare tutte queste dimensioni.

    TL;DR

    • Intlayer e next-translate: Le migliori scelte per le prestazioni di Next.js, con l'impronta minima e il miglior supporto al rendering statico.
    • next-intl: L'opzione più di tendenza, ma pesante e complessa da ottimizzare per grandi applicazioni.
    • next-i18next: Popolare e ricca di plugin, ma comporta un peso del bundle significativo (~3× Intlayer).
    • Da evitare: gt-next e lingo.dev a causa di gravi problemi di prestazioni, vendor lock-in ed errori che bloccano la build.

    Testa la tua app

    Per far emergere questi problemi, ho creato uno scanner gratuito che puoi provare qui.

    intlayer.org

    Il problema

    Esistono due modi principali per limitare l'impatto di un'app multilingue sul bundle:

    • Dividere i JSON (o contenuti) tra file / variabili / namespace in modo che il bundler possa scartare (tree-shake) i contenuti inutilizzati per una data pagina
    • Caricare dinamicamente i contenuti della pagina solo nella lingua dell'utente

    Limitazioni tecniche di questi approcci:

    Caricamento dinamico

    Anche quando dichiari percorsi come [locale]/page.tsx, con Webpack o Turbopack, e anche se generateStaticParams è definito, il bundler non tratta locale come una costante statica. Ciò significa che potrebbe includere contenuti per tutte le lingue in ogni pagina. Il modo principale per limitare questo è caricare i contenuti tramite un import dinamico (es. import('./locales/${locale}.json')).

    Ciò che accade al build time è che Next.js emette un bundle JS per lingua (es. ./locales_it_12345.js). Dopo che il sito è stato inviato al client, quando la pagina viene eseguita, il browser effettua una richiesta HTTP aggiuntiva per il file JS necessario (es. ./locales_it_12345.js).

    Un altro modo per affrontare lo stesso problema è usare fetch() per caricare dinamicamente i JSON. È così che funziona Tolgee quando i JSON risiedono sotto /public, o next-translate, che si affida a getStaticProps per caricare i contenuti. Il flusso è lo stesso: il browser effettua una richiesta HTTP aggiuntiva per caricare l'asset.

    Suddivisione dei contenuti (Content splitting)

    Se usi una sintassi come const t = useTranslation() + t('my-object.my-sub-object.my-key'), l'intero JSON deve solitamente essere presente nel bundle in modo che la libreria possa analizzarlo e risolvere la chiave. Gran parte di quel contenuto viene spedito anche quando è inutilizzato nella pagina.

    Per mitigare questo, alcune librerie chiedono di dichiarare per pagina quali namespace caricare - es. next-i18next, next-intl, lingui, next-translate, next-international.

    Al contrario, Paraglide aggiunge un passaggio extra prima della build per trasformare i JSON in simboli piatti come const en_my_var = () => 'my value'. In teoria questo abilita il tree-shaking dei contenuti inutilizzati nella pagina. Come vedremo, questo metodo presenta comunque dei compromessi.

    Infine, Intlayer applica un'ottimizzazione build-time in modo che useIntlayer('my-key') venga sostituito direttamente con il contenuto corrispondente.

    Metodologia

    Per questo benchmark, abbiamo confrontato le seguenti librerie:

    • Base App (Nessuna libreria i18n)
    • next-intlayer (v8.7.12)
    • next-i18next (v16.0.5)
    • next-intl (v4.9.1)
    • @lingui/core (v5.3.0)
    • next-translate (v3.1.2)
    • next-international (v1.3.1)
    • @inlang/paraglide-js (v2.15.1)
    • @tolgee/react (v7.0.0)
    • @lingo.dev/compiler (v0.4.0)
    • wuchale (v0.22.11)
    • gt-next (v6.16.5)

    Ho utilizzato Next.js versione 16.2.4 con App Router.

    Ho costruito un'app multilingue con 10 pagine e 10 lingue.

    Ho confrontato quattro strategie di caricamento:

    Mostra tutto il contenuto della tabella

    Apri la tabella in una finestra modale per visualizzare tutti i dati in modo chiaro

    Strategia Senza namespace (globale) Con namespace (scoped)
    Caricamento statico Static: Tutto in memoria all'avvio. Scoped static: Diviso per namespace; tutto caricato all'avvio.
    Caricamento dinamico Dynamic: Caricamento on-demand per lingua. Scoped dynamic: Caricamento granulare per namespace e lingua.

    Sintesi delle strategie

    • Static: Semplice; nessuna latenza di rete dopo il caricamento iniziale. Svantaggio: grandi dimensioni del bundle.
    • Dynamic: Riduce il peso iniziale (lazy-loading). Ideale quando si hanno molte localizzazioni.
    • Scoped static: Mantiene il codice organizzato (separazione logica) senza requisiti di rete complessi.
    • Scoped dynamic: Il miglior approccio per il code splitting e le prestazioni. Riduce al minimo la memoria caricando solo ciò di cui la vista corrente e la lingua attiva hanno bisogno.

    Cosa ho misurato:

    Ho eseguito la stessa applicazione multilingue in un browser reale per ogni stack, quindi ho annotato cosa è apparso effettivamente sulla rete e quanto tempo hanno impiegato le operazioni. Le dimensioni sono riportate dopo la normale compressione web, poiché è più vicina a ciò che le persone scaricano realmente rispetto al conteggio dei sorgenti grezzi.

    • Dimensioni della libreria di internazionalizzazione: Dopo bundling, tree-shaking e minificazione, la dimensione della libreria i18n è la dimensione dei provider (es. NextIntlClientProvider) + il codice degli hook (es. useTranslations) in un componente vuoto. Non include il caricamento dei file di traduzione. Risponde a quanto sia "costosa" la libreria prima che entrino in gioco i tuoi contenuti.

    • JavaScript per pagina: Per ogni percorso del benchmark, quanto script il browser scarica per quella visita, mediato su tutte le pagine della suite (e su tutte le lingue dove il rapporto le raggruppa). Le pagine pesanti sono pagine lente.

    • Leakage da altre lingue: È il contenuto della stessa pagina ma in un'altra lingua che verrebbe caricato per errore nella pagina esaminata. Questo contenuto è superfluo e dovrebbe essere evitato (es. contenuto della pagina /fr/about nel bundle della pagina /en/about).

    • Leakage da altri percorsi: La stessa idea per le altre schermate nell'app: se i loro testi sono inclusi nel bundle quando hai aperto solo una pagina (es. contenuto della pagina /en/about nel bundle della pagina /en/contact). Un punteggio elevato suggerisce uno splitting debole o bundle eccessivamente ampi.

    • Dimensione media del bundle per componente: I pezzi comuni della UI 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 esegue un re-rendering, 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.

    • Reattività al cambio di lingua: Cambio la lingua usando il controllo dell'app e cronometro quanto tempo ci vuole finché la pagina non è chiaramente cambiata - ciò che un visitatore noterebbe, non un micro-passaggio di laboratorio.

    • Lavoro di rendering dopo un cambio di lingua: Un approfondimento: 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.

    • Tempo di caricamento iniziale della pagina: Dalla navigazione fino a quando il browser considera la pagina completamente caricata per gli scenari che ho testato. Ottimo per confrontare i caricamenti a freddo (cold start).

    • Tempo di idratazione (Hydration): Quando l'app lo espone, quanto tempo impiega il client per trasformare l'HTML del server in qualcosa su cui si può effettivamente cliccare. Un trattino nelle tabelle significa che quell'implementazione non ha fornito un dato di idratazione affidabile in questo benchmark.

    Stelle di GitHub

    Le stelle di GitHub sono un forte indicatore della popolarità di un progetto, della fiducia della comunità e della pertinenza 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 la trazione tra le alternative e forniscono approfondimenti sulla crescita dell'ecosistema.

    Star History Chart

    Risultati nel dettaglio

    1 - Soluzioni da evitare

    Alcune soluzioni, come gt-next o lingo.dev, sono chiaramente da evitare. Combinano il vendor lock-in con l'inquinamento della base di codice. Nonostante molte ore trascorse cercando di implementarle, non sono mai riuscito a farle funzionare, né su TanStack Start né su Next.js.

    Problemi riscontrati:

    (General Translation) ([email protected]):

    • Per un'app da 110kb, gt-next aggiunge più di 440kb extra.
    • Quota Exceeded, please upgrade your plan al primissimo build con General Translation.
    • 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.
    • Durante l'implementazione di gt-next, ho riscontrato anche un problema 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.
    • La libreria blocca il rendering statico delle pagine Next.js.

    (Lingo.dev) (@lingo.dev/[email protected]):

    • Quota AI superata, bloccando interamente la build - quindi non puoi andare in produzione senza pagare.
    • Il compilatore perdeva quasi il 40% del contenuto tradotto. Ho dovuto riscrivere tutti i .map in blocchi di componenti piatti per farlo funzionare.
    • La loro CLI è buggata e tendeva a resettare il file di configurazione senza motivo.
    • Alla build, cancellava totalmente i JSON generati quando veniva aggiunto nuovo contenuto. Di conseguenza, una manciata di chiavi poteva cancellare più di 300 chiavi esistenti.

    2 - Soluzioni sperimentali

    (Wuchale) ([email protected]):

    L'idea alla base di Wuchale è interessante ma non ancora praticabile. Ho riscontrato problemi di reattività e ho dovuto forzare il re-rendering del provider per far funzionare l'app. La documentazione è inoltre piuttosto oscura, il che rende difficile l'onboarding.

    (Paraglide) (@inlang/[email protected]):

    Paraglide offre un approccio innovativo e ben ponderato. Tuttavia, in questo benchmark il tree-shaking pubblicizzato non ha funzionato per le mie configurazioni Next.js o TanStack Start. Il workflow e la DX sono più complessi rispetto ad altre opzioni. 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. 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.

    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.

    3 - Soluzioni accettabili

    (Tolgee) (@tolgee/[email protected]):

    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.

    (Next Intl) ([email protected]):

    next-intl è l'opzione più di tendenza e quella che gli assistenti AI spingono di più, ma a mio avviso a torto. Iniziare è facile. In pratica, ottimizzare per limitare i leakage è complesso. Combinare caricamento dinamico + namespace + tipi TypeScript rallenta molto lo sviluppo. Il pacchetto è anche piuttosto pesante (~13kb per NextIntlClientProvider + useTranslations, che è più di 2 volte next-intlayer). next-intl tendeva a bloccare il rendering statico delle pagine Next.js. Fornisce un helper chiamato setRequestLocale(). Sembra che ciò sia stato parzialmente risolto per file centralizzati come en.json / fr.json, ma il rendering statico si interrompe ancora quando il contenuto è diviso in namespace come en/shared.json / fr/shared.json / es/shared.json.

    (Next I18next) ([email protected]):

    next-i18next è probabilmente l'opzione più popolare perché è stata tra le prime soluzioni i18n per app JavaScript. Ha molti plugin della community. Condivide gli stessi svantaggi principali di next-intl. Il pacchetto è particolarmente pesante (~18kb per I18nProvider + useTranslation, circa 3 volte next-intlayer).

    I formati dei messaggi differiscono inoltre: next-intl usa ICU MessageFormat, mentre i18next usa il proprio formato.

    (Next International) ([email protected]):

    next-international affronta anch'esso i problemi di cui sopra ma non differisce molto da next-intl o next-i18next. Include scopedT() per traduzioni specifiche di un namespace, ma usarlo non ha praticamente alcun impatto sulle dimensioni del bundle.

    (Lingui) (@lingui/[email protected]):

    Lingui è spesso elogiato. Personalmente ho trovato il workflow lingui extract / lingui compile più complesso delle alternative, senza un chiaro vantaggio. Ho anche notato sintassi inconsistenti che confondono le AI (es. t(), t'', i18n.t(), <Trans>).

    4 - Raccomandazioni

    (Next Translate) ([email protected]):

    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.

    (Intlayer) ([email protected]):

    Non giudicherò personalmente next-intlayer per motivi di obiettività, essendo la mia propria soluzione.

    Nota personale

    Questa nota è personale e non influisce sui risultati del benchmark. Nel mondo i18n si vede spesso un consenso attorno a const t = useTranslation('xx') + <>{t('xx.xx')}</>.

    Nelle app React, iniettare una funzione come ReactNode è, a mio avviso, un anti-pattern. Inoltre aggiunge una complessità evitabile e un sovraccarico di esecuzione JavaScript (anche se appena percettibile).

    Benchmark
    TanStack
    Alt+→

    In questa pagina

      Le discussioni sono anonime e vengono regolarmente esaminate per affrontare problemi comuni. Sentiti libero di condividere idee per nuove funzionalità, feedback sulla documentazione o qualsiasi cosa relativa a Intlayer, utilizziamo questi input per definire la nostra roadmap e migliorare il prodotto.

      Caricamento JSON dinamico

      Carica le traduzioni in modalità lazy durante l'esecuzione

      JSON con ambito (namespacing)

      Spazi dei nomi di traduzione per pagina

      Benchmark delle prestazioni I18n

      Nessun dato disponibile

      Cos'è questa metrica?

      La dimensione totale compressa con gzip del bundle della libreria di internazionalizzazione. Include solo il provider e la logica di recupero dei contenuti dopo il tree-shaking e la minificazione.

      Perché è importante?

      Una dimensione della libreria più piccola riduce il payload JavaScript iniziale, portando a tempi di download ed esecuzione più rapidi sul client.

      Visualizza come