Angular and i18n

Well it has been a long while again, but I’ve been crazy busy at work.

I worked on a couple of interesting Single Page Applications written with Angular and I really like it because the Framework is so simple to use and very well documented with a crapload of nice plugins and extensions from the community. Anyhow I needed to implement some sort of internationalization and angular has a nifty extension called angular-translate which I used for that. In the process we created an i18n self hosted service in c# with nancy with a simple rest api where I can get a list of available languages for specific domains. Domains in this context are just language sections so that I only have to request and transfer the sections actually in use. The angular plugin has a nice way of supporting it with „partials“

so include the partials-loader and url-loader for angular-translate and set it up the following way:

 

.config(['$translateProvider','$translatePartialLoaderProvider', 
function ($translateProvider, $translatePartialLoaderProvider) {

        $translateProvider.useMissingTranslationHandler('MissingTranslationHandler');
        $translateProvider.useSanitizeValueStrategy('sanitizeParameters');
        $translateProvider.preferredLanguage('de-DE');
        
        $translatePartialLoaderProvider.addPart('default');
        $translatePartialLoaderProvider.addPart('Terminal');
        
        
        $translateProvider.useLoader('$translatePartialLoader', {
            urlTemplate: '{TranslationBaseURL/IP/Port}/api/v1/domains/{part}/translations/{lang}'
        });
});

This sets up the angular-translate plugin to work with partials which are loaded on demand from an rest service.

Since we are lazy we wanted to have some sort of automatically adding missing translations to the system.

The registered function in useMissingTranslationHandler handles this for us.

The handler is implemented as a angular factory and sends missing translations to a Model which hols a queue to bulk insert the translations to the i18n service.

This is done since we would exceed the allowed number of ajax connections on large applications at the first startup

/**
 * @ngdoc overview Handler for missing translations
 * @name MissingTranslationHandler
 * @author Frank Gehann <fg@code-works.de>
 *
 * @description
 * Provides an handler to send missind translations to the rosetta
 * server.
 */
(function() {
    'use strict';

    angular
        .module('MyModule')
        .factory('MissingTranslationHandler', MissingTranslationHandler);
    
    MissingTranslationHandler.$inject = [
        '$translate',
        'Translation'
    ];
    
    function MissingTranslationHandler($translate, Translation) {  
         
        /**
          * @ngdoc function
          * @name MissingTranslationHandler
          *
          * @description
          * Adds the missind translation to the tranbslation queue and returns the untranslated
          * translation id
          */
        return function (translationID, currentLanguage) {
            
            Translation.Add({
                Text: translationID,
                Code: currentLanguage
            })
            
            return translationID;
        };   
    }
})();
/**
 * @ngdoc overview
 * @name MyModule.Translation
 * @author Frank Gehann <fg@code-works.de>
 *
 * @description
 * This Service Queues new Translations and sends them in Bulk to the Rosetta
 * server. Afterwards it refreshes the translation collection
 */
(function() {
    'use strict';

    angular
        .module('MyModule')
        .factory('Translation', Translation);
    
    Translation.$inject = [
        '$rootScope', 
        '$interval', 
        '$http', 
        '$log', 
        '$translate', 
        '$translatePartialLoader'
    ];

    function Translation($rootScope, $interval, $http, $log, $translate, $translatePartialLoader) {
        
        /**
          * @ngdoc property
          * @name TranslationQueue
          * @propertyOf MyModule.Translation
          *
          * @description
          * Queue with Translations who needs to be sent to the Server
          */
        var TranslationQueue = [];
        
        /**
          * @ngdoc property
          * @name RefreshInProgress
          * @propertyOf MyModule.Translation
          *
          * @description
          * Locks the BulkUpdate functions so it doesen't run havoc
          */
        var RefreshInProgress = false;
        
        /**
          * @ngdoc property
          * @name intervalPromise
          * @propertyOf MyModule.Translation
          *
          * @description
          * Promise for the BulkUpdate Interval
          */
        var intervalPromise = undefined
        
        /**
          * @ngdoc property
          * @name factory
          * @propertyOf MyModule.Translation
          *
          * @description
          * represents the external interface
          */
        var factory = {
            Init: Init,
            Add: Add,
        };
        
        return factory;
        
        ////////////
        
        /**
          * @ngdoc function
          * @name Init
          * @propertyOf MyModule.Translation#Init
          *
          * @description
          * Initialized the BulkUpdate Interval
          */    
        function Init() {  
            if(intervalPromise === undefined) {
                intervalPromise = $interval(BulkUpdate, 2500);
            }
        } 
        
        /**
          * @ngdoc function
          * @name BulkUpdate
          * @propertyOf MyModule.Translation#BulkUpdate
          *
          * @description
          * Sends the pending translöations from the queue to the Rosetta Server
          * and locks the functions until the $translation service refreshed it's 
          * content.
          */ 
        function BulkUpdate() {
            
            if(RefreshInProgress) {
                return;
            }
            
            for(var LangQueue in TranslationQueue)
            {
                // Something to send for this language key?
                if(TranslationQueue[LangQueue].length == 0) {     
                    return;
                } else {
                    
                    var RequestUrl = {TranslationServerBaseUrl};
                    RequestUrl += "/api/v1/domains/{MyAppDomain}/translations/";
                    RequestUrl += LangQueue + "/";
                    
                    $http({
                        url: RequestUrl,
                        method: "POST",
                        data: TranslationQueue[LangQueue]
                    }).then(function (result) {
                        RefreshInProgress = true;              
                        $translatePartialLoader.deletePart("IosAuftragsterminal", true);
                        $translatePartialLoader.addPart("IosAuftragsterminal");
                        $translate.refresh()
                            .then(function(){
                                RefreshInProgress = false;
                                TranslationQueue[LangQueue] = [];
                            });
                    }, function (err) {
                        // Stuff already in DB?
                        if(err.status != 409)
                        {
                            $log.debug(err);
                        } else {
                            $log.debug(err);
                        }
                        RefreshInProgress = false;
                    });
                }
            }
        }
        
        /**
          * @ngdoc function
          * @name Add
          * @propertyOf MyModule.Translation#Add
          *
          * @description
          * Adds the Data to the translation queue
          * 
          * @param {object} Object with the Language Code and the Text
          */
        function Add(Data) {
            
            if(TranslationQueue[Data.Code] === undefined) {
                TranslationQueue[Data.Code] = [];
            } 
                  
            if(TranslationQueue[Data.Code].indexOf(Data.Text) === -1) {
                TranslationQueue[Data.Code].push(Data.Text);
            }
        }
    }
})();

I’m quite happy with this solution even though I think it has some room for Improvement.

Kommentar verfassen

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.