Tutorial

Internationalization of AngularJS Applications

Draft updated on Invalid Date
Default avatar

By Sergey Gospodarets

Internationalization of AngularJS Applications

This tutorial is out of date and no longer maintained.

Introduction

There is a time for each project when internationalization (i18n) becomes absolutely needed. Sometimes it’s because of regional specific customers or when the application has to be shown to people in many countries. Usually, when its architecture is not ready for that- it starts becoming a really painful process.

In the AngularJS world, there’s the awesome angular-translate (it’s used for handling language translation stuff) and the angular-dynamic-locale (it’s used for changing angular $locale- which means formatting dates, numbers, currencies, etc.) libraries.

On the other hand, we have the really popular Yeoman AngularJS generator, which is widely used for scaffolding AngularJS applications.

Let’s provide asynchronous translate (without page reload) to Yeoman’s AngularJS application.

Note: Some of the code examples in this article are shown in a reduced manner to show the main code and idea.

These symbols ... mean that some parts of the code are left out.

Creating an AngularJS App with Yeoman

In GitHub, you can find detailed instructions on how to create applications using generator-angular.

In our case just run:

  1. npm install -g generator-angular
  2. mkdir angular-translate-yeoman && cd $_
  3. yo angular translate

Answer "Yes" to all questions regarding the decision to use Sass and Sass Bootstrap versions. Leave the default list of AngularJS modules.

After a while you will have the Yeoman-based Angular application with some pre-generated file structure and pre-defined controllers, views, and even some tests.

To run it in you browser with the live-reload, on any changes, just run:

  1. grunt serve

It might look similar to that.

Adding i18n

  1. We have to download libraries and include them in the project.
  2. Then we’ll need to provide translation files and set them working.
  3. Next, we will change angular $locale after the language change.
  4. Once step 4 is complete, we need to have the drop-down with the list of languages to select from.
  5. Lastly, the selected language should be stored and applied after the page reloads.

Adding Bower Dependencies

Run in the shell:

  1. ### ANGULAR-TRANSLATE STUFF
  2. # adds angular-translate library
  3. bower install --save angular-translate
  4. # util for asynchronous loading translations files
  5. bower install --save angular-translate-loader-static-files
  6. # util to save selected language preferences to localStorage
  7. bower install --save angular-translate-storage-local
  8. # util to track missed IDs in translation files
  9. bower install --save angular-translate-handler-log
  10. # ANGULAR-DYNAMIC-LOCALE STUFF
  11. # adds angular-dynamic-locale library
  12. bower install --save angular-dynamic-locale
  13. # list of predefined settings for each locale
  14. bower install --save angular-i18n

Applying translations in the templates

Let’s add a translation for Splendid! text on the green button. We can do it by applying to this text translate filter in app/views/main.html:

{{ "views.main.Splendid!" | translate }}

I also provided a prefix for translations to quickly figure out where they are used:

views.main.Splendid! -> views folder, file main.html

Using this, in this way, you can provide translations for all your templates.

Providing Translations Files

Create files app/resources/locale-{{locale}}.json where {{locale}} - needed locales to handle. In the current example I’ll provide the following locales:

app/resources/locale-en_US.json for English (United States):

    {
        "views.main.Splendid!": "Splendid!",
        "directives.language-select.Language": "Language"
    }

app/resources/locale-ru_RU.json for Russian (Russia):

    {
        "views.main.Splendid!": "Отлично!",
        "directives.language-select.Language": "Язык"
    }

directives.language-select.Language will be used in the languages dropdown.

Including angular-translate and dynamic loading in AngularJS applications

In appscriptsapp.js file:

Add dependencies for the main app:

    angular.module('translateApp', [
     ...
     'pascalprecht.translate',// angular-translate
     'tmh.dynamicLocale'// angular-dynamic-locale
    ])

Provide info about locales and the preferred locale which are used in your app (key values will be used in languages dropdown):

    .constant('LOCALES', {
        'locales': {
            'ru_RU': 'Русский',
            'en_US': 'English'
        },
        'preferredLocale': 'en_US'
    })

To get warnings in the developer console, regarding forgotten IDs in translations, just add:

    .config(function ($translateProvider) {
        $translateProvider.useMissingTranslationHandlerLog();
    })

