IntlPull
Guide
16 min read

react-intl (FormatJS): La guía completa para la internacionalización de React en 2026

Domina react-intl de FormatJS para React i18n. Tutorial completo que cubre IntlProvider, FormattedMessage, sintaxis ICU, hooks y mejores prácticas de producción.

IntlPull Team
IntlPull Team
Jan 17, 2026
On this page
Summary

Domina react-intl de FormatJS para React i18n. Tutorial completo que cubre IntlProvider, FormattedMessage, sintaxis ICU, hooks y mejores prácticas de producción.

Respuesta rápida

react-intl es el binding de React para FormatJS, proporcionando soporte para el formato de mensajes ICU para internacionalización. Instálalo con npm install react-intl, envuelve tu app con <IntlProvider locale="es" messages={messages}>, y luego usa <FormattedMessage id="saludo" /> o el hook useIntl() para mostrar traducciones. Elige react-intl cuando necesites cumplimiento estricto de ICU o formateo integrado de fechas/números. Para necesidades más simples, react-i18next puede ser más fácil.


¿Qué es react-intl?

react-intl es parte del ecosistema FormatJS, una implementación oficial del Formato de Mensajes ICU para JavaScript. Proporciona:

  • Formato de Mensajes ICU: Sintaxis estándar de la industria para plurales, género y mensajes complejos
  • Formateadores integrados: Fechas, números, monedas, tiempo relativo
  • Soporte TypeScript: Extracción de mensajes y formateo con seguridad de tipos
  • Probado en producción: Usado por Yahoo, Microsoft, Airbnb

react-intl vs react-i18next

Característicareact-intlreact-i18next
Formato de MensajeICU (estricto)JSON (flexible)
Tamaño del Bundle~25KB~15KB
PluralesSintaxis ICUClaves con sufijo
FormateadoresIntegradosPlugin/manual
Curva de AprendizajeMás altaMás baja
TypeScriptExcelenteExcelente

Elige react-intl cuando:

  • Necesitas cumplimiento estricto de ICU
  • Tu equipo conoce la sintaxis ICU
  • Quieres formateo de fechas/números integrado
  • Estás integrando con herramientas de traducción que usan ICU

Elige react-i18next cuando:

  • Quieres una sintaxis más simple
  • Necesitas un tamaño de bundle menor
  • Tu equipo prefiere claves basadas en JSON

Instalación

Terminal
1npm install react-intl
2# o
3yarn add react-intl
4# o
5pnpm add react-intl

Herramientas CLI Opcionales

Terminal
1# Extraer mensajes del código
2npm install -D @formatjs/cli
3
4# Compilar mensajes para producción
5npm install -D @formatjs/cli

Configuración Básica

Paso 1: Crear Archivos de Traducción

src/
├── lang/
│   ├── en.json
│   ├── es.json
│   └── fr.json
└── App.tsx

en.json:

JSON
1{
2  "app.greeting": "Welcome to our application!",
3  "app.nav.home": "Home",
4  "app.nav.about": "About",
5  "app.buttons.submit": "Submit",
6  "app.buttons.cancel": "Cancel"
7}

es.json:

JSON
1{
2  "app.greeting": "¡Bienvenido a nuestra aplicación!",
3  "app.nav.home": "Inicio",
4  "app.nav.about": "Acerca de",
5  "app.buttons.submit": "Enviar",
6  "app.buttons.cancel": "Cancelar"
7}

Paso 2: Configurar IntlProvider

TSX
1// src/App.tsx
2import { IntlProvider } from 'react-intl';
3import { useState } from 'react';
4
5import enMessages from './lang/en.json';
6import esMessages from './lang/es.json';
7
8const messages: Record<string, Record<string, string>> = {
9  en: enMessages,
10  es: esMessages,
11};
12
13function App() {
14  const [locale, setLocale] = useState('en');
15
16  return (
17    <IntlProvider
18      locale={locale}
19      messages={messages[locale]}
20      defaultLocale="en"
21      onError={(err) => {
22        if (err.code !== 'MISSING_TRANSLATION') {
23          console.error(err);
24        }
25      }}
26    >
27      <MainApp onLocaleChange={setLocale} />
28    </IntlProvider>
29  );
30}

Paso 3: Usar FormattedMessage

TSX
1import { FormattedMessage } from 'react-intl';
2
3function Header() {
4  return (
5    <header>
6      <h1>
7        <FormattedMessage id="app.greeting" />
8      </h1>
9      <nav>
10        <a href="/"><FormattedMessage id="app.nav.home" /></a>
11        <a href="/about"><FormattedMessage id="app.nav.about" /></a>
12      </nav>
13    </header>
14  );
15}

