Arnaud
Arnaud

Reputation: 73

AngularJS and i18next

I have seen some i18n plugins for Angular but I don't want to re-invent the wheel. i18next is a good library and so, I intend to use it.

I have created a directive i18n which just calls i18n library:

define(['app', 'jquery', 'i18n'], function(app, $, i18n) {'use strict';
    app.directive('i18n', function() {
        return function($scope, elm, attrs) {
            attrs.$observe('i18n', function(value) {
                if ($.fn.i18n) {// for some reason, it isn't loaded quickly enough and first directives process fails
                    $(elm).i18n();
                }
            });
        };
    });
});

On my page, I can change language on the fly:

<a ng-repeat="l in languages"> <img ng-src="images/{{l.id}}.png" title="{{l.label}}" ng-click="setLanguage(l.id)" /> </a>

Now, my main controller defined on html tag:

define(['app', 'i18n', 'jquery'], function(app, i18n, $) {'use strict';

    return app.controller('BuilderController', ['$scope', '$location',
    function BuilderController($scope, $location) {

        /* Catch url changes */
        $scope.$watch(function() {
            return $location.path();
        }, function() {
            $scope.setLanguage(($location.search()).lang || 'en');
        });

        /* Language */
        $scope.languages = [{
            id : "en",
            label : "English"
        }, {
            id : "fr",
            label : "Français"
        }];

        $scope.$watch('language', function() {
            $location.search('lang', $scope.language);
            i18n.init({
                resGetPath : 'i18n/__lng__/__ns__.json',
                lng : $scope.language,
                getAsync : false,
                sendMissing : true,
                fallbackLng : 'en',
                debug : true
            });
            $(document).i18n();
        });

        $scope.setLanguage = function(id) {
            $scope.language = id;
        };

    }]);
});

How it works: watcher on language initialize i18n object with new locale and then update all DOM using i18n jquery extension. Outside of this special event, my directive just does the work perfectly for all other tasks (templates using i18n directive and rendered at a later moment).

While it is working fine, I know I shouldn't manipulate DOM inside a controller but since nothing happens in the end, I haven't found a better solution.

Ideally, I want Angular to re-compile all DOM, parsing all directives to update labels but I can't figure how to do it. I have tried $scope.$apply() : not working because already in digest at this point I have used Scope.onReady plugin without better results.

Obviously, I'm very new to Angular and it's quite difficult for me to understand exactly how things work.

Upvotes: 6

Views: 7522

Answers (1)

AndreM96
AndreM96

Reputation: 1375

As far as I can say, it is better not to use jQuery at all, since it isn't required. You can use i18next without jQuery, by simply using window.i18n.t("YourStringToTranslate"). ;)

Because you can write your own directive you also do not need to use $(document).i18n();. For that purpose I have written a simple directive for Angular.

https://gist.github.com/bugwelle/5239617

As you can see, you don't have to use jQuery and you also better initialize i18next in your directive instead of your controller.

You can use it by simlpy adding a ng-i18next="" attribute to any element you want. Pass your options to $rootScope.i18nextOptions anywhere in your code (for example in your controller). It then will automatically update all translated elements.

I edited your code, so it works with my directive.

define(['app', 'i18n', 'jquery'], function(app, i18n, $) {

  'use strict';

  return app.controller('BuilderController', ['$rootScope', '$scope', '$location',
    function BuilderController($rootScope, $scope, $location) {

      /* Catch url changes */
      $scope.$watch(function() {
          return $location.path();
      }, function() {
          $scope.setLanguage(($location.search()).lang || 'en');
      });

      /* Language */
      $scope.languages = [{
          id : "en",
          label : "English"
      }, {
          id : "fr",
          label : "Français"
      }];

      // This has changed
      $scope.$watch('language', function() {
        $location.search('lang', $scope.language);
        $rootScope.i18nextOptions = {
            resGetPath : 'i18n/__lng__/__ns__.json',
            lng : $scope.language,
            getAsync : false,
            sendMissing : true,
            fallbackLng : 'en',
            debug : true
        };
      });

      $scope.setLanguage = function(id) {
          $scope.language = id;
      };

  }]);
});

Please note that you have to set the directive as a dependence of your app. Also include the i18next version without AMD+jQuery. If you need more details, they are available in the gist. ;)


There is now an i18next angular module available on GitHub: https://github.com/i18next/ng-i18next

Upvotes: 7

Related Questions