How to translate in React Native app— Globalization, Internationalization, and Text to Speech
This post is all about implementing the multi-language translation and text to speech in your cool new react-native application. In this post, you will learn
- How to implement
react-native-tts
in our app. - How to use
i18n-js
in your app. - How to implement
react-native-localization
in our app.
Before start, first you will need a React-Native app to start with, hence you can follow how to create a react-native app for beginnersand start after that from here.
Complete source code of this tutorial is available here — RNInternationalizatio-TextToSpeech.
What is React-Native
React Native is a JavaScript framework for writing real, natively rendering mobile applications for iOS and Android. It’s based on React, Facebook’s JavaScript library for building user interfaces, but instead of targeting the browser, it targets mobile platforms. In other words: web developers can now write mobile applications that look and feel truly “native,” all from the comfort of a JavaScript library that we already know and love. Plus, because most of the code you write can be shared between platforms, React Native makes it easy to simultaneously develop for both Android and iOS.
Similar to React for the Web, React Native applications are written using a mixture of JavaScript and XML-Esque markup, known as JSX. Then, under the hood, the React Native “bridge” invokes the native rendering APIs in Objective-C (for iOS) or Java (for Android). Thus, your application will render using real mobile UI components, not web views, and will look and feel like any other mobile application. React Native also exposes JavaScript interfaces for platform APIs, so your React Native apps can access platform features like the phone camera, or the user’s location.
React Native currently supports both iOS and Android and has the potential to expand to future platforms as well. In this tutorial, we’ll cover translation and text to speech implementation. The vast majority of the code we write will be cross-platform. And yes: you can use React Native to build production-ready mobile applications! Some anecdota: Facebook, Palantir, and TaskRabbit are already using it in production for user-facing applications.
The fact that React Native actually renders using its host platform’s standard rendering APIs enables it to stand out from most existing methods of cross-platform application development, like Cordova or Ionic.
Existing methods of writing mobile applications using combinations of JavaScript, HTML, and CSS typically render using web views. While this approach can work, it also comes with drawbacks, especially around performance. Additionally, they do not usually have access to the host platform’s set of native UI elements. When these frameworks do try to mimic native UI elements, the results usually “feel” just a little off; reverse-engineering all the fine details of things like animations takes an enormous amount of effort, and they can quickly become out of date.
What and why Internationalization and Text to speech?
Internationalization in React Native. Internationalization (i18n for short) is the process of adapting an application to work with different languages and regions.
i18n
In computing, there’s a common term used when discussing localization & internationalization called i18n which is basically the first and last letters in the word internationalization and the 18 characters between them (thanks Wikipedia for that).
Multi-language translation OR internationalization-localization is a growing need for every app that wants to target international customers. Till date, the majority of apps have been developed in English, no surprise! In this blog post, we will learn how to implement translation, OR technically — Internationalization + Localization in Ionic 4 apps.
First, we need to understand the steps involved in the process. Translations, thanks to Google, look very easy, but they are a bit involved in the case of apps and websites, in our case Ionic 4 apps. There are 3 major steps in the translation process in an app —
- Translation Language — Detect the language you want to translate into. This can either be done automatically by detecting phone or browser’s language OR, can be done with user actions (say, by using a dropdown).
For our use case, we will detect the device’s language using react-native-localization. - Prepare Language Text — You need to have a pre-translated dictionary (or JSON file) that stores the translations of all your app’s text in the Translation language. There are several ways to do this, as we’ll see in the following steps. This is mostly a manual process for smaller apps, but you can use online tools like this for quick translations, or like POEditor for more standardized workflow.
- Translate — After the above two steps, you actually translate your app’s text to the Translation language text. We will use the i18n-js library for translating our texts.
Till now we got to know about our implementations and the basic idea of our need. Hence we will achieve the above target by following it step by step.
STEP 1: Prepare language JSON files
We will create these JSON files in android/app/src/main/assets/translations
folder. The assets
folder remains in the root even after a production builds, so the path does not break. I have created multiple JSON files en.json
(English), fr.json
(French),es.json
(Spanish),ar.json
(Arabic),it.json
(Italian),hi.json
(Hindi),ru.json
(Russian) and many more.
Folder structure for i18n files are like below
en.json
{ "hello": "Hello shadman! Welcome to localizationa nd internationalization tutorial. Hope you enjoyed the tutorial and implemented it in your app." }
The {{value}}
and {{name_value}}
are kind of variable/constants we can pass from our component. This can be used to keep a word constant across translations OR to give translations not supported by the library.
STEP 2: Implement libraries
We’ll start by adding an internationalization module to our React Native project called react-native-i18n
:
npm i react-native-i18n --save
Now install the npm package react-native-localization
npm i react-native-localization
Now install the npm package react-native-tts for text to speech
npm i react-native-tts
We will implement both internationalization and text to speech together in our app but step by step.
Here, we are working on react-native version 0.60.5. Hence we don’t need to link any package externally as in the latest versions above 0.60 react-native provides auto-linking functionality.
Add Interactive UI to Translate and text to speech
Now you are ready to use the localization and text to speech function provided by the React-Native react-native-tts and react-native-localization
package and get the result. Before this let us make a beautiful view to access these functions by clicking a button and getting the data in response.
The Result for the above code is:
As we have completed our UI for beautiful interaction, you can simply use the below function to complete translation and text to speech process.
Complete Logic of the application: —
In the above gist, I have used a function “I18n.t()” to translate our texts in different languages.
This plugin obtains information and performs operations specific to the user’s locale, language, and timezone. Note the difference between locale and language: locale controls how numbers, dates, and times are displayed for a region, while language determines what language text appears as, independently of locale settings. Often developers use locale to set both settings, but there is no reason a user couldn’t set her language to “English” but locale to “French”, so that text is displayed in English but dates, times, etc., are displayed as they are in France. Unfortunately, most mobile platforms currently do not make a distinction between these settings.
In the above example, I have called some events in the constructor to load default values like start, cancel, finish, speech rate to control these actions.
Tts.addEventListener("tts-start", event => this.setState({ ttsStatus: "started" }) ); Tts.addEventListener("tts-finish", event => this.setState({ ttsStatus: "finished" }) ); Tts.addEventListener("tts-cancel", event => this.setState({ ttsStatus: "cancelled" }) );
Tts.setDefaultRate(this.state.speechRate); Tts.setDefaultPitch(this.state.speechPitch); Tts.getInitStatus().then(this.initTts);
After this, we have called initTts function.
initTts = async () => {
const voices = await Tts.voices();
const availableVoices = voices
.filter(v => !v.networkConnectionRequired && !v.notInstalled)
.map(v => {
return { id: v.id, name: v.name, language: v.language };
});
let selectedVoice = null;
if (voices && voices.length > 0) {
selectedVoice = voices[4].id;
try {
await Tts.setDefaultLanguage(voices[0].language);
} catch (err) {
// My Samsung S9 has always this error: "Language is not supported"
console.log(`setDefaultLanguage error `, err);
}
await Tts.setDefaultVoice(voices[0].id);
this.setState({
voices: availableVoices,
selectedVoice,
ttsStatus: "initialized"
});
} else {
this.setState({ ttsStatus: "initialized" });
}
};
We have used this function to filter available languages and voices.
After executing these functions we can achieve our requirement. The code view and output of the above function in my app are as: —
In the above image, you can type any thing in the text input field and click on the Start reading Text button to listen to the text invoice.
A list of available voices is on the list which you can select for speech either for male or female.
To Install (additional) language data
Shows the Android Activity to install additional language/voice data.
Tts.requestInstallData();
There are some methods that you can use to get features according to your requirement —
Speaking
Add utterance to the TTS queue and start speaking. Returns promise with utteranceId.
Tts.speak('Hello, world!');
Additionally, speak() allows to pass platform-specific options ‘voiceId’ on IOS and ‘params’ on Android to underlying platform API:
Tts.speak('Hello, world!', { iosVoiceId: 'com.apple.ttsbundle.Moira-compact' });
Tts.speak('Hello, world!', { androidParams: { KEY_PARAM_PAN: -1, KEY_PARAM_VOLUME: 0.5, KEY_PARAM_STREAM: 'STREAM_MUSIC' } });
For more detail on androidParams
properties, please take a look at official android documentation. Please note that there are still unsupported key with this wrapper library such as KEY_PARAM_SESSION_ID
. The following are brief summarization of currently implemented keys:
KEY_PARAM_PAN
ranges from-1
to+1
.KEY_PARAM_VOLUME
ranges from0
to1
, where 0 means silence. Note that1
is a default value for Android.- For
KEY_PARAM_STREAM
the property, you can currently use one ofSTREAM_ALARM
,STREAM_DTMF
,STREAM_MUSIC
,STREAM_NOTIFICATION
,STREAM_RING
,STREAM_SYSTEM
,STREAM_VOICE_CALL
,
Stop speaking and flush the TTS queue.
Tts.stop();
Waiting for initialization
On some platforms, it could take some time to initialize the TTS engine, and Tts.speak() will fail to speak until the engine is ready.
To wait for successful initialization you could use getInitStatus() call.
Tts.getInitStatus().then(() => { Tts.speak('Hello, world!'); });
Ducking
Enable lowering other applications output level while speaking (also referred to as “ducking”).
Tts.setDucking(true);
List Voices
Returns list of available voices
(not supported on Android API Level < 21, returns empty list)
Tts.voices().then(voices => console.log(voices));
// Prints:
// [ { id: 'com.apple.ttsbundle.Moira-compact', name: 'Moira', language: 'en-IE', quality: 300 },
// ...
// { id: 'com.apple.ttsbundle.Samantha-compact', name: 'Samantha', language: 'en-US' } ]
Voice fieldDescriptionidUnique voice identifier (e.g. com.apple.ttsbundle.Moira-compact
)nameName of the voice (iOS only)languageBCP-47 language code (e.g. 'en-US')qualityVoice quality (300 = normal, 500 = enhanced/very high)latencyExpected synthesizer latency (100 = very low, 500 = very high) (Android only)networkConnectionRequiredTrue when the voice requires an active network connection (Android only)notInstalledTrue when the voice may need to download additional data to be fully functional (Android only)
Set default Language
Tts.setDefaultLanguage('en-IE');
Set default Voice
Sets default voice, pass one of the voiceId as reported by a call to Tts.voices()
(not available on Android API Level < 21)
Tts.setDefaultVoice('com.apple.ttsbundle.Moira-compact');
Set default Speech Rate
Sets default speech rate. The rate parameter is a float where 0.01 is a slowest rate and 0.99 is the fastest rate.
Tts.setDefaultRate(0.6);
There is a significant difference to how the rate value is interpreted by iOS and Android native TTS APIs. To provide unified cross-platform behavior, translation is applied to the rate value. However, if you want to turn off the translation, you can provide optional skipTransform
parameter to Tts.setDefaultRate()
to pass rate value unmodified.
Do not translate the rate parameter:
Tts.setDefaultRate(0.6, true);
Set default Pitch
Sets default pitch. The pitch parameter is a float where 1.0 is a normal pitch. On iOS min, pitch is 0.5 and max pitch is 2.0
Tts.setDefaultPitch(1.5);
Here in the tutorial, successfully implemented the plugin and got the perfect result. You can follow all the steps and achieve this very easily or you can clone my repo on Github for both the implementations here and enjoy 😎 😎 😎 🕺 🕺 🕺..…
Conclusion
In this post, we learned how to translate text in multi-language and text to speech and use it in React-Native applications. Since the packages are amazingly light and have great documentation, it is the developers’ first choice when it comes to creating translation app and text to speech.
Complete source code of this tutorial is available here — RNInternationalizatio-TextToSpeech.