El Hook useIntl

Para acceso programático al formateo:

TSX
1import { useIntl } from 'react-intl';
2
3function Form() {
4  const intl = useIntl();
5
6  const handleSubmit = () => {
7    const confirmMessage = intl.formatMessage({ id: 'app.confirm' });
8    if (confirm(confirmMessage)) {
9      // Enviar formulario
10    }
11  };
12
13  return (
14    <form onSubmit={handleSubmit}>
15      <button type="submit">
16        {intl.formatMessage({ id: 'app.buttons.submit' })}
17      </button>
18    </form>
19  );
20}

Métodos del Hook

MétodoPropósitoEjemplo
formatMessageTraducir mensajeintl.formatMessage({ id: 'key' })
formatDateFormatear fechaintl.formatDate(new Date())
formatTimeFormatear horaintl.formatTime(new Date())
formatNumberFormatear númerointl.formatNumber(1234.56)
formatRelativeTimeTiempo relativointl.formatRelativeTime(-1, 'day')

Formato de Mensajes ICU

ICU (Componentes Internacionales para Unicode) es el estándar de la industria para mensajes complejos.

Variables (Argumentos)

JSON
1{
2  "greeting": "Hola, {name}!",
3  "cartInfo": "Tienes {count} artículos por valor de {total}"
4}
TSX
1<FormattedMessage
2  id="greeting"
3  values={{ name: user.name }}
4/>
5
6<FormattedMessage
7  id="cartInfo"
8  values={{ count: 5, total: '$99.99' }}
9/>

Pluralización

ICU maneja reglas complejas de plurales para todos los idiomas:

JSON
{
  "items": "{count, plural, =0 {Sin artículos} one {# artículo} other {# artículos}}"
}
TSX
1<FormattedMessage id="items" values={{ count: 0 }} />
2// "Sin artículos"
3
4<FormattedMessage id="items" values={{ count: 1 }} />
5// "1 artículo"
6
7<FormattedMessage id="items" values={{ count: 5 }} />
8// "5 artículos"

Select (Género, Elección)

JSON
{
  "pronoun": "A {gender, select, male {él} female {ella} other {ellos}} le gustó tu publicación"
}
TSX
1<FormattedMessage
2  id="pronoun"
3  values={{ gender: 'female' }}
4/>
5// "A ella le gustó tu publicación"

Plural + Select Anidado

JSON
{
  "invites": "{gender, select, male {{count, plural, one {Él invitó a # persona} other {Él invitó a # personas}}} female {{count, plural, one {Ella invitó a # persona} other {Ella invitó a # personas}}} other {{count, plural, one {Ellos invitaron a # persona} other {Ellos invitaron a # personas}}}}"
}

Texto Rico (Componentes HTML)

JSON
1{
2  "terms": "Al registrarte, aceptas nuestros <link>Términos de Servicio</link>",
3  "welcome": "Bienvenido <bold>{name}</bold> a nuestra plataforma!"
4}
TSX
1<FormattedMessage
2  id="terms"
3  values={{
4    link: (chunks) => <a href="/terms">{chunks}</a>
5  }}
6/>
7
8<FormattedMessage
9  id="welcome"
10  values={{
11    name: user.name,
12    bold: (chunks) => <strong>{chunks}</strong>
13  }}
14/>

Formateadores Integrados

Formateo de Fecha

TSX
1import { FormattedDate } from 'react-intl';
2
3<FormattedDate
4  value={new Date()}
5  year="numeric"
6  month="long"
7  day="2-digit"
8/>
9// "17 de enero de 2026" (es)

Formateo de Hora

TSX
1import { FormattedTime } from 'react-intl';
2
3<FormattedTime
4  value={new Date()}
5  hour="numeric"
6  minute="numeric"
7  timeZoneName="short"
8/>
9// "14:30 EST" (es)

Formateo de Números

TSX
1import { FormattedNumber } from 'react-intl';
2
3// Número simple
4<FormattedNumber value={1234567.89} />
5// "1.234.567,89" (es)
6
7// Moneda
8<FormattedNumber
9  value={99.99}
10  style="currency"
11  currency="EUR"
12/>
13// "99,99 €" (es)
14
15// Porcentaje
16<FormattedNumber value={0.25} style="percent" />
17// "25 %"

Tiempo Relativo

TSX
1import { FormattedRelativeTime } from 'react-intl';
2
3<FormattedRelativeTime value={-1} unit="day" />
4// "ayer"
5
6<FormattedRelativeTime value={3} unit="hour" />
7// "en 3 horas"

Integración con TypeScript

Definir Tipos de Mensajes

