首页 > 解决方案 > 根据用户选择更新 React Native 语言

问题描述

我正在使用I18n创建多语言应用程序。

我创建了“首次启动”屏幕,为用户提供选择他喜欢的语言的选项,它工作正常但有问题。

一旦我选择语言,App()组件更新,它显示登录组件(initialRouteName="Login")。但是默认情况下语言仍然是英语,只有当我进入另一个屏幕时它才能工作或快速刷新登录屏幕。

const Stack = createStackNavigator();
const HAS_LAUNCHED = "hasLaunched";
const ENGLISH = "en";
const HEBREW = "he";


//Save the language as AsyncStorage for other times the user will open the app
async function setAppLaunched(en) {
  AsyncStorage.clear()
  AsyncStorage.setItem(HAS_LAUNCHED, "true");
  AsyncStorage.setItem(en ? ENGLISH : HEBREW, "true");
  if(await AsyncStorage.getItem(HEBREW)){
    i18n.locale = "he";
    I18nManager.forceRTL(true);
  }
  else{
    i18n.locale = "en";
    I18nManager.forceRTL(false);
  }
}


//If first launch show this screen
function CheckIfFirstLaunch({ onSelect }) {

  const selectLaunched = (value) => {
    setAppLaunched(value);
    onSelect();
  };


  return (
    <View>
        <Text>Choose Language</Text>
        <Button onPress={() => selectLaunched(false)} title="Hebrew"/>
        <Button onPress={() => selectLaunched(true)} title="English"/>
    </View>
  );
}

export default function App() {
  const [selected, setSelected] = useState(false);

  const verifyHasLaunched = async () => {
    try {
      const hasLaunched = await AsyncStorage.getItem(HAS_LAUNCHED);
      setSelected(hasLaunched != null);
    } catch (err) {
      setSelected(false);
    }
  };

  useEffect(() => verifyHasLaunched, []);

  if (!selected){
    return <CheckIfFirstLaunch onSelect={() => setSelected(true)} />;
  }
  else{
    const verifyLang = async () => {
      const lang = await AsyncStorage.getItem('he');
      if(lang != null){
        i18n.locale = "he";
        I18nManager.forceRTL(true);
      }
      else{
        i18n.locale = "en";
        I18nManager.forceRTL(false);
      }
   };
   () => verifyLang;
  }

  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{headerShown: false}} initialRouteName="Login">
        <Stack.Screen name="Login" component={Login} />
        <Stack.Screen name="Register" component={Register} />
        <Stack.Screen name="Dashboard" component={Dashboard} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

我想知道既然我已经更新了我的组件,那么语言也应该更新,不是吗?

以下是一些屏幕截图,可以直观地解释我的问题所在。

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

如何使用 I18n 插件根据用户选择更新 React Native 应用程序?

编辑

调试结果:

selectedLaunched(value)- value 正确返回布尔值。

检查setAppLaunched(en)if 语句以查看是否正确响应,它确实如此。

selectedstate 也可以正常工作,并在NavigationContainer组件设置为 true 后立即渲染组件。

标签: javascriptreact-native

解决方案


CheckIfFirstLaunch 启动屏幕也应该在 NavigationContainer 内,它在导航容器之外可能是问题的原因,您的所有屏幕都应该在 NavigationContainer 内。

对初始路由使用状态,

const [initialRoute, setRouteState] = useState('CheckIfFirstLaunch');

然后在您的条件更新状态中

  if (!selected){
     setRouteState('CheckIfFirstLaunch);
    return <CheckIfFirstLaunch onSelect={() => setSelected(true)} />;
  }
  else{
     setRouteState('Login');
   }

然后有条件地设置你的初始路线,

  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{headerShown: false}} initialRouteName{initialRoute}">
      <Stack.Screen name="CheckIfFirstLaunch" component={CheckIfFirstLaunch} />
        <Stack.Screen name="Login" component={Login} />
        <Stack.Screen name="Register" component={Register} />
        <Stack.Screen name="Dashboard" component={Dashboard} />
      </Stack.Navigator>
    </NavigationContainer>
  );

