Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Commit

Permalink
feat(directive): translate attributes using directive
Browse files Browse the repository at this point in the history
Add to the translate directive the ability to act on attributes. Using
the translate-attr-ATTR as an attribute, ATTR will be translated.
translate-default, translate-value and interpolation are possible.

Closes #568
  • Loading branch information
red-0ne authored and knalli committed Oct 12, 2014
1 parent aa1e924 commit 1d06d2a
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 52 deletions.
123 changes: 72 additions & 51 deletions src/directive/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ angular.module('pascalprecht.translate')
*
* @param {string=} translate Translation id which could be either string or interpolated string.
* @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object.
* @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute.
*
* @example
<example module="ngView">
Expand All @@ -22,12 +23,14 @@ angular.module('pascalprecht.translate')
<pre translate="TRANSLATION_ID"></pre>
<pre translate>TRANSLATION_ID</pre>
<pre translate translate-attr-title="TRANSLATION_ID"></pre>
<pre translate="{{translationId}}"></pre>
<pre translate>{{translationId}}</pre>
<pre translate="WITH_VALUES" translate-values="{value: 5}"></pre>
<pre translate translate-values="{value: 5}">WITH_VALUES</pre>
<pre translate="WITH_VALUES" translate-values="{{values}}"></pre>
<pre translate translate-values="{{values}}">WITH_VALUES</pre>
<pre translate translate-attr-title="WITH_VALUES" translate-values="{{values}}"></pre>
</div>
</file>
Expand Down Expand Up @@ -71,6 +74,10 @@ angular.module('pascalprecht.translate')
element = $compile('<p translate>{{translationId}}</p>')($rootScope);
$rootScope.$digest();
expect(element.text()).toBe('Hello there!');
element = $compile('<p translate translate-attr-title="TRANSLATION_ID"></p>')($rootScope);
$rootScope.$digest();
expect(element.attr('title')).toBe('Hello there!');
});
});
</file>
Expand Down Expand Up @@ -99,24 +106,45 @@ angular.module('pascalprecht.translate')
scope.preText = "";
scope.postText = "";

var fullTranslationIds = {};

// Ensures any change of the attribute "translate" containing the id will
// be re-stored to the scope's "translationId".
// If the attribute has no content, the element's text value (white spaces trimmed off) will be used.
iAttr.$observe('translate', function (translationId) {
var observeElementTranslation = function (translationId) {
if (angular.equals(translationId , '') || !angular.isDefined(translationId)) {
var interpolateMatches = iElement.text().match(interpolateRegExp);
if (angular.isArray(interpolateMatches)) {
scope.preText = interpolateMatches[1];
scope.postText = interpolateMatches[3];
scope.translationId = $interpolate(interpolateMatches[2])(scope.$parent);
fullTranslationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent);
} else {
scope.translationId = iElement.text().replace(/^\s+|\s+$/g,'');
fullTranslationIds.translate = iElement.text().replace(/^\s+|\s+$/g,'');
}
} else {
scope.translationId = translationId;
fullTranslationIds.translate = translationId;
}
};

iAttr.$observe('translate', function (translationId) {
observeElementTranslation(translationId);
updateTranslations();
});


var observeAttrTranslation = function (translateAttr) {
iAttr.$observe(translateAttr, function (translationId) {
fullTranslationIds[translateAttr] = translationId;
updateTranslations();
});
};

for (var translateAttr in iAttr) {
if(iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr') {
observeAttrTranslation(translateAttr);
}
}

iAttr.$observe('translateDefault', function (value) {
scope.defaultText = value;
});
Expand Down Expand Up @@ -144,61 +172,54 @@ angular.module('pascalprecht.translate')
}
}

