BHW Group Blog

Translation and Localization with AngularJS Part 2

Welcome Back!

This is part 2 of my article about i18n and l10n. If you don't know what these are, go see part 1 of my article. Otherwise let's dive right in and cover some of the big problems and steps to take while implementing angular-translate.

AngularJS Translation books

String Organization

An important feature of angular translate is that it provides a way to organize strings that you use for translation. There are many ways you can do this so I will only look at a few of the features and show examples of how I organized my app. Depending on the scope of your application you can use these or mix in other ways of organizing from the guide.

To organize my application I made a directory specifically for translation related js files. This was nested in the js folder for the application and contained one file for behavior and other files for translated strings. The string files were in the following format and there was one file for each language that the app was translated in. You could separate these by page, sub-domain, or however you choose. I personally found that, since it behaves like a dictionary, having the same strings on the same lines across different languages was extremely helpful. The following snippet is an example of how I set up the individual documents for each language. Line three specifies the language that this particular file is for and the translated strings begin at line 4. The strings are nested into pages, but again, these nestings are completely arbitrary.

var app = angular.module('sample_app');
app.config(['$translateProvider', function ($translateProvider) {
    $translateProvider.translations('en', {
        "HOME": {
            'SIGNUP_TEXT' : 'Looks like you need to <a href="/register" class="register-link">Sign Up!</a>.',
            'WELCOME' : 'Welcome to the site!',
            //Default interpolation
            'HELLO' : 'Hello .',
        },
        "ABOUT": {
            'ABOUT'     : 'This is some info about our site',
            //Example of pluralized interpolation
            'VISITORS' : 'We {NUM_VISITORS, plural,
                    zero  {haven\'t had visitors yet}
                    one   { had a single visitor}
                    other { had # visitors}}.',
            //If this version of interpolation is used ALL strings must conform to this type of interpolation.
            //Meaning the string above HOME.HELLO would be invalid syntax.
            //It would need to match the following ABOUT.HELLO string.
            'HELLO'     : 'Hello {userName}.',
        }
    });
}]);

As I said, this example only nests once based on what page the string appears on. You can choose to do a deeper nesting if it is required. To access nested strings you simply request by nested name <div translate="HOME.WELCOME"></div>. If strings appeared more than once I grouped them either in a general category or within a category that was shared for the pages that displayed that translated string. In the example above, I also included another example of using pluralized interpolation (covered in part 1). Again, this assumes that you have included the message format interpolator as explained before. More information about syntax for this format can be found here and how to include it.

Alerts, Pop-Ups, and Drop-downs

In our application, the majority of the alerts and drop-downs were in controllers and services. Angular-translate provides a number of features to assist in this process allowing you to translate text at any level of the application. Passing a key, or a key and the correct values, to the translate module returns the string associated with that key. You can also get information about the current language if you use the $translate.use() function. This allows you to make queries or select information in your database based on the current language.

angular.module('sample_app').controller('randomCtrl', 
            ['$translate', 'notification',
            function ($translate, notification) {
    'use strict';
    function successCallback (response) {
        $translate('ALERTS_AND_POPUPS.SAVED').then(function (translatedSavedMessage){
            notification.alert(translatedSavedMessage);
        });
    }

    function failureCallback (response) {
        $translate('ALERTS_AND_POPUPS.FAILED', 
        {response.number_of_reasons, response.list_of_reasons}).then(function(translatedFailedMessage){
            notification.error(translatedFailedMessage);
        });
    }
}]);

In this example there are no data values passed in the successCallback function, but there are a few passed to the failureCallback. This would assume that these values, and the pluralization rules for the string, are handled in the file of strings. This is not the only way to do alerts as javascript provides many ways to accomplish the same thing, however this is generally how to access the translation features within js code.

Drop-downs are a similar issue; there are several ways this could be accomplished. In the case of the following example, this instance of such a problem only had a few values that could easily be stored in the strings file and so we chose to simply make the lookup return the string's key that could be used to look up the correct value based on the current language.

angular.module('sample_app').controller('SignupController',
                ['$scope', '$translate', function ($scope, $translate) {
    'use strict';
    var sizes = [{id: 1, translationTag: "SETTINGS_PAGE.EXTRA_SMALL"},
                 {id: 2, translationTag: "SETTINGS_PAGE.SMALL"},
                 {id: 3, translationTag: "SETTINGS_PAGE.MEDIUM"},
                 {id: 4, translationTag: "SETTINGS_PAGE.LARGE"},
                 {id: 4, translationTag: "SETTINGS_PAGE.EXTRA_LARGE"}]
    ...
<div class="input">
    <select name="size_id" ng-model="editor.tshirt.size_id" ng-options="size.id as (size.translationTag | translate) for size in editor.tshirt_sizes" required>
        <option style="display: none;" value=""></option>
    </select>
</div>

Language Selection and User Preference

Most of the language selection can be handled by angular-translate. There are extension packages that handle cookies or local storage. It can also determine the preferred language from their browser, redirect certain language keys to keys that you have defined and translated, and provides a way to quickly switch between languages by letting you specify a language key.

//Gets the preferred language from the user's browser
var langKey = $translateProvider.determinePreferredLanguage();
//$translateProvider.preferredLanguage('en');

//redirects some language keys to other language keys.
$translateProvider.registerAvailableLanguageKeys(['en', 'es'], {
    'en_US': 'en',
    'en-us': 'en',
    'en_UK': 'en',
    'en-gb': 'en',
    'en-uk': 'en',
    'es-mx': 'es',
    'es-gt': 'es',
    'es-cr': 'es',
});

//changing the current language.
$translate.use("es");

This function makes it incredibly easy to save a user’s preferred language key and retrieve it on sign in. It also allows you to quickly toggle the language with input from the user.

Changes in CSS caused by Translation

AngularJS Translation English
AngularJS Translation Spanish

When designing an app that is going to be translated into multiple languages, make sure that you account for changes in css. Phrases in one language may be much longer than another and you need to account for this in the design of your app. Sometimes, you may need to change an element in a specific language and this can be accomplished in css with the html language attribute. At the top of your html file there should be an attribute for the language of the document. This attribute can be placed on other elements as well, but it is typically on the starting html element.

<html lang="en" ng-app="sample_app">

This attribute can be updated using the following syntax. This should be updated each time you change the language using the $translate.use() function you should also update this attribute.

$translate.use('en');
document.documentElement.lang = $translate.use();

Once this works properly you can select elements based on the current language attribute. There are a few css selectors that need to be used for this to work in certain browsers so you may want to include both and test to ensure that it works in your targeted browser. Here I select the elements with some class only if the current language is ‘es’.

.some-class {
    width: 25px;
}

html:lang(es) .some-class
html[lang="es"] .some-class {
    width: 50px;
}

Conclusion

Hopefully this article has helped explain and give examples for some of the features of angular-translate. As you go and translate your app don't forget to check and make sure all strings are being translated properly and that the design after translation looks correct. It can be a tedious process but angular-translate helps to keep it organized and functional. Just a reminder, there are many ways to go about translation and this is not a definitive guide on how to use angular-translate. Do your research and remember to ask the correct questions early so you can ensure that you have the best solution for your app.

Do you need an expert in web development? With a team of web development specialists covering a wide range of skill sets and backgrounds, The BHW Group is prepared to help your company make the transformations needed to remain competitive in today’s high-tech marketplace.

You may also like

Categories:
Taylor graduated with his B.S. in computer science from the University of Texas at Austin. His hobbies include tinkering with all sorts of things including cars, oculus rift, and cnc machines. He enjoys spending time with his wife and their two dogs, Ferris and Lola.