Skip to content

Cómo Traducir una App Next.js: Guía Completa

Aprende cómo localizar tu app Next.js usando next-intl o next-i18next. Esta guía completa cubre routing, traducción de cadenas, pluralización, formato de fechas y mejores prácticas para la t

  • date icon

    17 de noviembre de 2025

  • 05 min de lectura
Cómo Traducir una App Next.js: Guía Completa

Traducir tu app Next.js es esencial para llegar a una audiencia global. Next.js proporciona excelente soporte integrado para internacionalización (i18n) a través de routing y puede mejorarse con bibliotecas como next-intl o next-i18next. Esta guía te guiará a través del proceso de traducir una app Next.js.

Entendiendo la Localización de Next.js

Next.js soporta internacionalización a través de:

  1. Routing i18n integrado - Detección automática de locale y routing
  2. next-intl - Biblioteca moderna construida para Next.js App Router
  3. next-i18next - Biblioteca popular para Pages Router

Cubriremos ambos enfoques en esta guía.

Método 1: Usando next-intl (App Router - Recomendado)

next-intl es la solución recomendada para Next.js 13+ con App Router.

Paso 1: Instalar Dependencias

npm install next-intl

O con yarn:

yarn add next-intl

Paso 2: Crear Archivos de Traducción

Crea un directorio messages en la raíz de tu proyecto:

messages/en.json (Inglés - por defecto):

{
  "app": {
    "welcome": "Welcome to our app!",
    "title": "My Next.js App"
  },
  "button": {
    "submit": "Submit",
    "cancel": "Cancel",
    "delete": "Delete"
  },
  "error": {
    "network": "Network error. Please try again.",
    "notFound": "Page not found"
  },
  "items": {
    "count": "{count, plural, =0 {No items} one {# item} other {# items}}"
  },
  "user": {
    "greeting": "Hello, {name}! You have {count, plural, =0 {no messages} one {# message} other {# messages}}."
  }
}

messages/es.json (Español):

{
  "app": {
    "welcome": "¡Bienvenido a nuestra aplicación!",
    "title": "Mi Aplicación Next.js"
  },
  "button": {
    "submit": "Enviar",
    "cancel": "Cancelar",
    "delete": "Eliminar"
  },
  "error": {
    "network": "Error de red. Por favor, inténtalo de nuevo.",
    "notFound": "Página no encontrada"
  },
  "items": {
    "count": "{count, plural, =0 {No hay elementos} one {# elemento} other {# elementos}}"
  },
  "user": {
    "greeting": "¡Hola, {name}! Tienes {count, plural, =0 {no hay mensajes} one {# mensaje} other {# mensajes}}."
  }
}

messages/fr.json (Francés):

{
  "app": {
    "welcome": "Bienvenue dans notre application!",
    "title": "Mon Application Next.js"
  },
  "button": {
    "submit": "Soumettre",
    "cancel": "Annuler",
    "delete": "Supprimer"
  },
  "error": {
    "network": "Erreur réseau. Veuillez réessayer.",
    "notFound": "Page non trouvée"
  },
  "items": {
    "count": "{count, plural, =0 {Aucun élément} one {# élément} other {# éléments}}"
  },
  "user": {
    "greeting": "Bonjour, {name}! Vous avez {count, plural, =0 {aucun message} one {# message} other {# messages}}."
  }
}

Paso 3: Configurar next-intl

Crea i18n.ts en la raíz de tu proyecto:

i18n.ts:

import { getRequestConfig } from 'next-intl/server';
import { notFound } from 'next/navigation';

export const locales = ['en', 'es', 'fr'] as const;
export const defaultLocale = 'en' as const;

export default getRequestConfig(async ({ locale }) => {
  if (!locales.includes(locale as any)) notFound();

  return {
    messages: (await import(`./messages/${locale}.json`)).default
  };
});

Paso 4: Actualizar next.config.js

next.config.js:

const createNextIntlPlugin = require('next-intl/plugin');

const withNextIntl = createNextIntlPlugin();

/** @type {import('next').NextConfig} */
const nextConfig = {};

module.exports = withNextIntl(nextConfig);

Paso 5: Crear Middleware

Crea middleware.ts en la raíz de tu proyecto:

middleware.ts:

import createMiddleware from 'next-intl/middleware';
import { locales, defaultLocale } from './i18n';

export default createMiddleware({
  locales,
  defaultLocale,
  localePrefix: 'always' // o 'as-needed'
});

export const config = {
  matcher: ['/((?!api|_next|_vercel|.*\\..*).*)']
};

Paso 6: Actualizar Estructura del App Router

Reestructura tu directorio app para incluir [locale]:

app/
  [locale]/
    layout.tsx
    page.tsx
    about/
      page.tsx

app/[locale]/layout.tsx:

import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { locales } from '@/i18n';

export function generateStaticParams() {
  return locales.map((locale) => ({ locale }));
}

export default async function LocaleLayout({
  children,
  params: { locale }
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  if (!locales.includes(locale as any)) {
    notFound();
  }

  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

app/[locale]/page.tsx:

import { useTranslations } from 'next-intl';

export default function HomePage() {
  const t = useTranslations();

  return (
    <div>
      <h1>{t('app.welcome')}</h1>
      <p>{t('app.title')}</p>
    </div>
  );
}

Paso 7: Usar Traducciones en Componentes

Componentes del Servidor:

import { useTranslations } from 'next-intl';

export default function ServerComponent() {
  const t = useTranslations();
  
  return <h1>{t('app.welcome')}</h1>;
}

Componentes del Cliente:

'use client';

import { useTranslations } from 'next-intl';

export default function ClientComponent() {
  const t = useTranslations();
  
  return <h1>{t('app.welcome')}</h1>;
}

Paso 8: Interpolación de Cadenas

import { useTranslations } from 'next-intl';

export default function UserGreeting({ name, messageCount }: { name: string; messageCount: number }) {
  const t = useTranslations('user');
  
  return (
    <p>
      {t('greeting', { name, count: messageCount })}
    </p>
  );
}

Paso 9: Pluralización

import { useTranslations } from 'next-intl';

export default function ItemCount({ count }: { count: number }) {
  const t = useTranslations('items');
  
  return <p>{t('count', { count })}</p>;
}

Paso 10: Formatear Fechas y Números

import { useTranslations, useFormatter } from 'next-intl';

export default function DateDisplay({ date }: { date: Date }) {
  const format = useFormatter();
  
  return (
    <p>
      {format.dateTime(date, {
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        weekday: 'long'
      })}
    </p>
  );
}

export default function NumberDisplay({ number }: { number: number }) {
  const format = useFormatter();
  
  return (
    <div>
      <p>Número: {format.number(number)}</p>
      <p>Moneda: {format.number(number, { style: 'currency', currency: 'USD' })}</p>
    </div>
  );
}

Paso 11: Selector de Idioma

'use client';

import { usePathname, useRouter } from 'next/navigation';
import { useLocale } from 'next-intl';

export default function LanguageSwitcher() {
  const router = useRouter();
  const pathname = usePathname();
  const locale = useLocale();

  const switchLocale = (newLocale: string) => {
    const newPathname = pathname.replace(`/${locale}`, `/${newLocale}`);
    router.push(newPathname);
  };

  return (
    <div>
      <button onClick={() => switchLocale('en')}>English</button>
      <button onClick={() => switchLocale('es')}>Español</button>
      <button onClick={() => switchLocale('fr')}>Français</button>
    </div>
  );
}

Método 2: Usando next-i18next (Pages Router)

Para Next.js Pages Router, usa next-i18next.

Paso 1: Instalar Dependencias

npm install next-i18next react-i18next i18next

Paso 2: Crear Archivos de Traducción

public/locales/en/common.json:

{
  "welcome": "Welcome to our app!",
  "button": {
    "submit": "Submit",
    "cancel": "Cancel"
  }
}

public/locales/es/common.json:

{
  "welcome": "¡Bienvenido a nuestra aplicación!",
  "button": {
    "submit": "Enviar",
    "cancel": "Cancelar"
  }
}

Paso 3: Configurar next-i18next

next-i18next.config.js:

module.exports = {
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'es', 'fr'],
  },
  localePath: './public/locales',
};

