簡體   English   中英

根據用戶選擇更新 React Native 語言

[英]Updating React Native language by user choice

我正在使用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 正確返回 boolean 值。

檢查setAppLaunched(en) if 語句以查看是否正確響應,它確實如此。

selected的 state 也可以正常工作,並在NavigationContainer組件設置為 true 后立即呈現。

CheckIfFirstLaunch 啟動屏幕也應該在 NavigationContainer 內,它在導航容器之外可能是問題的原因,您的所有屏幕都應該在 NavigationContainer 內。

使用 state 進行初始路由,

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

然后在你的條件內更新 state

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

完整的虛擬代碼


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

正如我所看到的,初始路由是登錄,因此默認情況下它會以英文呈現。 首次啟動時選擇語言的彈出窗口會呈現在登錄屏幕本身上。 因此,一旦您 select 使用不同的語言,它就會更新該語言,但組件不會重新呈現,因為沒有 state 更新。 這就是語言在導航到其他屏幕或快速重新加載時更新的原因。 可以實施兩種解決方案:-

  1. 將語言選擇創建為不同的屏幕,並在首次啟動時導航到該屏幕。
  2. 您可以探索的另一個選項是嘗試更新登錄屏幕的本地 state 中的某些內容,這將重新呈現組件。

問題是嘗試使用async函數和使用await更改語言,這導致語言僅在第二個屏幕后更改。 所以在那種情況下,我只是將語言切換向后移動了一步。

我將其移至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