This tutorial is out of date and no longer maintained.
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.
In GitHub, you can find detailed instructions on how to create applications using generator-angular
.
In our case just run:
- npm install -g generator-angular
- mkdir angular-translate-yeoman && cd $_
- 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:
- grunt serve
It might look similar to that.
$locale
after the language change.Run in the shell:
- ### ANGULAR-TRANSLATE STUFF
- # adds angular-translate library
- bower install --save angular-translate
-
- # util for asynchronous loading translations files
- bower install --save angular-translate-loader-static-files
- # util to save selected language preferences to localStorage
- bower install --save angular-translate-storage-local
- # util to track missed IDs in translation files
- bower install --save angular-translate-handler-log
-
- # ANGULAR-DYNAMIC-LOCALE STUFF
- # adds angular-dynamic-locale library
- bower install --save angular-dynamic-locale
-
- # list of predefined settings for each locale
- bower install --save angular-i18n
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.
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.
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');
})
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;
}
};
});
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);
};
}
};
});
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 -->
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'
}]
}
...
}
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:
- grunt serve # will compile and open project in the browser
To test the distribution build just run:
- grunt # will create distribution version of the application
And then open dist/index.html
in your browser.
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.
In this section, we will review some examples of providing language-specific content.
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.
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);
});
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).
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.
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.
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!
Enter your email to get $200 in credit for your first 60 days with DigitalOcean.
New accounts only. By submitting your email you agree to our Privacy Policy.