How to integrate i18n-js in React Native Expo app - react-native

I'm having issues instantiating I18n in my Expo app. The TL;DR of the problem is that components that need the translations are rendered before
Expo.Util.getLocaleAsync()
returns and sets the locale. I can't figure out how to best set it up. As of now, I have a file for my instance of I18n, which I then import and use everywhere else in my app. It looks something like this:
import Expo from 'expo';
import I18n from 'i18n-js';
import english from './resources/strings/en';
import danish from './resources/strings/da';
I18n.defaultLocale = 'en-GB';
I18n.fallbacks = true;
I18n.initAsync = async () => {
var locale = 'en-GB';
try {
locale = await Expo.Util.getCurrentLocaleAsync();
} catch (error) {
console.log('Error getting locale: ' + error);
}
I18n.locale = locale ? locale.replace(/_/, '-') : '';
};
I18n.translations = {
en: english,
da: danish,
};
export default I18n;
Then, in my root app component, I do the following:
import I18n from './src/i18n';
class App extends React.Component {
async componentWillMount() {
console.log('Current locale (main1): ' + I18n.currentLocale());
try {
await I18n.initAsync();
} catch (error) {
console.log('Error setting locale ' + error);
}
console.log('Current locale (main2): ' + I18n.currentLocale());
}
render() {
return <AppContainer />;
}
}
Expo.registerRootComponent(App);
The logs state the expected values - first the default locale, and then the updated locale in main2. The problem is that the child views are rendered with the first locale before the change is made, and I don't understand why?
I can't figure out a better way to do this, any ideas/tips would be much appreciated :-)

This is what worked for me:
In main.js:
import I18n from 'react-native-i18n';
class App extends React.Component {
state = {
appIsReady: false,
}
async componentWillMount() {
await I18n.initAsync();
this.setState({appIsReady: true});
}
render() {
if (!this.state.appIsReady) {
return (
<AppLoading />
);
}
return (
<RootComponent>
);
)
and in some component:
import I18n from '../i18n';
render() {
return (
<View>
<Text>{I18n.t('favorites')}</Text>
<Text>{I18n.locale}</Text>
</View>
)
}
And from the root dir I created i18n/index.js:
import I18n from 'react-native-i18n';
I18n.fallbacks = true; // let 'en-GB' fallback to 'en' if not found
I18n.translations = {
'en': require('./en.json'),
'nl': require('./nl.json'),
}
export default I18n;
my initial Dutch translation file in i18n/nl.json:
{
favorites: 'Jouw Favorieten',
}
I have this in my package.json:
"react-native-i18n": "git+https://github.com/xcarpentier/ex-react-native-i18n.git",

This might be a solution for you: https://github.com/xcarpentier/ex-react-native-i18n

in react native expo it's easy.
1- step 1 install and import
expo install expo-localization
app.js
import { I18nManager } from 'react-native';
import * as Localization from 'expo-localization';
import i18n from 'i18n-js';
1- step 2 create an object with supported language.
i18n.translations = {
en: { addPrice: 'add Price Here',subTax:' Sub Tax -'},
ar: {addPrice: 'ادخل السعر هنا' ,subTax:'قبل ضريبة - '}
};
// Set the locale once at the starting app.
i18n.locale = Localization.locale;
// When a value is missing from a language it'll fallback to another language with the key present.
i18n.fallbacks = true;
//don't change app dir to rtl.
I18nManager.forceRTL(false);
I18nManager.allowRTL(false);
1- step 3 use it Everywhere
<TouchableOpacity >
<Text >{i18n.t('subTax')}</Text>
</TouchableOpacity>

Related

Vue3 vue-i18n Lazy loading

I would like to implement lazy loading for individual languages in my APP, however I don't really understand the example.
Example: https://vue-i18n.intlify.dev/guide/advanced/lazy.html
i18n.js
import { nextTick } from 'vue'
import { createI18n } from 'vue-i18n'
export const SUPPORT_LOCALES = ['de', 'en']
export function setupI18n(options = { locale: 'de' }) {
const i18n = createI18n(options)
setI18nLanguage(i18n, options.locale)
return i18n
}
export function setI18nLanguage(i18n, locale) {
if (i18n.mode === 'legacy') {
i18n.global.locale = locale
} else {
i18n.global.locale.value = locale
}
document.querySelector('html').setAttribute('lang', locale)
}
export async function loadLocaleMessages(i18n, locale) {
// load locale messages with dynamic import
const messages = await import(
/* webpackChunkName: "locale-[request]" */ `./locales /${locale}.json`
)
// set locale and locale message
i18n.global.setLocaleMessage(locale, messages.default)
return nextTick()
}
My folder structure looks quite similar.
I don't use the composition API at this point.
Instead of loading the languages via vue-router I would like to define a default language which can be changed in the user settings.
Where and how do I have to load the function "loadLocaleMessages()" now?
Currently I load the configuration in my main.js like this so that I have "$t" available in the template:
import { setupI18n } from "#/plugins/i18n";
...
app.use(setupI18n());
...
The i18n.js looks like in the example only that my path for the languages is different.
Also I would like to know how I have to include everything so that I have e.g. "$t" also available in other (not components)?
E.g. in the routes.js or in the store (vuex)
EDIT:
middlewares.js - beforeEachHook
import { i18n } from "#/plugins/i18n"
const { t } = i18n.global
/**
* Translate/set page title
*/
export function setPageTitleMiddleware (to, from, next) {
const pageTitle = to.matched.find(item => item.meta.title)
if (to.meta && to.meta.title)
window.document.title = process.env.VUE_APP_DOMAIN_TITLE + ' | ' + t(to.meta.title)
else if
(pageTitle && pageTitle.meta) window.document.title = process.env.VUE_APP_DOMAIN_TITLE + ' | ' + t(pageTitle.meta.title)
next()
}
To be able to get i18n translation from anywhere in your application (vue3 in my case) :
import i18n from "#/i18n";
i18n.global.t("your_key")

How to make react native app save the language when changed from the app with i18next?

I'm using i18next in react native to use multi languages in the app :
the user can change the language from the app by clicking on a button
in this button I make an action to set the language in AsyncStorage ,
in i18next init file I want to use the value of the AsyncStorage, but its not changing it because AsyncStorage it need async and await so it take long time to change the value ,
this is the code :
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import English from '../Translation/Languages/English.json';
import Spanish from '../Translation/Languages/Spanish.json';
import Arabic from '../Translation/Languages/Arabic.json';
import AsyncStorage from '#react-native-async-storage/async-storage';
let language = null;
const changeLanguage = async () => {
try {
const Lang = await AsyncStorage.getItem('Language');
if (Lang !== null) {
language = Lang;
}
}
catch (error) {
console.log("Error ", error)
}
};
changeLanguage();
i18n
.use(initReactI18next)
.init({
lng: language,
fallbackLng: 'en',
resources: {
en: English,
es: Spanish,
ar: Arabic
}
});
export default i18n;
Checkout this medium-post for a working sample...
Github Repo
https://github.com/hend-elsahli/ReactNativeLocalization
use languageDetector
i18n
.use(languageDetector)
.init(...)
const languageDetector = {
init: Function.prototype,
type: 'languageDetector',
async: true, // flags below detection to be async
detect: async callback => {
const selectedLanguage = await AsyncStorage.getItem('Language');
/** ... */
callback(selectedLanguage);
},
cacheUserLanguage: () => {},
};

i18n-js in react native: language not loaded on startup

I have a react native app (ios) with multiple languages. I'm currently facing a weird issue: when I open the app, for a split second, I don't see the translations but I see an error: 'missing translation ...'. This error normally shows up when the translation key is not found (key-value pair) in the json file. Like I said, this error is only there for a split second, after like 500ms, the translations pop up. It seems like the translation file is not loaded properly when the app starts up.
This error is new since the last few days, before that it didn't happen. It also does not happen on the ios simulator, but only when I'm testing on a device. Both the debug scheme and the release scheme have this issue.
My language file looks like this:
// en.json
// { key: value }
{
"hello": "Hello",
"world": "World",
...
}
This is my code in react-native:
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { _setLanguage } from '../redux/Actions';
import _ from 'lodash';
import i18n from 'i18n-js';
import * as RNLocalize from "react-native-localize";
const translationGetters = {
'en': () => require('./translations/en.json')
};
export default useLanguage = (props) => {
const dispatch = useDispatch();
// get the stored language in the redux store (if set)
const _language = useSelector(state => state.data.config.language);
// translate function to memoize the language file
const translate = _.memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key)
);
// useEffect function loaded on start up to get the language from redux
// (if set) or from the device
// setLanguage() is called to load the correct language file
useEffect(() => {
if (_.isNull(_language)) {
const fallback = { languageTag: 'en' };
const { languageTag } = RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) || fallback;
dispatch(_setLanguage({ language: languageTag }));
setLanguage(languageTag);
} else {
setLanguage(_language);
}
}, []);
// setLanguage()
// sets the language file
const setLanguage = (language) => {
translate.cache.clear();
i18n.translations = { ['en']: translationGetters['en'](), [language]: translationGetters[language]() };
i18n.locale = language;
i18n.defaultLocale = 'en';
i18n.fallbacks = true;
i18n.missingTranslation = () => (null);
dispatch(_setLanguage({ language }));
};
return {
setLanguage,
language: _language,
translate
};
};

How to provide localization and multilanguage text into app using Backend server webservies which contains translations like en.json and da.json?

I want to add multilanguage support for the application but all the keywords come from the backend Webservices. How to fetch that keyword and add translation in app?
I am using static keywords right at the moment, like in this code:
I have used react-native-i18n as a localization.
import React, { Component } from 'react';
const en = {
login: 'LOGIN',
logout: 'LOGOUT',
usernameInput: 'USERNAME',
passwordInput: 'PASSWORD',
dashboardDrawer: 'Dashboard',
historyDrawer: 'History',
feedbackDrawer: 'Feed back',
settingsDrawer: 'Settings',
helpDrawer: 'Help',
}
export default en
First, make a class for translation,
import ReactNative from "react-native";
import I18n from "react-native-i18n";
import * as RNLocalize from "react-native-localize";
import { SafeAreaView, AsyncStorage, NetInfo, Alert } from "react-native";
import en from "./en.json"; // JSON file of words in English
import lv from "./lv.json"; // JSON file of words in Your language
I18n.fallbacks = true;
I18n.translations = {
en,
lv
};
global.selectedLanguage = "";
AsyncStorage.getItem("SELECTED_LANGUAGE").then(value => {
if (value) {
let languageDict = JSON.parse(value);
let language = languageDict.language;
global.selectedLanguage = language;
if (language === "English") {
I18n.locale = "en";
} else {
I18n.locale = "lv";
}
} else {
I18n.locale = "en";
}
});
export function changeLanguage() {
return (I18n.locale = "lv");
}
export function strings(name, params = {}) {
return I18n.t(name, params);
}
export default I18n;
Second, Use this in your UI conponents,
eg:
....
import { strings } from "../../i18n;
....
....
<Text style={styles.titleText}>{strings("sign_up")}</Text>
....
The key 'sign_up' will show you the selected language.
And when you switch the language update the async storage.
eg:
let dict = {
language: this.state.selectedLanguage
};
AsyncStorage.setItem("SELECTED_LANGUAGE", JSON.stringify(dict));

How to get the device token in react native

I am using react-native 0.49.3 version, My Question is how to get the device token in react native for both IOS and Android I tried with this link but it not working for me, right now I tried in IOS. how to resolve it can one tell me how to configure?
I tried different solutions and I've decided to use React Native Firebase.
Here you will find everything about Notifications.
Also, you can use the others libraries that come with Firebase, like Analytics and Crash Reporting
After set up the library you can do something like:
// utils/firebase.js
import RNFirebase from 'react-native-firebase';
const configurationOptions = {
debug: true,
promptOnMissingPlayServices: true
}
const firebase = RNFirebase.initializeApp(configurationOptions)
export default firebase
// App.js
import React, { Component } from 'react';
import { Platform, View, AsyncStorage } from 'react-native';
// I am using Device info
import DeviceInfo from 'react-native-device-info';
import firebase from './utils/firebase';
class App extends Component {
componentDidMount = () => {
var language = DeviceInfo.getDeviceLocale();
firebase.messaging().getToken().then((token) => {
this._onChangeToken(token, language)
});
firebase.messaging().onTokenRefresh((token) => {
this._onChangeToken(token, language)
});
}
_onChangeToken = (token, language) => {
var data = {
'device_token': token,
'device_type': Platform.OS,
'device_language': language
};
this._loadDeviceInfo(data).done();
}
_loadDeviceInfo = async (deviceData) => {
// load the data in 'local storage'.
// this value will be used by login and register components.
var value = JSON.stringify(deviceData);
try {
await AsyncStorage.setItem(config.DEVICE_STORAGE_KEY, value);
} catch (error) {
console.log(error);
}
};
render() {
...
}
}
Then you can call the server with the token and all the info that you need.