📜 ⬆️ ⬇️

Localization of templates on the client in AngularJS

image

When developing a multilingual web application on AngularJS, you will most likely need to somehow solve the issue with the translation. Today I would like to share one of the ways in which this can be implemented.

Suppose we already have a service that provides the ability to translate using a simple dictionary of strings.

/** * @param {Settings} Settings * @constructor */ function Language(Settings) { this.Settings = Settings; } Language.$inject = ['Settings']; app.service('Language', Language); /** * @param {String} string * @returns {String} */ Language.prototype.translate = function(string) { var translation = __lang[this.Settings.getValue('lang')]; if (typeof translation[string] !== 'undefined') { string = translation[string]; } return string; }; 

')
Knowing AngularJS, the very first idea that comes to mind is to use standard expressions with filters to translate strings on the fly:

 <span>{{ 'some_english_string' | translate }}</span> 

But this approach has its drawbacks:
- first, all the same, the amount of code executed in each $ digest cycle increases, and if you have a complex application, a large number of lines are displayed on the page at the same time and / or your translation algorithm is more complicated than simply supplying a string from a dictionary with a key - then productivity will decline.
- secondly, the use of angular expressions will no longer be so convenient if you use them, say, inside the attributes of some directives that themselves accept expressions, since the execution of expressions within other expressions will not work. In this case, of course, it will be possible to use a global function instead of a filter for converting such strings, but still these are additional difficulties.

The second option, which is recommended to use is to translate templates on the server and from the client side to have access to localized copies of the form some_partial_en.html , some_partial_ru.html . In this case, to change the language, you will need to restart the application. In addition, this is not always possible: for example, on my past project, the application used data from a third-party service, and the writing of the backend was not supposed at all.

But there is another way - namely, to translate templates on the client, but not by means of AngularJS. For example, you can use a template engine from Lo-Dash / underscorejs and use constructions like:

 <span><%= t('some_english_string') %></span> 

The advantage is that it will be possible to use both angular expressions inside a line feed in the dictionary.

 __lang.ru = { "some_english_string": "    {{string.number}}" }; 

so, and vice versa, to substitute this construct into other angular expressions, for example, in some of its directive:

 <div class="input-text-right"> <input type="text" my-num-format="00000.00" my-validate="[ { type: 'notGreaterThen', compareTo: 'somecondition()', message: '<%= t('some_validation_error_message') %>' } ]" ng-model="model.value"> </div> 


To do this, we will add a method for translating templates into our Language service:

 ..... /** * @param {String} template * @returns {String} */ Language.prototype.translateTemplate = function(template) { //     this.translate()      Lo-Dash  t() return _.template(template, { t: angular.bind(this, this.translate) }); }; 


Now we need to intercept the templates used by angular and translate them before it compiles them. There is no separate service for getting templates in AngularJS, but wherever it happens (routing, ngInclude directive, etc.) - $ templateCache is used for caching. Here we will redefine it:

 //  angular-    ,       app.factory('$templateCache', [ '$cacheFactory', 'Language', function($cacheFactory, Language) { /** * @constructor */ function MyTemplateCache() { /** * @param {String} key * @param {*} value */ this.put = function(key, value) { //   -  promise-,    $http if (typeof value.then !== 'undefined') { //    promise-,       value = value.then(function(response) { response.data = Language.translateTemplate(response.data); return response; }); } //   -  -,  angular      promise- $http else if (value instanceof Array) { value[1] = Language.translateTemplate(value[1]); } //   -    // ( ,        <script type="text/ng-template"></script> //   ) else if (typeof value === 'string') { value = Language.translateTemplate(value); } //    put() MyTemplateCache.prototype.put(key, value); }; } //     $templateCache MyTemplateCache.prototype = $cacheFactory('templates'); return new MyTemplateCache(); } ]); 


That's all. Now, to change the language without reloading the page, it will be enough to clear the cache and trigger the reload of the route:

 $templateCache.removeAll(); $route.reload(); 

The only note is that when the route is restarted, all changes that were not saved in the state will be reset, because when you recompile the templates, all the scans will be recreated again.

Source: https://habr.com/ru/post/191074/


All Articles