Angular Translate Custom Loaders

Breaking from my RavenDB theme, I wanted to touch on an issue I encountered with Angular Translate today.

For those who aren’t familiar, Angular Translate is a library for AngularJS that is used to change the text on your website to the user’s language of choice. It’s a library that I’m generally quite happy with, and that has worked well for our app.

Our app has an unusual requirement that almost all text in the app be customizable on a per-customer basis, along with being translatable. Because of this, we are compelled to keep all text in a database, and load it into the app via HTTP. Our app is single-instance, so this can only be done after users authenticate.

Angular translate uses a provider called a ‘loader’ to get translation data into the app asynchronously. There are several pre-made loaders that can be used to get data from a specific URL, an API endpoint, or a file on the file system.

Because our data needed to use a HTTP request, we first tried using the angular translate partial loader, which is designed to make http requests to store parts of a translation set. However, we encountered an interesting issue. When we called $translate.refresh() to clear all the translations and fetch the new ones for the next authenticated user, the translations did not change. Why? As it turns out, the translations were cleared from the core angular translate tables as expected, but they were not similarly cleared from the translate partial loader. They were still cached in that provider, waiting to be used.

The solution? Our own custom loader for Angular Translate. As it turns out, this is very easy to do.

Angular translate expects that any custom loader simply needs to be a factory which returns a function that expects one options parameter. Because anything can be injected into that factory, we were able to concentrate all of our loading logic into one factory that:

  1. Returns the customer specific translations if the user is authenticated
  2. Returns the generic translation set from a configuration in angular if the user is not authenticated
  3. Clears all data and starts over when a call is made to $translate.refresh()

    function translationLoader($http, $q, staticTranslations, appContext){

        return function(options){
            var deferred = $q.defer(),
                translations;

            if(!appContext.apiBasePath)
            {
                translations = staticTranslations[options.key];
                deferred.resolve(translations);
            }
            else
            {
                $http.get(appContext.apiBasePath + '/translations/' + options.key + '/labels').then(function(res){
                    translations = res.data;
                    deferred.resolve(translations);
                });
            }

            return deferred.promise;

        };

    }

I will have to extend this if I wish to load partial translation sets instead of all of the app’s translations at the same time, but given the openness of this interface, that shouldn’t be hard to do.

Moral of the story: sometimes things don’t work as expected, and when that happens, great extensibility points in the code base make up for it.

2 thoughts on “Angular Translate Custom Loaders

  1. That’s awesome. Of course, it would be even more awesome to submit a pull request (“patch” if you prefer) to Angular Translate to make caching optional (or fix a bug, if it’s actually a bug and they are not respecting the contract of `.refresh()`, in case other people could benefit. 🙂

    Like

  2. Kevin – it’s on my list. I’m not entirely sure whether they will consider it a bug though. The partial loader is technically an add on to angular translate, not the core library.

    Like

Leave a comment