TypeScript
1// src/types/intl.d.ts
2import en from '../lang/en.json';
3
4type Messages = typeof en;
5
6declare global {
7  namespace FormatjsIntl {
8    interface Message {
9      ids: keyof Messages;
10    }
11  }
12}

Ahora TypeScript autocompleta y valida los IDs de mensajes:

TSX
<FormattedMessage id="app.greeting" />  // ✅ Válido
<FormattedMessage id="invalid.key" />   // ❌ Error TypeScript

defineMessages para Extracción

TSX
1import { defineMessages, useIntl } from 'react-intl';
2
3const messages = defineMessages({
4  title: {
5    id: 'app.title',
6    defaultMessage: 'My Application',
7    description: 'Main application title',
8  },
9  greeting: {
10    id: 'app.greeting',
11    defaultMessage: 'Hello, {name}!',
12    description: 'User greeting message',
13  },
14});

Extracción de Mensajes

Extraer con FormatJS CLI

Terminal
# Extraer mensajes de archivos fuente
npx formatjs extract 'src/**/*.ts*' --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]'

Compilar para Producción

Terminal
# Compilar a AST para mejor rendimiento
npx formatjs compile lang/en.json --out-file lang/compiled/en.json
npx formatjs compile lang/es.json --out-file lang/compiled/es.json

Usando mensajes compilados:

TSX
1import compiledMessages from './lang/compiled/en.json';
2
3<IntlProvider
4  locale="en"
5  messages={compiledMessages}
6/>

Los mensajes compilados están pre-analizados, mejorando el rendimiento en tiempo de ejecución en un 30-50%.

Cambio de Idioma

TSX
1import { useState, useCallback } from 'react';
2import { IntlProvider } from 'react-intl';
3
4const SUPPORTED_LOCALES = ['en', 'es', 'fr', 'de'] as const;
5type Locale = typeof SUPPORTED_LOCALES[number];
6
7function App() {
8  const [locale, setLocale] = useState<Locale>('en');
9  const [messages, setMessages] = useState(enMessages);
10
11  const handleLocaleChange = useCallback(async (newLocale: Locale) => {
12    // Importar traducciones dinámicamente
13    const newMessages = await import(`./lang/${newLocale}.json`);
14    setMessages(newMessages.default);
15    setLocale(newLocale);
16
17    // Persistir preferencia
18    localStorage.setItem('locale', newLocale);
19    document.documentElement.lang = newLocale;
20  }, []);
21
22  return (
23    <IntlProvider locale={locale} messages={messages}>
24      <LocaleSwitcher
25        current={locale}
26        available={SUPPORTED_LOCALES}
27        onChange={handleLocaleChange}
28      />
29      <MainContent />
30    </IntlProvider>
31  );
32}

Preguntas Frecuentes

¿Qué es react-intl?

react-intl es el binding de React para FormatJS, proporcionando internacionalización con soporte para el Formato de Mensajes ICU. Ofrece componentes como FormattedMessage, FormattedDate, y FormattedNumber, además del hook useIntl. Es usado por grandes compañías y es la elección principal para proyectos que requieren cumplimiento estricto de ICU.

¿Cuál es la diferencia entre react-intl y react-i18next?

react-intl usa el Formato de Mensajes ICU con sintaxis estricta para plurales y selecciones. react-i18next usa claves JSON más simples con plurales basados en sufijos. react-intl tiene formateadores integrados; react-i18next requiere plugins.

¿Cómo uso FormattedMessage con variables?

Usa sintaxis de llaves en mensajes ICU: "greeting": "Hola, {name}!". Luego pasa valores: <FormattedMessage id="greeting" values={{ name: 'Juan' }} />.

¿Cómo funciona la pluralización en react-intl?

Usa sintaxis plural ICU: "{count, plural, one {# artículo} other {# artículos}}". ICU soporta categorías zero, one, two, few, many, other y coincidencias exactas.

Resumen

react-intl proporciona internacionalización robusta con Formato de Mensajes ICU:

AspectoDetalles
Instalaciónnpm install react-intl
Provider<IntlProvider locale messages>
Componente<FormattedMessage id values />
HookuseIntl().formatMessage()
Formato de MensajeICU (estándar)
FormateadoresIntegrados fecha, número, moneda

Lo mejor para: Equipos que necesitan cumplimiento de ICU, formateadores integrados o integración con herramientas CAT.

¿Listo para gestionar traducciones de react-intl? Empieza gratis con IntlPull — soporte completo de ICU con traducción por IA.

Tags
react-intl
formatjs
react
i18n
internationalization
icu
2026
IntlPull Team
IntlPull Team
Engineering

Building tools to help teams ship products globally. Follow us for more insights on localization and i18n.