What's the best way implement i18n in expo projects?

#1

i thinking in using server side json external data (rails in my case) but i don’t know if it’s a better way to do it in react navite + expo

1 Like

#2

Hey @yestoall

So in general when you are internationalizing, you want any static content to be translated with a tool. This can be done in Expo pretty easily just like any other javascript package by installing the npm and using it directly. With a quick search, I found this i18n library https://www.npmjs.com/package/i18n-js

When dealing with dynamic content, you’ll have to run it through a translation service.

Typically you can expect all text coming back from the server to be translated by the server through some type of service etc so it’s not a big problem on the client side. The only part that becomes tricky is when dealing with languages that go in different directions like Arabic that reads right to left.

Sterling

1 Like

#3

I don’t know if you found a solution for this already, but it seems that someone already took care of creating an extension specifically for Expo:

  • Disclaimer 1: it’s not mine :slight_smile:
  • Disclaimer 2: haven’t used yet (but am about to)
0 Likes

#4

we are hoping that people will standardize to depending on https://github.com/react-community/react-native-languages and then any i18n lib should work. it also will polyfill navigator.languages and navigator.language so it can work with web libs. until then the above lib works or anything that lets you specify the language rather than automatically reading it from an assumed web api

2 Likes

#5

@notbrent does react-native-languages work with expo? It requires linking

0 Likes

#6

Hey all, we’ve got some major improvements to localization that we expect to ship in the next SDK, including a locale store where you can switch between localized strings, and support for iOS system localization.

Here’s a link to the unreleased docs (subject to change). This isn’t available yet but we’ll ship it in the next SDK. If you find this answer months after I wrote it, you can likely just check the official docs by that point.

(edit: This was released: https://docs.expo.io/versions/latest/sdk/localization )

0 Likes

#7

not currently, we’re likely going to add it soon

0 Likes

#8

Ok I got this to work, But I want to translate the names of my screens and every time I do so, it tell me the the translation is missing.

static navigationOptions = {
title: I18n.t(‘WelcomePage’)
};

constructor(props) {
    super(props);
    this.serState = {
        appIsReady: false,
    }
}


async componentWillMount() {
    await I18n.initAsync();
    this.setState({appIsReady: true});
}

render() {

    return (
        <View style={styles.LoginContainer}>
            <Button onPress={signInWithGoogleAsync.bind(this)}
                    title={I18n.t('buttonTranslation')}/>
        </View>
    );


    async function signInWithGoogleAsync() {
        try {
            const result = await Expo.Google.logInAsync({
                androidClientId: testAndroid,
                iosClientId: testIOS,
                androidStandaloneAppClientId: androidID,
                iosStandaloneAppClientId: iosId,
                webClientId: webID,
                scopes: ['profile', 'email'],
            });

            if (result.type === 'success') {

                let details = {
                    'domainName': 'ispcloudservices.com',
                    'userEmail': result.user.email,
                    'langId': I18n.locale,
                    'userToken': result.accessToken
                };

                console.log(details);

                let formBody = [];
                for (let property in details) {
                    let encodedKey = encodeURIComponent(property);
                    let encodedValue = encodeURIComponent(details[property]);
                    formBody.push(encodedKey + "=" + encodedValue);
                }
                formBody = formBody.join("&");

                let postData = {
                    method: 'POST',
                    headers: {
                        'User-Agent': 'ToogleBoxMobile',
                        'referer': 'https://toogleboxmobile.com',
                        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                    },
                    body: formBody
                };

                fetch(url, postData)
                    .then((response) => response.json())
                    .then((responseJson) => {
                        this.props.navigation.navigate('Second', {responseJson})
                    })

            } else {
                return {cancelled: true};
            }
        } catch (e) {
            return {error: true};
        }
    }
}

}

I18n.fallbacks = true;

I18n.translations = {
en: {
buttonTranslation: ‘Sing-in with google’,
WelcomePage: ‘Welcome’
},
es: {
buttonTranslation: ‘Regístrese con Google’,
WelcomePage: ‘Bienvenidos’
},
pt: {
buttonTranslation: ‘Criar sua Conta do Google’,
WelcomePage: ‘Bem vindo’
}
};

0 Likes

#9

that’s because I18n doesn’t initialize until componentWillMount, which happens after the static navigationOptions definition. You should create a startup script somewhere that waits for I18 to be ready.

0 Likes

#10

Hello again

I am trying to display a new image base on my locale, but is not working

import React, {Component} from 'react';
import {Text, TouchableOpacity, View, Platform, Image} from 'react-native';
import {styles} from '../styles/MainContainer'
import {DangerZone, Location, Permissions } from 'expo';
const { Localization } = DangerZone;
import I18n from 'ex-react-native-i18n'


export default class LoginScreen extends Component {

    static navigationOptions = {
        title: 'ToogleBoard'
    };

    constructor(props) {
        super(props);
        this.State = {
            locationResult: '',
            dl: null,
        };
    }


    getLocationAsync = async () => {
        let { status } = await Permissions.askAsync(Permissions.LOCATION);
        if (status !== 'granted') {
            this.setState({
                locationResult: 'Permission to access location was denied',
            });
        }

        let location = await Location.getCurrentPositionAsync();
        this.setState({ locationResult: JSON.stringify(location)});
    };



    async componentDidMount() {
        await this.getLocationAsync();
        this.setState({appIsReady: true});
        await I18n.initAsync()
    }

    render() {
        console.log(I18n.locale);
        if (I18n.locale === 'en') {
            return (
                <View style={styles.container}>
                    <TouchableOpacity>
                        <Image source={require('../assets/btn_google_signin_en.png')}/>
                    </TouchableOpacity>
                </View>
            )
        }
        else if (I18n.locale === 'es') {
            return (
            <View style={styles.container}>
                <TouchableOpacity>
                    <Image source={require('../assets/btn_google_signin_es.png')}/>
                </TouchableOpacity>
            </View>

            )
        }
        else if (I18n.locale === 'pt'){
            return(
                <View style={styles.container}>
                    <TouchableOpacity>
                        <Image source={require('../assets/btn_google_signin_pt.png')}/>
                    </TouchableOpacity>
                </View>

            )
        }
    }
}

Can somebody please assist me with this, thanks

0 Likes

#11

i’m not familiar with that specific library but this one has a good example of how to do i18n with react-navigation/expo https://github.com/i18next/react-i18next/blob/master/example/react-native-expo/App.js

1 Like

#12

I just added i18n using the RN example from react-i18next that @notbrent posted and I highly recommend the library itself and following the specific example. Pretty much just worked with very little troubleshooting.

0 Likes