next.config.js:

const { i18n } = require('./next-i18next.config');

module.exports = {
  i18n,
};

Paso 4: Crear App Personalizado

pages/_app.js:

import { appWithTranslation } from 'next-i18next';

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default appWithTranslation(MyApp);

Paso 5: Usar Traducciones en Páginas

pages/index.js:

import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

export default function HomePage() {
  const { t } = useTranslation('common');

  return <h1>{t('welcome')}</h1>;
}

export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common'])),
    },
  };
}

Método 3: Routing i18n Integrado de Next.js

Next.js tiene soporte i18n integrado para Pages Router.

Paso 1: Configurar next.config.js

next.config.js:

module.exports = {
  i18n: {
    locales: ['en', 'es', 'fr'],
    defaultLocale: 'en',
    localeDetection: true, // Detectar automáticamente el locale del usuario
  },
};

Paso 2: Crear Archivos de Traducción

locales/en.json:

{
  "welcome": "Welcome to our app!"
}

locales/es.json:

{
  "welcome": "¡Bienvenido a nuestra aplicación!"
}

Paso 3: Usar en Páginas

pages/index.js:

import { useRouter } from 'next/router';
import translations from '../locales';

export default function HomePage() {
  const router = useRouter();
  const { locale } = router;
  const t = translations[locale];

  return <h1>{t.welcome}</h1>;
}

Mejores Prácticas

1. Usar Claves de Traducción Descriptivas

Malo:

{
  "msg1": "Submit"
}

Bueno:

{
  "button": {
    "submit": "Submit"
  }
}

2. Organizar por Característica

{
  "auth": {
    "login": "Login",
    "logout": "Logout"
  },
  "dashboard": {
    "title": "Dashboard"
  }
}

3. Usar Namespaces

Para apps grandes, divide las traducciones en namespaces:

next-i18next:

export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common', 'auth', 'dashboard'])),
    },
  };
}

next-intl:

const t = useTranslations('auth');

4. Manejar Traducciones Faltantes

const t = useTranslations();
const text = t('some.key', { defaultValue: 'Texto por defecto' });

5. Consideraciones SEO

Establece el atributo lang apropiado y etiquetas hreflang:

app/[locale]/layout.tsx:

