Skip to content

Cómo Traducir una App React con react-intl: Guía Completa

Aprende cómo localizar tu app React usando react-intl (FormatJS). Esta guía completa cubre traducción de cadenas, pluralización, formato de fechas y mejores prácticas para la traducción de a

  • date icon

    16 de noviembre de 2025

  • 06 min de lectura
Cómo Traducir una App React con react-intl: Guía Completa

Traducir tu app React es esencial para llegar a una audiencia global. react-intl es una biblioteca de internacionalización potente de FormatJS que proporciona componentes React y una API para formatear fechas, números y cadenas, incluyendo pluralización y manejo de traducciones. Esta guía te guiará a través del proceso de traducir una app React usando react-intl.

Entendiendo la Localización de React con react-intl

react-intl es parte del conjunto FormatJS y proporciona componentes React y una API para internacionalización. Usa formato de mensaje ICU y proporciona excelente soporte para pluralización, formateo de fecha/hora y formateo de números.

Paso 1: Instalar Dependencias

Primero, instala los paquetes necesarios:

npm install react-intl

O con yarn:

yarn add react-intl

Paso 2: Crear Archivos de Traducción

Crea un directorio locales en tu carpeta src y agrega archivos de traducción para cada idioma:

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

{
  "app.welcome": "Welcome to our app!",
  "button.submit": "Submit",
  "button.cancel": "Cancel",
  "button.delete": "Delete",
  "error.network": "Network error. Please try again.",
  "error.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}}.",
  "date.today": "Today is {date, date, full}",
  "currency.price": "Price: {amount, number, currency}"
}

src/locales/es.json (Español):

{
  "app.welcome": "¡Bienvenido a nuestra aplicación!",
  "button.submit": "Enviar",
  "button.cancel": "Cancelar",
  "button.delete": "Eliminar",
  "error.network": "Error de red. Por favor, inténtalo de nuevo.",
  "error.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}}.",
  "date.today": "Hoy es {date, date, full}",
  "currency.price": "Precio: {amount, number, currency}"
}

src/locales/fr.json (Francés):

{
  "app.welcome": "Bienvenue dans notre application!",
  "button.submit": "Soumettre",
  "button.cancel": "Annuler",
  "button.delete": "Supprimer",
  "error.network": "Erreur réseau. Veuillez réessayer.",
  "error.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}}.",
  "date.today": "Aujourd'hui est {date, date, full}",
  "currency.price": "Prix: {amount, number, currency}"
}

Paso 3: Cargar Mensajes de Traducción

Crea un archivo de utilidad para cargar traducciones:

src/locales/index.js:

import en from './en.json';
import es from './es.json';
import fr from './fr.json';

export const messages = {
  en,
  es,
  fr
};

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

O con TypeScript (src/locales/index.ts):

import en from './en.json';
import es from './es.json';
import fr from './fr.json';

export const messages = {
  en,
  es,
  fr
} as const;

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

Paso 4: Configurar IntlProvider

Envuelve tu app con IntlProvider en tu componente App principal:

src/App.js:

import React, { useState } from 'react';
import { IntlProvider } from 'react-intl';
import { messages, defaultLocale, locales } from './locales';
import HomePage from './components/HomePage';

function App() {
  const [locale, setLocale] = useState(defaultLocale);
  
  return (
    <IntlProvider 
      locale={locale} 
      messages={messages[locale]}
      defaultLocale={defaultLocale}
    >
      <div className="App">
        <HomePage locale={locale} setLocale={setLocale} />
      </div>
    </IntlProvider>
  );
}

export default App;

O con TypeScript (src/App.tsx):

import React, { useState } from 'react';
import { IntlProvider } from 'react-intl';
import { messages, defaultLocale, locales } from './locales';
import HomePage from './components/HomePage';

const App: React.FC = () => {
  const [locale, setLocale] = useState<string>(defaultLocale);
  
  return (
    <IntlProvider 
      locale={locale} 
      messages={messages[locale as keyof typeof messages]}
      defaultLocale={defaultLocale}
    >
      <div className="App">
        <HomePage locale={locale} setLocale={setLocale} />
      </div>
    </IntlProvider>
  );
};

