Cargando traducciones
Usando un proveedor para gestionar mensajes
Problema
Codificar texto directamente, como 'Hello World', en los componentes de una aplicación acopla el contenido con el código. Para mostrar un idioma diferente, los desarrolladores deben duplicar el componente o añadir lógica if/else, haciendo que la traducción sea poco escalable y requiriendo un cambio completo de código para cada nuevo texto.
Solución
Utilizar el IntlProvider de react-intl para proporcionar traducciones. Cargar los mensajes de traducción en el servidor en el layout raíz, pasarlos a un componente proveedor del lado del cliente, y luego consumirlos en otros componentes cliente utilizando el hook useIntl.
Pasos
1. Instalar react-intl
Primero, añade react-intl como dependencia a tu proyecto.
npm install react-intl
2. Crear archivos de traducción planos
Crea una carpeta dictionaries. Dentro, añade un archivo JSON para cada idioma. react-intl funciona mejor con una estructura plana de clave-valor.
// dictionaries/en.json
{
"home.title": "Home Page",
"home.welcome": "Hello, welcome to our site!",
"about.title": "About Us"
}
// dictionaries/es.json
{
"home.title": "Página de Inicio",
"home.welcome": "¡Hola, bienvenido a nuestro sitio!",
"about.title": "Sobre Nosotros"
}
3. Crear una función para cargar diccionarios
Crea una función auxiliar para cargar el archivo de diccionario correcto en el servidor basado en el parámetro lang.
// app/get-dictionary.ts
import 'server-only';
// Define the type for our flat message object
type Messages = Record<string, string>;
const dictionaries: { [key: string]: () => Promise<Messages> } = {
en: () => import('@/dictionaries/en.json').then((module) => module.default),
es: () => import('@/dictionaries/es.json').then((module) => module.default),
// fr: () => import('@/dictionaries/fr.json').then((module) => module.default),
};
export const getDictionary = async (lang: string) => {
const load = dictionaries[lang];
if (load) {
return load();
}
// Fallback to English
return dictionaries.en();
};
4. Crear un proveedor del lado del cliente
IntlProvider es un componente de cliente que utiliza el Context de React. Debemos crear un wrapper para él que pueda aceptar mensajes cargados desde el servidor.
// app/components/IntlClientProvider.tsx
'use client';
import { IntlProvider } from 'react-intl';
type Props = {
children: React.ReactNode;
locale: string;
messages: Record<string, string>; // Objeto de mensajes plano
};
export default function IntlClientProvider({
children,
locale,
messages,
}: Props) {
return (
<IntlProvider messages={messages} locale={locale} defaultLocale="en">
{children}
</IntlProvider>
);
}
5. Actualizar el layout raíz
Modifica tu app/[lang]/layout.tsx para que sea un componente async. Cargará los mensajes y los pasará al IntlClientProvider.
// app/[lang]/layout.tsx
import { getDictionary } from '@/app/get-dictionary';
import IntlClientProvider from '@/app/components/IntlClientProvider';
export async function generateStaticParams() {
return [{ lang: 'en' }, { lang: 'es' }];
}
export default async function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: string };
}) {
// Cargar mensajes en el servidor
const messages = await getDictionary(params.lang);
return (
<html lang={params.lang}>
<body>
{/* Pasar mensajes al proveedor del cliente */}
<IntlClientProvider locale={params.lang} messages={messages}>
{children}
</IntlClientProvider>
</body>
</html>
);
}
6. Usar traducciones en un componente de cliente
Ahora puedes usar el hook useIntl en cualquier componente de cliente. Los componentes del servidor no pueden usar este hook.
Crea un nuevo componente de cliente para mostrar el texto traducido:
// app/components/HomePageContent.tsx
'use client';
import { useIntl } from 'react-intl';
export default function HomePageContent() {
const intl = useIntl();
return (
<div>
<h1>{intl.formatMessage({ id: 'home.title' })}</h1>
<p>{intl.formatMessage({ id: 'home.welcome' })}</p>
</div>
);
}
7. Añade el componente a tu página
Finalmente, añade tu nuevo componente cliente a tu página.
// app/[lang]/page.tsx
import HomePageContent from '@/app/components/HomePageContent';
export default function Home() {
// Esta página es un Componente de Servidor
return (
<div>
{/* Renderiza el Componente Cliente */}
<HomePageContent />
</div>
);
}