Next step is about adding asynchronous loading for the translations:

    .config(function ($translateProvider) {
        $translateProvider.useStaticFilesLoader({
            prefix: 'resources/locale-',// path to translations files
            suffix: '.json'// suffix, currently- extension of the translations
        });
        $translateProvider.preferredLanguage('en_US');// is applied on first load
        $translateProvider.useLocalStorage();// saves selected language to localStorage
    })

And, finally, provide the config with direction of where to load the $locale settings files for angular-dynamic-locale:

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

AngularJS service for getting / setting current locale

We need to have some Services to change language and apply some additional logic (e.g. change AngularJS $locale). This will be used for creation and interaction with the languages drop down later.

Create app/scripts/services/LocaleService.js file with the following content:

    angular.module('translateApp') .service('LocaleService', function ($translate, LOCALES, $rootScope, tmhDynamicLocale) {
        'use strict';
        // PREPARING LOCALES INFO
        var localesObj = LOCALES.locales;

        // locales and locales display names
        var _LOCALES = Object.keys(localesObj);
        if (!_LOCALES || _LOCALES.length === 0) {
          console.error('There are no _LOCALES provided');
        }
        var _LOCALES_DISPLAY_NAMES = [];
        _LOCALES.forEach(function (locale) {
          _LOCALES_DISPLAY_NAMES.push(localesObj[locale]);
        });

        // STORING CURRENT LOCALE
        var currentLocale = $translate.proposedLanguage();// because of async loading

        // METHODS
        var checkLocaleIsValid = function (locale) {
          return _LOCALES.indexOf(locale) !== -1;
        };

        var setLocale = function (locale) {
          if (!checkLocaleIsValid(locale)) {
            console.error('Locale name "' + locale + '" is invalid');
            return;
          }
          currentLocale = locale;// updating current locale

          // asking angular-translate to load and apply proper translations
          $translate.use(locale);
        };

        // EVENTS
        // on successful applying translations by angular-translate
        $rootScope.$on('$translateChangeSuccess', function (event, data) {
          document.documentElement.setAttribute('lang', data.language);// sets "lang" attribute to html

           // asking angular-dynamic-locale to load and apply proper AngularJS $locale setting
          tmhDynamicLocale.set(data.language.toLowerCase().replace(/_/g, '-'));
        });

        return {
          getLocaleDisplayName: function () {
            return localesObj[currentLocale];
          },
          setLocaleByDisplayName: function (localeDisplayName) {
            setLocale(
              _LOCALES[
                _LOCALES_DISPLAY_NAMES.indexOf(localeDisplayName)// get locale index
                ]
            );
          },
          getLocalesDisplayNames: function () {
            return _LOCALES_DISPLAY_NAMES;
          }
        };
    });

Language Dropdown

In this part, we will add the language drop-down and set actions to changing the language in it. This select element has to be shown only when there is more than 1 language provided.

Let’s create an AngularJS directive app/scripts/directives/LanguageSelectDirective.js:

    angular.module('translateApp') .directive('ngTranslateLanguageSelect', function (LocaleService) { 'use strict';

            return {
                restrict: 'A',
                replace: true,
                template: ''+
                '<div class="language-select" ng-if="visible">'+
                    '<label>'+
                        '{{"directives.language-select.Language" | translate}}:'+
                        '<select ng-model="currentLocaleDisplayName"'+
                            'ng-options="localesDisplayName for localesDisplayName in localesDisplayNames"'+
                            'ng-change="changeLanguage(currentLocaleDisplayName)">'+
                        '</select>'+
                    '</label>'+
                '</div>'+
                '',
                controller: function ($scope) {
                    $scope.currentLocaleDisplayName = LocaleService.getLocaleDisplayName();
                    $scope.localesDisplayNames = LocaleService.getLocalesDisplayNames();
                    $scope.visible = $scope.localesDisplayNames &&
                    $scope.localesDisplayNames.length > 1;

                    $scope.changeLanguage = function (locale) {
                        LocaleService.setLocaleByDisplayName(locale);
                    };
                }
            };
        });

Including locale service and language select

And don’t forget to include these scripts and language selects in the app/index.html:

    ...
    <div ng-translate-language-select></div>
    ...

    <!-- build:js({.tmp,app}) scripts/scripts.js -->
    ...
    <script src="scripts/services/LocaleService.js"></script>
    <script src="scripts/directives/LanguageSelectDirective.js"></script>
    <!-- endbuild -->

