简体   繁体   English

根据用户选择更新 React Native 语言

[英]Updating React Native language by user choice

I'm using I18n to create multiple language application.我正在使用I18n创建多语言应用程序。

I have created "on first launch" screen that offers the user option to choose his preferred language, it works fine but there is problem.我创建了“首次启动”屏幕,为用户提供选择他喜欢的语言的选项,它工作正常但有问题。

Once I choose language, App() component updates, it showing login component ( initialRouteName="Login" ).一旦我选择了语言, App()组件就会更新,它会显示登录组件( initialRouteName="Login" )。 But the language is still by default English, only when I proceed to another screen it works or either FastRefresh login screen.但是默认情况下语言仍然是英语,只有当我进入另一个屏幕时它才能工作或快速刷新登录屏幕。

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>
  );
}

I wonder since I have updated my component, the language should update as well, isn't?我想知道既然我已经更新了我的组件,那么语言也应该更新,不是吗?

Here are some screenshots that will visually explain what my problem is.以下是一些屏幕截图,可以直观地解释我的问题所在。

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

How can I update React Native app by user choice using I18n plugin?如何使用 I18n 插件根据用户选择更新 React Native 应用程序?

EDIT编辑

Debugging results:调试结果:

selectedLaunched(value) - value returns boolean value correctly. selectedLaunched(value) - value 正确返回 boolean 值。

Checking setAppLaunched(en) if statement to see if responding correctly, it does.检查setAppLaunched(en) if 语句以查看是否正确响应,它确实如此。

selected state is also working fine and rendering the NavigationContainer component right after it set to true. selected的 state 也可以正常工作,并在NavigationContainer组件设置为 true 后立即呈现。

CheckIfFirstLaunch Launch Screen should also be inside NavigationContainer, It's outside navigation container might be cause of problem, All your screen be should inside navigationContainer. CheckIfFirstLaunch 启动屏幕也应该在 NavigationContainer 内,它在导航容器之外可能是问题的原因,您的所有屏幕都应该在 NavigationContainer 内。

Use an state for inital route,使用 state 进行初始路由,

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

then inside your condition update state然后在你的条件内更新 state

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

then conditionally set your initial route,然后有条件地设置你的初始路线,

  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>
  );

you can keep other app screens in a separate stack您可以将其他应用程序屏幕保留在单独的堆栈中

const AppStack = createStackNavigator();

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

and then main navigationContainer as然后主导航容器为

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

then in the end when language is selected store it to asyncStorage your language ie "en " and the wether you have selected a language or not然后最后在选择语言时将其存储到 asyncStorage 您的语言,即“en”以及您是否选择了一种语言


AsyncStorage.setItem("languageSelected",true)

and navigate to the Login screen.并导航到登录屏幕。

and other than that on App Lauch, you check with Asyncstorage rather than isSelected state if the language has already been selected除了在 App Lauch 上,如果已经选择了语言,您可以使用 Asyncstorage 而不是 isSelected state 进行检查

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')
}

An full Dummy Code完整的虚拟代码


// 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
    }
  );
}

As I can see the initial route is login so it get's rendered in english by default.正如我所看到的,初始路由是登录,因此默认情况下它会以英文呈现。 And the popup for selecting the language on first launch is rendered on the login screen itself.首次启动时选择语言的弹出窗口会呈现在登录屏幕本身上。 So once you select a different language it updates that language but the component doesn't re-render as there are no state updates.因此,一旦您 select 使用不同的语言,它就会更新该语言,但组件不会重新呈现,因为没有 state 更新。 That's why the language updates on navigating to other screens or on fast reload.这就是语言在导航到其他屏幕或快速重新加载时更新的原因。 There are two solutions that can be implemented:-可以实施两种解决方案:-

  1. Create the language selection as a different screen and navigate to that screen initially on first launch.将语言选择创建为不同的屏幕,并在首次启动时导航到该屏幕。
  2. And the other option that you can explore is try updating something in the local state of the login screen that will re-render the component.您可以探索的另一个选项是尝试更新登录屏幕的本地 state 中的某些内容,这将重新呈现组件。

The problem was trying to change the language using async functions and using await which caused the language to change only after the second screen.问题是尝试使用async函数和使用await更改语言,这导致语言仅在第二个屏幕后更改。 So in that case I just moved one step backwards the language switch.所以在那种情况下,我只是将语言切换向后移动了一步。

Instead having it in async function setAppLaunched(en) function, I moved it to const selectLaunched = (value) => function like that:我将其移至const selectLaunched = (value) => async function setAppLaunched(en)

const selectLaunched = (value) => {
      if(!value){
        i18n.locale = "he";
        I18nManager.forceRTL(true);
      }
      else{
        i18n.locale = "en";
        I18nManager.forceRTL(false);
      }
    setAppLaunched(value);
    onSelect();
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM