Applying internationalization and localization to AngularJS application

As we all know, first impression is very important. It is good user experience to display the text and apply the locale of the application in the user’s own language. In this blog, we will see how to achieve this by adding few plugins to an existing project. Prerequisits We already have a project developed […]

by Denitsa Atanasova

May 9, 2017

4 min read

technology news - Applying internationalization and localization to AngularJS application

As we all know, first impression is very important. It is good user experience to display the text and apply the locale of the application in the user’s own language.

In this blog, we will see how to achieve this by adding few plugins to an existing project.

Prerequisits

We already have a project developed in AngularJS.It contains some of the most common components.

This is how it looks like:

How can we customize this application to be displayed in multiple languages and also to show localized dates and number?
Just follow these steps 🙂

Downloading and configuring plugins

1. First of all download the plugins:

2. Add them in the html:

– The i18n for Angular apps

<script src="plugins/angular-translate.min.js" type="text/javascript"></script>

– Module that creates a loading function for a typical static file url pattern: “lang-en.json”, “lang-de.json”, etc.

<script src="plugins/angular-translate-loader-static-files.min.js"
 type="text/javascript"></script>

– Module that helps dynamically change of locale

<script src="plugins/tmhDynamicLocale.min.js" type="text/javascript"></script>

 

3. Add a list of dependencies in the main.js, where our main module “App” is defined

var app = angular.module('App', ["pascalprecht.translate", "tmh.dynamicLocale", "ngCookies"]);

4. In the main.js, let’s configure the translateProvider

app.config(['$translateProvider',
   function ($translateProvider) {
       $translateProvider.useStaticFilesLoader({
           prefix: 'i18n/locale-',
           suffix: '.json'
       });
       $translateProvider.preferredLanguage('en');
       $translateProvider.useSanitizeValueStrategy('escape');
   }]);

The translations can be defined in the code but instead are moved to separate files.There are reasons behind this decision – defined in the code makes it hard to use by translators, it is not a good practise.
We use the static file loader to load these files, the response of these urls must be an object of key-value pairs.ex.{ WELCOME_MSG”: “Welcome to our application!”}
The method useSanitizeValueStrategy( strategy ) defines strategy for escaping HTML in the translation, for security reasons.
In the main.js again, let’s configure the tmhDynamicLocaleProvider

app.config(function (tmhDynamicLocaleProvider) {   
tmhDynamicLocaleProvider.localeLocationPattern('i18n/angular-locale_{{locale}}.min.js');                            

This method is used to set the path of the angular locale files in the project. The locale files define the format of the numbers and dates displayed per language.You can download locale files from https://cdnjs.com/libraries/angular-i18n.

Creating language service

1. Let’s create LanguageService, inject some dependencies and define some member variables and methods

   angular.module('App').factory('LanguageService',  LanguageService);
   LanguageService.$inject = ['$rootScope', '$translate','tmhDynamicLocale'];

   function LanguageService($rootScope, $translate, tmhDynamicLocale) {
       var service = {};
       service.listLangs = ['en', 'de', 'es', 'fr'];
       service.language = 'en';      
       service.setLanguage = setLanguage;
   
       function setLanguage(langKey){
           setUserLanguageLocale(langKey);
}

function setUserLanguageLocale(langKey){
           service.language = langKey;
           $translate.use(langKey);
           tmhDynamicLocale.set(langKey);
       }  
       return service;  
   }

Using the language service

1. Let’s add the LanguageService as dependency in the controller and define methods for changing the language

  angular.module('App').controller('AppController', 
    ['$scope', 'ChartService', 'LanguageService',
    function ($scope, ChartService, LanguageService)] 

    $scope.listLangs = LanguageService.listLangs;
    $scope.language = LanguageService.language;
    $scope.changeLanguage = function (lang) {
       LanguageService.setLanguage(lang);
    };

2. And now, we can add buttons for selecting different language In the html

<div ng-repeat="lang in listLangs">
   <a href="#" ng-click="changeLanguage(lang)" class="lang_button">{{lang}}</a>
</div>

!NB – Things to be aware of

At this stage of development a problem pops up – the chart does not apply the locale when changing the language.The chart must be forced to redraw.This should happen once the locale is changed.

According to the documentation: “The locale is changed asynchronously. After the locale is changed, the event ‘$localeChangeSuccess’ will be triggered”.That is why  we add the following lines of code:

$rootScope.$on('$localeChangeSuccess', function () {
   redrawChartsOnLocaleChange();
})

function redrawChartsOnLocaleChange() {
   if ($rootScope.chartsToRedraw) {
       var i = 0;
       for (; i < $rootScope.chartsToRedraw.length; i++) {
           $rootScope.chartsToRedraw[i].redraw();
       }
   }
}

The array $rootScope.chartsToRedraw   contains the chart itself and is used in case there are more than one chart in the project.

Cookies

Everything works fine so far!

Except that every time you reload the application, the language by default is displayed.

Would it not be better to load the user’s language selected in the previous session?

Let’s use cookies!

1. The cookies module is added in the html:

<script src="plugins/angular-cookies.min.js" type="text/javascript"></script>

2. And also injected in the LanguageService

LanguageService.$inject = ['$rootScope','$window', '$translate', '$cookies', 'tmhDynamicLocale'];

3. That is how we read from and write in the browser cookies.

function writeCookie(langKey) {
   var now = new Date(),
      exp = new Date(now.getFullYear() + 1, now.getMonth(), now.getDate());

   $cookies.put('angular_app', langKey, {
       expires: exp
   });
}

function readCookie(){
   return $cookies.get('angular_app');
}

Detecting browser language

1. And last but not least, for best user experience, if browser cookie is not available, let’s use the browser default language.

function retrieveUserLanguage() {

   //Check browser cookies
   var userLanguage = readCookie();
   var isInCookies = (userLanguage) ? true :false;

   //Check browser default language
   if(!isInCookies){
       userLanguage = detectBrowserLanguage();
   }

   if(service.listLangs.indexOf(userLanguage) == -1){
       userLanguage = 'en';
   }
   setLanguage(userLanguage);
}

function detectBrowserLanguage() {

   var browserLang = 'en';
   var browserLanguges = $window.navigator.languages;

   // Firefox 1.0+
   var isFirefox = typeof InstallTrigger !== 'undefined';

   // Safari 3.0+ "[object HTMLElementConstructor]"
   var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 
|| (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari']
 || safari.pushNotification);

   // Internet Explorer 6-11
   var isIE = /*@cc_on!@*/false || !!document.documentMode;

   // Chrome 1+
   var isChrome = !!window.chrome && !!window.chrome.webstore;

   if(isFirefox || isChrome || isIE){
       browserLang = (browserLanguges && browserLanguges.length >=1)  ? browserLanguges[0] : 
browserLang;
       browserLang = (browserLang.indexOf('-') != -1) ? 
browserLang.substring(0,browserLang.indexOf('-')) : browserLang;
   }

   return browserLang;
}

You can find the project in this git repository : https://github.com/denitsaoooooo/i18n-app

Java Developer at Dreamix