Pose una domanda e ottieni un riassunto del documento facendo riferimento a questa pagina e al provider AI di tua scelta
Il contenuto di questa pagina è stato tradotto con un'IA.
Vedi l'ultima versione del contenuto originale in ingleseSe hai un’idea per migliorare questa documentazione, non esitare a contribuire inviando una pull request su GitHub.
Collegamento GitHub alla documentazioneCopia il Markdown del documento nella porta-documenti
Lingui VS @intlayer/lingui | Stesse Macro, Runtime Diverso
@intlayer/lingui è un adattatore di compatibilità per @lingui/core e @lingui/react. Le tue chiamate a t`...` , <Trans>, useLingui() e i18n._() rimangono esattamente identiche; le macro continuano a compilare; ciò che cambia è l'origine dei messaggi a runtime. Invece di un unico catalogo compilato per lingua, ogni punto di chiamata è collegato a un dizionario Intlayer compilato appositamente per esso.
Questo articolo misura questa sostituzione sulla stessa applicazione TanStack Start, compilata una volta con Lingui e una volta con l'adattatore. I numeri provengono da Benchmark Bloom. Per un confronto diretto tra le due librerie, leggi Lingui vs Intlayer. Questo articolo si concentra su cosa cambia l'adattatore e su dove non offre vantaggi.
tl;dr: Sulla stessa app TanStack Start,@intlayer/linguiha ridotto la dimensione media dei componenti da 85,5 KB a 12,8 KB gzip, l'idratazione da 28 ms a 19,7 ms e il cambio di lingua da 5,9 ms a 2,9 ms, senza toccare le macro. Nella configurazione semplice (tutti i cataloghi caricati subito) ha anche rimosso il 90% di page leakage e 12 KB per pagina. Tuttavia, nella configurazione lazy-loaded distribuisce 137 KB per pagina contro i 115 KB di Lingui puro: l'adattatore risolve ICU a runtime, mentre Lingui fornisce array di token precompilati. Il leakage della lingua sorgente (~9-10%) è identico su entrambi i lati, poiché proviene dal fallbackmessageintegrato nei componenti e non dal runtime. L'adattatore è un plugin Vite; è stato misurato su TanStack Start.
Cos'è @intlayer/lingui
Lingui è un compilatore unito a un runtime. Le macro nel codice sorgente vengono estratte in un catalogo .po (o JSON) per lingua, compilate in un modulo JS per lingua e caricate in un'istanza globale I18n tramite i18n.load() + i18n.activate(). Ogni useLingui() si iscrive a quell'istanza; ogni chiamata a _() cerca il proprio identificatore nel catalogo attivo.
@intlayer/lingui mantiene le macro e l'API e sostituisce la ricerca nel catalogo:
- Alias di importazione. Il plugin
lingui()di@intlayer/lingui/pluginavvolgevite-intlayere aggiunge voci inresolve.aliasin modo che@lingui/coree@lingui/reactpuntino a@intlayer/lingui. I tuoi import non cambiano. - Cataloghi come fonte di verità. Il plugin
syncJSON(osyncPOper i file.po) legge i tuoi cataloghi esistenti e li trasforma in dizionari Intlayer, riscrivendo le traduzioni quando la CLI o il CMS li aggiornano. ConsplitKeys: "key-prefix", un catalogo piatto di ID con punti (footer.github,hero.title) diventa un insieme di piccoli dizionari suddivisi per prefisso invece di un singolo file da 244 KB. - Collegamento al punto di chiamata. Il passaggio di ottimizzazione di Intlayer raccoglie gli ID passati a
_,te<Trans>in ogni file e fornisce al componente solo i dizionari corrispondenti.<Trans id="hero.title">si collega autonomamente;useLingui()si collega a ogni prefisso utilizzato nel file. Gli ID privi di punto (ID con hash,mockBanner) ripiegano sul dizionariomessagesunico di Lingui.
Copiare il codice nella clipboard
Copiare il codice nella clipboard
Il componente non accede più all'istanza globale né al catalogo monolitico sottostante. Accede solo a hero. Questa è la ragione principale per cui la colonna delle dimensioni dei componenti si riduce di 7 volte nella tabella seguente.
Cosa l'adattatore mantiene, ignora e non sostituisce
Apri la tabella in una finestra modale per visualizzare tutti i dati in modo chiaro
| API di Lingui | Con @intlayer/lingui |
|---|---|
Macro t`...` , msg, plural, select, <Trans> | ✅ Mantenuto. Mantieni @lingui/babel-plugin-lingui-macro o @lingui/swc-plugin nella build, prima di Intlayer |
useLingui() → { i18n, _, t } | ✅ Mantenuto. Funziona anche fuori da un provider (lingua derivata da react-intlayer) |
i18n._(id, values), i18n.t() | ✅ Mantenuto. Risolve sia ID espliciti sia ID con hash |
Plurali ICU, select, selectordinal, # | ✅ Mantenuto, tramite il resolver ICU di Intlayer |
i18n.date(), i18n.number(), formats | ✅ Mantenuto, supportato da Intl nativo |
I18nProvider | ✅ Mantenuto. Avvolge un IntlayerProvider; ascolta i18n.on("change") così che activate() riesegua il render |
i18n.activate(locale) | ✅ Mantenuto |
i18n.load(locale, messages) / loadAndActivate() | ⚠️ Accettato come fallback a runtime. I dizionari compilati hanno priorità; un avviso suggerisce la rimozione |
setupI18n({ messages, missing }) | ⚠️ messages uniti come fallback a runtime; missing viene ignorato |
lingui extract / lingui compile | ✅ Il tuo workflow rimane invariato. Punta syncPO / syncJSON sui cataloghi estratti |
defaultComponent su I18nProvider | ⚠️ Memorizzato nel contesto, non applicato durante il rendering |
| Next.js | ❌ Il plugin avvolge vite-intlayer. Solo Vite, TanStack Start e React Router |
Il benchmark
Cosa è stato misurato
La suite Benchmark Bloom compila la stessa applicazione per ciascuna configurazione: 10 pagine (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 lingue (en, fr, es, de, it, pt, zh, ja, ko, ru), componenti identici e contenuto identico. Le pagine sono misurate in en e fr.
Lingui è stato testato con quattro strategie di caricamento, dall'importazione iniziale di tutti i cataloghi compilati (static) fino a un catalogo per route caricato in modalità lazy (scoped-dynamic). L'adattatore è stato compilato sugli stessi componenti, modificando esclusivamente vite.config.ts e intlayer.config.ts. La sua riga static include tutte le lingue; la sua riga dynamic (importMode: 'dynamic') carica la lingua attiva su richiesta. Non esiste una variante "scoped": la fase di ottimizzazione gestisce automaticamente la suddivisione per punto di chiamata.
Per ogni build vengono registrate le seguenti metriche:
- Lib size: dimensione gzip di un componente vuoto che importa solo la libreria di i18n.
- Page JS: JavaScript gzip scaricato per pagina, mediato su tutte le pagine e lingue.
- Locale leak %: percentuale di stringhe tradotte nel JS scaricato appartenenti a una lingua che l'utente non sta visualizzando.
- Page leak %: percentuale di stringhe tradotte nel JS scaricato appartenenti a una pagina in cui l'utente non si trova.
- Component avg: dimensione media gzip di ciascun componente compilato isolatamente.
- E2E reactivity: tempo trascorso tra la selezione di una nuova lingua e l'aggiornamento di
html[lang]nel DOM (Playwright, 5 iterazioni). - Hydration: durata della fase di idratazione di React.
I dati sottostanti provengono dal test eseguito il 2026-09-12 con@lingui/react6.6.0 e@intlayer/lingui9.5.1. L'applicazione di test è volutamente contenuta (poche decine di stringhe per lingua), per cui le percentuali di dispersione descrivono un modello strutturale: crescono con l'aumento dei contenuti mentre il costo a runtime rimane fisso.
Risultati su TanStack Start
Apri la tabella in una finestra modale per visualizzare tutti i dati in modo chiaro
| Configurazione | Strategia | Lib size (gz) | Page JS media (gz) | Leak lingua | Leak pagina | Componente medio (gz) | Reattività E2E | Idratazione |
|---|---|---|---|---|---|---|---|---|
| base (senza i18n) | - | 0,0 KB | 111,0 KB | 0,0% | 0,0% | 0,7 KB | 8,1 ms | 21,6 ms |
| Lingui | static | 11,2 KB | 152,2 KB | 50,0% | 90,0% | 58,0 KB | 3,9 ms | 19,9 ms |
| Lingui | dynamic | 11,2 KB | 115,2 KB | 9,3% | 0,0% | 85,5 KB | 5,9 ms | 28,0 ms |
| Lingui | scoped-static | 11,2 KB | 120,8 KB | 4,0% | 0,0% | 147,9 KB | 7,1 ms | 33,9 ms |
| Lingui | scoped-dynamic | 11,2 KB | 120,2 KB | 8,6% | 0,0% | 83,7 KB | 42,1 ms | 32,9 ms |
@intlayer/lingui | static | 10,3 KB | 140,5 KB | 50,0% | 0,0% | 14,9 KB | 3,3 ms | 11,3 ms |
@intlayer/lingui | dynamic | 10,3 KB | 137,0 KB | 9,9% | 0,0% | 12,8 KB | 2,9 ms | 19,7 ms |
intlayer (nativo) | static | 5,0 KB | 125,8 KB | 50,0% | 0,0% | 8,1 KB | 3,2 ms | 11,5 ms |
intlayer (nativo) | dynamic | 5,0 KB | 118,6 KB | 0,0% | 0,0% | 6,3 KB | 3,6 ms | 14,1 ms |
Come interpretare i dati
- Componenti: 7 volte più piccoli. Questo è l'effetto principale dell'adattatore. Un componente Lingui compilato isolatamente pesa in media da 58 a 148 KB a seconda della strategia, poiché
useLingui()accede all'istanza globale e a ogni catalogo caricato in essa. Lo stesso componente con l'adattatore pesa in media 12,8-14,9 KB: importa solo i propri dizionari e il resolver ICU, nient'altro. - Idratazione: 8-14 ms più veloce.
i18n.load()+i18n.activate()vengono eseguiti sul client prima che React possa idratare; più la configurazione di Lingui è lazy, più tempo richiede questa fase (28-34 ms). Con l'adattatore, i dizionari arrivano come import diretti già posizionati nel chunk della pagina: 11,3 ms in modalitàstatic, 19,7 ms in modalitàdynamic. - Cambio lingua: 2x più veloce, senza rallentamenti. La configurazione ottimizzata
scoped-dynamicdi Lingui impiega 42 ms per aggiornarehtml[lang], poiché il catalogo della route deve essere scaricato, caricato e attivato prima che il cambiamento sia visibile. L'adattatore rimane stabile a 2,9-3,3 ms in entrambe le modalità. - La configurazione semplice viene risanata automaticamente. Lingui statico scarica ogni catalogo su ogni pagina: 152,2 KB e 90% di page leakage. L'adattatore statico: 140,5 KB, 0% di page leakage, con gli stessi identici componenti.
- Byte per pagina: Lingui vince in
dynamic, di 22 KB. Questo è l'aspetto da valutare con chiarezza. Lingui compila i messaggi in array di token durante il build e include un runtime da 11 KB che si limita a scorrerli. L'adattatore distribuisce il resolver ICU di Intlayer (circa 15 KB in più di@intlayer/corerispetto alla build nativa), lo strato dell'adattatore (~10 KB) ereact-intlayer(~6 KB). Su questa applicazione, ciò significa 137,0 KB contro 115,2 KB. Se il budget primario è il peso grezzo per pagina e usi già Lingui con lazy loading, l'adattatore non offre un guadagno su questa metrica. - Il leakage della lingua è analogo su entrambi i fronti. 9,3% per Lingui, 9,9% per l'adattatore in modalità
dynamic. Deriva dai componenti:i18n._({ id: "careers-benefits.pay", message: "Top-of-market compensation" })contiene la stringa inglese di fallback, così come l'output delle macro salvo eliminazione esplicita del campo message. Tale stringa inglese finisce nel chunkfrindipendentemente dal runtime. Intlayer nativo (.content.ts, senza sorgente inline) garantisce lo 0%.
Perché i numeri si muovono e perché uno resta fisso
Due fattori determinano queste colonne: a cosa è collegato un componente e in quale formato viaggiano i messaggi.
Collegamento. Con Lingui, l'unità fondamentale è la lingua. Il file messages.mjs per fr forma un unico modulo; qualsiasi componente che importa l'istanza associata può accedere all'intero contenuto, impedendo al bundler di suddividere a un livello più granulare. Con l'adattatore, l'unità è il punto di chiamata: hero e footer sono import separati, suddivisi e caricati su richiesta per ciascun componente. Questo spiega i guadagni su peso dei componenti, idratazione e dispersione di pagina.
Copiare il codice nella clipboard
Copiare il codice nella clipboard
Formato. La fase di compilazione di Lingui converte {count, plural, one {# item} other {# items}} in un array di token; il runtime non effettua mai il parsing della sintassi ICU. L'adattatore mantiene il messaggio come testo e lo analizza tramite il resolver ICU di Intlayer. Si tratta di un costo fisso di circa 15 KB sostenuto una sola volta per pagina, motivo per cui la riga dynamic perde sui byte totali pur vincendo su ogni altro aspetto. Intlayer nativo evita questo costo perché i dizionari in .content.ts usano nodi enu() / insert() risolti preventivamente dal compilatore.
Migrazione in tre passaggi
Installazione
bashCopiare il codiceCopiare il codice nella clipboard
Il comando rileva Lingui, legge
lingui.config.tsper sceglieresyncPO(cataloghi.po) osyncJSON(cataloghi JSON), installaintlayer,react-intlayer,@intlayer/linguie il plugin di sincronizzazione corrispondente, e sostituisce@lingui/vite-plugincon il plugin dell'adattatore invite.config.ts. Mantieni installati@lingui/core,@lingui/reacte il tuo plugin di macro: le macro continuano a compilare e l'adattatore sfrutta i tipi di Lingui.Collegare Intlayer ai tuoi cataloghi
Per cataloghi JSON (
format: "minimal"inlingui.config.ts):intlayer.config.tsCopiare il codiceCopiare il codice nella clipboard
Per i cataloghi
.po, sostituiscisyncJSONconsyncPOda@intlayer/sync-po-plugincon il medesimo patternsourcee l'estensione.po. Consulta la documentazione del plugin Sync PO.splitKeys: "key-prefix"è ciò che rende possibile la drastica riduzione delle dimensioni dei componenti. Il file di catalogo mantiene la sua struttura lineare; la suddivisione esiste unicamente nei dizionari compilati, e la riscrittura ricompone automaticamente le chiavi.Aggiungere il plugin
vite.config.tsCopiare il codiceCopiare il codice nella clipboard
lingui()integravite-intlayer(monitoraggio dei file, compilazione dei dizionari, passaggio di ottimizzazione) e configura alias per reindirizzare@lingui/coree@lingui/reactverso l'adattatore. Compila, e ottieni subito i miglioramenti misurati.
Cosa puoi rimuovere dopo
Apri la tabella in una finestra modale per visualizzare tutti i dati in modo chiaro
| File / pattern | Motivo |
|---|---|
await import(`./locales/${locale}/messages.mjs`) | I dizionari vengono importati dai componenti che li usano. i18n.load() diventa un fallback | |
i18n.load() / i18n.loadAndActivate() | Mantieni i18n.activate(locale); rimuovi il caricamento manuale dei cataloghi |
lingui compile nello script di build | Solo se usi JSON o .po come sorgente e non importi più moduli compilati |
Cosa guadagni oltre ai byte
- Rilevamento traduzioni mancanti.
npx intlayer testblocca la CI se una lingua manca di un identificatore;lingui extractsi limita a riportare statistiche. npx intlayer filltraduce le voci mancanti con il provider a tua scelta (OpenAI, Anthropic, Mistral, Gemini...) e le riscrive direttamente nei tuoi cataloghi.- Visual Editor e CMS interagiscono direttamente con questi stessi dizionari, consentendo modifiche ai file
.poe JSON tramite interfaccia grafica a utenti non tecnici. - Passaggio incrementale a
.content.ts. Un componente può passare in qualunque momento dauseLingui()auseIntlayer("hero")con un file di contenuto locale dedicato. Entrambi i tipi di dizionario coesistono armoniosamente.
Limiti da conoscere prima di iniziare
- Il costo per pagina in modalità
dynamic. Come descritto in precedenza: aspettati circa +20 KB per pagina rispetto a una configurazione Lingui lazy-loaded su una piccola applicazione. Questo divario non aumenta con i contenuti (è legato al resolver, non ai cataloghi), ma non si riduce. - Il leakage della lingua sorgente permane. I descrittori di messaggi e le macro compilate incorporano il testo inglese originale come ripiego. Per eliminarlo, è necessario rimuovere il campo
messageo migrare a file.content.ts. i18n.load()è un fallback, non la soluzione ideale. Se continui a importare cataloghi compilati chiamandoload(), caricherai sia il vecchio sia il nuovo bundle. Rimuovi tali importazioni.- Solo per Vite. Non esiste un plugin Next.js per
@intlayer/lingui. I progetti Next.js che usano Lingui dovrebbero valutare direttamentenext-intlayer. defaultComponentnon viene applicato. Se fai affidamento su di esso per avvolgere automaticamente ciascun<Trans>, aggiungi esplicitamente l'involucro nel componente.
Quando usare cosa?
- Resta su Lingui se utilizzi già l'architettura
scoped-dynamic, il tuo unico vincolo sono i byte per pagina e ritieni accettabili i 42 ms di transizione lingua e i 30 ms di idratazione per la tua applicazione. - Usa
@intlayer/linguise sei su Lingui e desideri componenti più compatti, idratazione e cambio lingua più rapidi, 0% di dispersione pagina nella configurazione semplice, ID tipizzati, controlli in CI e completamento automatico tramite IA, senza modificare le macro. È la via d'accesso perfetta per una codebase esistente. - Passa a Intlayer nativo (
react-intlayer) non appena decidi di aggiornare i componenti. È l'unica soluzione in tabella a garantire 0% di leakage della lingua, un runtime di 5 KB e soli +7,6 KB per pagina rispetto all'applicazione di base.
Confronti correlati
- Lingui vs Intlayer (le due librerie a confronto, stesso benchmark)
- next-intl vs @intlayer/next-intl (stessa serie di adattatori)
- i18next vs @intlayer/i18next (stessa serie di adattatori)
- vue-i18n vs @intlayer/vue-i18n (stessa serie di adattatori)
- Guida all'adattatore di compatibilità: Lingui
- Compilatore vs i18n dichiarativo
Conclusione
@intlayer/lingui modifica il meccanismo di associazione dei punti di chiamata in Lingui: invece di collegarsi all'istanza globale e al suo catalogo per lingua, ciascun componente fa riferimento a un dizionario compilato specificamente per esso. Sulla medesima applicazione TanStack Start questo significa componenti 7 volte più compatti, idratazione più rapida di 8-14 ms, cambio lingua 2 volte più veloce senza rallentamenti di 42 ms, senza dover modificare alcuna macro. Non altera i fallback integrati nei componenti (il leakage della lingua sorgente permane) e analizza ICU a runtime (la configurazione dinamica pesa circa 20 KB in più per pagina rispetto a Lingui standard). Valuta con attenzione quale parametro risponde meglio al tuo budget di prestazioni prima di scegliere.
Tutti i dati grezzi, le applicazioni di prova e gli script sono disponibili nel repository Benchmark Bloom. Puoi testarli in autonomia.
Consulta la guida 'Perché Intlayer?' per maggiori dettagli.
Commenti
Ancora nessun commento. Sii il primo a condividere i tuoi pensieri.