Updating Grunt Config

The last thing is we have to let Grunt know about our additions.

For that we will update Gruntfile.js:

  • "live-reload" task config - will add live reload on changing of translations.
    // Watches files for changes and runs tasks based on the changed files
    watch: {
        ...
        livereload: {
            ...
            files: [
                ...
                '<%= yeoman.app %>/resources/{,*/}*.json'
            ]
        }
    }
  • "copy" task config - to copy resources files and locales settings to the result build.
    // Copies remaining files to places other tasks can use
    copy: {
        dist: {
            files: [{
                ...
                src: [
                    ...
                    'resources/{,*/}*.*'
                ]
            },
            ...
            {
            expand: true,
            cwd: 'bower_components/angular-i18n/',
            src: '*.js',
            dest: '<%= yeoman.dist %>/bower_components/angular-i18n'
            }]
        }
        ...
    }

Running

After that, you can test the text Splendid! and see that it really is changed after switching language in the dropdown. For that just run:

  1. grunt serve # will compile and open project in the browser

To test the distribution build just run:

  1. grunt # will create distribution version of the application

And then open dist/index.html in your browser.

Demo

You can play with the working project at GitHub.

As you can see, until the page is loaded, the translations with $locale are not set and an animation is shown. Also, this can be due to the added on language change.

On changing the language, you can see that the page title, all of the content, and the time format have changed.

You can compare which parts of the original Yeoman AngularJS project were changed to add localization looking at GitHub diff between the branches with the clear AngularJs Yeoman app and with the applied asynchronous translation.

Tips and tricks

In this section, we will review some examples of providing language-specific content.

Templates

Text

As above, we can apply translations for templates using e.g. translate filter:

{{ "views.main.Splendid!" | translate }}

There are also a couple of other techniques.

Title and attributes

To apply a changing page title and a meta[name="description"] attribute (which is used to provide text for sharing in social networks), you can use angular ng-bind and ng-attr-content directives (see how it’s done in the demo app):

<title ng-bind="pageTitle">i18n for your AngularJS applications</title>
<meta name="description" ng-attr-content="{{pageContent}}" content="How to translate your AngularJS applications without page reload with angular-translate">

and to provide an update to these fields in controller:

    $translate(pageTitleTranslationId, pageContentTranslationId) // ON INIT
     .then(function (translatedPageTitle, translatedPageContent) {
      $rootScope.pageTitle = translatedPageTitle;
      $rootScope.pageContent = translatedPageContent;
    });

    $rootScope.$on('$translateChangeSuccess', function (event, data) { // ON LANGUAGE CHANGED
     $rootScope.pageTitle = $translate.instant(pageTitleTranslationId);
     $rootScope.pageContent = $translate.instant(pageContentTranslationId);
    });

Images

You can replace your images when you switch the language providing {{locale}} part to ng-src attribute in your views:

    <img ng-src="images/no-filerev/yeoman-{{locale}}.png" />

And in the controller:

    $scope.locale = $translate.use(); // ON INIT

    $rootScope.$on('$translateChangeSuccess', function (event, data) { // ON LANGUAGE CHANGED
     $scope.locale = data.language;
    });

You, also, can check it out on the Home page (Yeoman image).

CSS

If you want to apply some specific CSS, depending on the current locale, you can do it, because in our example we provided the lang attribute for the <html/> tag with the current locale value. e.g.:

    [lang="ru_RU"]{
        /*some special styles*/
    }

You can see that by switching the language on the About page - it will, then, change the page layout.

Conclusion

Step-by-step we have provided localization for our Yeoman AngularJS app.

We understand how to create translations files, handling translations in html-templates, and AngularJs controllers.

Also, we discussed how to provide specific styles/images/page titles and even some HTML attribute values for different locales.

Who knows, maybe it’s a good idea for creating a Yeoman “angular-translate” generator.

Let me know what your experience has been with the localization? Would you like to see some more of the above or some other parts to be highlighted/described in more detail? I want to hear from you!

All code and working AngularJS applications, with asynchronous languages loading, can be found in GitHub.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Sergey Gospodarets

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel