Blog

How to Speed Up AngularJS Apps That Use Internalization Libraries

Ilya Drabenia

angularjs-logo

For a while, I was involved into development of a document workflow management-as-a-service system. The client app was created using AngularJS and I was to decide on the best option for its internalization (i18n) and localization (l10n). However, the service had a rather complex UI with plenty of messages to be translated and transferred between the server and the client. Therefore, I was concerned about the impact of client-side translation on performance.

In this post, I explore this in detail and suggest an approach that accelerates performance of AngularJS internationalization.

 

Why AngularJS

After a brief investigation, we agreed that AngularJS would be a good match for this project for a number of reasons. First, development using this framework is quick and the code quality is good. Second, delivered by Google, this tool is stable and reliable. In addition, it provides great testing capabilities. Another argument in favor of AngularJS is that it already provides features that should be added through third-party libraries in other solutions. For instance, to enable validation in Backbone.js, an additional jQuery validation plug-in is required; to provide a module structure, you will need RequireJS or any other library.

 

Internationalization with AngularJS

One of the simplest ways of translating AngularJS apps is to use internationalization libraries, such as Angular-translate, Angular-gettext, etc.

All these libraries use directives and filters to substitute values. However, this approach involves multiple modifications of the Document Object Model. In addition, large data sets are transferred between a server and a client. I analyzed how these two factors affected performance and measured the results using the R programming language.

This diagram illustrates how many milliseconds were added to a loading time depending on the number of words to be translated. For instance, translating 100 words at runtime results in a latency of 100+ ms. If an application has a complex UI with multiple menus and tabs, then the number of translated messages may reach 300 and more.

According to surveys, users are ready to wait for 2 seconds while a Web page is loading and after 3 seconds 40% of visitors leave a Web site. An additional 100 ms delay led to Amazon’s sales to decrease by 1%. Therefore, in case of AngularJS i18n and l10n, directives and filters should only be used for applications with few DOM elements.

I also analyzed how the size of a translated message bundle affects the entire page loading time. It turned out that loading 5–10 MB had a very low impact on performance.

 

Improving performance of AngularJS i18n

The latencies illustrated by the diagram above were primarily caused by modification of multiple DOM objects. So, reducing the number of such updates could help to improve performance.

To achieve this, the templates should be localized before they are converted into DOM elements. E.g., the translated words can be added at the moment the templates are received by the server.

To analyze how this method of localization influences performance, we created a script that:

  • loaded message bundles to a client
  • used an AJAX interceptor to process loaded templates before they were converted to DOM elements
  • utilized the Underscore.js template to replace text in templates

The source code of this solution:

.factory('i18nInterceptor', ['$q', '$translate', function($q, $translate) {
            return {
                'request': function(config) {
                    return config;
                },

                'response': function(response) {
                    _.templateSettings = {
                        'interpolate': /\[\[([\s\S]+?)\]\]/g
                    };
                    if (typeof response.data === 'string') {
                        response.data = _.template(response.data, {t: $translate});
                    }
                    return response;
                }
            };
        }])

Usage sample:

<div class=”box-header”>    
    <span class=”title”> [[t('common.tickets.page.search')]]: </span>
</div>

As you can see form the figure below, this approach works much faster. Translating 500 words resulted in a latency of less than 20 ms, which is almost 35 times faster compared to the default Angular-gettext method.

The following diagram demonstrates the achieved performance improvement. The green line stands for the Angular-gettext library, the red line is based on optimized Angular-gettext solution that reduces the number of DOM modifications.

In this case, the optimized solution decreased the latency for translating 500 words by 35x: from 700 ms to less than 20 ms.

The project demonstrated how most of AngularJS libraries for internationalization and localization slow down the performance. While in a single-page app the latency may be slight, in large Web systems this approach may increase loading time significantly. Therefore, it will only work for small-scale apps that do not have strict performance requirements.

To reduce the number of DOM modifications, maps with translations should be generated on the server side and the ready values should be inserted into templates on the client side. After all, a template with translated words should be converted to DOM elements. As it was demonstrated, this may improve performance by up to 35x.

 

About the author

Ilya Drabenia is a Java Developer at Altoros. Since 2008, he has been helping customers in the insurance, e-commerce, telecom, and other industries to build enterprise-grade solutions focused on Java, Swing, Spring, Hibernate, and Groovy/Grails. With this domain-driven expertise and knowledge of software architecture patterns, Ilya brings excellent code quality and ease of maintenance to the projects he is involved into.

 

Related reading:

3 Comments
  • nico

    Hi, we also have concerns about performance using “filter” tranaslations method with Angular-translate especially when going about normal static templates. So we decided to go with server-side translations. And using angular-translate only on dynamic places like broadcrumbs etc.

    Did you figure out any issues with such approach?

    • Ilya Drabenia

      Hi, nico!

      “Did you figure out any issues with such approach?”
      We use our i18n mechanism from January of 2014. Technically for our application it works great.

      But we found some interesting improvements for this approach in case of browser caching:
      1. Use separate http requests to load each template may slow down page loading on high latency connections. And for improve this case we may compile all templates to single JS file. For apply current i18n approach to compiled templates we may just decorate $templateCache and process templates before insert to cache.
      2. Reloading of html file with large message data set may significantly slow down page loading time. Load messages as separate JS file allow to cache messages without caching of html file and speedup page loading.
      3. Also HTML 5 Offline Cache great approach to improve performance of our initial i18n mechanism.

  • xDisfigure

    Hi,
    I’m using your exemple for a project which uses Internationalization over server side json files.
    I’m trying to make this with the $translate service that has changed and it returns me a promise.

    I tried a lot of things to make my ‘t’ function returns the translated string instead of its promise (I check out the change log of angular-translate and $translate service returns now a promise).

    Do you know how could I achieve this. And I was wondering, If you I make it work, if the langage is switch on the current state no translate update will be done, I should re-render the state.

    Hope you answer me after 2 years 🙂

    Regards,
    dsf.

Benchmarks and Research

Subscribe to new posts

Get new posts right in your inbox!