Autore:
    Creazione:2026-04-20Ultimo aggiornamento:2026-09-27

    Librerie i18n per TanStack Start - Rapporto Benchmark 2026

    Questa pagina รจ un rapporto benchmark per le soluzioni i18n su TanStack Start.

    Sommario

    Benchmark Interattivo

    Metrica

    Caricamento JSON dinamico

    Carica le traduzioni in modalitร  lazy durante l'esecuzione

    JSON con ambito (namespacing)

    Spazi dei nomi di traduzione per pagina

    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

    Riferimento risultati:

    Introduzione

    Le soluzioni di internazionalizzazione sono tra le dipendenze piรน pesanti in un'app React. Su TanStack Start, il rischio principale รจ spedire contenuti non necessari: traduzioni per altre pagine e altre lingue nel bundle di una singola rotta.

    Man mano che l'app cresce, questo problema puรฒ far esplodere rapidamente il JavaScript inviato al client e rallentare la navigazione.

    In pratica, per le implementazioni meno ottimizzate, una pagina internazionalizzata puรฒ finire per essere diverse volte piรน pesante della versione senza i18n.

    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.

    Per capire da dove vengono queste librerie, leggi la storia dell'i18n in JavaScript.

    TL;DR

    • Intlayer: Fornisce le migliori prestazioni e la dimensione del bundle piรน ridotta (v9.5.10) per TanStack Start.
    • react-i18next & use-intl: Alternative mature con ecosistemi ampi, ma significativamente piรน pesanti e complesse da ottimizzare.
    • Paraglide: Idea innovativa di tree-shaking che non funziona nella pratica. DX complessa e sovraccarico di reattivitร  in TanStack Start.
    • Da evitare: General Translation (GT) e Lingo.dev a causa di gravi problemi di prestazioni, limiti di quota AI e vendor lock-in.

    Testa la tua app

    Per individuare rapidamente i problemi di leak i18n, puoi provare lo scanner SEO i18n gratuito.

    Il problema

    Due leve sono essenziali per limitare il costo di un'app multilingue:

    • Suddividere i contenuti per pagina / namespace per non caricare interi dizionari quando non servono
    • Caricare la lingua corretta dinamicamente, solo quando necessario

    Capire i limiti tecnici di questi approcci:

    Caricamento dinamico

    Senza caricamento dinamico, la maggior parte delle soluzioni mantiene i messaggi in memoria fin dal primo rendering, il che aggiunge un sovraccarico significativo per le app con molti percorsi e lingue.

    Con il caricamento dinamico, accetti un compromesso: meno JS iniziale, ma a volte una richiesta extra quando si cambia lingua.

    Suddivisione dei contenuti (Content splitting)

    Le sintassi costruite attorno a const t = useTranslation() + t('a.b.c') sono molto comode ma spesso incoraggiano il mantenimento di grandi oggetti JSON a runtime. Quel modello rende difficile il tree-shaking a meno che la libreria non offra una reale strategia di splitting per pagina.

    Metodologia

    Per questo benchmark, abbiamo confrontato le seguenti librerie:

    Il framework รจ TanStack Start con un'app multilingue di 10 pagine e 10 lingue.

    Abbiamo confrontato quattro strategie di caricamento:

    StrategiaSenza namespace (globale)Con namespace (scoped)
    Caricamento staticoStatic: Tutto in memoria all'avvio.Scoped static: Diviso per namespace; tutto caricato all'avvio.
    Caricamento dinamicoDynamic: 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 app multilingue in un vero browser per ogni stack, quindi ho annotato cosa รจ effettivamente apparso sulla rete e quanto tempo ha impiegato. Le dimensioni sono riportate dopo la normale compressione web, perchรฉ รจ piรน vicino a quello che le persone scaricano rispetto ai conteggi del codice sorgente grezzo.

    • Dimensione della libreria di internazionalizzazione: Dopo il bundling, tree-shaking e minification, la dimensione della libreria i18n รจ la dimensione dei provider (ad es. NextIntlClientProvider) + il codice degli hook (ad es. useTranslations) in un componente vuoto. Non include il caricamento dei file di traduzione. Risponde a quanto รจ costosa la libreria prima che il tuo contenuto entri in gioco.

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

    • Perdita da altre lingue: รˆ il contenuto della stessa pagina ma in un'altra lingua che verrebbe caricato per errore nella pagina controllata. Questo contenuto รจ non necessario e dovrebbe essere evitato. (ad es. il contenuto della pagina /fr/about nel bundle della pagina /en/about)

    • Perdita da altre route: La stessa idea per altri schermi nell'app: se la loro copia viene trascinata quando apri solo una pagina. (ad es. il contenuto della pagina /en/about nel bundle della pagina /en/contact). Un punteggio alto suggerisce uno splitting debole o bundle troppo ampi.

    • Dimensione media del bundle del componente: I pezzi comuni dell'interfaccia utente vengono misurati uno alla volta invece di nascondersi all'interno di un numero gigante dell'app. Mostra se l'internazionalizzazione gonfia silenziosamente i componenti di tutti i giorni. Ad esempio, se il tuo componente si rirenderizza, caricherร  tutti questi dati dalla memoria. Allegare un JSON gigante a qualsiasi componente รจ come collegare un grande archivio di dati inutilizzati che rallenterร  le prestazioni dei tuoi componenti.

    • Reattivitร  del cambio di lingua: Cambio la lingua usando il controllo dell'app e misuro quanto tempo impiega fino a quando la pagina non รจ chiaramente cambiata, quello che un visitatore noterebbe, non un micro-step di laboratorio.

    • Lavoro di rendering dopo un cambio di lingua: Un follow-up piรน ristretto: quanto sforzo ha impiegato l'interfaccia per ridisegnare la nuova lingua una volta che lo switch รจ in volo. Utile quando il tempo "percepito" e il costo del framework divergono.

    • Tempo di caricamento iniziale della pagina: Dalla navigazione al browser che considera la pagina completamente caricata per gli scenari che ho testato. Buono per confrontare i cold start.

    • Tempo di idratazione: Quando l'app lo espone, quanto tempo il client spende a trasformare l'HTML del server in qualcosa su cui puoi effettivamente fare clic. Un trattino nelle tabelle significa che l'implementazione non ha fornito una cifra 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

    Attivitร  dei commit

    Le stelle indicano la popolaritร . I commit indicano quanto lavoro viene investito in un progetto. Al momento della scrittura, Intlayer conta circa 7.500 commit, piรน della maggior parte delle librerie confrontate qui e circa 5 volte piรน di next-intl o next-i18next.

    • formatjs/formatjs
    • i18next/react-i18next
    • lingui/js-lingui
    • amannn/next-intl
    • opral/paraglide-js
    • tolgee/tolgee-js
    • generaltranslation/gt
    • lingodotdev/lingo.dev
    • wuchalejs/wuchale
    • aymericzip/intlayer

    Commit sul branch predefinito, fonte: API GitHub.

    Intlayer รจ un monorepo, quindi il totale include ogni pacchetto framework, la CLI e la documentazione. Leggi i commit come un segnale di attivitร , non di qualitร .

    Download npm

    • react-intl
    • react-i18next
    • @lingui/react
    • use-intl
    • @inlang/paraglide-js
    • @tolgee/react
    • gt-react
    • lingo.dev
    • wuchale
    • react-intlayer

    Fonte: API dei download del registro npm.

    I download premiano le soluzioni piรน vecchie, non le migliori. Una libreria pubblicata anni fa viene ancora installata da ogni progetto che l'ha scelta allora, da ogni esecuzione della CI e da ogni pacchetto che ne dipende. Il numero misura l'inerzia piรน che una scelta attuale.

    Gli assistenti IA amplificano l'effetto. next-intl, i18next e vue-i18n sono ovunque nel codice su cui sono stati addestrati, quindi li suggeriscono di default, senza confrontare le alternative. Ogni suggerimento aggiunge download, che alimentano il suggerimento successivo. Confronta sul benchmark, non sul numero di download.

    Risultati nel dettaglio

    1 - Soluzioni da evitare

    Alcune soluzioni, come gt-react o lingo.dev, sono chiaramente da evitare. Combinano il vendor lock-in con l'inquinamento della base di codice. Peggio ancora: nonostante molte ore trascorse cercando di implementarle, non sono mai riuscito a farle funzionare correttamente su TanStack Start (come per Next.js con gt-next).

    Problemi riscontrati:

    (General Translation) (gt-react@10.18.3):

    • Per un'app di circa 111kb, gt-react puรฒ aggiungere piรน di 170kb extra (ordine di grandezza osservato sull'implementazione Next.js nello stesso benchmark).
    • 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-tanstack-start-react, ho riscontrato anche un problema con la libreria: does not provide an export named 'printAST' - @formatjs/icu-messageformat-parser, che causava il crash dell'applicazione. Dopo la segnalazione, il manutentore lo ha risolto in 24 ore.
    • Queste librerie usano un anti-pattern tramite la funzione initializeGT(), impedendo al bundle di essere ripulito correttamente tramite tree-shaking.

    (Lingo.dev) (lingo.dev@0.138.7):

    • Quota AI superata (o dipendenza del server bloccata), rendendo build/produzione rischiosi 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, poche chiavi potevano cancellare centinaia di chiavi esistenti.
    • Ho riscontrato problemi di reattivitร  con la libreria su TanStack Start: al cambio di lingua ho dovuto forzare il re-rendering del provider per farlo funzionare.

    2 - Soluzioni sperimentali

    (Wuchale) (wuchale@0.26.6):

    L'idea alla base di Wuchale รจ interessante ma non ancora una soluzione praticabile. Ho riscontrato problemi di reattivitร  con questa libreria e ho dovuto forzare il re-rendering del provider per far funzionare l'app su TanStack Start. La documentazione รจ inoltre piuttosto oscura, il che rende difficile l'onboarding.

    3 - Soluzioni accettabili

    (Paraglide) (@inlang/paraglide-js@2.15.1):

    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.

    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 3 a 10 volte piรน leggere rispetto a i18next o next-intl.

    (Tolgee) (@tolgee/react@7.2.0):

    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.

    Il package รจ abbastanza pesante (~11.1 kb, che รจ piรน di 2.3ร— react-intlayer).

    Su TanStack Start ho avuto anche problemi di reattivitร : al cambio di lingua, dovevo forzare il provider a rieseguire il rendering e sottoscrivermi agli eventi di cambio lingua in modo che il caricamento in un'altra lingua si comportasse correttamente.

    (use-intl) (use-intl@4.14.2):

    use-intl รจ il pezzo "intl" piรน alla moda nell'ecosistema React (stessa famiglia di next-intl) ed รจ spesso spinto dagli assistenti AI, ma a mio avviso a torto in un contesto orientato alle prestazioni. Iniziare รจ abbastanza semplice. In pratica, il processo per ottimizzare e limitare i leakage รจ piuttosto complesso. Allo stesso modo, combinare caricamento dinamico + namespace + tipi TypeScript rallenta molto lo sviluppo.

    Su TanStack Start eviti le trappole specifiche di Next.js (setRequestLocale, rendering statico), ma il problema di fondo รจ lo stesso: senza una disciplina rigida, il bundle trasporta rapidamente troppi messaggi e la manutenzione dei namespace per ogni rotta diventa faticosa.

    (react-i18next) (react-i18next@17.0.13):

    react-i18next รจ probabilmente l'opzione piรน popolare perchรฉ รจ stata tra le prime a soddisfare le esigenze i18n delle app JavaScript. Ha anche un ampio set di plugin della community per problemi specifici.

    Tuttavia, condivide gli stessi svantaggi principali degli stack basati su t('a.b.c'): le ottimizzazioni sono possibili ma richiedono molto tempo, e i grandi progetti rischiano cattive pratiche (namespace + caricamento dinamico + tipi).

    Il package รจ particolarmente pesante (~18.4kb, che รจ circa 3.5ร— react-intlayer).

    I formati dei messaggi divergono inoltre: use-intl usa ICU MessageFormat, mentre i18next usa il proprio formato - il che complica il tooling o le migrazioni se li mescoli.

    (Lingui) (@lingui/core@6.6.0):

    Lingui รจ spesso elogiato. Personalmente ho trovato il workflow attorno a lingui extract / lingui compile piรน complesso di altri approcci, senza un chiaro vantaggio in questo benchmark su TanStack Start. Ho anche notato sintassi inconsistenti che confondono le AI (es. t(), t'', i18n.t(), <Trans>).

    (react-intl) (react-intl@10.1.26):

    react-intl รจ un'implementazione performante del team Format.js. La DX rimane prolissa: const intl = useIntl() + intl.formatMessage({ id: "xx.xx" }) aggiunge complessitร , lavoro extra JavaScript e lega l'istanza globale i18n a molti nodi nell'albero React.

    Il package รจ anche pesante (~14.4kb, che รจ circa 3ร— react-intlayer).

    4 - Raccomandazioni

    Questo benchmark su TanStack Start non ha un equivalente diretto di next-translate (plugin Next.js + getStaticProps). Per i team che vogliono davvero un'API t() con un ecosistema maturo, react-i18next e use-intl rimangono scelte "ragionevoli", ma preparatevi a investire molto tempo nell'ottimizzazione per evitare leakage.

    (Intlayer) (react-intlayer@9.5.10):

    Non giudicherรฒ personalmente react-intlayer per motivi di obiettivitร , essendo la mia propria soluzione.

    Nota personale

    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 il contenuto tradotto.

    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).