export default App;

Paso 5: Usar Componente FormattedMessage

Usa el componente FormattedMessage para traducciones:

Antes:

function Welcome() {
  return <h1>Welcome to our app!</h1>;
}

Después:

import { FormattedMessage } from 'react-intl';

function Welcome() {
  return (
    <h1>
      <FormattedMessage id="app.welcome" />
    </h1>
  );
}

Paso 6: Usar Hook useIntl

Para más flexibilidad, usa el hook useIntl:

import { useIntl } from 'react-intl';

function Welcome() {
  const intl = useIntl();
  
  return <h1>{intl.formatMessage({ id: 'app.welcome' })}</h1>;
}

Con TypeScript:

import { useIntl } from 'react-intl';

const Welcome: React.FC = () => {
  const intl = useIntl();
  
  return <h1>{intl.formatMessage({ id: 'app.welcome' })}</h1>;
};

Paso 7: Interpolación de Cadenas con Variables

Pasa variables a las traducciones usando formato de mensaje ICU:

translation.json:

{
  "user.greeting": "Hello, {name}!"
}

Componente:

import { FormattedMessage } from 'react-intl';

function UserGreeting({ name }) {
  return (
    <p>
      <FormattedMessage 
        id="user.greeting" 
        values={{ name: name }}
      />
    </p>
  );
}

// Uso: <UserGreeting name="John" />
// Inglés: "Hello, John!"
// Español: "¡Hola, John!"

O con useIntl:

import { useIntl } from 'react-intl';

function UserGreeting({ name }) {
  const intl = useIntl();
  
  return (
    <p>
      {intl.formatMessage(
        { id: 'user.greeting' },
        { name: name }
      )}
    </p>
  );
}

Paso 8: Pluralización

react-intl usa formato de mensaje ICU para pluralización:

translation.json:

{
  "items.count": "{count, plural, =0 {No items} one {# item} other {# items}}"
}

Componente:

import { FormattedMessage } from 'react-intl';

function ItemCount({ count }) {
  return (
    <p>
      <FormattedMessage 
        id="items.count" 
        values={{ count: count }}
      />
    </p>
  );
}

// Ejemplos de uso:
// <ItemCount count={0} /> → "No items" (Inglés) / "No hay elementos" (Español)
// <ItemCount count={1} /> → "1 item" (Inglés) / "1 elemento" (Español)
// <ItemCount count={5} /> → "5 items" (Inglés) / "5 elementos" (Español)

Reglas de Plural ICU:

  • =0 - Exactamente cero
  • one - Singular (1)
  • other - Plural (2, 3, 4, etc.)
  • # - Abreviación para el valor numérico

Paso 9: Formatear Fechas

Usa el componente FormattedDate o la API formatDate:

Enfoque con componente:

import { FormattedDate } from 'react-intl';

function DateDisplay({ date }) {
  return (
    <p>
      <FormattedDate 
        value={date} 
        year="numeric"
        month="long"
        day="numeric"
        weekday="long"
      />
    </p>
  );
}

// Inglés: "Wednesday, March 20, 2025"
// Español: "miércoles, 20 de marzo de 2025"

Enfoque con hook:

import { useIntl } from 'react-intl';

function DateDisplay({ date }) {
  const intl = useIntl();
  
  const formattedDate = intl.formatDate(date, {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    weekday: 'long'
  });
  
  return <p>{formattedDate}</p>;
}

Con formato de mensaje ICU:

{
  "date.today": "Today is {date, date, full}"
}
import { FormattedMessage, FormattedDate } from 'react-intl';

function TodayDate() {
  const today = new Date();
  
  return (
    <p>
      <FormattedMessage 
        id="date.today" 
        values={{ date: today }}
      />
    </p>
  );
}

Paso 10: Formatear Números

Usa el componente FormattedNumber o la API formatNumber:

Enfoque con componente:

import { FormattedNumber } from 'react-intl';

function NumberDisplay({ number }) {
  return (
    <p>
      <FormattedNumber value={number} />
    </p>
  );
}

// Inglés: "1,234.56"
// Español: "1.234,56"

Enfoque con hook:

import { useIntl } from 'react-intl';

