You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Redux Persist重hydrate后初始化react-i18next实例?

这个问题我之前也碰到过!核心原因就是你的i18n实例在文件被导入时就立刻执行了初始化,这时候redux-persist还在后台恢复持久化状态,store.getState()根本拿不到缓存的语言设置,自然会 fallback 到设备语言或者默认值。

给你两个靠谱的解决方案,推荐第一个,更优雅:


方案一:利用PersistGate的状态订阅,等Store完全hydrate后再初始化i18n

首先改造你的i18n.js,把初始化逻辑封装成一个异步函数,而不是直接执行:

// i18n.js
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
import * as RNLocalize from 'react-native-localize';
import store from 'Api/store';
import { setLanguageTag, sincronizeSettings } from 'Api/store/actions';
import { languages } from 'Base/config.json';

const translations = {
  en: { translation: require('Assets/translations/en.json') },
  pt: { translation: require('Assets/translations/pt.json') },
};

const languageDetector = {
  type: 'languageDetector',
  async: true,
  detect: async (callback) => {
    const storedLanguageTag = store.getState().settings.data?.languageTag;
    if (storedLanguageTag) {
      return callback(storedLanguageTag);
    }
    const { languageTag: deviceBestAvailableLanguageTag } = RNLocalize.findBestAvailableLanguage(
      languages.available,
    );
    const currentLanguageTag = deviceBestAvailableLanguageTag || languages.default;
    store.dispatch(setLanguageTag(currentLanguageTag));
    store.dispatch(sincronizeSettings());
    callback(currentLanguageTag);
  },
  init: () => {},
  cacheUserLanguage: () => {},
};

// 封装初始化函数,返回Promise
export const initI18n = async () => {
  const i18n = i18next.createInstance();
  await i18n
    .use(languageDetector)
    .use(initReactI18next)
    .init({
      fallbackLng: languages.default,
      resources: translations,
      react: {
        useSuspense: false,
      },
    });
  return i18n;
};

然后修改你的index.js,通过订阅persistor的状态,等到Store完成hydrate(bootstrapped为true)后再初始化i18n,确保初始化时能拿到缓存的语言:

// index.js(类组件版本)
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import store, { persistor } from 'Api/store';
import { initI18n } from 'Api/i18n';
import Launch from './Launch';
import ReduxNetworkProvider from './ReduxNetworkProvider';
import AppContainer from './AppContainer';

export default class App extends Component {
  state = {
    i18nReady: false,
  };

  componentDidMount() {
    // 订阅persistor的状态变化
    const unsubscribe = persistor.subscribe(() => {
      const { bootstrapped } = persistor.getState();
      // bootstrapped为true表示Store已经完成hydrate
      if (bootstrapped) {
        initI18n().then(() => {
          this.setState({ i18nReady: true });
          unsubscribe(); // 完成后取消订阅,避免重复触发
        });
      }
    });
  }

  render() {
    // 等待i18n初始化完成再渲染主应用
    if (!this.state.i18nReady) {
      return <Launch />;
    }

    return (
      <Provider store={store}>
        <PersistGate persistor={persistor} loading={<Launch />}>
          <ReduxNetworkProvider pingInBackground>
            <AppContainer />
          </ReduxNetworkProvider>
        </PersistGate>
      </Provider>
    );
  }
}

如果用函数组件的话,写法更简洁:

// index.js(函数组件版本)
import React, { useState, useEffect } from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import store, { persistor } from 'Api/store';
import { initI18n } from 'Api/i18n';
import Launch from './Launch';
import ReduxNetworkProvider from './ReduxNetworkProvider';
import AppContainer from './AppContainer';

export default function App() {
  const [i18nReady, setI18nReady] = useState(false);

  useEffect(() => {
    const unsubscribe = persistor.subscribe(() => {
      const { bootstrapped } = persistor.getState();
      if (bootstrapped) {
        initI18n().then(() => {
          setI18nReady(true);
          unsubscribe();
        });
      }
    });

    return unsubscribe;
  }, []);

  if (!i18nReady) {
    return <Launch />;
  }

  return (
    <Provider store={store}>
      <PersistGate persistor={persistor} loading={<Launch />}>
        <ReduxNetworkProvider pingInBackground>
          <AppContainer />
        </ReduxNetworkProvider>
      </PersistGate>
    </Provider>
  );
}

方案二:轮询等待Store状态就绪(备选方案)

如果不想大改结构,也可以在languageDetector的detect方法里加一个等待逻辑,直到Store的settings数据加载完成:

// 修改i18n.js里的languageDetector.detect方法
detect: async (callback) => {
  // 等待Store的settings.data存在,轮询检查
  await new Promise(resolve => {
    const checkSettings = () => {
      const { data } = store.getState().settings;
      if (data) {
        resolve();
      } else {
        setTimeout(checkSettings, 50); // 每50ms检查一次
      }
    };
    checkSettings();
  });

  // 之后的逻辑不变
  const storedLanguageTag = store.getState().settings.data?.languageTag;
  if (storedLanguageTag) {
    return callback(storedLanguageTag);
  }
  const { languageTag: deviceBestAvailableLanguageTag } = RNLocalize.findBestAvailableLanguage(
    languages.available,
  );
  const currentLanguageTag = deviceBestAvailableLanguageTag || languages.default;
  store.dispatch(setLanguageTag(currentLanguageTag));
  store.dispatch(sincronizeSettings());
  callback(currentLanguageTag);
},

不过这个方案是轮询,不如方案一利用persistor的订阅机制可靠,更推荐方案一。

核心思路就是让i18n的初始化严格晚于Redux Store的hydrate完成时间,这样就能确保初始化时能拿到持久化的语言配置了。

内容的提问来源于stack exchange,提问作者Tiago Correia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 07:59:10