Getting Started internationalizing (i18n) with Intlayer and Next.js 15 App Router

    Che cos'è Intlayer?

    Intlayer è una libreria open-source innovativa per l'internazionalizzazione (i18n) progettata per semplificare il supporto multilingue nelle applicazioni web moderne. Intlayer si integra perfettamente con l'ultimo framework Next.js 15, inclusa la sua potente App Router. È ottimizzato per funzionare con i Server Components per un rendering efficiente ed è completamente compatibile con Turbopack.

    Con Intlayer, puoi:

    • Gestire facilmente le traduzioni utilizzando dizionari dichiarativi a livello di componente.
    • Localizzare dinamicamente metadati, percorsi e contenuti.
    • Accedere alle traduzioni sia nei componenti lato client che lato server.
    • Garantire il supporto TypeScript con tipi autogenerati, migliorando l'autocompletamento e il rilevamento degli errori.
    • Beneficiare di funzionalità avanzate, come il rilevamento dinamico della lingua e il cambio di lingua.

    Intlayer è compatibile con Next.js 12, 13, 14 e 15. Se stai utilizzando Next.js Page Router, puoi fare riferimento a questa guida. Per Next.js 12, 13, 14 con App Router, fai riferimento a questa guida.


    Guida passo-passo per configurare Intlayer in un'applicazione Next.js

    Passo 1: Installa le dipendenze

    Installa i pacchetti necessari utilizzando npm:

    bash
    npm install intlayer next-intlayer
    • intlayer

      Il pacchetto principale che fornisce strumenti di internazionalizzazione per la gestione della configurazione, traduzione, dichiarazione dei contenuti, transpilation e comandi CLI.

    • next-intlayer

      Il pacchetto che integra Intlayer con Next.js. Fornisce provider di contesto e hook per l'internazionalizzazione di Next.js. Inoltre, include il plugin Next.js per integrare Intlayer con Webpack o Turbopack, oltre a middleware per rilevare la lingua preferita dell'utente, gestire i cookie e gestire i reindirizzamenti URL.

    Passo 2: Configura il tuo progetto

    Crea un file di configurazione per configurare le lingue della tua applicazione:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [      Locales.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // Altre lingue    ],    defaultLocale: Locales.ENGLISH,  },};export default config;

    Tramite questo file di configurazione, puoi configurare URL localizzati, reindirizzamenti middleware, nomi dei cookie, la posizione e l'estensione delle dichiarazioni dei contenuti, disabilitare i log di Intlayer nella console e altro ancora. Per un elenco completo dei parametri disponibili, fai riferimento alla documentazione di configurazione.

    Passo 3: Integra Intlayer nella configurazione di Next.js

    Configura il tuo setup Next.js per utilizzare Intlayer:

    typescript
    import type { NextConfig } from "next";import { withIntlayer } from "next-intlayer/server";const nextConfig: NextConfig = {  /* opzioni di configurazione */};export default withIntlayer(nextConfig);

    Il plugin withIntlayer() di Next.js viene utilizzato per integrare Intlayer con Next.js. Garantisce la creazione dei file di dichiarazione dei contenuti e li monitora in modalità sviluppo. Definisce le variabili d'ambiente di Intlayer all'interno degli ambienti Webpack o Turbopack. Inoltre, fornisce alias per ottimizzare le prestazioni e garantisce la compatibilità con i componenti server.

    Passo 4: Definisci percorsi dinamici per le lingue

    Rimuovi tutto da RootLayout e sostituiscilo con il seguente codice:

    src/app/layout.tsx
    import type { PropsWithChildren, FC } from "react";import "./globals.css";const RootLayout: FC<PropsWithChildren> = ({ children }) => children;export default RootLayout;

    Mantenere il componente RootLayout vuoto consente di impostare gli attributi lang e dir sul tag <html>.

    Per implementare il routing dinamico, fornisci il percorso per la lingua aggiungendo un nuovo layout nella tua directory [locale]:

    src/app/[locale]/layout.tsx
    import type { NextLayoutIntlayer } from "next-intlayer";import { Inter } from "next/font/google";import { getHTMLTextDir } from "intlayer";const inter = Inter({ subsets: ["latin"] });const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {  const { locale } = await params;  return (    <html lang={locale} dir={getHTMLTextDir(locale)}>      <body className={inter.className}>{children}</body>    </html>  );};export default LocaleLayout;

    Il segmento di percorso [locale] viene utilizzato per definire la lingua. Esempio: /en-US/about si riferirà a en-US e /fr/about a fr.

    Poi, implementa la funzione generateStaticParams nel layout della tua applicazione.

    src/app/[locale]/layout.tsx
    export { generateStaticParams } from "next-intlayer"; // Riga da inserireconst LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {  /*... Resto del codice*/};export default LocaleLayout;

    generateStaticParams garantisce che la tua applicazione pre-costruisca le pagine necessarie per tutte le lingue, riducendo il calcolo a runtime e migliorando l'esperienza utente. Per maggiori dettagli, fai riferimento alla documentazione di Next.js su generateStaticParams.

    Passo 5: Dichiara i tuoi contenuti

    Crea e gestisci le dichiarazioni dei tuoi contenuti per memorizzare le traduzioni:

    src/app/[locale]/page.content.ts
    import { t, type Dictionary } from "intlayer";const pageContent = {  key: "page",  content: {    getStarted: {      main: t({        en: "Get started by editing",        fr: "Commencez par éditer",        es: "Comience por editar",        it: "Inizia modificando",      }),      pageLink: "src/app/page.tsx",    },  },} satisfies Dictionary;export default pageContent;

    Le dichiarazioni dei tuoi contenuti possono essere definite ovunque nella tua applicazione purché siano incluse nella directory contentDir (di default, ./src). E corrispondano all'estensione del file di dichiarazione dei contenuti (di default, .content.{ts,tsx,js,jsx,mjs,cjs}). Per maggiori dettagli, fai riferimento alla documentazione sulla dichiarazione dei contenuti.

    Passo 6: Utilizza i contenuti nel tuo codice

    Accedi ai tuoi dizionari di contenuti in tutta l'applicazione:

    src/app/[locale]/page.tsx
    import type { FC } from "react";import { ClientComponentExample } from "@components/ClientComponentExample";import { ServerComponentExample } from "@components/ServerComponentExample";import { type NextPageIntlayer, IntlayerClientProvider } from "next-intlayer";import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";const PageContent: FC = () => {  const { title, content } = useIntlayer("page");  return (    <>      <p>{content.getStarted.main}</p>      <code>{content.getStarted.pageLink}</code>    </>  );};const Page: NextPageIntlayer = async ({ params }) => {  const { locale } = await params;  return (    <IntlayerServerProvider locale={locale}>      <PageContent />      <ServerComponentExample />      <IntlayerClientProvider locale={locale}>        <ClientComponentExample />      </IntlayerClientProvider>    </IntlayerServerProvider>  );};export default Page;
    • IntlayerClientProvider viene utilizzato per fornire la lingua ai componenti lato client. Può essere posizionato in qualsiasi componente genitore, incluso il layout. Tuttavia, è consigliato posizionarlo in un layout perché Next.js condivide il codice del layout tra le pagine, rendendolo più efficiente. Utilizzando IntlayerClientProvider nel layout, eviti di inizializzarlo nuovamente per ogni pagina, migliorando le prestazioni e mantenendo un contesto di localizzazione coerente in tutta l'applicazione.
    • IntlayerServerProvider viene utilizzato per fornire la lingua ai figli lato server. Non può essere impostato nel layout.

      Layout e pagina non possono condividere un contesto server comune perché il sistema di contesto server si basa su un archivio dati per richiesta (tramite il meccanismo React’s cache), causando la ricreazione di ogni "contesto" per segmenti diversi dell'applicazione. Posizionare il provider in un layout condiviso interromperebbe questo isolamento, impedendo la corretta propagazione dei valori del contesto server ai tuoi componenti server.

    src/components/ClientComponentExample.tsx
    "use client";import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ClientComponentExample: FC = () => {  const content = useIntlayer("client-component-example"); // Crea dichiarazione dei contenuti correlata  return (    <div>      <h2>{content.title} </h2>      <p>{content.content}</p>    </div>  );};
    src/components/ServerComponentExample.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer/server";export const ServerComponentExample: FC = () => {  const content = useIntlayer("server-component-example"); // Crea dichiarazione dei contenuti correlata  return (    <div>      <h2>{content.title} </h2>      <p>{content.content}</p>    </div>  );};

    Se desideri utilizzare i tuoi contenuti in un attributo string, come alt, title, href, aria-label, ecc., devi chiamare il valore della funzione, come:

    jsx
    <img src={content.image.src.value} alt={content.image.value} />

    Per saperne di più sull'hook useIntlayer, fai riferimento alla documentazione.

    (Opzionale) Passo 7: Configura Middleware per il rilevamento della lingua

    Configura il middleware per rilevare la lingua preferita dell'utente:

    src/middleware.ts
    export { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = {  matcher:    "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\..*|_next).*)",};

    Il intlayerMiddleware viene utilizzato per rilevare la lingua preferita dell'utente e reindirizzarlo all'URL appropriato come specificato nella configurazione. Inoltre, consente di salvare la lingua preferita dell'utente in un cookie.

    (Opzionale) Passo 8: Internazionalizzazione dei tuoi metadati

    Nel caso in cui desideri internazionalizzare i tuoi metadati, come il titolo della tua pagina, puoi utilizzare la funzione generateMetadata fornita da Next.js. All'interno della funzione utilizza la funzione getTranslation per tradurre i tuoi metadati.

    src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx
    import {  type IConfigLocales,  getTranslation,  getMultilingualUrls,} from "intlayer";import type { Metadata } from "next";import type { LocalPromiseParams } from "next-intlayer";export const generateMetadata = async ({  params,}: LocalPromiseParams): Promise<Metadata> => {  const { locale } = await params;  const t = <T>(content: IConfigLocales<T>) => getTranslation(content, locale);  /**   * Genera un oggetto contenente tutti gli URL per ogni lingua.   *   * Esempio:   * ```ts   *  getMultilingualUrls('/about');   *   *  // Restituisce   *  // {   *  //   en: '/about',   *  //   fr: '/fr/about',   *  //   es: '/es/about',   *  // }   * ```   */  const multilingualUrls = getMultilingualUrls("/");  return {    title: t<string>({      en: "My title",      fr: "Mon titre",      es: "Mi título",      it: "Il mio titolo",    }),    description: t({      en: "My description",      fr: "Ma description",      es: "Mi descripción",      it: "La mia descrizione",    }),    alternates: {      canonical: "/",      languages: { ...multilingualUrls, "x-default": "/" },    },    openGraph: {      url: multilingualUrls[locale],    },  };};// ... Resto del codice

    Per saperne di più sull'ottimizzazione dei metadati nella documentazione ufficiale di Next.js.

    (Opzionale) Passo 9: Internazionalizzazione del tuo sitemap.xml e robots.txt

    Per internazionalizzare il tuo sitemap.xml e robots.txt, puoi utilizzare la funzione getMultilingualUrls fornita da Intlayer. Questa funzione ti consente di generare URL multilingue per il tuo sitemap.

    src/app/sitemap.ts
    import { getMultilingualUrls } from "intlayer";import type { MetadataRoute } from "next";const sitemap = (): MetadataRoute.Sitemap => [  {    url: "https://example.com",    alternates: {      languages: { ...getMultilingualUrls("https://example.com") },    },  },  {    url: "https://example.com/login",    alternates: {      languages: { ...getMultilingualUrls("https://example.com/login") },    },  },  {    url: "https://example.com/register",    alternates: {      languages: { ...getMultilingualUrls("https://example.com/register") },    },  },];export default sitemap;
    src/app/robots.ts
    import type { MetadataRoute } from "next";import { getMultilingualUrls } from "intlayer";const getAllMultilingualUrls = (urls: string[]) =>  urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);const robots = (): MetadataRoute.Robots => ({  rules: {    userAgent: "*",    allow: ["/"],    disallow: getAllMultilingualUrls(["/login", "/register"]),  },  host: "https://example.com",  sitemap: `https://example.com/sitemap.xml`,});export default robots;

    Per saperne di più sull'ottimizzazione del sitemap nella documentazione ufficiale di Next.js. Per saperne di più sull'ottimizzazione del robots.txt nella documentazione ufficiale di Next.js.

    (Opzionale) Passo 10: Cambia la lingua dei tuoi contenuti

    Per cambiare la lingua dei tuoi contenuti, puoi utilizzare la funzione setLocale fornita dall'hook useLocale. Questa funzione ti consente di impostare la lingua dell'applicazione e aggiornare i contenuti di conseguenza.

    src/components/LocaleSwitcher.tsx
    "use client";import type { FC } from "react";import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocale } from "next-intlayer";import Link from "next/link";export const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales, setLocale } =    useLocale();  return (    <div>      <button popoverTarget="localePopover">{getLocaleName(locale)}</button>      <div id="localePopover" popover="auto">        {availableLocales.map((localeItem) => (          <Link            href={getLocalizedUrl(pathWithoutLocale, localeItem)}            hrefLang={localeItem}            key={localeItem}            aria-current={locale === localeItem ? "page" : undefined}            onClick={(e) => {              e.preventDefault();              setLocale(localeItem);            }}          >            <span>              {/* Locale - es. IT */}              {localeItem}            </span>            <span>              {/* Lingua nella propria lingua - es. Italiano */}              {getLocaleName(localeItem, locale)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* Lingua nella lingua corrente - es. Italian con lingua corrente impostata su Locales.ENGLISH */}              {getLocaleName(localeItem)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* Lingua in Inglese - es. Italian */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>          </Link>        ))}      </div>    </div>  );};

    Riferimenti alla documentazione:

    Per garantire che la navigazione della tua applicazione rispetti la lingua corrente, puoi creare un componente Link personalizzato. Questo componente aggiunge automaticamente il prefisso agli URL interni con la lingua corrente. Ad esempio, quando un utente francofono clicca su un link alla pagina "About", viene reindirizzato a /fr/about invece che a /about.

    Questo comportamento è utile per diversi motivi:

    • SEO e User Experience: Gli URL localizzati aiutano i motori di ricerca a indicizzare correttamente le pagine specifiche per lingua e forniscono agli utenti contenuti nella loro lingua preferita.
    • Coerenza: Utilizzando un link localizzato in tutta l'applicazione, garantisci che la navigazione rimanga all'interno della lingua corrente, evitando cambiamenti di lingua inaspettati.
    • Manutenibilità: Centralizzando la logica di localizzazione in un unico componente, semplifichi la gestione degli URL, rendendo il tuo codice più facile da mantenere ed estendere man mano che l'applicazione cresce.

    Di seguito l'implementazione di un componente Link localizzato in TypeScript:

    src/components/Link.tsx
    "use client";import { getLocalizedUrl } from "intlayer";import NextLink, { type LinkProps as NextLinkProps } from "next/link";import { useLocale } from "next-intlayer";import type { PropsWithChildren, FC } from "react";/** * Funzione di utilità per verificare se un URL è esterno. * Se l'URL inizia con http:// o https://, viene considerato esterno. */export const checkIsExternalLink = (href?: string): boolean =>  /^https?:///.test(href ?? "");/** * Un componente Link personalizzato che adatta l'attributo href in base alla lingua corrente. * Per i link interni, utilizza `getLocalizedUrl` per aggiungere il prefisso all'URL con la lingua (es. /fr/about). * Questo garantisce che la navigazione rimanga all'interno dello stesso contesto linguistico. */export const Link: FC<PropsWithChildren<NextLinkProps>> = ({  href,  children,  ...props}) => {  const { locale } = useLocale();  const isExternalLink = checkIsExternalLink(href.toString());  // Se il link è interno e viene fornito un href valido, ottieni l'URL localizzato.  const hrefI18n: NextLinkProps["href"] =    href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;  return (    <NextLink href={hrefI18n} {...props}>      {children}    </NextLink>  );};

    Come funziona

    • Rilevamento dei link esterni:
      La funzione helper checkIsExternalLink determina se un URL è esterno. I link esterni vengono lasciati invariati perché non necessitano di localizzazione.

    • Recupero della lingua corrente:
      L'hook useLocale fornisce la lingua corrente (es. fr per il francese).

    • Localizzazione dell'URL:
      Per i link interni (cioè non esterni), getLocalizedUrl viene utilizzato per aggiungere automaticamente il prefisso all'URL con la lingua corrente. Ciò significa che se il tuo utente è in francese, passando /about come href verrà trasformato in /fr/about.

    • Restituzione del Link:
      Il componente restituisce un elemento <a> con l'URL localizzato, garantendo che la navigazione sia coerente con la lingua.

    Integrando questo componente Link in tutta l'applicazione, mantieni un'esperienza utente coerente e consapevole della lingua, beneficiando anche di un miglior SEO e usabilità.

    Configura TypeScript

    Intlayer utilizza l'augmentazione dei moduli per ottenere i benefici di TypeScript e rendere il tuo codice più robusto.

    alt text

    alt text

    Assicurati che la configurazione di TypeScript includa i tipi autogenerati.

    tsconfig.json
    {  // ... Le tue configurazioni TypeScript esistenti  "include": [    // ... Le tue configurazioni TypeScript esistenti    ".intlayer/**/*.ts", // Includi i tipi autogenerati  ],}

    Configurazione Git

    Si consiglia di ignorare i file generati da Intlayer. Questo ti consente di evitare di commetterli nel tuo repository Git.

    Per farlo, puoi aggiungere le seguenti istruzioni al tuo file .gitignore:

    .gitignore
    # Ignora i file generati da Intlayer.intlayer

    Approfondisci

    Per approfondire, puoi implementare l'editor visivo o esternalizzare i tuoi contenuti utilizzando il CMS.

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

    Collegamento GitHub alla documentazione