function NumberDisplay({ number }) {
  const intl = useIntl();
  
  const formattedNumber = intl.formatNumber(number);
  
  return <p>{formattedNumber}</p>;
}

Formateo de moneda:

import { FormattedNumber } from 'react-intl';

function PriceDisplay({ amount }) {
  return (
    <p>
      <FormattedNumber 
        value={amount} 
        style="currency"
        currency="USD"
      />
    </p>
  );
}

// Inglés (US): "$1,234.56"
// Español (ES): "1.234,56 €"

Con formato de mensaje ICU:

{
  "currency.price": "Price: {amount, number, currency}"
}
import { FormattedMessage } from 'react-intl';

function Price({ amount }) {
  return (
    <p>
      <FormattedMessage 
        id="currency.price" 
        values={{ amount: amount }}
      />
    </p>
  );
}

Paso 11: Formatear Tiempo Relativo

Usa FormattedRelativeTime para fechas relativas:

import { FormattedRelativeTime } from 'react-intl';

function RelativeTime({ date }) {
  const now = new Date();
  const diffInSeconds = Math.floor((date - now) / 1000);
  
  return (
    <p>
      <FormattedRelativeTime 
        value={diffInSeconds} 
        numeric="auto"
        updateIntervalInSeconds={60}
      />
    </p>
  );
}

// Ejemplos: "en 2 horas", "hace 2 horas", "ayer"

Paso 12: Cambiar Idioma Programáticamente

Permite a los usuarios cambiar idiomas:

import { useIntl } from 'react-intl';

function LanguageSwitcher({ locale, setLocale }) {
  const intl = useIntl();
  
  const changeLanguage = (newLocale) => {
    setLocale(newLocale);
    // Opcionalmente guardar en localStorage
    localStorage.setItem('language', newLocale);
  };
  
  return (
    <div>
      <button onClick={() => changeLanguage('en')}>English</button>
      <button onClick={() => changeLanguage('es')}>Español</button>
      <button onClick={() => changeLanguage('fr')}>Français</button>
    </div>
  );
}

Con detección de idioma:

import { useState, useEffect } from 'react';

function App() {
  const [locale, setLocale] = useState(() => {
    // Intentar obtener idioma guardado de localStorage
    const saved = localStorage.getItem('language');
    if (saved) return saved;
    
    // Detectar idioma del navegador
    const browserLang = navigator.language.split('-')[0];
    return ['en', 'es', 'fr'].includes(browserLang) ? browserLang : 'en';
  });
  
  useEffect(() => {
    localStorage.setItem('language', locale);
  }, [locale]);
  
  return (
    <IntlProvider locale={locale} messages={messages[locale]}>
      {/* Tu app */}
    </IntlProvider>
  );
}

Paso 13: Manejar Idiomas de Derecha a Izquierda (RTL)

Para idiomas RTL como árabe y hebreo:

import { useEffect } from 'react';
import { useIntl } from 'react-intl';

function App() {
  const intl = useIntl();
  const locale = intl.locale;
  
  useEffect(() => {
    const isRTL = ['ar', 'he', 'fa'].includes(locale);
    document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
    document.documentElement.lang = locale;
  }, [locale]);
  
  return (
    <div className="App">
      {/* Contenido de tu app */}
    </div>
  );
}

Mejores Prácticas

1. Usar IDs de Mensaje Descriptivos

Malo:

{
  "msg1": "Submit"
}

Bueno:

{
  "button.submit": "Submit"
}

2. Organizar Mensajes por Característica

translation.json:

{
  "auth": {
    "login": "Login",
    "logout": "Logout",
    "signup": "Sign Up"
  },
  "dashboard": {
    "title": "Dashboard",
    "welcome": "Welcome back!"
  }
}

3. Usar Formato de Mensaje ICU

Siempre usa formato ICU para mensajes complejos:

{
  "user.status": "{name} has {count, plural, =0 {no tasks} one {# task} other {# tasks}}"
}

4. Proporcionar Mensajes por Defecto

Usa la prop defaultMessage para desarrollo:

<FormattedMessage 
  id="app.welcome" 
  defaultMessage="Welcome to our app!"
