Carga de traducciones
Uso de un proveedor para gestionar mensajes
Problema
Codificar texto de forma fija, como 'Hello World', directamente 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 agregar lógica if/else, lo que hace que la traducción no sea escalable y requiera un cambio completo de código para cada nuevo fragmento de texto.
Solución
Usa IntlProvider de react-intl para proporcionar traducciones. Carga los mensajes de traducción en el servidor en el layout raíz, pásalos a un componente proveedor del lado del cliente y luego consúmelos en otros componentes cliente usando el hook useIntl.
Pasos
1. Instala react-intl
Primero, agrega react-intl como dependencia a tu proyecto.
npm install react-intl
2. Crea archivos de traducción planos
Crea una carpeta dictionaries. Dentro, agrega 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. Crea una función para cargar diccionarios
Crea una función auxiliar para cargar el archivo de diccionario correcto en el servidor según 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. Crea un proveedor del lado del cliente
IntlProvider es un componente cliente que usa 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>; // Flat messages object
};
export default function IntlClientProvider({
children,
locale,
messages,
}: Props) {
return (
<IntlProvider messages={messages} locale={locale} defaultLocale="en">
{children}
</IntlProvider>
);
}
5. Actualiza 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 };
}) {
// Load messages on the server
const messages = await getDictionary(params.lang);
return (
<html lang={params.lang}>
<body>
{/* Pass messages to the client provider */}
<IntlClientProvider locale={params.lang} messages={messages}>
{children}
</IntlClientProvider>
</body>
</html>
);
}
6. Usa traducciones en un componente cliente
Ahora puedes usar el hook useIntl en cualquier componente cliente. Los componentes 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 de cliente a tu página.
// app/[lang]/page.tsx
import HomePageContent from '@/app/components/HomePageContent';
export default function Home() {
// This page is a Server Component
return (
<div>
{/* It renders the Client Component */}
<HomePageContent />
</div>
);
}