Implementar la internacionalización (i18n) con Module Federation en micro-frontends de React
Consulta aquí la lista de proyectos de ejemplo: React i18n
Descripción general
En el ámbito de los micro-frontends, es fundamental que cada aplicación sea lo más autónoma posible. Sin embargo, surgen escenarios en los que la comunicación entre aplicaciones es inevitable. Un requisito habitual es sincronizar las preferencias de idioma entre micro-frontends, por ejemplo, cuando un usuario cambia la configuración de idioma en una aplicación y el cambio se propaga a todos los micro-frontends integrados. Esta documentación describe una estrategia para implementar la internacionalización con la biblioteca react-i18next dentro de una arquitectura micro-frontend basada en Module Federation.
Escenario
Imagina dos aplicaciones: App A (la aplicación contenedora) y App B (la aplicación remota). Aunque App B funciona de forma independiente como aplicación autónoma, también está diseñada para integrarse dentro de App A en runtime. Nuestro objetivo es sincronizar sin fricciones la configuración de idioma entre App A y App B, de modo que ambas aplicaciones puedan gestionar y mostrar su contenido localizado.
Arquitectura e implementación
Ampliar la configuración del bundler
Para dar cabida a la integración de micro-frontends, ampliamos las configuraciones existentes de Webpack/Rspack/Rsbuild y de Create React App (CRA) con el plugin de Module Federation. Esta ampliación permite que App B exponga distintos elementos (por ejemplo, componentes, temas o hooks) para que los consuma App A o cualquier otra aplicación integrada, sin necesidad de hacer eject del código.
Configurar la internacionalización
La implementación de react-i18next es la base de nuestra funcionalidad de traducción. En primer lugar, se configura una instancia de i18next y se importa directamente en el archivo principal de la aplicación (App.js). Esta instancia se apoya en un context store para gestionar el estado, los recursos (traducciones) y los plugins.
Resolver los problemas de sobrescritura de traducciones
Un enfoque directo en el que cada aplicación inicializa su instancia de i18next podría provocar conflictos de recursos, sobre todo cuando se sobrescriben términos traducidos. Para evitarlo, creamos instancias de i18next separadas para App A y App B, de modo que cada aplicación mantenga sus términos de traducción de forma independiente.
Pasos de implementación
Configurar las instancias de i18next
Tanto para App A como para App B, configura instancias de i18next separadas de la siguiente manera:
Integrar los providers de i18next
Envuelve los componentes de la aplicación en I18nextProvider y pásale la instancia de i18next correspondiente para que los contextos de traducción se apliquen correctamente.
Lógica de cambio de idioma
Para App B, escribe un hook personalizado que facilite el cambio de idioma, por ejemplo:
Expón este hook mediante Module Federation para que App A u otras aplicaciones integradas puedan consumirlo.
En App A, implementa un hook que coordine el cambio de idioma en todos los micro-frontends integrados:
Interfaz para cambiar de idioma
Implementa un componente de interfaz de usuario, como un botón, que active el cambio de idioma en todas las aplicaciones:
Gestionar entornos integrados
Para mostrar de forma condicional los componentes de cambio de idioma (por ejemplo, ocultar el selector en App B cuando está integrada en App A), aprovecha un hook personalizado como useIsRemote.
El hook useIsRemote está pensado para determinar si la aplicación actual (por ejemplo, App B) se ejecuta en modo autónomo o integrada dentro de otra aplicación (por ejemplo, App A). Esta distinción nos permite renderizar componentes de forma condicional según el contexto de la aplicación.
A continuación tienes un ejemplo de implementación simplificada del hook useIsRemote, que comprueba una condición concreta para determinar el entorno de la aplicación. En una aplicación real, esta condición podría basarse en parámetros de la URL, en la comprobación de la presencia de elementos del DOM o en cualquier otro indicador que distinga entre estar integrada y ejecutarse de forma autónoma:
Usar el hook useIsRemote
Con el hook useIsRemote implementado, ya puedes usarlo en tus componentes para renderizar elementos de forma condicional según si la aplicación se ejecuta de forma autónoma o integrada. Aquí tienes un ejemplo de cómo podría usarse para mostrar de forma condicional un componente de cambio de idioma en App B:
En este ejemplo, LanguageSwitcher solo se renderiza cuando App B no está integrada dentro de App A, según el estado isRemote que determina el hook useIsRemote. Este enfoque garantiza que componentes como los selectores de idioma solo se muestren en los contextos adecuados, lo que mejora la experiencia de usuario y mantiene la independencia de las aplicaciones micro-frontend.