var applyElementContent = function (value, scope, successful) {
if (!successful && typeof scope.defaultText !== 'undefined') {
value = scope.defaultText;
// Master update function
var updateTranslations = function () {
for (var key in fullTranslationIds) {
if (fullTranslationIds[key]) {
translateItem(key, fullTranslationIds[key]);
}
}
};

// Put translation processing function outside loop
var translateItem = function(translateAttr, translationId) {
$translate(translationId, scope.interpolateParams, translateInterpolation)
.then(function (translation) {
applyTranslation(translation, scope, true, translateAttr);
}, function (translationId) {
applyTranslation(translationId, scope, false, translateAttr);
});
};

var applyTranslation = function (value, scope, successful, translateAttr) {
if (translateAttr === 'translate') {
if (!successful && typeof scope.defaultText !== 'undefined') {
value = scope.defaultText;
}
iElement.html(scope.preText + value + scope.postText);
var globallyEnabled = $translate.isPostCompilingEnabled();
var locallyDefined = typeof tAttr.translateCompile !== 'undefined';
var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false';
if ((globallyEnabled && !locallyDefined) || locallyEnabled) {
$compile(iElement.contents())(scope);
}
}
iElement.html(scope.preText + value + scope.postText);
var globallyEnabled = $translate.isPostCompilingEnabled();
var locallyDefined = typeof tAttr.translateCompile !== 'undefined';
var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false';
if ((globallyEnabled && !locallyDefined) || locallyEnabled) {
$compile(iElement.contents())(scope);
else {
if (!successful && typeof scope.defaultText !== 'undefined') {
value = scope.defaultText;
}
iElement.attr(iAttr.$attr[translateAttr].substr(15), value);
}
};

var updateTranslationFn = (function () {
if (!translateValuesExist && !translateValueExist) {
return function () {
var unwatch = scope.$watch('translationId', function (value) {
if (scope.translationId && value) {
$translate(value, {}, translateInterpolation)
.then(function (translation) {
applyElementContent(translation, scope, true);
unwatch();
}, function (translationId) {
applyElementContent(translationId, scope, false);
unwatch();
});
}
}, true);
};
} else {
return function () {

var updateTranslations = function () {
if (scope.translationId && scope.interpolateParams) {
$translate(scope.translationId, scope.interpolateParams, translateInterpolation)
.then(function (translation) {
applyElementContent(translation, scope, true);
}, function (translationId) {
applyElementContent(translationId, scope, false);
});
}
};

// watch both interpolateParams and translationId, because watchers are triggered non-deterministic
scope.$watch('interpolateParams', updateTranslations, true);
scope.$watch('translationId', updateTranslations);
};
}
}());

scope.$watch('interpolateParams', updateTranslations, true);

// Ensures the text will be refreshed after the current language was changed
// w/ $translate.use(...)
var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslationFn);
var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);

updateTranslationFn();
updateTranslations();
scope.$on('$destroy', unbind);
};
}
Expand Down
66 changes: 65 additions & 1 deletion test/unit/directive/translate.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ describe('pascalprecht.translate', function () {
element = $compile('<div translate="{{translationId}}"></div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toBe('Lorem Ipsum ');
});
});

it('should replace interpolation directive with empty string if translation id is in content', function () {
element = $compile('<div translate>TD_WITH_VALUE</div>')($rootScope);
Expand Down Expand Up @@ -574,4 +574,68 @@ describe('pascalprecht.translate', function () {
});
});

describe('translate-attr-* attributes', function () {

var element;

beforeEach(module('pascalprecht.translate', function ($translateProvider) {
$translateProvider
.translations('en', {
'ANOTHER_ONE': 'bar',
'TRANSLATION_ID': 'foo',
'TEXT_WITH_VALUE': 'This is a text with given value: {{value}}',
'ANOTHER_TEXT_WITH_VALUE': 'And here is another value: {{another_value}}'
})
.preferredLanguage('en');
}));

var $compile, $rootScope;

beforeEach(inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
}));


it('should make simple attribute translation', function () {
element = $compile('<div translate translate-attr-title="TRANSLATION_ID"></div>')($rootScope);
$rootScope.$digest();
expect(element.attr('title')).toBe('foo');
});

it('should translate multiple attributes', function () {
element = $compile('<div translate translate-attr-name="ANOTHER_ONE" translate-attr-title="TRANSLATION_ID"></div>')($rootScope);
$rootScope.$digest();
expect(element.attr('title')).toBe('foo');
expect(element.attr('name')).toBe('bar');
});

it('should translate attributes and content', function () {
element = $compile('<div translate="ANOTHER_ONE" translate-attr-title="TRANSLATION_ID"></div>')($rootScope);
$rootScope.$digest();
expect(element.attr('title')).toBe('foo');
expect(element.text()).toBe('bar');
});

it('should translate attributes with value interpolation', function () {
element = $compile('<div translate translate-attr-title="TEXT_WITH_VALUE" translate-values="{value: \'bar\'}"></div>')($rootScope);
$rootScope.$digest();
expect(element.attr('title')).toBe('This is a text with given value: bar');
});

it('should translate attributes and text with multiplue values interpolation', function () {
element = $compile('<div translate="ANOTHER_TEXT_WITH_VALUE" translate-attr-title="TEXT_WITH_VALUE" translate-values="{value: \'bar\', another_value: \'foo\'}"></div>')($rootScope);
$rootScope.$digest();
expect(element.attr('title')).toBe('This is a text with given value: bar');
expect(element.text()).toBe('And here is another value: foo');
});

it('should translate attributes with simple interpolation', function () {
$rootScope.who = 'there!';
element = $compile('<div translate translate-attr-title="Helloo {{who}}"></div>')($rootScope);
$rootScope.$digest();
expect(element.attr('title')).toBe('Helloo there!');
});
});

});

0 comments on commit 1d06d2a

Please sign in to comment.