stholzm
stholzm

Reputation: 3455

Falling back to another translationId with angular-translate directive

angular-translate 2.7.2, AngularJS 1.4.2

I have the requirement (don't ask...) that for every translatable snippet of text on my site, there are actually two candidate translations of which one is derived by adding a prefix to the translationId.

Example translations:

greeting => "hi"
abc.greeting => "yo"

abc. is my prefix here. If abc.greeting can be translated, then its translation should be shown, otherwise fall back to the translationId greeting if available, and if neither translationId exists: give up, handle the missing translation.

I want to wrap this behavior into a directive so that I can still use conveniently it like this:

<p my-translate="greeting"></p>

which produces either <p>yo</p> or <p>hi</p> depending on the existance of translationIds. I came up with this code in my directive that essentially wraps $translate:

function translateWithFallback(translationId, translationValues, element, prefix) {

  function successHandler(translation) {
    element.html(translation);
  }

  function failureHandler(translationId) {
    element.html("???" + translationId + "???");
  }

  var prefixedTranslationId = prefix + translationId;
  $translate(prefixedTranslationId, translationValues).then(successHandler, function() {
    $translate(translationId, translationValues).then(successHandler, failureHandler);
  });
}

Soon I realized that this solution is lacking compared to the angular-translate directive: It does not notice language selection (I guess I'd need to listen on $translateChangeSuccess) and does not set up watchers for changes to translationId and translationValues. Doing all that and in an efficient manner is what the angular-translate directive already does: https://stackoverflow.com/a/23866441

So my question is: how can I extend or reuse the translate directive but with a fallback regarding translationIds? Any ideas?

Upvotes: 1

Views: 683

Answers (2)

stholzm
stholzm

Reputation: 3455

I finally found a way to modify angular-translate while still being able to use its directive and filter. In essence, I replaced $translate with my own function. $provide allows me to decorate $translate, which then is used by angular-translate's other tools.

module.config([ "$provide", function($provide) {
        $provide.decorator('$translate', [ "$delegate", function($delegate) {

                function translate(translationId, tValues, iId, dTText, myPrefix) {
                       // fallback logic which makes use of $delegate
                }
                translate.preferredLanguage = $delegate.preferredLanguage;
                translate.cloakClassName = $delegate.cloakClassName;
                translate.fallbackLanguage = $delegate.fallbackLanguage;
                translate.useFallbackLanguage = $delegate.useFallbackLanguage;
                translate.proposedLanguage = $delegate.proposedLanguage;
                translate.storage = $delegate.storage;
                translate.use = $delegate.use;
                translate.storageKey = $delegate.storageKey;
                translate.isPostCompilingEnabled = $delegate.isPostCompilingEnabled;
                translate.isForceAsyncReloadEnabled = $delegate.isForceAsyncReloadEnabled;
                translate.refresh = $delegate.refresh;
                translate.instant = $delegate.instant;
                translate.versionInfo = $delegate.versionInfo;
                translate.loaderCache = $delegate.loaderCache;
                translate.directivePriority = $delegate.directivePriority;
                translate.statefulFilter = $delegate.statefulFilter;
                return translate;
        } ]);
} ]);

Had to copy a bunch of properties, too; otherwise my translate function does not work as a replacement for $translate. Still looks kind of hacky, but that is the best solution I could come up with. At least no other changes (or even own directives or filters) were necessary, just this config block.

Upvotes: 1

user3335966
user3335966

Reputation: 2745

As I understand, You need check translation to exist.
The first thing that came to mind:

//it's return translate with prefix, if exist
//and  translate without prefix or original value;
$scope.isExist = function(value){
    //value = 'greeting';
    var tr1 = $filter('translate')(value);
    var tr2 = $filter('translate')('abc.' + value);

    return tr2 !== 'abc.' + value ? tr2 : tr1;
};

and it is already possible to use with:

$rootScope.$on('$translateChangeSuccess', function(){
  //do something with $scope.isExist(); like:
  $scope.somethingValue = $scope.isExist(somethingValue);
});

Be careful with translations data, it will not work with data like:

abc.greeting => "abc.greeting"
abc.value=> "abc.value"
//but it is unlikely, that You could have tr. like 'abc.Hello';

I hope this will help you;
Sorry for my English;

Upvotes: 0

Related Questions