React Native, how to update view after language change

12,802

Solution 1

You can try something like this

<View style={styles.container}>
    <Button onPress={this.setLocale()} title={strings('test.b1')}> 
</View>

public setLocale() {
    this.setState({stateOfLocale: 'en'});
    I18n.locale = stateOfLocale;
}

Solution 2

On the page where I have the button or Select to change the language, nothing will happen unless the texts are dependant on the language.

On every string I can pass the locale as an optional parameter like this {I18n.t("email", { locale: language })} and this will cause a rerender immediately if it changes.

No restart or refresh needed.

In the example below, I am using a select to modify the language in the state. The text field is dependant on it because of the optional locale parameter and will immediately re-render.

To persist the changes, I've implemented a Save button that will save the locale to AsyncStorage and call a changeLanguage function defined in a separate i18n.js file

import React, { useState } from "react";
import { View, Text, Button } from "react-native";
import I18n, { changeLanguage } from "../i18n/i18n";
import RNPickerSelect as Select from 'react-native-picker-select';
import AsyncStorage from '@react-native-async-storage/async-storage';

function ProfileScreen() {
  const locale = I18n.currentLocale();
  const [language, setLanguage] = useState(locale);

  const saveProfile = async () => {
    if (locale !== language) {
      AsyncStorage.setItem("language", language);
      changeLanguage(language);
    }
  }
  
  return (
  <View>
    <Text>{I18n.t("email", { locale: language })}</Text>
    <Select 
      value={language}
      onValueChange={(value) => setLanguage(value)}
      placeholder={I18n.t("selectAppLanguage")}
      items={[
        { label: I18n.t("English", { locale: language }), value: "en" },
        { label: I18n.t("Finnish", { locale: language }), value: "fi" },
        { label: I18n.t("Swedish", { locale: language }), value: "se" },
      ]}
    />
    <Button
      title={I18n.t("save", { locale: language })}
      onPress={saveProfile} 
    />    
  </View>
  );
}
export default ProfileScreen;

here is my i18n.js file. I am using react-native-localize as react-native-i18n is not maintained anymore and creates problems on Android once released. Changing the language is as simple as I18n.locale = lang

//i18n/i18n.js
import I18n from "i18n-js";
import * as RNLocalize from "react-native-localize";
import en from './languages/en';
import fi from './languages/fi';
import se from './languages/se';

const locales = RNLocalize.getLocales();

if (Array.isArray(locales)) {
  I18n.locale = locales[0].languageCode;
}

export const changeLanguage = lang => {
  if (lang) {
    I18n.locale = lang;
  } else if (Array.isArray(locales)) {
    I18n.locale = locales[0].languageCode;
  }
}

I18n.fallbacks = true;

I18n.translations = {
  en,
  fi,
  se,
};

export default I18n;

One last thing, in App.js, at app start, I am retrieving the language from local storage inside useEffect and overriding the device's language. So language is persisted.

//App.js
import React, { useEffect } from "react";
import I18n, { changeLanguage } from "./src/i18n/i18n";
import AsyncStorage from '@react-native-async-storage/async-storage';

function App() {
  useEffect(() => {
    const setLanguage = async () => {
      const language = await AsyncStorage.getItem("language");
      changeLanguage(language)
    }
    setLanguage()
  }, [])
  return ( // ...navigation, etc.
  );
}
export default <App />

Solution 3

Here are few suggestions,

  1. When your App initializes, you can set the I18n.locale
      I18n.locale = 'en'
  1. When you try to change language make sure all the components have i18n translations ie. i18.t() i.e I18n.translate functions as shown below:
  ...
  <TouchableOpacity>
    <View>
     <Text>{I18n.t('LANG.CHANGE')}</Text>
   </View>
  </TouchableOpacity>
  ...

Your en.json

  ...
  { 
    'LANG': {
       'CHANGE': "Change Language"
     }
  }
  ...
  1. On language change, i.e when you change i18.locale, you can notify all the components connected to store, so that they re-render i.e typical react-redux flow.

Hope this helps.

Share:
12,802
Omri Attiya
Author by

Omri Attiya

Fullstack developer Software Engineer, BGU, IL LinkedIn | GitHub | StackOverflow

Updated on June 04, 2022

Comments

  • Omri Attiya
    Omri Attiya almost 2 years

    I want to support language change (manually by user). I'm using react-native-i18n for that. I found how to change the displayed language at run time but I didn't find how to update the current view.

    My Code

    enter image description here

    Environment

    Environment:

    • Node: 8.9.4
    • Yarn: 1.3.2
    • npm: 4.0.5

    Expected Behavior

    When I use I18n.locale ='en'; not in function.. just as it is, the text will be in English and when I use I18n.locale ='he'; the text will be in Hebrew. However I need to change the language at run time. So I want that when I click each button the language will change and will be displayed.

    Actual Behavior

    Nothing happened.. I assume I need to reload / re-render / update the view but I didn't find how to.

  • Omri Attiya
    Omri Attiya about 6 years
    since I need to change the language at run-time option 1 cant help me. As for option 2, it functions just like my original code. I didn't consider option 3, thanks. I decided to use State but I'm still having troubles with making it general and avoiding code coping
  • Omri Attiya
    Omri Attiya about 6 years
    completely working! thank you, plus now I understands better how react native works
  • Mertcan Diken
    Mertcan Diken about 6 years
    Great to hear that!