Skip to content

Instantly share code, notes, and snippets.

@bugwelle
Last active December 15, 2015 09:39
Show Gist options
  • Save bugwelle/5239617 to your computer and use it in GitHub Desktop.
Save bugwelle/5239617 to your computer and use it in GitHub Desktop.
This is a simple directive for AngularJS to use i18next.
/*
*
* There is now an Angular directive, filter and provider!
* It can be found here: https://github.com/i18next/ng-i18next
* ng-i18next is now part of the i18next rganization!
*
*/
/*
* AngularJS directive for using i18next (http://jamuhl.github.com/i18next)
* Usage:
* - include i18next-1.x.x.js
* - include your language files
* - add 'i18n' to your dependencies of your module
* -> 'angular.module('MyApp', ['i18next']);'
* - anywhere in your code change $rootScope.i18nextOptions to your options.
* Best practise: in
* angular.module('MyApp', ['i18next']).run(function ($rootScope) {
* $rootScope.i18nextOptions = {
* lng: 'de-DE',
* ...
* };
* });
* (Because it will run before the directive is initialized)
* - use the 'ng-i18next' attribute on any element you want
* -> '<p ng-i18next="myStringToTranslate"></p>'
* - You can also listen for the 'i18nextInit' event. Then you can translate
* via i18n.t('Your String here');
* -> $scope.$on('i18nextInit', function () {
* console.log(i18n.t('hello'));
* });
*/
angular.module('i18next', []).directive('ngI18next', function ($rootScope) {
'use strict';
var
/**
* This will be our translation function (see code below)
*/
t = null,
/**
* Default options for i18next
* @type {Object}
*/
options = {},
callbacks = [],
translated = [];
/**
* Translate the string given by the ng-i18next attribute and put it into the element.
* @param {DOMElement} element Element with the ng-i18next attribute
* @param {String} translateString The string we want to translate
* @param {Boolean} retranslate Whether it is the first time we translate the element or not
*/
function setText (element, translateString, retranslate) {
if (!retranslate) {
translated[translated.length] = function () {
setText(element, translateString, true);
};
}
if (t !== null) {
element.text(t(translateString));
} else {
/*
* We have to wait for i18next to initialize, so we
* add the string (and element) we want to translate
* to the callback array. It will get executed when
* i18next is ready.
*/
callbacks[callbacks.length] = function () {
setText(element, translateString);
};
}
}
/**
* Initializes i18next
* @param {Boolean} reinitialization Have the options (in $rootScope) changed, so
* we have to translate every string again?
*/
function init (reinitialization) {
window.i18n.init(options, function (tFunction) {
$rootScope.$broadcast('i18nextInit');
$rootScope.i18nextLoaded = true;
var i;
t = tFunction;
if (!reinitialization) {
for (i = 0; i < callbacks.length; i++) {
callbacks[i]();
}
callbacks = [];
} else {
for (i = 0; i < translated.length; i++) {
translated[i]();
}
}
});
}
$rootScope.$watch('i18nextOptions', function () {
options = $rootScope.i18nextOptions || options;
// Note: !! -> make i18nextOptions a boolean (true if it is defined)
init(!!$rootScope.i18nextOptions);
});
return {
// 'A': only as attribute
restrict: 'A',
link: function postLink (scope, element, attrs) {
attrs.$observe('ngI18next', function (value) {
if (!value) {
// Well, seems that we don't have anything to translate...
return;
}
setText(element, value);
});
}
};
});
@jamuhl
Copy link

jamuhl commented Apr 24, 2013

i18next relevant repositories/gist under one joint organisation

let's discuss this: i18next/i18next#99

@julien51
Copy link

Want to show html in the inner element? Change line 58 to element.html(t(translateString));

@guntram
Copy link

guntram commented Aug 19, 2014

if you wanna use count and context from i18next, put them into data attrs:

in the observer function, add:

attrs.$observe('ngI18next', function (value) {

        var options = {};

    if (!value) {
            // Well, seems that we don't have anything to translate...
        return;
    }

    if (attrs.count) {
        options.count = parseInt(attrs.count, 10);
    }

    if (attrs.context) {
        options.context = attrs.context;
    }

    setText(element, value, false, options);

});

extend the setText function like this:

function setText (element, translateString, retranslate, options) {

    if (!retranslate) {
        translated[translated.length] = function () {
            setText(element, translateString, true, options);
        };
    }

    if (t !== null) {

        element.text(t(translateString, options));

    } else {
        /*
         * We have to wait for i18next to initialize, so we
         * add the string (and element) we want to translate
         * to the callback array. It will get executed when
             * i18next is ready.
         */
        callbacks[callbacks.length] = function () {
            setText(element, translateString, false, options);
        };
    }

}

use it like this:

<div ng-i18next="years" data-count="5"></div>

=> outputs "5 years"

<div ng-i18next="years" data-count="1"></div>

=> outputs "1 year"

with a translation like:

"year": "year",
"year_plural": "$t(year)s",
"years": "__count__ $t(year)",

@guntram
Copy link

guntram commented Aug 19, 2014

just figured out sprintf support...
this also goes into the observer:

if (attrs.sprintf) {
    options.postProcess = 'sprintf';
    options.sprintf = JSON.parse(attrs.sprintf);
}

and use it like this:

<div ng-i18next="percent" data-sprintf='["{{ngScopeVar}}"]'></div>

in the translation json, the percent var defines a string output and an escaped percent sign:

"percent": "%s %%"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment