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
    /
    Alt+←
    Cos'è l'internazionalizzazione (i18n)?
    SEO e i18n
    Guida
    • i18n con next-i18next
    • i18n con next-intl
    Usa Intlayer sulla tua soluzione
    • Automatizza next-i18next
    • Automatizza react-i18next
    • Automatizza next-intl
    • Automatizza react-intl
    • Automatizza vue-i18n
    Comparazioni
    • next-i18next vs next-intl vs Intlayer
    • react-i18next vs react-intl vs Intlayer
    Documentazione
    1. Blog
    2. Per component vs centralized i18n
    Creation:2025-09-10Last update:2025-09-10
    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

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

    Vedi l'ultima versione del contenuto originale in inglese
    Edit this doc

    If you have an idea for improving this documentation, please feel free to contribute by submitting a pull request on GitHub.

    GitHub link to the documentation
    Copy

    Copy doc Markdown to clipboard

    i18n per componente vs i18n centralizzato

    L'approccio per-componente non è un concetto nuovo. Ad esempio, nell'ecosistema Vue, vue-i18n supporta l'i18n SFC (Single File Component). Nuxt offre anche traduzioni per-componente, e Angular utilizza un pattern simile attraverso i suoi Feature Modules.

    Anche in un'app Flutter spesso troviamo questo schema:

    bash
    Copiare il codice

    Copiare il codice nella clipboard

    lib/└── features/    └── login/        ├── login_screen.dart        └── login_screen.i18n.dart  # <- Le traduzioni risiedono qui
    lib/features/login/login_screen.i18n.dart
    Copiare il codice

    Copiare il codice nella clipboard

    import 'package:i18n_extension/i18n_extension.dart';extension Localization on String {  static var _t = Translations.byText("en") +      {        "Hello": {          "en": "Hello",          "fr": "Bonjour",        },      };  String get i18n => localize(this, _t);}

    Tuttavia, nel mondo React vediamo principalmente approcci differenti, che raggrupperò in tre categorie:

    Approccio centralizzato (i18next, next-intl, react-intl, lingui)

    • (senza namespaces) considera un'unica sorgente per recuperare i contenuti. Per impostazione predefinita, carichi i contenuti di tutte le pagine quando la tua app si avvia.

    Approccio granulare (intlayer, inlang)

    • dettagliare il recupero dei contenuti per chiave o per componente.

    In questo blog non mi concentrerò sulle soluzioni compiler-based, che ho già trattato qui: Compiler vs Declarative i18n. Nota che l'i18n compiler-based (ad es., Lingui) automatizza semplicemente l'estrazione e il caricamento dei contenuti. Sotto il cofano, spesso condividono le stesse limitazioni di altri approcci.

    Nota che più granularizzi il modo in cui recuperi i tuoi contenuti, maggiore è il rischio di inserire stato e logica aggiuntiva nei tuoi componenti.

    Gli approcci granulari sono più flessibili di quelli centralizzati, ma spesso è un compromesso. Anche se quelle librerie pubblicizzano il "tree shaking", nella pratica finirai spesso per caricare una pagina in tutte le lingue.

    Quindi, in termini generali, la decisione si riduce a:

    • Se la tua applicazione ha più pagine che lingue, dovresti favorire un approccio granulare.
    • Se hai più lingue che pagine, dovresti orientarti verso un approccio centralizzato.

    Naturalmente, gli autori delle librerie sono consapevoli di queste limitazioni e forniscono delle soluzioni. Tra queste: suddividere in namespace, caricare dinamicamente file JSON (await import()), o eliminare i contenuti in fase di build.

    Allo stesso tempo, dovresti sapere che quando carichi dinamicamente i tuoi contenuti, introduci richieste aggiuntive al tuo server. Ogni ulteriore useState o hook comporta una richiesta aggiuntiva al server.

    Per risolvere questo punto, Intlayer suggerisce di raggruppare più definizioni di contenuto sotto la stessa chiave; Intlayer poi effettuerà il merge di quei contenuti.

    Ma da tutte queste soluzioni risulta chiaro che l'approccio più popolare è quello centralizzato.

    Perché dunque l'approccio centralizzato è così popolare?

    • In primo luogo, i18next è stata la prima soluzione ad essere ampiamente adottata, seguendo una filosofia ispirata alle architetture PHP e Java (MVC), che si basano su una rigorosa separazione delle responsabilità (mantenere il contenuto separato dal codice). È arrivata nel 2011, stabilendo i suoi standard prima della grande transizione verso le architetture basate su componenti (come React).
    • Inoltre, una volta che una libreria viene ampiamente adottata, diventa difficile spostare l'intero ecosistema verso altri pattern.
    • L'utilizzo di un approccio centralizzato rende anche le cose più semplici in sistemi di gestione delle traduzioni (Translation Management Systems) come Crowdin, Phrase o Localized.
    • La logica alla base di un approccio per componente è più complessa rispetto a quella centralizzata e richiede più tempo di sviluppo, soprattutto quando bisogna risolvere problemi come identificare dove si trova il contenuto.

    Ok, ma perché non limitarsi semplicemente a un approccio centralizzato?

    Lasciami spiegare perché può essere problematico per la tua app:

    • Dati non utilizzati: Quando una pagina si carica, spesso carichi anche i contenuti di tutte le altre pagine. (In un'app di 10 pagine, significa il 90% di contenuti caricati e non usati). Carichi in lazy un modal? La libreria i18n non se ne cura, carica comunque prima le stringhe.
    • Prestazioni: A ogni re-render, ciascuno dei tuoi componenti viene idratato con un enorme payload JSON, il che impatta la reattività dell'app man mano che cresce.
    • Manutenzione: Gestire grandi file JSON è doloroso. Devi saltare tra file diversi per inserire una traduzione, assicurandoti che non manchino traduzioni e che non rimangano orphan keys.
    • Design system: Ciò crea incompatibilità con i design system (ad es., un componente LoginForm) e vincola la duplicazione dei componenti tra diverse app.

    "Ma abbiamo inventato i Namespaces!"

    Certo, ed è un enorme passo avanti. Diamo un'occhiata al confronto delle dimensioni del bundle principale di una configurazione Vite + React + React Router v7 + Intlayer. Abbiamo simulato un'applicazione di 20 pagine.

    Il primo esempio non include traduzioni lazy-loaded per locale né splitting dei namespace. Il secondo include content purging + caricamento dinamico delle traduzioni.

    Mostra tutto il contenuto della tabella

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

    Bundle ottimizzato Bundle non ottimizzato
    bundle non ottimizzato bundle ottimizzato

    Quindi, grazie ai namespaces, siamo passati da questa struttura:

    bash
    Copiare il codice

    Copiare il codice nella clipboard

    locale/├── en.json├── fr.json└── es.json

    To this one:

    bash
    Copiare il codice

    Copiare il codice nella clipboard

    locale/├── en/│   ├── common.json│   ├── navbar.json│   ├── footer.json│   ├── home.json│   └── about.json├── fr/│   └── ...└── es/    └── ...

    Ora devi gestire con precisione quale parte del contenuto della tua app debba essere caricata e dove. In conclusione, la stragrande maggioranza dei progetti salta semplicemente questa fase a causa della complessità (vedi ad esempio la guida next-i18next per vedere le sfide che comporta (anche solo) seguire le buone pratiche). Di conseguenza, questi progetti finiscono col trovarsi di fronte al problema del caricamento massiccio dei JSON spiegato in precedenza.

    Nota che questo problema non è specifico di i18next, ma riguarda tutti gli approcci centralizzati elencati sopra.

    Tuttavia, voglio ricordarti che non tutti gli approcci granulari risolvono questo problema. Ad esempio, gli approcci basati su vue-i18n SFC o su inlang non effettuano intrinsecamente il lazy loading delle traduzioni per locale, quindi si finisce semplicemente per scambiare il problema delle dimensioni del bundle con un altro.

    Inoltre, senza una corretta separazione delle responsabilità, diventa molto più difficile estrarre e fornire le traduzioni ai traduttori per la revisione.

    Come l'approccio per componente di Intlayer risolve questo

    Intlayer procede in diversi passaggi:

    1. Dichiarazione: Dichiara i tuoi contenuti ovunque nella tua codebase usando file *.content.{ts|jsx|cjs|json|json5|...}. Questo garantisce la separazione delle responsabilità mantenendo i contenuti collocati insieme. Un file di contenuto può essere per-locale o multilingue.
    2. Elaborazione: Intlayer esegue uno step di build per elaborare la logica JS, gestire i fallback per traduzioni mancanti, generare i tipi TypeScript, gestire contenuti duplicati, recuperare contenuti dal tuo CMS e altro.
    3. Pulizia: Quando la tua app viene buildata, Intlayer elimina i contenuti non utilizzati (un po' come Tailwind gestisce le tue classi) sostituendo il contenuto come segue:

    Dichiarazione:

    tsx
    Copiare il codice

    Copiare il codice nella clipboard

    // src/MyComponent.tsxexport const MyComponent = () => {  const content = useIntlayer("my-key");  return <h1>{content.title}</h1>;};
    tsx
    Copiare il codice

    Copiare il codice nella clipboard

    // src/myComponent.content.tsexport const {  key: "my-key",  content: t({    it: { title: "Il mio titolo" },    en: { title: "My title" },    fr: { title: "Mon titre" }  })}

    Elaborazione: Intlayer costruisce il dizionario basato sul file .content e genera:

    json5
    Copiare il codice

    Copiare il codice nella clipboard

    // .intlayer/dynamic_dictionary/en/my-key.json{  "key": "my-key",  "content": { "title": "My title" },}

    Sostituzione: Intlayer trasforma il tuo componente durante la fase di build dell'applicazione.

    - Modalità di importazione statica:

    tsx
    Copiare il codice

    Copiare il codice nella clipboard

    // Rappresentazione del componente in sintassi simile a JSXexport const MyComponent = () => {  const content = useDictionary({    key: "my-key",    content: {      nodeType: "translation",      translation: {        en: { title: "My title" },        fr: { title: "Mon titre" },      },    },  });  return <h1>{content.title}</h1>;};

    - Modalità di importazione dinamica:

    tsx
    Copiare il codice

    Copiare il codice nella clipboard

    // Rappresentazione del componente in sintassi simile a JSXexport const MyComponent = () => {  const content = useDictionaryAsync({    en: () =>      import(".intlayer/dynamic_dictionary/en/my-key.json", {        with: { type: "json" },      }).then((mod) => mod.default),    // Lo stesso per altre lingue  });  return <h1>{content.title}</h1>;};
    useDictionaryAsync utilizza un meccanismo simile a Suspense per caricare il JSON localizzato solo quando necessario.

    Vantaggi principali di questo approccio per componente:

    • Mantenere la dichiarazione del contenuto vicino ai componenti consente una migliore manutenibilità (es. spostare un componente in un'altra app o in un design system. Eliminando la cartella del componente si rimuovono anche i contenuti correlati, come probabilmente già fai per i file .test, .stories)

    • Un approccio per componente evita che gli agenti AI debbano saltare tra tutti i tuoi file. Tratta tutte le traduzioni in un unico posto, limitando la complessità del compito e la quantità di token utilizzati.

    Limitazioni

    Naturalmente, questo approccio comporta dei compromessi:

    • È più difficile connettersi ad altri sistemi di l10n e a tooling aggiuntivo.
    • Sei vincolato (cosa che è praticamente già il caso con qualsiasi soluzione i18n a causa della loro sintassi specifica).

    Per questo motivo Intlayer cerca di fornire un set completo di strumenti per l'i18n (100% gratuito e OSS), inclusa la traduzione AI usando il tuo AI Provider e le tue API keys. Intlayer fornisce anche tooling per sincronizzare i tuoi JSON, funzionando come formatter di messaggi tipo ICU / vue-i18n / i18next per mappare il contenuto nei loro formati specifici.

    Cos'è l'internazionalizzazione (i18n)?
    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.

      lib/└── features/    └── login/        ├── login_screen.dart        └── login_screen.i18n.dart  # <- Le traduzioni risiedono qui
      import 'package:i18n_extension/i18n_extension.dart';extension Localization on String {  static var _t = Translations.byText("en") +      {        "Hello": {          "en": "Hello",          "fr": "Bonjour",        },      };  String get i18n => localize(this, _t);}
      locale/├── en.json├── fr.json└── es.json
      locale/├── en/│   ├── common.json│   ├── navbar.json│   ├── footer.json│   ├── home.json│   └── about.json├── fr/│   └── ...└── es/    └── ...
      // src/MyComponent.tsxexport const MyComponent = () => {  const content = useIntlayer("my-key");  return <h1>{content.title}</h1>;};
      // src/myComponent.content.tsexport const {  key: "my-key",  content: t({    it: { title: "Il mio titolo" },    en: { title: "My title" },    fr: { title: "Mon titre" }  })}
      // .intlayer/dynamic_dictionary/en/my-key.json{  "key": "my-key",  "content": { "title": "My title" },}
      // Rappresentazione del componente in sintassi simile a JSXexport const MyComponent = () => {  const content = useDictionary({    key: "my-key",    content: {      nodeType: "translation",      translation: {        en: { title: "My title" },        fr: { title: "Mon titre" },      },    },  });  return <h1>{content.title}</h1>;};
      // Rappresentazione del componente in sintassi simile a JSXexport const MyComponent = () => {  const content = useDictionaryAsync({    en: () =>      import(".intlayer/dynamic_dictionary/en/my-key.json", {        with: { type: "json" },      }).then((mod) => mod.default),    // Lo stesso per altre lingue  });  return <h1>{content.title}</h1>;};