How to translate in Ionic 4 - Globalization, Internationalization and Localization
Tags: ionic, translation, globalization, internationalization
Published: 2019-09-18 17:40:13

<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_6" g_editable="true" role="textbox" contenteditable="true" data-default-value="Title
Tell your story…" spellcheck="false"><section name="cf92" 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"><blockquote name="8f14" class="graf graf--blockquote graf-after--figure">Complete code of this blog is available at <a href="https://github.com/enappd/ionic4-i18n"  class="markup--anchor markup--blockquote-anchor" rel="nofollow noopener" target="_blank">https://github.com/enappd/ionic4-i18n</a></blockquote><p name="88ed" class="graf graf--p graf-after--blockquote is-selected">Multi-language translation, OR internationalization-localization 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;! In this blog post we will learn how to implement translation, OR technically&#8202;—&#8202;Internationalization + Localization in Ionic 4 apps.</p><p name="6150" 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 4 apps. There are 3 major steps in the translation process in an app —</p><ol class="postList"><li name="b895" class="graf graf--li graf-after--p"><strong class="markup--strong markup--li-strong">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, OR, can be done with user actions (say, by using a dropdown).<br>For our use case, we will detect device’s language using <a href="https://github.com/apache/cordova-plugin-globalization"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Cordova Globalization plugin</a>.</li><li name="b140" class="graf graf--li graf-after--li"><strong class="markup--strong markup--li-strong">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"    target="_blank">this</a> for quick translations, or like <a href="https://poeditor.com/"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">POEditor</a> for more standardized workflow.</li><li name="7429" class="graf graf--li graf-after--li"><strong class="markup--strong markup--li-strong">Translate</strong>&#8202;—&#8202;After the above two steps, you actually translate your app’s text to the Translation language text. We will use <a href="https://github.com/ngx-translate/core"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">ngx-translate</a> library for translating our texts.</li></ol><h3 name="289c" class="graf graf--h3 graf-after--li">Development Pre-requisites</h3><p name="e218" class="graf graf--p graf-after--h3">Following is my development environment info, just in case you want to match. Out of these having <strong class="markup--strong markup--p-strong">Ionic 4.x</strong> is essential. <strong class="markup--strong markup--p-strong">Node v10.x</strong> would be good to have as well.</p><pre name="d2cd" class="graf graf--pre graf-after--p"><strong class="markup--strong markup--pre-strong">Ionic:</strong></pre><pre name="1cd7" class="graf graf--pre graf-after--pre">ionic (Ionic CLI)             : 4.11.0 <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> 4.1.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.13.5<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    : 7.2.4<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>                  : 7.3.5<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>        : 1.4.0</pre><pre name="a5e8" class="graf graf--pre graf-after--pre"><strong class="markup--strong markup--pre-strong">Cordova:</strong></pre><pre name="647d" class="graf graf--pre graf-after--pre">cordova (Cordova CLI) : 8.0.0<br>Cordova Platforms     : ios 4.5.5</pre><pre name="9013" class="graf graf--pre graf-after--pre"><strong class="markup--strong markup--pre-strong">System:</strong></pre><pre name="77b1" class="graf graf--pre graf-after--pre">ios-deploy : 1.9.2<br>ios-sim    : 5.0.13<br>NodeJS     : v10.0.0 (/usr/local/bin/node)<br>npm        : 5.6.0<br>OS         : macOS Mojave<br>Xcode      : Xcode 10.1 Build version 10B61</pre><p name="bd36" class="graf graf--p graf-after--pre">Also, for running the app on iOS device, you need to have correct version of Xcode. My current version is <strong class="markup--strong markup--p-strong">Xcode 10.1</strong>, when iOS is v12.x. For android device, you need the correct android SDK and libraries installed, along with a device (or simulator).</p><h3 name="b603" class="graf graf--h3 graf-after--p">Outline</h3><p name="7e81" class="graf graf--p graf-after--h3">So the development outline of this blog will be</p><ol class="postList"><li name="3427" class="graf graf--li graf-after--p">Create a starter Ionic 4 tab app (I’ll tell later why I chose tab app)</li><li name="ba2a" class="graf graf--li graf-after--li">Prepare multiple language JSON files</li><li name="efcf" class="graf graf--li graf-after--li">Implement <a href="https://github.com/ngx-translate/core"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">ngx-translate</a> library to detect and translate</li><li name="e3be" class="graf graf--li graf-after--li">Implement a basic page layout to show translated text in browser</li><li name="5829" class="graf graf--li graf-after--li">Implement <a href="https://github.com/apache/cordova-plugin-globalization"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Cordova Globalization plugin</a> to detect device language.</li><li name="dcc2" class="graf graf--li graf-after--li">Run the app on iOS simulator to translate by phone’s default language</li></ol><p name="58d2" class="graf graf--p graf-after--li">We will translate text in 2 other 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="912e" class="graf graf--p graf-after--p">Sounds pretty easy, right&nbsp;? Well, let’s dive right into it.</p><p name="4083" class="graf graf--p graf-after--p">That did not&nbsp;…. translate very well 😛</p><h3 name="113b" class="graf graf--h3 graf-after--p">STEP 1&nbsp;: Create Ionic 4&nbsp;app</h3><p name="7577" class="graf graf--p graf-after--h3">This section is mostly for beginners. Once you have Ionic 4 installed in your system, you can create a basic <strong class="markup--strong markup--p-strong">Tab</strong> app by running</p><pre name="b3e9" class="graf graf--pre graf-after--p">ionic start yourAppName tabs</pre><pre name="1259" class="graf graf--pre graf-after--pre">// Change to app folder<br>cd yourAppName</pre><pre name="1f50" class="graf graf--pre graf-after--pre">// Start the app in browser<br>ionic serve</pre><p name="f212" class="graf graf--p graf-after--pre">This will give you the pretty app view in the browser. Cool, right?</p><p name="08e5" class="graf graf--p graf-after--p">Ionic serve on browser</p><p name="faff" class="graf graf--p graf-after--p">Also get familiar with the Folder structure. <code class="markup--code markup--p-code">src/app/tab1</code> and<code class="markup--code markup--p-code">src/app/tab2</code> are of our interest for further development. These are the folders for Tab 1 and Tab 2 respectively.</p><h3 name="60e5" class="graf graf--h3 graf-after--p">STEP 2: Prepare language JSON&nbsp;files</h3><p name="0ec0" 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><p name="f909" class="graf graf--p graf-after--p">Folder structure for i18n files</p><p name="877e" class="graf graf--p graf-after--p"><code class="markup--code markup--p-code"><strong class="markup--strong markup--p-strong">en.json</strong></code></p><pre name="be7b" class="graf graf--pre graf-after--p">{<br>"TITLE": "Hello sir",</pre><pre name="6425" class="graf graf--pre graf-after--pre">"TITLE_2": "Hello `{{value}}`",</pre><pre name="984c" class="graf graf--pre graf-after--pre">"description": "Ooohh .... did you just translate this text ?",</pre><pre name="b630" class="graf graf--pre graf-after--pre">"data": {"name": "My name is `{{name_value}}`"}<br>}</pre><p name="4b3b" 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><pre name="934c" class="graf graf--pre graf-after--p">{<br>"TITLE"         : "Bonjour Monsieur",</pre><pre name="defc" class="graf graf--pre graf-after--pre">"TITLE_2"       : "Bonjour `{{value}}`",</pre><pre name="e6ec" class="graf graf--pre graf-after--pre">"description"   : "Ooohh .... vous venez de traduire ce texte?",</pre><pre name="743d" class="graf graf--pre graf-after--pre">"data"          :{"name": "je m'appelle `{{name_value}}`"}</pre><pre name="3a0c" class="graf graf--pre graf-after--pre">}</pre><p name="ddd9" 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><pre name="b90c" class="graf graf--pre graf-after--p">{</pre><pre name="e2cf" class="graf graf--pre graf-after--pre">"TITLE": "Hola señor",</pre><pre name="b124" class="graf graf--pre graf-after--pre">"TITLE_2": "Hola `{{value}}`",</pre><pre name="859a" class="graf graf--pre graf-after--pre">"description": "Ooohh .... acabas de traducir este texto?",</pre><pre name="ad63" class="graf graf--pre graf-after--pre">"data": {"name": "Me llamo `{{name_value}}`"}</pre><pre name="8d26" class="graf graf--pre graf-after--pre">}</pre><p name="3b03" class="graf graf--p graf-after--pre">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 keep a word constant across translations OR to give translations not supported by the library.</p><h3 name="b231" class="graf graf--h3 graf-after--p">STEP 3: Implement ngx-translate library</h3><p name="44ba" class="graf graf--p graf-after--h3"><strong class="markup--strong markup--p-strong">ngx-translate</strong> is the internationalization (i18n) library for Angular. Since Ionic 4 has Angular under the hood by default, we can use this library for app as well as progressive web apps.</p><h4 name="6c45" class="graf graf--h4 graf-after--p">Install library</h4><pre name="33e7" class="graf graf--pre graf-after--h4">// Install core library<br><code class="markup--code markup--pre-code">npm install --save @ngx-translate/core</code></pre><pre name="7ae5" class="graf graf--pre graf-after--pre">// Install http loader<br><code class="markup--code markup--pre-code">npm install @ngx-translate/http-loader --save</code></pre><p name="b976" 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><h4 name="6386" class="graf graf--h4 graf-after--p">Setup the library and http-loader</h4><p name="dc43" 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. This function looks like this</p><pre name="ffa7" class="graf graf--pre graf-after--p">export function HttpLoaderFactory(http: HttpClient) {<br>return new TranslateHttpLoader(http, "./assets/i18n/", ".json");<br>}</pre><p name="03a1" class="graf graf--p graf-after--pre">where we have provided the external path of our JSON files to the function.</p><p name="79b0" 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 (app.module.ts). This is how the file looks after setup.</p><p name="224b" class="graf graf--p graf-after--p">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="8f7e" class="graf graf--h4 graf-after--p">Setup the translate library in child component</h4><p name="dc2c" 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="b7fd" class="graf graf--pre graf-after--p">import { TranslateService } from '@ngx-translate/core';</pre><p name="5061" 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><p name="8f5b" class="graf graf--p graf-after--p">Let’s break down the code to understand a bit more</p><ul class="postList"><li name="9398" class="graf graf--li graf-after--p"><code class="markup--code markup--li-code">ionViewDidEnter()</code> runs when the page loads. We then get default browser language using <code class="markup--code markup--li-code">this._translate.getBrowserLang()</code> (for browsers) and set a default language if no browser</li><li name="dd1f" 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 (in our case English)</li><li name="4b1a" 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="bd85" 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="035b" class="graf graf--h3 graf-after--li">STEP 4: Basic page layout to show translated text</h3><p name="89c8" class="graf graf--p graf-after--h3">We will create a very basic layout to enable language selection and show translated text in the app.<br>Language selection can be done with a simple <code class="markup--code markup--p-code">ion-select</code> which acts like a selection list. We will show different types of translated text in <code class="markup--code markup--p-code">ion-card</code> layout. Final layout will look like the following</p><p name="8fb8" class="graf graf--p graf-after--p">Text translated in all three languages. Select option at the top of the page</p><p name="785d" class="graf graf--p graf-after--p">The layout file <code class="markup--code markup--p-code">tab1.page.html</code> will have the following layout</p><p name="cb62" class="graf graf--p graf-after--p">Run all these changes on your <code class="markup--code markup--p-code">ionic serve</code> and Voila&nbsp;! You got yourself some translations 🎉 🎉 🎉 🎉</p><p name="8e45" class="graf graf--p graf-after--p">Text translation on browser</p><h4 name="fbd2" class="graf graf--h4 graf-after--p">The Directive GOTCHA&nbsp;😎</h4><p name="8175" class="graf graf--p graf-after--h4">If you have followed the above steps exactly, you might not get the exact result as shown in the above GIF.</p><p name="cb8e" 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="2fbd" 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="0eb1" class="graf graf--p graf-after--pre">to get the translation, and shown it in the HTML using the local variable like this</p><pre name="194f" class="graf graf--pre graf-after--p">&lt;h1&gt;`{{ title }}`&lt;/h1&gt;</pre><pre name="a5eb" class="graf graf--pre graf-after--pre">&lt;p&gt;`{{ description }}`&lt;/p&gt;</pre><p name="5778" 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><pre name="3081" class="graf graf--pre graf-after--p">&lt;h1 translate&gt;TITLE&lt;/h1&gt;</pre><pre name="c93f" class="graf graf--pre graf-after--pre">&lt;p [translate]="'description'"&gt;&lt;/p&gt;</pre><p name="d54b" class="graf graf--p graf-after--pre">This is because in our Ionic 4 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="6536" 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><p name="4159" class="graf graf--p graf-after--p">Now, if you run the app again, the <code class="markup--code markup--p-code">directive</code> translations will also work fine. 😎 😎 😎</p><p name="4ee4" class="graf graf--p graf-after--p">Translating in tab app&#8202;—&#8202;like a boss</p><blockquote name="fc3d" class="graf graf--blockquote graf-after--p"><code class="markup--code markup--blockquote-code"><em class="markup--em markup--blockquote-em">directive</em></code> method is preferred for bigger apps with lots of code, since this method results in smaller code size and no need of local variables.</blockquote><h3 name="55de" class="graf graf--h3 graf-after--blockquote">STEP 5: Implement Cordova Globalization plugin</h3><p name="8f86" class="graf graf--p graf-after--h3">To perform the translation using device’s default language, we need to detect the device’s default language first. This can be done using globalization plugin. Install the plugin using</p><pre name="3fd7" class="graf graf--pre graf-after--p">ionic cordova plugin add cordova-plugin-globalization</pre><pre name="0820" class="graf graf--pre graf-after--pre">npm install @ionic-native/globalization</pre><p name="7ddb" class="graf graf--p graf-after--pre">Import the plugin in your component using</p><pre name="9d46" class="graf graf--pre graf-after--p"><code class="markup--code markup--pre-code">import { Globalization } from '@ionic-native/globalization/ngx';</code></pre><pre name="526f" class="graf graf--pre graf-after--pre"><code class="markup--code markup--pre-code">constructor(private globalization: Globalization) { }</code></pre><p name="dde2" class="graf graf--p graf-after--pre">Now you can call a simple function on your page start, which will detect the device’s default language</p><pre name="ed1c" class="graf graf--pre graf-after--p">getDeviceLanguage() {<br>this.globalization.getPreferredLanguage().then(res =&gt; {<br>// Run other functions after getting device default lang<br>this._initTranslate()<br>})<br>.catch(e =&gt; console.log(e));<br>}</pre><h3 name="1ea2" class="graf graf--h3 graf-after--pre">STEP 6: Run the app on simulator</h3><p name="a69f" class="graf graf--p graf-after--h3">To run the app on simulator, first add iOS platform</p><pre name="4849" class="graf graf--pre graf-after--p">ionic cordova platform add ios</pre><p name="b3bc" class="graf graf--p graf-after--pre">Then prepare the app for iOS build</p><pre name="2984" class="graf graf--pre graf-after--p">ionic cordova prepare ios</pre><p name="232f" class="graf graf--p graf-after--pre">After this, you can either build the app directly using Xcode, or use Ionic CLI to run the app in default simulator. I prefer Xcode, as it can handle all provisioning related errors better. To run from Ionic CLI directly, use</p><pre name="d520" class="graf graf--pre graf-after--p">ionic cordova run ios --target="iPhone-6s"</pre><p name="90ca" class="graf graf--p graf-after--pre">This is what you will see in Safari Console if you are running on device or simulator. I changed the default language on the simulator to <code class="markup--code markup--p-code">french</code></p><p name="7bd2" class="graf graf--p graf-after--p">Getting device default language&#8202;—&#8202;iOS simulator</p><p name="b04a" class="graf graf--p graf-after--p"><strong class="markup--strong markup--p-strong">Interesting fact: </strong>Since Ionic apps are based on browser itself, detecting browser’s default language is enough. As you see in the above example,<br>- device default language comes out to be <code class="markup--code markup--p-code">fr-US</code>&nbsp;<br>- browser’s default language comes out to be <code class="markup--code markup--p-code">fr</code></p><p name="3914" class="graf graf--p graf-after--p">So, for our case, detecting browser language itself is enough. But, it is good to be able to detect device’s default language in case it differs from browser’s language.</p><h3 name="cf57" class="graf graf--h3 graf-after--p">Conclusion</h3><p name="e89e" 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.</p><p name="29bb" class="graf graf--p graf-after--p">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&nbsp;.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><p name="4f90" class="graf graf--p graf-after--p graf--trailing"><em class="markup--em markup--p-em">This blog was originally published on </em><a href="https://enappd.com/blog/"  class="markup--anchor markup--p-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank"><strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">Enappd</em></strong></a><em class="markup--em markup--p-em">.</em></p></div></div></section><section name="7264" class="section section--body"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><p name="be02" class="graf graf--p graf--leading">👉<strong class="markup--strong markup--p-strong"> Other articles based on Ionic you may like:</strong></p><p name="4377" class="graf graf--p graf-after--p">• <a href="https://enappd.com/blog/firebase-with-ionic-4-overview/8/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">How to use Firebase with Ionic 4&#8202;—&#8202;Complete guide for beginners</a></p><p name="10af" class="graf graf--p graf-after--p">• <a href="https://enappd.com/blog/how-to-use-geolocation-geofencing-and-beacon-plugins-in-ionic-4/10/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">How to use Geolocation, Geofencing and Beacon plugins in Ionic 4</a></p><p name="28fe" class="graf graf--p graf-after--p">• <a href="https://enappd.com/blog/qr-code-scanning-and-optical-character-recognition-ocr-in-ionic-4/9/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">QR code, Scanning and Optical Character Recognition (OCR) in Ionic 4</a></p><p name="9a04" class="graf graf--p graf-after--p graf--trailing">• <a href="https://enappd.com/blog/payment-gateway-solutions-in-ionic-4-paypal-apple-pay-stripe-and-others/6/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Payment Gateway Solutions in Ionic 4&#8202;—&#8202;Paypal, Apple Pay, Stripe and others</a></p></div></div></section><section name="fe1d" class="section section--body"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><p name="9f2f" class="graf graf--p graf--leading"><strong class="markup--strong markup--p-strong">NEED FREE IONIC 4 STARTERS?</strong></p><ul class="postList"><li name="2b12" class="graf graf--li graf-after--p"><a href="https://store.enappd.com/product/free-chat-themes-ionic4/"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Free Chat Themes Starter for Ionic 4</a></li><li name="a4be" class="graf graf--li graf-after--li"><a href="https://store.enappd.com/product/ionic4-firebase-starter/"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Free Ionic 4&#8202;—&#8202;Firebase Starter</a></li></ul><p name="e482" class="graf graf--p graf-after--li graf--trailing">You can also find free Ionic 4 starters on our website <a href="https://store.enappd.com"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">enappd.com</a></p></div></div></section><section name="d196" 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"><p name="10de" class="graf graf--p graf--leading">🐦 <em class="markup--em markup--p-em">Follow Enappd on </em><a href="https://twitter.com/EnappdStore"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank"><strong class="markup--strong markup--p-strong">Twitter</strong></a><strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">&nbsp;</em></strong><em class="markup--em markup--p-em">, </em><a href="https://www.facebook.com/EnappdStore/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank"><strong class="markup--strong markup--p-strong">Facebook</strong></a><em class="markup--em markup--p-em">, </em><a href="https://in.linkedin.com/company/enappdstore"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank"><strong class="markup--strong markup--p-strong">Linkedin</strong></a>🕸</p><h4 name="2932" class="graf graf--h4 graf-after--p graf--trailing">It’s great having you here, please click 👏 button and share 👭 this article to help others find it! Feel free to leave a comment&nbsp;below.</h4></div></div></section></div></article></main>