您可以将其他应用程序屏幕保留在单独的堆栈中

const AppStack = createStackNavigator();

function MyAppStack() {
   <APPStack.Screen name="Login" component={Login} />
   <APPStack.Screen name="Register" component={Register} />
     <APPStack.Screen name="Dashboard" component={Dashboard} />
}

然后主 navigationContainer 为

<NavigationContainer>
      <Stack.Navigator screenOptions={{headerShown: false}} initialRouteName="Login">
      <Stack.Screen name="CheckIfFirstLaunch" component={CheckIfFirstLaunch} />
        <Stack.Screen name="Login" component={MyAppStack} />
      </Stack.Navigator>
    </NavigationContainer>

然后最后在选择语言时将其存储到 asyncStorage 您的语言,即“en”以及您是否选择了一种语言


AsyncStorage.setItem("languageSelected",true)

并导航到登录屏幕。

除了在 App Lauch 上,如果已经选择了语言,您可以使用 Asyncstorage 而不是 isSelected 状态进行检查

const langSelected = await AsyncStorage.getItem('languageSelected');

if(langSelected) {
   setInitialRoute('Login')
   // check which language has been Selected
   const lang = await AsyncStorage.getItem('he');
      if(lang != null){
        i18n.locale = "he";
        I18nManager.forceRTL(true);
      }
      else{
        i18n.locale = "en";
        I18nManager.forceRTL(false);
      }
} else {
    setInitialRoute('CheckIfFirstScreen')
}

完整的虚拟代码


// CheckIfFirstLaunch screen component
export function CheckIfFirstLaunch(props) {
  
 //set default language on button selection
  setDefaultLanguage = (lang) => {
     //set default language language
      i18n.locale = lang;
      //If Hebrew switch to RTL
      if(lang === "he") {
              I18nManager.forceRTL(true);
       }
       AsyncStorage.setItem("language",lang)
           props.navigation.navigate("Login")
   }

  return (
    <View>
        <Text>Choose Language</Text>
        <Button onPress={() => setDefaultLanguage("he")} title="Hebrew"/>
        <Button onPress={() => setDefaultLanguage("en")} title="English"/>
    </View>
  );
}

const Stack = createStackNavigator();

export default function App() {
  //intial default route set FirstLaunch Screen
  const [initialAppRoute, setInitialRoute] = useState("CheckIfFirstLaunch");
  
  // a loading state to check if react has checked for data from asyncStorage
  const [dataLoaded, setDataLoaded] = useState("false");
  
  //verify if language has been selected
  const verifyHasLaunched = async () => {
    try {
      //get language from asyncStorage
      const lang = await AsyncStorage.getItem("language");
      
      // if language value stored in asyncStorage    
      if(hasLaunched) {
        
         // if language is hebrew do this  else do that
        if(lang === 'he'){
          i18n.locale = "he";
          I18nManager.forceRTL(true);
          }
        else{
           i18n.locale = "en";
           I18nManager.forceRTL(false);
          }
        // set initial route to Login
        setInitialRoute(initialAppRoute:'Login')           
      }else {
         // else initial route should language screen
         setInitialRoute(initialAppRoute:'CheckIfFirstLaunch')
      }
    } catch (err) {
          // if error do something here
    }
  };

  useEffect(() => verifyHasLaunched, []);
  return (
   {dataLoaded  ? 
    <NavigationContainer>
     <Stack.Navigator screenOptions={{headerShown: false}} initialRouteName={initialAppRoute}">
      <Stack.Screen name="CheckIfFirstLaunch" component={CheckIfFirstLaunch} />
        <Stack.Screen name="Login" component={Login} />
        <Stack.Screen name="Register" component={Register} />
        <Stack.Screen name="Dashboard" component={Dashboard} />
      </Stack.Navigator>
      </Stack.Navigator>
    </NavigationContainer>
    :
     // or keep loading loader until react has checked for data from asyncStorage
      null
    }
  );
}

推荐阅读