/>

5. Extraer Mensajes para Traducción

Usa @formatjs/cli para extraer mensajes:

npm install --save-dev @formatjs/cli

# Extraer mensajes
formatjs extract "src/**/*.{js,jsx,ts,tsx}" --out-file locales/en.json

6. Probar Longitudes de Cadenas

Algunos idiomas son más largos que otros. Diseña tu UI para acomodar:

  • Alemán y finlandés: 30-50% más largo que inglés
  • Idiomas asiáticos: Pueden necesitar más espacio vertical

Errores Comunes

1. Olvidar Envolver App con IntlProvider

Siempre envuelve tu app con IntlProvider:

// ❌ No funcionará
function App() {
  return <Welcome />;
}

// ✅ Correcto
function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <Welcome />
    </IntlProvider>
  );
}

2. No Proporcionar Mensajes para Locale

Asegúrate de que los mensajes existan para el locale seleccionado:

// ❌ Mostrará IDs de mensaje si messages.es no existe
<IntlProvider locale="es" messages={messages.en}>

// ✅ Correcto
<IntlProvider locale="es" messages={messages.es}>

3. Codificar Cadenas de Formato Directamente

Malo:

const price = `$${amount.toFixed(2)}`;

Bueno:

<FormattedNumber 
  value={amount} 
  style="currency"
  currency="USD"
/>

4. No Manejar Mensajes Faltantes

Proporciona fallbacks:

<FormattedMessage 
  id="app.welcome" 
  defaultMessage="Welcome"
/>

Avanzado: Formateo de Texto Enriquecido

Usa FormattedMessage con texto enriquecido:

{
  "welcome": "Welcome to <bold>our app</bold>!"
}
import { FormattedMessage } from 'react-intl';

function Welcome() {
  return (
    <FormattedMessage
      id="app.welcome"
      values={{
        bold: (chunks) => <strong>{chunks}</strong>
      }}
    />
  );
}

Avanzado: Descripciones de Mensajes

Agrega descripciones para ayudar a los traductores:

{
  "button.delete": "Delete",
  "@button.delete": {
    "description": "Button to delete an item. Shown in the item detail view."
  }
}

Avanzado: Soporte TypeScript

Para mejor soporte TypeScript:

src/types/react-intl.d.ts:

import { Messages } from '@formatjs/intl';

declare module 'react-intl' {
  interface IntlMessages extends Messages {
    'app.welcome': string;
    'button.submit': string;
    // ... otras claves de mensaje
  }
}

Avanzado: Carga Perezosa de Mensajes

Carga traducciones bajo demanda:

import { useState, useEffect } from 'react';
import { IntlProvider } from 'react-intl';

function App() {
  const [locale, setLocale] = useState('en');
  const [messages, setMessages] = useState({});
  
  useEffect(() => {
    import(`./locales/${locale}.json`)
      .then((module) => setMessages(module.default))
      .catch(() => setMessages({}));
  }, [locale]);
  
  return (
    <IntlProvider locale={locale} messages={messages}>
      {/* Tu app */}
    </IntlProvider>
  );
}

Usar con Next.js

Para aplicaciones Next.js, usa next-intl o configura manualmente:

pages/_app.js:

import { IntlProvider } from 'react-intl';
import { useRouter } from 'next/router';
import { messages } from '../locales';

export default function App({ Component, pageProps }) {
  const { locale } = useRouter();
  
  return (
    <IntlProvider 
      locale={locale} 
      messages={messages[locale]}
    >
      <Component {...pageProps} />
    </IntlProvider>
  );
}

Conclusión

Localizar tu app React con react-intl es sencillo cuando sigues estos pasos:

  1. Instalar paquete react-intl
  2. Crear archivos JSON de traducción para cada idioma usando formato de mensaje ICU
  3. Cargar traducciones y envolver tu app con IntlProvider
  4. Usar componente FormattedMessage o hook useIntl
  5. Manejar pluralización con formato ICU
  6. Formatear fechas, números y moneda usando componentes integrados
  7. Permitir a los usuarios cambiar idiomas
  8. Probar exhaustivamente en todos los idiomas soportados

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 React

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 React 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