Angular.js

Learning Angular: Set your language culture before any UI is displayed

In this article I briefly outline an issue I had, namely to make sure that my language’s locale files are loaded by angular-translate before any UI is shown. First, this prevents from any unwanted flickering and second it helps to avoid wrong displaying of localized data. But see for yourself.

This article is part of my “Learning NG” series, presenting some of my adventures while learning Angular. Check out the series intro and other articles. Note, I’m an Angular newbie, so I’m more than happy for any kind of feedback and improvement suggestions from more experienced people than me.

Problem

In my application I set the user’s language based on his personal preferences. Meaning I have a backend api /api/v1/users/current that returns the information about the current user:

{
  'username': 'juristr',
  ...
  'language': 'de'
}

As most angular devs, I use angular-translate for i18n stuff. So besides some details about how I use angular-translate’s partial loading capabilities to get all the different localization files, I use $translate.use(..) to set the language based on the user’s preferences. Changing the language will automatically refresh any translations that are bound on the UI

{{ 'message' | translate }}

So far so good. But there’s an issue: I have to make sure the language is set before the UI is being rendered to make sure the data is displayed in the correct locale. But wait, didn’t you just say the UI will be refreshed when the language changes? So at least you see some flickering.

Well, not exactly. My data structure looks like this:

{
  name: {
    de: 'Deutscher',
    en: 'English value',
    it: 'valore in italiano'
  },
  ...
}

In the HTML code I bind it like

{{ vm.data.name[vm.currentLanguage] }}

..and the controller obviously binds the vm.currentLanguage to $translate.use() and vm.data to the sample data structure shown above.

Now this doesn’t refresh any more.

Solution

First of all I had to make sure the user as well as the language are set before anything else. Since a user could enter the application through some client-side route like /index.html#people/edit, the route’s resolve function offered itself as a good candidate.

Furthermore, $translate.use() returns a promise! The result

$stateProvider
  .state('home', {
     url: '/',
     ...
     resolve: {
       ensureUserAndLanguage: function($log, $q, $translate, user){
         var deferred = $q.defer();
         user.getCurrent()
           .then(function(result){
             $log.debug('Route resolve. Setting lang to ' + result.language);

             // HERE'S THE IMPORTANT PART!!
             $translate.use(result.language)
               .then(function(){
                  $log.debug('$translate.use. Lang is: ' + $translate.use());

                  deferred.resolve();
               });

           });

         return deferred.promise;
       }
     }
  });

And it works! Here’s a Plunker file with the code. Note that you have to open the edit view to see it work properly.

Link

Attention: In my real application, I dynamically attach the above shown resolve function to my routes. Obviously you need to cache calls like user.getCurrent() s.t. they don’t call the backend each time a client-side route changes. Furthermore while simply resolving with nothing, it would be more meaningful to resolve with an object that contains the user object and current language s.t. they are at disposal for an eventual controller, like

resolve: {
    metaInfo: function(...) {
      var deferred = $q.defer();
      ...
      user.getCurrent()
        .then(function(result){
          ...
          $translate.use(result.language)
            .then(function(currentLanguage){
                deferred.resolve({
                  user: result,
                  language: currentLanguage
                });
            });
        });
      ...
    }
}

An eventual “route controller” can then take an object metaInfo containing the user and current language.

Juri Strumpflohner

Juri Strumpflohner mainly operates in the web sector developing rich applications with HTML5 and JavaScript. Beside having a Java background and developing Android applications he currently works as a software architect in the e-government sector. When he’s not coding or blogging about his newest discoveries, he is practicing Yoseikan Budo where he owns a 2nd DAN.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button