Lokalise and Ionic translation

Ionic internationalization i18n tutorial with ngx-translate

Ionic is a popular framework for creating hybrid applications running on IOS, Android, and Windows Phone. In this tutorial we are going to discuss how to perform Ionic translation using ngx-translate library with examples.

One of the primary reasons for using hybrid frameworks is the speed of development. Therefore, in this article, we will also see how to employ the power of Ionic and third-party libraries to speed up the development of applications with multi-language support, and make the translation management more convenient.

You may be also interested in learning how to localize Angular applications with the built-in I18n module and with Transloco library.

    Applications Translations

    Why should you care? I can always do it later.

    The earlier you start building your translation infrastructure, the easier the translation workflow will be. I see many times that translation becomes an issue later in the project. For example, once I encountered a funny case. A charity app of a famous world-wide brand had a slogan in the Russian language reading as “Let’s make injuries” (though the original slogan was “Let’s fight with injuries”). It took the company about two months to change it after we filed the report.

    If you start without proper preparations, you are risking ending up with a complete chaos.

    Ionic Translation Issues

    Here are some common issues with the translations:

    • Scattered translation, that ruins your application look and feel. It’s typical that internationalization is not a systematic process, but an occasional activity.
    • Localization doesn’t take into account the application’s design and context. Words are too broad, and translation goes off the screen, or there’s an unsolicited word-wrap. Translation out of context often doesn’t make any sense.
    • Manually transfer different formats of translation files for different platforms. It takes time, and mistakes are quite probable.
    • Not reusing your translations. Often we end up having completely different translations for slightly different phrases.
    • Not paying much attention to legal binding text translations. Legal texts are essential. Don’t employ the “Google translate and forget” approach.
    • Not testing your translations. For instance, your production application might have placeholders instead of an actual text after deploying.

    You can avoid most of these problems by using the translation management system like Lokalise.

    Open-source Translation Libraries

    Is it hard to translate Ionic?

    There are several libraries you can use to translate Ionic applications:

    Feature Angular internationalization Ngx-translate Transloco
    Translation of built-in values (dates, numbers, currencies) x
    Translation in templates x x
    Translation in code –  x x
    Run-time language change x x
    Ionic support Hard x x

     

    From a feature standpoint, the built-in I18n module looks the weakest, and it is much harder to set up, therefore we won’t cover it in this article. At the moment, I do not recommend using the built-in Angular internationalization module for the Ionic applications.

    Transloco is new and fresh, and has some new cool features. ngx-translate has a proven track record, and it is also feature-rich therefore we are going to be using it in this article.

    Ionic 4 Translation Modules

    Installation and Initialization of ngx-translate/core

    To start off, clone a sample application and install all the necessary modules:

    $ git clone git@github.com:kutanov/ionic-conference-app.git
    $ cd ionic-conference-app
    $ npm install
    $ npm install @ngx-translate/core @ngx-translate/http-loader --save

    Like always, you should initialize the installed modules. Open the app.module.ts file and import them. Then configure the TranslateModule with the forRoot({}) method:

    [gist id=”3467129f04bf4a917da958bc326f5b15″]

    Translation Files

    Let’s place our JSON files with Russian and English translations inside the /assets/locale/ folder. We’ll start with a very simple example to make sure everything is working fine:

    [gist id=”5eb3ae68783fadf99c70098115596361″]
    [gist id=”d33b6e2b4fc9301855032410f11a9ef2″]

    The translation files are in standard JSON key: value format.

    With innerHTML you can use HTML code inside translation strings, so it’s perfectly normal to write something like "greetings": "<h1>Привет</h1>". To use this translation inside the markup, write <div [innerHTML]='"greetings" | translate'></div>.

    Another important remark is that it’s better to structure your JSON files properly (especially if you are working with complex applications). You may use nesting for that:

    [gist id=”3a9a791ac1348fb62ff29ef9010bab36″]

    Later you may refer to the nested id with a special dot notation, for example: welcomeScreen.greeting.

    After the installation and setting up steps are done, we can employ a translation pipe. Pipes are a powerful instrument which resembles *nix pipes. Basically, you take the return value of one function (or a variable) and then pass this output as an input to another function.

    [gist id=”ef634001ac54aa01ff1cfe8a1fb5ddef”]

    Switching the Language

    So, to switch the current language of the app, you can use the translation service.

    [gist id=”7aad8c072c5e646561527d1328cde2d9″]

    You may get access to the current language code through the currentLang variable: private currentLand: string = this.translateService.currentLang;.

    Usually, Ionic applications consist of more than one component. What about lazy loading components? In this case, import the translation module into a child’s module and then use the same service. Inside lazy loaded modules, you have to use forChild():

    [gist id=”f72fff728065671844a33bc3d8f4a301″]

    After you import the translation module into the child’s module, you can use the same service.

    Let’s make a language selector in the parent module and see whether the values in the child module will change.

    [gist id=”2ca0dd15d617650864441caf57139cb7″]

    [gist id=”4ade15417569408eb2fa7be5c1b29127″]

    To test it in the child’s module let’s edit the about.component.html file:

    [gist id=”cb6dde0895cfd059229f4932cfebadd3″]

    Now we can switch the current language in the parent component, and this change will immediately propagate to the child’s components.

    What else the ngx-translate can do? Let explore the core features.

    Multi-language Support Using Pipe

    {{ 'stringId' | translate }} is the simplest case of translating something. But sometimes you may want to translate a text with embedded variables. You can achieve that with parameters passed to the translate:

    {{ 'stringId' | translate:{"variable": value  } }}

    or

    {{ 'stringId' | translate:variableDeclaredInComponent }}.

    Inside the JSON file you should just provide the variable in double curly brackets {{}}.

    Add a new translation to the /assets/locale/ru.json file:

    "greeting": "Здравствуте {{name}}".

    Define a variable in the text:

    [gist id=”603c642ea9bd26a45137213597076ee9″]

    And then use it inside the template:

    [gist id=”2fd7d9ac546de09a8651a8c12a954e7b”]

    Multi-language Support Using Service

    If you require multi-language support inside your components, you may inject translation service into the necessary component inside the constructor function.

    That allows you to both set and get translation values like this:

    [gist id=”6b961c9d0dbed3f52a5c56cb01dfaa2e”]

    Multi-language Support Using Directive

    A directive is an HTML attribute that Angular treats as an instruction. We may use a special directive to support multi-language localization:

    [gist id=”47e5269106fa26111eb0c204885f1e33″]

    Please note that you can use a translation id inside the tag and pass this translation id as the parameter to the directive. Directive inside the brackets tells Angular to interpret parameters as a variable name and try to resolve it.

    translateParams allows you to pass parameters to the translation.

    Pluralization

    Human-spoken languages are very different. In most languages, there is such a thing as the declension.

    By default, ngx-translate doesn’t support pluralization and declension of nouns. But there are libraries to help us here: ngx-translate-messageformat-compier and messageformat.

    First of all, install these modules:

    $ npm install ngx-translate-messageformat-compiler messageformat --save

    To initialize the module we have to rewrite ngx-translate initialization a little bit:

    [gist id=’a00c7754a454d00c180043f763ea48ed’]

    As you see, we’ve changed the compiler. The new compiler allows us to use the special format to define conditional wording. For example, how to treat countable nouns, define how words are changed by the masculine and feminine gender.

    Let’s take a closer look at how this format works and the most popular use-cases. What we are talking about is called ICU MessageFormat. The whole expression is provided in curly brackets {} . The important thing to do is to convert all your expressions so that they use one curly bracket instead of two. So the 'hello': 'hello {{name}}' expression turns into 'hello': 'hello {name}'.

    The message starts with the variable name we pass to evaluate inside the expression. After the comma, the second parameter is a keyword defining what mode we are going to use. There are two keywords: plural and select. Plural means that we pass a countable parameter and the expression will return a value based on the given number.

    The third parameter is an expression with options. If plural is passed as a second parameter, the third parameter should look like:

    =0{String or expressing if the first param = 0} 1{String or expression if the first param = 1} ... someNumber{String or expression if the first param = someNumber}... other{String or expression if the first param is equal to something not specified here. Default value}

    Let’s take a look at a simple example. Suppose we would like to show how many apples John has. There are three possible cases:

    • John has no apples
    • John has one apple
    • John has X apples (more than one apple, that is)

    Now represent this logic in your code:

    "john": "John {applesCount, plural, =0{has no apples} 1{has an apple} other{has {applesCount} apples}}"]

    [gist id=”4ba40715c1d85bb664a54b26bff9f2ce”]

    Using Gender Information

    Now what about select as the second option? Remember, we were talking about two possible options: plural and select. If we set the second option to select,  the first parameter variable will be matched against the provided options. Take a look at the following example:

    person: {gender, select, male{He is} female{She is} other{It is} friend}

    Here the first parameter is a variable gender, the second parameter is a keyword select, and the third parameter is the options string male{He is} female{She is} other{It is} friend}.

    If gender is male, then the expression will be resolved into He is friend, if femaleShe is friend. For all other values it will be It is friend.

    The logic here is very simple. The string compiler takes the value of the first parameter, in our case that’s the variable gender, checks it and looks for an exact match in the third parameter. If it hasn’t found it, then it yields the other option value. This format may seem a little bit awkward, but you’ll get used to it very fast.

    Using Shared module

    What if we have some shared module and we want the TranslateService to work with this module? We also want to export this module so it is available everywhere the shared module is imported.

    Shared modules are a good way to make sure that you have exactly one instance of a specific service. Let’s say you have a shopping cart, and  you add goods to this shopping cart from different parts of your application. It will be very sad if you end up having multiple instances of the shopping cart and a user leaves your service because the desired goods missed the right basket.

    Let’s initialize the shared module:

    [gist id=”bc9d31951e3c75ff7411e1ee6b8a48fd”]

    What we do here is create a module and import everything we need. We pass isolate: false to TranslateModule to make sure that it will be available outside of the module.

    exports: [TranslateModule] exposes it to lazy-loading modules.

    After configuring we define the method forRoot and expose providers we want to share with the rest of the app.

    We then  call the SharedModule.forRoot() in the imports section of the main app module app.module.ts.

    Don’t forget to initiate the TranslateModule in the app.module.ts as we did in the first section. SharedModule doesn’t substitute the initial initialization in the root module.

    Translation with Lokalise

    As I wrote earlier, to avoid many pitfalls it’s better to have some translation management system. It allows you to track changes, distribute one translation to many platforms, avoid repeating translations of the same text in different places, and many many more.

    Setting up Lokalise for React Native is easy. Follow these steps:

    • To get started, grab your free trial.
    • Download and install Lokalise CLIv2 that will allow you to upload and download translation files.
    • Open your profile page, go to the “API tokens” section, and generate a read/write token.
    • Create a new project, give it a name
    • , and set English as a base language.
    • In the project section, press “More” and select “Settings”. There you can find the project id.
    • To upload your translations files, run lokalise2 file upload --token <token> --project-id <project_id> --lang-iso en --file assets/locale/en.json. Pay attention as on Windows it’s better to provide full path to the translation file. This command will upload translation file to Lokalise. To upload the Russian version repeat the operation with “ru” language code and the correct path to the language file.
    • If you go back to the project overview page, you will be able to see your translation keys and values. There are tons of options to further adjust the process, so please make sure to read the documentation.
    • After you’ve edited the translations, download them back by running lokalise2 file download --token <token> --project-id <project_id> --bundle_structure %LANG_ISO%.json --unzip_to ~/your_app/assets/locale/.

    That’s it. Check out the official documentation for the command-line interface to learn about other commands and options.  As you see, Lokalise reduces the number of complex tasks and make the translation process a piece of cake.

    Conclusion

    ngx-translate is a quite simple yet powerful instrument to work with Ionic 4 translations. It’s easy to install and use and it allows you to manage translations both in templates and in components effectively. It fully supports Ionic 4 module injections and lazy loading.

    Further reading

     

     

    Related articles
    Stop wasting time with manual localization tasks. 

    Launch global products days from now.