在Micro-Frontend项目中实现i18n拆分与优化 - 第二篇

39 阅读3分钟

Module Federation项目中实现i18n拆分与优化 - 第一篇

接着上一篇文章,本以为已经完美的完成了分离。项目测试的过程中,发现加载MF组件之后,宿主应用的翻译就全乱掉了。本篇博客将分享这一经历,并通过阅读源码来定位和解决问题。

问题描述

在我们的项目中,宿主应用和MF(Module Federation)组件各自使用了自己的i18n配置,并且都调用了 i18n.use(initReactNext)。不幸的是,这导致了一个严重的问题:后初始化的i18n配置覆盖了前者,从而引发宿主应用的翻译文案全部消失的问题。


// 宿主应用的i18n初始化

import i18n from 'i18next';

import { initReactI18next } from 'react-i18next';

i18n.use(initReactI18next).init({
    resources: {
        en: {
            translation: {
                welcome: "Welcome to our app"
            }
        }
    },
    lng: "en",
    fallbackLng: "en"
});

  


// MF组件的i18n初始化

import i18n from 'i18next';

import { initReactI18next } from 'react-i18next';

i18n.createInstance().use(initReactI18next).init({
    resources: {
        en: {
            translation: {
                hello: "Hello from MF component"
            }
        }
    },
    lng: "en",
    fallbackLng: "en"
});

上面是示例代码

问题分析

经过研究,我们发现问题的根源在于i18n的 use 方法。 i18n.use(initReactI18next) 其实是向i18n实例中注入了一个插件,这样在多个实例之间会发生配置覆盖。

我们来简单的看一看源码,来理解这个问题。

//initReactI18next.js
import { setDefaults } from './defaults.js';
import { setI18n } from './i18nInstance.js';

export const initReactI18next = {
  type: '3rdParty',

  init(instance) {
    setDefaults(instance.options.react);
    setI18n(instance);
  },
};


// useTranslation.js
export const useTranslation = (ns, props = {}) => {
  // assert we have the needed i18nInstance
  const { i18n: i18nFromProps } = props;
  const { i18n: i18nFromContext, defaultNS: defaultNSFromContext } = useContext(I18nContext) || {};
  const i18n = i18nFromProps || i18nFromContext || getI18n();
  //...
}

上面是initReactI18next和useTranslation的两段源码。不难发现,useTranslation会通过getI18n()来获取i18n对象的。然而initReactI18next的init方法里面调用了setI18n(instance)。所以后一个总是会覆盖前一个。放到具体的例子中来说,因为MF组件是在宿主之后运行的,所以他会将宿主应用的i18n对象覆盖掉,导致宿主应用翻译发生错误。

解决方案

为了避免覆盖问题,我们决定采用一种分离策略:MF组件不再运行 i18n.use(initReactI18next),而是通过 I18nNextProvider 来传入自己的i18n对象。这可以有效避免配置被覆盖,并确保每个模块都有独立的i18n实例。

使用 I18nNextProvider

最后,在MF组件中使用 I18nNextProvider 将i18n实例传递给React:


import React from 'react';

import { I18nextProvider } from 'react-i18next';

import i18n from './i18n';

const MyComponent = () => {

    return (

        <I18nextProvider i18n={i18n}>

        // 你的组件代码

        </I18nextProvider>

    );

};

export default MyComponent;

通过这种方式,宿主应用与MF组件各自拥有独立的i18n实例,从而避免了配置覆盖的问题。

总结

在Module Federation项目中实现i18n的分离可以通过避免多次调用 i18n.use(initReactI18next) 来实现。具体的解决方案是让MF组件不再调用该方法,而是通过 I18nextProvider 传入独立的i18n对象。这样不仅能有效避免配置覆盖,还能确保每个模块都能正确处理自己的翻译逻辑。希望这篇博客能为你在微前端架构中的i18n实现提供一些帮助。