i18n with Angular
Angular provides support for localizing dates, numbers and currency right out of the box with the ngLocale module.
Localizing text will require using a third-party module. There are several such modules, but we'll use angular-translate by Pascal Precht.
ngLocale
The ngLocale module provides filters for formatting dates, numbers and currency. The English version of the module ships with Angular by default, but if you want support for other locales you will need to add them individually.
You can install the module with bower:
bower install angular-i18n
Now, assuming you've used bower to install Angular as well, you can load ngLocale for a specific locale.
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-i18n/angular-locale_fr-fr.js"></script>
This will load the locale rules for formatting dates, numbers and currency as expected by someone who speaks French living in France.
You will ideally want to load the proper version of ngLocale dynamically. To do this we can simply apply what we've done earlier with our hybrid locale detection approach.
<script src="bower_components/angular-i18n/angular-locale_<%= locale.toLowerCase() %>.js"></script>
Now we are dynamically loading whatever locale the user prefers.
Let's look at how to implement the filters provided by ngLocale.
Dates
Dates are formatted by ngLocale using the date filter.
HTML
{{ date_expression | date[:format] }}
JavaScript
$filter('date')(date[, format]);
The date_expression has three different options:
DateobjectNumberrepresenting epoch timeStringISO 8601 formatted date
The optional format parameter allows you to specify how you want the date to be formatted. You can specify your own format (e.g. 'MMM d, y h:mm:ss a'), but it is best to use the predefined formats as they are localized properly.
'medium': equivalent to'MMM d, y h:mm:ss a'for en_US locale (e.g. Sep 3, 2010 12:05:08 pm)'short': equivalent to'M/d/yy h:mm a'for en_US locale (e.g. 9/3/10 12:05 pm)'fullDate': equivalent to'EEEE, MMMM d, y'for en_US locale (e.g. Friday, September 3, 2010)'longDate': equivalent to'MMMM d, y'for en_US locale (e.g. September 3, 2010)'mediumDate': equivalent to'MMM d, y'for en_US locale (e.g. Sep 3, 2010)'shortDate': equivalent to'M/d/yy'for en_US locale (e.g. 9/3/10)'mediumTime': equivalent to'h:mm:ss a'for en_US locale (e.g. 12:05:08 pm)'shortTime': equivalent to'h:mm a'for en_US locale (e.g. 12:05 pm)
Example
<!doctype html>
<html lang="<%= locale %>" ng-app>
<meta charset="utf-8"/>
<div ng-controller="ExampleCtrl">
<div>Date {{ dateValue | date }}</div>
<div>Millis {{ millisValue | date : "shortDate"}}</div>
<div>ISO 8601 {{ isoValue | date : "longDate"}}</div>
</div>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-i18n/angular-locale_<%= locale.toLowerCase() %>.js"></script>
<script>
function ExampleCtrl($scope) {
$scope.dateValue = new Date();
$scope.millisValue = Date.now();
$scope.isoValue = new Date().toISOString();
}
</script>
Numbers
Numbers are formatted by ngLocale using the number filter.
HTML
{{ number_expression | number[:fractionSize] }}
JavaScript
$filter('number')(number[, fractionSize]);
The number_expression has two possible values:
NumberStringvalue that can be parsed as a number
The optional fractionSize parameter allows you to control precision. By default the number filter will round to three decimal places.
Example
<!doctype html>
<html lang="<%= locale %>" ng-app>
<meta charset="utf-8"/>
<div ng-controller="ExampleCtrl">
<div>Population {{ population | number }}</div>
<div>PI {{ pi | number : 5}}</div>
</div>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-i18n/angular-locale_<%= locale.toLowerCase() %>.js"></script>
<script>
function ExampleCtrl($scope) {
$scope.population = 1234567890;
$scope.pi = Math.PI;
}
</script>
Currency
Currency is formatted by ngLocale using the currency filter.
HTML
{{ currency_expression | currency[:symbol] }}
JavaScript
$filter('currency')(amount[, symbol]);
The currency_expression has two possible values:
NumberStringvalue that can be parsed as a number
The optional symbol parameter allows you to specify the currency symbol to be displayed.
Example
<!doctype html>
<html lang="<%= locale %>" ng-app>
<meta charset="utf-8"/>
<div ng-controller="ExampleCtrl">
<div>Salary {{ salary | currency }}</div>
<div>Tax {{ tax | currency : '€'}}</div>
</div>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-i18n/angular-locale_<%= locale.toLowerCase() %>.js"></script>
<script>
function ExampleCtrl($scope) {
$scope.salary = 1234567890;
$scope.tax = $scope.salary * .396;
}
</script>
Angular Translate
Angular doesn't offer anything itself for translating text. Pascal Precht however has written a very well thought out module to handle this. With this module text can be translated using a filter, a directive or a service. For the sake of this article we will just be looking at the filter.
HTML
{{ translate_expression | translate[:interpolateParams] }}
JavaScript
$filter('translate')(translationId[, interpolateParams])
The translate_expression is the key of the text that you would like translated. All translations are specified in a JSON object and will be looked up by this identifier.
Stringkey of the translation to be used
The optional interpolateParams parameter allows you to pass an object as an expression. This object will then be interpolated by replacing keys in the translation string with values in the parameter's object. This parameter can be defined inline as an expression, or can be added as a value to $scope.
Example
<!doctype html>
<html lang="<%= locale %>" ng-app="example">
<meta charset="utf-8"/>
<div ng-controller="ExampleCtrl">
<div>{{ 'HELLO_WORLD' | translate }}</div>
<div>{{ 'HEY_PERSON' | translate : '{name: "Joe"}'}}</div>
</div>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-translate/angular-translate.js"></script>
<script>
var app = angular.module('example', ['pascalprecht.translate']);
app.config(function ($translateProvider) {
$translateProvider
.translations('en', {
HELLO_WORLD: 'Hello World',
HEY_PERSON: 'Hey {{name}}'
})
.translations('de', {
HELLO_WORLD: 'Hallo Welt',
HEY_PERSON: 'Hallo {{name}}'
})
.translations('fr', {
HELLO_WORLD: 'Bonjour tout le monde',
HEY_PERSON: 'Hé {{name}}'
});
$translateProvider.preferredLanguage(document.documentElement.getAttribute('lang'));
});
app.controller('ExampleCtrl', function () {});
</script>
In this example all the translations are defined inline. This is acceptable for a simple example since the data size is small. In a large production application however this would have serious performance implications.
Angular translate addresses this by providing the ability to asynchronously load the translations file for the preferred language. This has a huge impact on improving performance since not only is it loaded asynchronously, but it only loads a single translation file.
There are a couple ways provided for doing this and both require an additional extension module.
urlLoader
First off install the module with bower:
bower install angular-translate-loader-url
Now in your config method you can replace all the translations with the following:
$translateProvider.useUrlLoader('foo/bar.json');
$translateProvider.preferredLanguage('en');
This will make a request to the server that looks like foo/bar.json?lang=en. You can then dynamically provide the translation for the requested lang.
staticFilesLoader
This extension is similar to the urlLoader, but deals with static files.
Again start by installing the module with bower:
bower install angular-translate-loader-static-files
To use this module modify your config method by replacing translations with this:
$translateProvider.useStaticFilesLoader({
prefix: 'locale-',
suffix: '.json'
});
$translateProvider.preferredLanguage('en');
Based on this config when loading the translations a request will be made that looks like locale-en.json.
Sample App
For an example of putting all this together, I have created a simple application.
Further Reading
Open source hacker. Community organizer. Co-organizer @ReactRally. Software Sommelier.