Translate in Ionic 5 using apps and PWA
Tags: translate, ionic, localization, angular
Published: 2020-07-31 18:21:55

<main role="main"><article class=" u-minHeight100vhOffset65 u-overflowHidden postArticle postArticle--full is-withAccentColors" lang="en"><div class="postArticle-content js-postField js-notesSource editable" id="editor_8" g_editable="true" role="textbox" contenteditable="true" data-default-value="Title
Tell your story…"><section name="d73e" class="section section--body section--first"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><p name="26e9" class="graf graf--p graf-after--figure">In this post you’ll learn how to translate text in Ionic 5 apps and PWA. You will also learn how to get device specific language and convert your app’s text to the same language / locale.</p><p name="7ef8" class="graf graf--p graf-after--p is-selected">Ionic has a variety of app types nowadays (Angular/React/Vue&nbsp;, Cordova/Capacitor). This post will explore the Ionic apps made in Angular Cordova, but the same process can apply in Angular Capacitor apps as well.</p><h3 name="3afa" class="graf graf--h3 graf-after--p">Translation in Apps&#8202;—&#8202;how is it done&nbsp;?</h3><p name="8079" class="graf graf--p graf-after--h3">Multi-language translation, OR internationalization is a growing need for every app that wants to target international customers. Till date, majority of apps have been developed in English, no surprise&nbsp;! But with growing apps users, every app is now focusing on translating to local languages.</p><p name="85ed" class="graf graf--p graf-after--p">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 case of apps and websites, in our case Ionic 5 apps. There are 3 major steps in the translation process in an app —</p><ol class="postList"><li name="484b" class="graf graf--li graf-after--p"><strong class="markup--strong markup--li-strong"> 1. Translation Language</strong>&#8202;—&#8202;Detect the language you want to translate into. This can either be done automatically by detecting phone or browser’s language (Globalization), OR, can be done with user actions (say, by using a dropdown).<br>For our use case, we will detect device’s language using both <a href="https://github.com/apache/cordova-plugin-globalization"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener"    target="_blank">Cordova Globalization plugin</a> and Browser’s Internationalization API.</li><li name="7265" class="graf graf--li graf-after--li"><strong class="markup--strong markup--li-strong">2. Prepare Language Text</strong>&#8202;—&#8202;You need to have a pre-translated dictionary (or JSON file) which 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 following steps. This is mostly a manual process for smaller apps, but you can use online tools like <a href="https://www.logisticinfotech.com/translate-language-files-online-json/"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener"    target="_blank">this</a> for quick translations, or like <a href="https://poeditor.com/"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener"    target="_blank">POEditor</a> for more standardized workflow.</li><li name="f81b" class="graf graf--li graf-after--li"><strong class="markup--strong markup--li-strong">3. Translate</strong>&#8202;—&#8202;After the above two steps, you finally translate your app’s text to the intended language. We will use <a href="https://github.com/ngx-translate/core"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener"    target="_blank">ngx-translate</a> library for translating our texts as we are talking about only Angular Ionic apps in this post</li></ol><h3 name="26b2" class="graf graf--h3 graf-after--li">Structure of the&nbsp;post</h3><p name="69e3" class="graf graf--p graf-after--h3">So the development outline of this blog will be</p><ol class="postList"><li name="4729" class="graf graf--li graf-after--p">1. Create a starter Ionic 5 tab app</li><li name="cbfb" class="graf graf--li graf-after--li">2. Prepare multiple language JSON files in assets</li><li name="f6c5" class="graf graf--li graf-after--li">3. Implement <a href="https://github.com/ngx-translate/core"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener"    target="_blank">ngx-translate</a> library to detect and translate AND Implement <a href="https://github.com/apache/cordova-plugin-globalization"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener"    target="_blank">Cordova Globalization plugin</a> or browser Internationalization API to detect device language</li><li name="b12d" class="graf graf--li graf-after--li">4. Test translations on browser</li><li name="d1e6" class="graf graf--li graf-after--li">5. The Directive Gotcha</li><li name="6e3a" class="graf graf--li graf-after--li">6. Setup stand alone translations</li><li name="fa5b" class="graf graf--li graf-after--li">7. Test translations on Android / iOS</li></ol><p name="ed83" class="graf graf--p graf-after--li">We will translate English text in 2 languages&#8202;—&#8202;<strong class="markup--strong markup--p-strong">French </strong>and<strong class="markup--strong markup--p-strong"> Spanish</strong></p><p name="d53b" class="graf graf--p graf-after--p">Sounds pretty easy, right&nbsp;? Well, let’s dive right into it.</p><h3 name="bf1f" class="graf graf--h3 graf-after--p">Step 1— Create a basic Ionic Angular&nbsp;app</h3><p name="6470" class="graf graf--p graf-after--h3">First you need to make sure you have the latest Ionic CLI. This will ensure you are using everything latest. Ensure latest Ionic CLI installation using</p><pre name="e081" class="graf graf--pre graf-after--p">$ npm install -g ionic@latest</pre><p name="5ac8" class="graf graf--p graf-after--pre">Here’s my environment for this blogpost</p><pre name="afb5" class="graf graf--pre graf-after--p"><strong class="markup--strong markup--pre-strong">Ionic</strong>:<br>Ionic CLI                     : 6.10.1 <br>Ionic Framework               : <a href="http://twitter.com/ionic/angular"  class="markup--anchor markup--pre-anchor" title="Twitter profile for @ionic/angular" rel="noopener"    target="_blank">@ionic/angular</a> 5.3.1<br><a href="http://twitter.com/angular"  class="markup--anchor markup--pre-anchor" title="Twitter profile for @angular" rel="noopener"    target="_blank">@angular</a>-devkit/build-angular : 0.901.12<br><a href="http://twitter.com/angular"  class="markup--anchor markup--pre-anchor" title="Twitter profile for @angular" rel="noopener"    target="_blank">@angular</a>-devkit/schematics    : 9.1.12<br><a href="http://twitter.com/angular/cli"  class="markup--anchor markup--pre-anchor" title="Twitter profile for @angular/cli" rel="noopener"    target="_blank">@angular/cli</a>                  : 9.1.12<br><a href="http://twitter.com/ionic/angular-toolkit"  class="markup--anchor markup--pre-anchor" title="Twitter profile for @ionic/angular-toolkit" rel="noopener"    target="_blank">@ionic/angular-toolkit</a>        : 2.3.0</pre><pre name="2860" class="graf graf--pre graf-after--pre"><strong class="markup--strong markup--pre-strong">Cordova</strong>:<br>Cordova CLI       : 9.0.0 (cordova-lib@9.0.1)</pre><pre name="f754" class="graf graf--pre graf-after--pre"><strong class="markup--strong markup--pre-strong">System</strong>:<br>Android SDK Tools : 26.1.1 <br>NodeJS            : v12.14.0<br>npm               : 6.13.4<br>OS                : macOS Catalina<br>Xcode             : Xcode 11.5 Build version 11E608c</pre><p name="2a7b" class="graf graf--p graf-after--pre">Creating a basic Ionic-Angular app. Start a basic <code class="markup--code markup--p-code">tabs</code> starter using</p><pre name="250d" class="graf graf--pre graf-after--p">$ ionic start ionicTranslate tabs --type=angular --cordova</pre><p name="d16b" class="graf graf--p graf-after--pre">The <code class="markup--code markup--p-code">--type=angular</code> told the CLI to create an <strong class="markup--strong markup--p-strong">Angular</strong> app, and <code class="markup--code markup--p-code">--cordova</code> tells the CLI to integrate Cordova in the app.</p><p name="06f2" class="graf graf--p graf-after--p">Run the app in browser using</p><pre name="2d3b" class="graf graf--pre graf-after--p">$ ionic serve</pre><p name="726e" class="graf graf--p graf-after--pre">You won’t see much in the homepage created in the starter. I have modified pages ( tab1 and tab2) to align it to our translation functionality.</p><p name="579a" class="graf graf--p graf-after--p">My tab pages look like this</p><figure tabindex="0" contenteditable="false" name="3862" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 319px; max-height: 679px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 212.9%;"></div><img class="graf-image" data-image-id="1*SHE6-PZheRgQkD6tuj9NgQ.png" data-width="319" data-height="679" src="https://cdn-images-1.medium.com/max/800/1*SHE6-PZheRgQkD6tuj9NgQ.png"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Tab UI for translation</figcaption></figure><p name="1fff" class="graf graf--p graf-after--figure">HTML and SCSS file code for the above UI, if you want to just get started</p><figure tabindex="0" contenteditable="false" name="9fe3" class="graf graf--figure graf--iframe graf-after--p is-defaultValue"><div class="aspectRatioPlaceholder is-locked"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 311.714%;"></div><div class="iframeContainer">{% gist https://gist.github.com/enappd/d7c286e408a6df3fa43e4e4201cab078 %}</div></div></figure><h3 name="c449" class="graf graf--h3 graf-after--figure">Step 2&#8202;—&#8202;Prepare multiple language JSON files in&nbsp;assets</h3><p name="c6f2" class="graf graf--p graf-after--h3">We will create these JSON files in <code class="markup--code markup--p-code">src/assets/i18n</code> folder. The <code class="markup--code markup--p-code">assets</code> folder remains in the root even after a production build, so the path does not break. We will create three JSON files <code class="markup--code markup--p-code">en.json</code> (English), <code class="markup--code markup--p-code">fr.json</code> (French) and <code class="markup--code markup--p-code">es.json</code> (Spanish).</p><h4 name="8d12" class="graf graf--h4 graf-after--p">Folder structure for i18n&nbsp;files</h4><p name="f38b" class="graf graf--p graf-after--h4"><code class="markup--code markup--p-code"><strong class="markup--strong markup--p-strong">en.json</strong></code></p>
```
{
"TITLE": "Hello sir",
"TITLE_2": "Hello {{value}}",
"description": "Ooohh .... did you just translate this text ?",
"data": {
  "name": "My name is {{name_value}}"}
}
```
<p name="720b" class="graf graf--p graf-after--pre"><code class="markup--code markup--p-code"><strong class="markup--strong markup--p-strong">fr.json</strong></code></p>
```
{
"TITLE": "Bonjour Monsieur",
"TITLE_2": "Bonjour {{value}}",
"description": "Ooohh .... vous venez de traduire ce texte?",
"data" :{
  "name": "je m'appelle {{name_value}}"}
}
```
<p name="5fc5" class="graf graf--p graf-after--pre"><code class="markup--code markup--p-code"><strong class="markup--strong markup--p-strong">es.json</strong></code></p>
```
{
"TITLE": "Hola señor",
"TITLE_2": "Hola {{value}}",
"description": "Ooohh .... acabas de traducir este texto?",
"data": {
  "name": "Me llamo {{name_value}}"}
}
```
<p name="c188" class="graf graf--p graf-after--pre"><strong class="markup--strong markup--p-strong">Note</strong>, the <code class="markup--code markup--p-code">value</code> and <code class="markup--code markup--p-code">name_value</code> are kind of variable/constants we can pass from our component. This can be used to</p><ul class="postList"><li name="4cf0" class="graf graf--li graf-after--p">Replace the variable with a user input or a value depending on the situation OR</li><li name="b9a1" class="graf graf--li graf-after--li">To give translations not supported by the library OR</li><li name="3d85" class="graf graf--li graf-after--li">Keep a word constant across translations</li></ul><h3 name="d9d0" class="graf graf--h3 graf-after--li">STEP 3: Implement ngx-translate library and Cordova Globalization plugin</h3><p name="4de2" class="graf graf--p graf-after--h3">Cordova globalization plugin is used to detect device’s default language/locale. Unfortunately, this plugin is deprecated, but it is still supported by Ionic. Hence, you can opt to use it. However, the latest way of detecting the language / locale of the browser is by using browsers’s <a href="https://cordova.apache.org/news/2017/11/20/migrate-from-cordova-globalization-plugin.html"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">default Internationalization API.</a></p><p name="8ac2" class="graf graf--p graf-after--p">Install Cordova globalization Plugin using</p><pre name="c70c" class="graf graf--pre graf-after--p">$ ionic cordova plugin add cordova-plugin-globalization</pre><pre name="577a" class="graf graf--pre graf-after--pre">$ npm install @ionic-native/globalization</pre><h4 name="8dbb" class="graf graf--h4 graf-after--pre">Install <strong class="markup--strong markup--h4-strong">ngx-translate </strong>library</h4><p name="12be" class="graf graf--p graf-after--h4"><strong class="markup--strong markup--p-strong">ngx-translate</strong> is the internationalization (i18n) library for Angular. Since our app has Angular under the hood, we can use this library for app as well as progressive web apps.</p><pre name="5c84" class="graf graf--pre graf-after--p">// Install core library<br>npm install --save @ngx-translate/core</pre><pre name="10be" class="graf graf--pre graf-after--pre">// Install http loader<br>npm install @ngx-translate/http-loader --save</pre><p name="e6d9" class="graf graf--p graf-after--pre"><strong class="markup--strong markup--p-strong">http-loader </strong>is used for loading the translation files (JSONs in our case) via Angular’s HttpClient module.</p><p name="4e5e" class="graf graf--p graf-after--p">Note the versions of ngx-translate you should have as per your Angular version</p><figure tabindex="0" contenteditable="false" name="ef05" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 500px; max-height: 119px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 23.799999999999997%;"></div><img class="graf-image" data-image-id="1*AesnbxaXR-zE-qXYC4G4YQ.png" data-width="500" data-height="119" src="https://cdn-images-1.medium.com/max/800/1*AesnbxaXR-zE-qXYC4G4YQ.png"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Use appropriate version of ngx-translate based on your Angular&nbsp;version</figcaption></figure><h4 name="2b8f" class="graf graf--h4 graf-after--figure">Setup the translation library and http-loader</h4><p name="f4dd" class="graf graf--p graf-after--h4">We need to define a function that loads the external JSON files to the app using http-loader. Add the following function to <code class="markup--code markup--p-code">app.module.ts</code></p><pre name="5be0" class="graf graf--pre graf-after--p">export function HttpLoaderFactory(http: HttpClient) {<br>  return new TranslateHttpLoader(http, "./assets/i18n/", ".json");<br>}</pre><p name="fff4" class="graf graf--p graf-after--pre">where we have provided the external path of our JSON files to the function.</p><p name="e0b7" class="graf graf--p graf-after--p">We need to add the <strong class="markup--strong markup--p-strong">translation</strong> and <strong class="markup--strong markup--p-strong">http-loader </strong>modules in our root module <code class="markup--code markup--p-code">app.module.ts</code>. This is how the file looks after setup.</p><figure tabindex="0" contenteditable="false" name="0796" class="graf graf--figure graf--iframe graf-after--p is-defaultValue"><div class="aspectRatioPlaceholder is-locked"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 132.714%;"></div><div class="iframeContainer">{% gist https://gist.github.com/enappd/42a2a7f0e6f2346a91662023b261dac8 %}</div></div></figure><p name="5d13" class="graf graf--p graf-after--figure">Pay attention to <code class="markup--code markup--p-code">TranslateModule.forRoot()</code> This is used in case of a Tabbed application, or general non lazy-loaded module. For a tab child, however, we will have to use <code class="markup--code markup--p-code">TranslateModule.forChild()</code>&nbsp;. We will see later how this will affect our functionality.</p><h4 name="8871" class="graf graf--h4 graf-after--p">Setup the translate library in child component</h4><p name="ac60" class="graf graf--p graf-after--h4">Let’s say, we want to implement the translation in <strong class="markup--strong markup--p-strong">Tab1. </strong>As mentioned previously, <code class="markup--code markup--p-code">src/app/tab1</code> folder contains all the files for this page. We will import the <code class="markup--code markup--p-code">translationService</code> in <code class="markup--code markup--p-code">tab1.page.ts</code> file using</p><pre name="6e0f" class="graf graf--pre graf-after--p">import { TranslateService } from '@ngx-translate/core';</pre><p name="6c08" class="graf graf--p graf-after--pre">The completed <code class="markup--code markup--p-code">tab1.page.ts</code> file will look like this</p><figure tabindex="0" contenteditable="false" name="2f01" class="graf graf--figure graf--iframe graf-after--p is-defaultValue"><div class="aspectRatioPlaceholder is-locked"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 233.286%;"></div><div class="iframeContainer">{% gist https://gist.github.com/enappd/cc7cd552dd5dd2be8fda8e31541d99e2 %}</div></div></figure><p name="d02e" class="graf graf--p graf-after--figure">Let’s break down the code to understand a bit more</p><ul class="postList"><li name="bf3f" class="graf graf--li graf-after--p">On page load we check if we have default browser internationalization API by checking <code class="markup--code markup--li-code">window.Intl</code>&nbsp;. We then get default browser language using <code class="markup--code markup--li-code">navigator.language</code>&nbsp;. We also set a fall back on Cordova globalization plugin (deprecated) and set a default language if neither browser nor Cordova plugin works.</li><li name="dc37" class="graf graf--li graf-after--li">Then we use <code class="markup--code markup--li-code">this._translate.use(this.language)</code> to tell the translation service which language to translate to. If the app has just loaded, it should default to your browser’s default language OR <code class="markup--code markup--li-code">en</code> (english)</li><li name="13ac" class="graf graf--li graf-after--li"><strong class="markup--strong markup--li-strong">Important: </strong>Use<code class="markup--code markup--li-code">this._translate.get('TITLE').subscribe()</code> function from <code class="markup--code markup--li-code">translateService</code> to asynchronously fetch translations. Here,<br>- <code class="markup--code markup--li-code">get()</code> is the function to fetch translations.<br>- <code class="markup--code markup--li-code">TITLE</code> is the key to search for in the JSON files. If the data is in a nested JSON, then we use the dot notation to fetch e.g. <code class="markup--code markup--li-code">data.name</code><br>- <code class="markup--code markup--li-code">subscribe</code> is used for asynchronous fetching (no need to use timeouts)</li><li name="b5cf" class="graf graf--li graf-after--li"><code class="markup--code markup--li-code">changeLanguage</code> is called from user action. This function will use the previous two steps to translate to the intended language</li></ul><h3 name="43c8" class="graf graf--h3 graf-after--li">Step 4— Test translations on&nbsp;browser</h3><p name="ef23" class="graf graf--p graf-after--h3">Run the app on browser using <code class="markup--code markup--p-code">ionic serve</code> and test the translations using the dropdown. Here’s a GIF to show the functionality on browser</p><figure tabindex="0" contenteditable="false" name="7e50" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 314px; max-height: 658px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 209.60000000000002%;"></div><img class="graf-image" data-image-id="1*GuMnfaPGW82fSOfmo8TfDg.gif" data-width="314" data-height="658" alt="Translate in Ionic 5 using ngx-translate" src="https://cdn-images-1.medium.com/max/800/1*GuMnfaPGW82fSOfmo8TfDg.gif"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Translate in Ionic 5 using ngx-translate</figcaption></figure><p name="0f0f" class="graf graf--p graf-after--figure">Notice that the text loads in English by default in my browser. You can change your browser language from browser settings and check if the initial text loads in your default language. For chrome, the settings can be found here</p><figure tabindex="0" contenteditable="false" name="1c51" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 386px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 55.2%;"></div><img class="graf-image" data-image-id="1*h_vLfApAfs38WU0Wn_MMrg.png" data-width="1385" data-height="764" alt="Change your browser language to load app text in default language" src="https://cdn-images-1.medium.com/max/800/1*h_vLfApAfs38WU0Wn_MMrg.png"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Change your browser language to load app text in default&nbsp;language</figcaption></figure><p name="f4c1" class="graf graf--p graf-after--figure">And now my text loads default in French&nbsp;…. Bonjour&nbsp;!!</p><h3 name="98f4" class="graf graf--h3 graf-after--p">Step 5&#8202;—&#8202;The Directive GOTCHA&nbsp;😎</h3><p name="1ab1" class="graf graf--p graf-after--h3">If you have followed the above steps exactly, you might not get the exact result as shown in the above GIF.</p><p name="e664" class="graf graf--p graf-after--p">You will realize that the <strong class="markup--strong markup--p-strong">translation works</strong> in places where you have used</p><pre name="7e16" class="graf graf--pre graf-after--p">this._translate.get('TITLE').subscribe((res: string) =&gt; {           <br>this.title = res;<br>});<br>this._translate.get('description').subscribe((res: string) =&gt; {           <br>this.description = res;<br>});</pre><p name="d840" class="graf graf--p graf-after--pre">to get the translation, and shown it in the HTML using the local variable like this</p>
```
<h1>{{ title }}</h1><p>{{ description }}</p>
```
<p name="bf9b" class="graf graf--p graf-after--pre">BUT, the <strong class="markup--strong markup--p-strong">translation does not work</strong> in places where you have used a <code class="markup--code markup--p-code">directive</code> like either of the following</p>
```
<h1 translate>TITLE</h1><p [translate]="'description'"></p>
```
<p name="b6ed" class="graf graf--p graf-after--pre">This is because in our Ionic 5 tab app, the tab pages are lazy-loaded. In lazy-loaded modules, you need to import the translation module in child modules as well for everything to work correctly.</p><p name="193f" class="graf graf--p graf-after--p">Let’s go to our <code class="markup--code markup--p-code">tab1.module.ts</code> file, and import the translation and http-loader modules similar to our root module. But this time, we will use <code class="markup--code markup--p-code">TranslateModule.forChild</code>&nbsp;. The complete module file looks like the following</p><figure tabindex="0" contenteditable="false" name="7d43" class="graf graf--figure graf--iframe graf-after--p is-defaultValue"><div class="aspectRatioPlaceholder is-locked"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 113.857%;"></div><div class="iframeContainer">{% gist https://gist.github.com/enappd/35d52b4e3c6bf4df8b3a0916c5bd5237 %}</div></div></figure><p name="e603" class="graf graf--p graf-after--figure">Now, if you run the app again, the <code class="markup--code markup--p-code">directive</code> translations will also work fine. 😎 😎 😎</p><blockquote name="950a" class="graf graf--blockquote graf-after--p"><code class="markup--code markup--blockquote-code"><em class="markup--em markup--blockquote-em">directive</em></code><em class="markup--em markup--blockquote-em"> method is preferred for bigger apps with lots of code, since this method results in smaller code size and no need of local variables.</em></blockquote><h3 name="3ad7" class="graf graf--h3 graf-after--blockquote">Step 6— Setup stand alone translations</h3><p name="8ad0" class="graf graf--p graf-after--h3">The process of setting up separate language file in <code class="markup--code markup--p-code">assets</code> for each language is the standard way of translation in Angular. But sometimes it becomes a little cumbersome, especially when you don’t have that much data to translate.</p><p name="0679" class="graf graf--p graf-after--p">In case you want to quickly translate in the component itself, so that there is no spill over on other components, you can declare the variables in the components itself instead of reading them from the JSON files from <code class="markup--code markup--p-code">assets</code></p><p name="db43" class="graf graf--p graf-after--p">Let’s do these changes in <strong class="markup--strong markup--p-strong">tab2, </strong>so it doesn’t affect the global translations in <strong class="markup--strong markup--p-strong">tab1</strong></p><h4 name="032c" class="graf graf--h4 graf-after--p">HTML and&nbsp;SCSS</h4><p name="ff33" class="graf graf--p graf-after--h4">Similar to <code class="markup--code markup--p-code">tab1.page.html</code>&nbsp;, just remove the usage of variable <code class="markup--code markup--p-code">data</code> from the HTML. You can keep the styling same</p><h4 name="9400" class="graf graf--h4 graf-after--p">tab2.page.ts</h4><p name="0322" class="graf graf--p graf-after--h4">Stays pretty much same as <code class="markup--code markup--p-code">tab1.page.ts</code>&nbsp;. Just add the following in the constructor</p>
```
_translate.setTranslation('en', {
 "TITLE": "Bye sir",
 "TITLE_2": "Bye {{value}}",
 "description": "Thanks for translating this text"
});
_translate.setTranslation('fr', {
 "TITLE": "Au revoir Monsieur",
 "TITLE_2": "Au revoir  {{value}}",
 "description": "Merci d'avoir traduit ce texte"
});
_translate.setTranslation('es', {
 "TITLE": "Adiós señor",
 "TITLE_2": "Adiós {{value}}",
 "description": "Gracias por traducir este texto"
});
```
<p name="cc44" class="graf graf--p graf-after--pre">Here, you are defining the translations locally in your component itself. Also, to let angular know that these are standalone translations, you use <code class="markup--code markup--p-code">isolate:true</code> in your <code class="markup--code markup--p-code">tab2.module.ts</code></p><pre name="5847" class="graf graf--pre graf-after--p">....<br>TranslateModule.forChild({<br> loader: {<br>  provide: TranslateLoader,<br>  useFactory: HttpLoaderFactory,<br>  deps: [HttpClient]<br> },<br> <strong class="markup--strong markup--pre-strong">isolate: true</strong><br>}),<br>....</pre><p name="c5ba" class="graf graf--p graf-after--pre">Now run the app in browser and test translations. Your translations will be picked from the local component itself</p><figure tabindex="0" contenteditable="false" name="bf31" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 314px; max-height: 660px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 210.2%;"></div><img class="graf-image" data-image-id="1*tX8q68qoJg9nzrPaJjdYbA.gif" data-width="314" data-height="660" alt="Translation in Ionic 5 apps using component level translations" src="https://cdn-images-1.medium.com/max/800/1*tX8q68qoJg9nzrPaJjdYbA.gif"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Translation in Ionic 5 apps using component level translations</figcaption></figure><p name="334d" class="graf graf--p graf-after--figure">Notice, the <code class="markup--code markup--p-code">directive</code> method and <code class="markup--code markup--p-code">variable</code> also work well with the local definitions.</p><h3 name="f04e" class="graf graf--h3 graf-after--p">Step 7&#8202;—&#8202;Test translations in iOS/Android</h3><p name="411b" class="graf graf--p graf-after--h3">To build the app on android, run the following commands one after another</p><pre name="589e" class="graf graf--pre graf-after--p">$ ionic cordova platform add android</pre><pre name="bdf5" class="graf graf--pre graf-after--pre">$ ionic cordova run android</pre><p name="8761" class="graf graf--p graf-after--pre">The final command will run the app on either default emulator, or an android device attached to your system. Here’s the translation running on my android device</p><figure tabindex="0" contenteditable="false" name="3bd5" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 337px; max-height: 600px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 178%;"></div><img class="graf-image" data-image-id="1*N4bM3_7dTU5VQEhmsmo7-w.gif" data-width="337" data-height="600" alt="Ionic 5 Angular app&#8202;—&#8202;Translation and Globalization" src="https://cdn-images-1.medium.com/max/800/1*N4bM3_7dTU5VQEhmsmo7-w.gif"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Ionic 5 Angular app&#8202;—&#8202;Translation and Globalization</figcaption></figure><p name="4467" class="graf graf--p graf-after--figure"><strong class="markup--strong markup--p-strong">Interesting fact: </strong>In device, the language id may be different from browser. For me<br>- Device default language comes out to be <code class="markup--code markup--p-code">en-US</code><br>- Browser’s default language comes out to be <code class="markup--code markup--p-code">en</code></p><p name="15b0" class="graf graf--p graf-after--p">Hence, you need to be careful to detect all variations of a language. Accordingly, you’ll need to have JSON file named accordingly.</p><h3 name="3a8d" class="graf graf--h3 graf-after--p">Conclusion</h3><p name="35bc" class="graf graf--p graf-after--h3">In this post we learnt how to detect device / browser language, create multiple language files, and implement translation using different methods in Ionic 5 apps.</p><p name="65ba" class="graf graf--p graf-after--p graf--trailing">The only limitation of using the <strong class="markup--strong markup--p-strong">ngx-translate </strong>library is that you will need to define your language texts on each page of your application beforehand. These will be stored as country code titled JSON files (i.e. en.json, jp.json, it.json etc) within the <strong class="markup--strong markup--p-strong">src/assets/i18n/</strong> directory. You can’t dynamically generate the language translation on the fly using this library so if you require that type of functionality you’ll need to look into using the Google Translate API or something similar.</p></div></div></section><section name="fc29" class="section section--body"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><h3 name="da5e" class="graf graf--h3 graf--leading">Next Steps</h3><p name="ca6a" class="graf graf--p graf-after--h3">If you liked this blog, you will also find the following Ionic blogs interesting and helpful. Feel free to ask any questions in the comment section</p><ul class="postList"><li name="1a12" class="graf graf--li graf-after--p">Ionic Payment Gateways&#8202;—&#8202;<a href="https://enappd.com/blog/ionic-4-stripe-payment-integration-with-firebase-for-apps-and-pwa/17"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Stripe</a> | <a href="https://enappd.com/blog/ionic-4-paypal-payment-integration-for-apps-and-pwa/16"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">PayPal</a> | <a href="https://enappd.com/blog/how-to-integrate-apple-pay-in-ionic-4-apps/21"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Apple Pay</a> | <a href="https://enappd.com/blog/how-to-integrate-razorpay-in-ionic-4-apps-and-pwa/20"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">RazorPay</a></li><li name="9210" class="graf graf--li graf-after--li">Ionic Charts with&#8202;—&#8202;<a href="https://enappd.com/blog/ionic-4-charts-using-google-charts/66"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Google Charts </a>| <a href="https://enappd.com/blog/adding-charts-in-ionic-4-apps-and-pwa-part-3/60"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">HighCharts</a> | <a href="https://enappd.com/blog/adding-charts-in-ionic-4-apps-and-pwa-part-2/54"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">d3.js</a> | <a href="https://enappd.com/blog/charts-in-ionic-4-apps-and-pwa-part-1/52"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Chart.js</a></li><li name="e50b" class="graf graf--li graf-after--li">Ionic Social Logins&#8202;—&#8202;<a href="https://enappd.com/blog/facebook-login-in-ionic-4-apps-using-firebase/25"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Facebook</a> | <a href="https://enappd.com/blog/google-login-in-ionic-4-apps-using-firebase/39"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Google</a> | <a href="https://enappd.com/blog/twitter-login-in-ionic-4-apps-using-firebase/24"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Twitter</a></li><li name="ce2c" class="graf graf--li graf-after--li">Ionic Authentications&#8202;—&#8202;<a href="https://enappd.com/blog/email-authentication-with-firebase-in-ionic-4/38"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Via Email</a> | <a href="https://enappd.com/blog/firebase-anonymous-login-in-ionic-4/37"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Anonymous</a></li><li name="7bd1" class="graf graf--li graf-after--li">Ionic Features&#8202;—&#8202;<a href="https://enappd.com/blog/using-geolocation-geocoding-and-reverse-geocoding-in-ionic-4/45"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Geolocation</a> | <a href="https://enappd.com/blog/qr-code-scanning-and-optical-character-recognition-ocr-in-ionic-4/9"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">QR Code reader </a>| <a href="https://enappd.com/blog/best-fitness-plugins-for-ionic-4-how-to-use-pedometer/15"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Pedometer</a></li><li name="bba3" class="graf graf--li graf-after--li">Media in Ionic&#8202;—&#8202;<a href="https://enappd.com/blog/spotify-like-music-in-ionic-4-apps/48"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Audio</a> | <a href="https://enappd.com/blog/adding-video-player-in-ionic-4-app/64"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Video</a> | <a href="https://enappd.com/blog/image-picker-in-ionic-4-app/67"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Image Picker</a> | <a href="https://enappd.com/blog/how-to-add-image-cropper-in-ionic-4-app/68"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Image Cropper</a></li><li name="ca53" class="graf graf--li graf-after--li">Ionic Essentials&#8202;—&#8202;<a href="https://enappd.com/blog/ionic-4-save-and-retrieve-data-locally-on-device/59"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Native Storage</a> | <a href="https://enappd.com/blog/how-to-translate-in-ionic-4-globalization-internationalization-and-localization/11"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Translations</a> | <a href="https://enappd.com/blog/rtl-right-to-left-use-in-ionic-4/50"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">RTL</a></li><li name="b3a4" class="graf graf--li graf-after--li">Ionic messaging&#8202;—&#8202;<a href="https://enappd.com/blog/implement-ionic-4-firebase-push/34"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Firebase Push</a> | <a href="https://enappd.com/blog/automatically-read-sms-in-ionic-4-apps/42"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Reading SMS</a></li><li name="1cc4" class="graf graf--li graf-after--li graf--trailing">Ionic with Firebase&#8202;—&#8202;<a href="https://enappd.com/blog/how-to-integrate-firebase-in-ionic-4-apps/23"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Basics</a> | <a href="https://enappd.com/blog/firebase-with-ionic-4-hosting-auth-and-db-connection/58"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Hosting and DB</a> | <a href="https://enappd.com/blog/firebase-cloud-functions-in-ionic-4-complete-guide/32"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener noopener noopener noopener noopener"    target="_blank">Cloud functions</a></li></ul></div></div></section><section name="b3b8" class="section section--body section--last"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><h3 name="adc5" class="graf graf--h3 graf--leading">Ionic React Full App with Capacitor</h3><p name="4fa5" class="graf graf--p graf-after--h3">If you need a base to start your next Ionic 5 React Capacitor app, you can make your next awesome app using <a href="https://store.enappd.com/product/ionic-react-full-app-capacitor/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Ionic 5 React Full App in Capacitor</a></p><figure tabindex="0" contenteditable="false" name="0d2d" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 441px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 63%;"></div><img class="graf-image" data-image-id="1*G3yOFVX4oABx9MhUy-3kng.png" data-width="760" data-height="479" alt="Ionic 5 React Full App in Capacitor from Enappd" src="https://cdn-images-1.medium.com/max/800/1*G3yOFVX4oABx9MhUy-3kng.png"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Ionic 5 React Full App in Capacitor from&nbsp;Enappd</figcaption></figure><h3 name="30ba" class="graf graf--h3 graf-after--figure">Ionic Capacitor Full App (Angular)</h3><p name="9a60" class="graf graf--p graf-after--h3">If you need a base to start your next Angular <strong class="markup--strong markup--p-strong">Capacitor app</strong>, you can make your next awesome app using <a href="https://store.enappd.com/product/capacitor-full-app-with-ionic-angular/"  class="markup--anchor markup--p-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Capacitor Full App</a></p><figure tabindex="0" contenteditable="false" name="c219" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 761px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 108.7%;"></div><img class="graf-image" data-image-id="0*UwSHcofTMStMnzAX.png" data-width="736" data-height="800" alt="Capacitor Full App with huge number of layouts and features" src="https://cdn-images-1.medium.com/max/800/0*UwSHcofTMStMnzAX.png"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Capacitor Full App with huge number of layouts and&nbsp;features</figcaption></figure><h3 name="2f1c" class="graf graf--h3 graf-after--figure">Ionic Full App (Angular and&nbsp;Cordova)</h3><p name="1008" class="graf graf--p graf-after--h3">If you need a base to start your next Ionic 5 app, you can make your next awesome app using <a href="https://store.enappd.com/product/ionic-full-app"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Ionic 5 Full App</a></p><figure tabindex="0" contenteditable="false" name="16d6" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 442px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 63.1%;"></div><img class="graf-image" data-image-id="0*1ArpKyhiDHZnevwB.jpeg" data-width="700" data-height="442" alt="Ionic Full App with huge number of layouts and features" src="https://cdn-images-1.medium.com/max/800/0*1ArpKyhiDHZnevwB.jpeg"><div class="crosshair u-ignoreBlock"></div></div><br/><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Ionic Full App in Cordova, with huge number of layouts and&nbsp;features</figcaption></figure><p name="c117" class="graf graf--p graf--empty graf-after--figure graf--trailing"><br></p></div></div></section></div></article></main>