Cómo cargar traducciones desde archivos en Next.js (Pages Router) v16

Separa el contenido traducible del código

Problema

Escribir cadenas visibles para el usuario directamente en los componentes crea una fuerte dependencia entre el contenido y el código. Cada vez que se cambia un texto, los desarrolladores tienen que buscar y modificar los archivos de implementación. Agregar soporte para un segundo idioma requiere encontrar todas las cadenas escritas a mano y envolverlas en lógica condicional. Un tercer idioma complica aún más este proceso. Este enfoque hace que los flujos de traducción dependan de los despliegues de código y evita que miembros no técnicos del Team actualicen el contenido por su cuenta.

A medida que la aplicación crece, gestionar las traducciones se vuelve más difícil. Las cadenas repartidas entre docenas de componentes son difíciles de auditar, duplicar o actualizar de forma coherente. Los traductores no pueden trabajar en paralelo con los desarrolladores porque necesitan acceder al propio código.

Solución

Guarda todo el texto visible en archivos de recursos externos, organizados por idioma, con un archivo JSON por cada localización. Cada mensaje se identifica por una clave única en lugar del texto literal. Los componentes hacen referencia a estas claves en vez de usar cadenas incrustadas.

Carga el archivo de traducción adecuado según la localización que recibe Next.js y pasa los mensajes al proveedor de react-intl. Así la aplicación puede cambiar de idioma cargando un archivo diferente, sin modificar el código de los componentes. Esto separa el contenido de la implementación y permite que los traductores trabajen en archivos JSON estándar, mientras los desarrolladores solo referencian claves estables.

Pasos

1. Crea archivos de traducción organizados por localización

Coloca los mensajes de traducción en archivos JSON separados por idioma. Cada archivo contiene pares de clave-valor, donde las claves son identificadores estables y los valores son las cadenas traducidas para ese idioma.

{
"welcome": "Welcome back",
"greeting": "Hello, {name}",
"itemCount": "You have {count, plural, one {# item} other {# items}}"
}

Crea un archivo por cada idioma soportado (por ejemplo, messages/en.json, messages/es.json, messages/fr.json) en un directorio messages en la raíz de tu proyecto. Usa las mismas claves en todos los archivos para que react-intl pueda buscar la traducción adecuada según el idioma activo.

2. Carga los mensajes en getStaticProps

Lee el archivo de traducción según el idioma recibido de Next.js en getStaticProps. Así te aseguras de que los mensajes estén disponibles en el servidor y sean pasados a la página como props.

import { GetStaticProps } from "next";

export const getStaticProps: GetStaticProps = async (context) => {
  const locale = context.locale || "en";
  const messages = (await import(`../messages/${locale}.json`)).default;

  return {
    props: {
      messages,
    },
  };
};

La importación dinámica solo carga el archivo para el idioma actual. Next.js provee automáticamente el valor locale según la URL o las preferencias del usuario.

3. Pasa los mensajes a IntlProvider en _app

Envuelve tu componente raíz con IntlProvider y configúralo con el idioma actual del usuario y los mensajes traducidos correspondientes. Accede a los mensajes desde pageProps para que cada página pueda proporcionar sus propias traducciones.

import { AppProps } from "next/app";
import { IntlProvider } from "react-intl";
import { useRouter } from "next/router";

export default function App({ Component, pageProps }: AppProps) {
  const { locale, defaultLocale } = useRouter();

  return (
    <IntlProvider
      locale={locale || "en"}
      defaultLocale={defaultLocale || "en"}
      messages={pageProps.messages}
    >
      <Component {...pageProps} />
    </IntlProvider>
  );
}

El proveedor pone los mensajes a disposición de todos los componentes en el árbol. Cada página carga su propio archivo de mensajes mediante getStaticProps, y _app recibe esos mensajes a través de pageProps.

4. Referencia los mensajes por clave en los componentes

Utiliza el componente FormattedMessage de react-intl o el hook useIntl para mostrar textos traducidos. Referencia los mensajes por su clave en lugar de poner textos directamente.

import { FormattedMessage, useIntl } from "react-intl";

export default function HomePage() {
  const intl = useIntl();
  const userName = "Alice";

  return (
    <div>
      <h1>
        <FormattedMessage id="welcome" />
      </h1>
      <p>
        <FormattedMessage id="greeting" values={{ name: userName }} />
      </p>
      <input placeholder={intl.formatMessage({ id: "searchPlaceholder" })} />
    </div>
  );
}

React-intl busca y formatea el mensaje traducido según el id dado. Si falta una traducción, usará el defaultMessage si está definido. Las variables pasadas en la prop values se interpolan en el texto del mensaje.