export default function LocaleLayout({
  children,
  params: { locale }
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  return (
    <html lang={locale}>
      <head>
        <link rel="alternate" hrefLang="en" href="/en" />
        <link rel="alternate" hrefLang="es" href="/es" />
        <link rel="alternate" hrefLang="fr" href="/fr" />
      </head>
      <body>{children}</body>
    </html>
  );
}

6. Generación Estática con Locales

Genera páginas estáticas para todos los locales:

next-intl:

export function generateStaticParams() {
  return locales.map((locale) => ({ locale }));
}

next-i18next:

export async function getStaticPaths() {
  return {
    paths: locales.map((locale) => ({ params: { locale } })),
    fallback: false,
  };
}

Errores Comunes

1. No Configurar Middleware

Para App Router con next-intl, siempre crea middleware:

// middleware.ts es requerido

2. Olvidar Locale en la Estructura de URL

Asegúrate de que tus rutas incluyan locale:

/en/about
/es/about
/fr/about

3. No Manejar Componentes Cliente/Servidor

Usa 'use client' para componentes del cliente:

'use client';
import { useTranslations } from 'next-intl';

4. Codificar Cadenas Directamente

Malo:

<h1>Bienvenido</h1>

Bueno:

const t = useTranslations();
<h1>{t('app.welcome')}</h1>

Avanzado: Cambio Dinámico de Idioma

next-intl:

'use client';

import { useRouter, usePathname } from 'next/navigation';
import { useLocale } from 'next-intl';

export function LanguageSwitcher() {
  const router = useRouter();
  const pathname = usePathname();
  const locale = useLocale();

  const changeLocale = (newLocale: string) => {
    const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
    router.push(newPath);
    router.refresh();
  };

  return (
    <select value={locale} onChange={(e) => changeLocale(e.target.value)}>
      <option value="en">English</option>
      <option value="es">Español</option>
      <option value="fr">Français</option>
    </select>
  );
}

Avanzado: Soporte RTL

import { useLocale } from 'next-intl';
import { useEffect } from 'react';

export default function RTLHandler() {
  const locale = useLocale();
  const isRTL = ['ar', 'he', 'fa'].includes(locale);

  useEffect(() => {
    document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
    document.documentElement.lang = locale;
  }, [locale, isRTL]);

  return null;
}

Avanzado: Soporte TypeScript

types/next-intl.d.ts:

type Messages = typeof import('./messages/en.json');

declare global {
  interface IntlMessages extends Messages {}
}

Conclusión

Localizar tu app Next.js se puede hacer a través de múltiples enfoques:

  1. next-intl (Recomendado para App Router) - Moderno, construido para Next.js 13+
  2. next-i18next (Para Pages Router) - Popular, bien documentado
  3. Routing i18n integrado (Pages Router) - Simple pero limitado

Elige el método que mejor se ajuste a tu versión de Next.js y requisitos. Siguiendo estas prácticas, crearás una app que proporciona una experiencia nativa para usuarios en todo el mundo, expandiendo significativamente tu base de usuarios potencial.

Optimiza tu Flujo de Trabajo de Localización Next.js

Gestionar traducciones para múltiples idiomas puede volverse complejo a medida que tu app crece. Considera usar una plataforma de gestión de traducciones para:

  • Colaborar con traductores
  • Mantener traducciones sincronizadas con tu código base
  • Automatizar el flujo de trabajo de traducción
  • Mantener consistencia en todos los idiomas
  • Generar archivos de traducción automáticamente

¿Listo para llevar tu app Next.js a nivel global? Explora la plataforma de localización de AZbox y optimiza tu flujo de trabajo de traducción:

Ver Planes y Precios de AZbox

Blog

Últimas Publicaciones

Descubre nuestros últimos artículos y actualizaciones.

Servicios de localización de videojuegos: guía 2026 para llevar tu juego al mundo
date icon

21 de febrero de 2026

05 min de lectura

Servicios de localización de videojuegos: guía 2026 para llevar tu juego al mundo

Llevar tu juego a nivel global no es solo traducir texto: es hacer que los jugadores de cada mercado sientan que el jueg

Leer más
Software de localización de apps: guía 2026 para elegir y usar la herramienta adecuada
date icon

20 de febrero de 2026

05 min de lectura

Software de localización de apps: guía 2026 para elegir y usar la herramienta adecuada

Si estás llevando tu app más allá de un solo idioma, el software de localización de apps es la palanca que convierte

Leer más
Traducción para SaaS: Guía Completa para Localizar Tu Producto
date icon

15 de febrero de 2026

04 min de lectura

Traducción para SaaS: Guía Completa para Localizar Tu Producto

La traducción para SaaS ya no es opcional. Si quieres crecer más allá de tu mercado local, necesitas una estrategia

Leer más
Fondo de llamada a la acción

Comienza el Crecimiento Global Hoy

Sube tus archivos de idioma, recibe las traducciones y mantén cada idioma sincronizado mientras tu producto sigue cambiando.

Comenzar - Es Gratis