mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
feat(widget): add ng:pluralize as an Angular widget
This commit is contained in:
parent
0da4902e9d
commit
e068addadb
2 changed files with 318 additions and 0 deletions
192
src/widgets.js
192
src/widgets.js
|
|
@ -1445,3 +1445,195 @@ angularWidget('ng:view', function(element) {
|
|||
compiler.directives(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc widget
|
||||
* @name angular.widget.ng:pluralize
|
||||
*
|
||||
* @description
|
||||
* # Overview
|
||||
* ng:pluralize is a widget that displays messages according to en-US localization rules.
|
||||
* These rules are bundled with angular.js and the rules can be overridden
|
||||
* (see {@link guide/dev_guide.i18n Angular i18n} dev guide). You configure ng:pluralize by
|
||||
* specifying the mappings between
|
||||
* {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
|
||||
* plural categories} and the strings to be displayed.
|
||||
*
|
||||
* # Plural categories and explicit number rules
|
||||
* There are two
|
||||
* {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
|
||||
* plural categories} in Angular's default en-US locale: "one" and "other".
|
||||
*
|
||||
* While a pural category may match many numbers (for example, in en-US locale, "other" can match
|
||||
* any number that is not 1), an explicit number rule can only match one number. For example, the
|
||||
* explicit number rule for "3" matches the number 3. You will see the use of plural categories
|
||||
* and explicit number rules throughout later parts of this documentation.
|
||||
*
|
||||
* # Configuring ng:pluralize
|
||||
* You configure ng:pluralize by providing 2 attributes: `count` and `when`.
|
||||
* You can also provide an optional attribute, `offset`.
|
||||
*
|
||||
* The value of the `count` attribute can be either a string or an {@link guide/dev_guide.expressions
|
||||
* Angular expression}; these are evaluated on the current scope for its binded value.
|
||||
*
|
||||
* The `when` attribute specifies the mappings between plural categories and the actual
|
||||
* string to be displayed. The value of the attribute should be a JSON object so that Angular
|
||||
* can interpret it correctly.
|
||||
*
|
||||
* The following example shows how to configure ng:pluralize:
|
||||
*
|
||||
* <pre>
|
||||
* <ng:pluralize count="personCount"
|
||||
when="{'0': 'Nobody is viewing.',
|
||||
* 'one': '1 person is viewing.',
|
||||
* 'other': '{} people are viewing.'}">
|
||||
* </ng:pluralize>
|
||||
*</pre>
|
||||
*
|
||||
* In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
|
||||
* specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
|
||||
* would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
|
||||
* other numbers, for example 12, so that instead of showing "12 people are viewing", you can
|
||||
* show "a dozen people are viewing".
|
||||
*
|
||||
* You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted
|
||||
* into pluralized strings. In the previous example, Angular will replace `{}` with
|
||||
* <span ng:non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
|
||||
* for <span ng:non-bindable>{{numberExpression}}</span>.
|
||||
*
|
||||
* # Configuring ng:pluralize with offset
|
||||
* The `offset` attribute allows further customization of pluralized text, which can result in
|
||||
* a better user experience. For example, instead of the message "4 people are viewing this document",
|
||||
* you might display "John, Kate and 2 others are viewing this document".
|
||||
* The offset attribute allows you to offset a number by any desired value.
|
||||
* Let's take a look at an example:
|
||||
*
|
||||
* <pre>
|
||||
* <ng:pluralize count="personCount" offset=2
|
||||
* when="{'0': 'Nobody is viewing.',
|
||||
* '1': '{{person1}} is viewing.',
|
||||
* '2': '{{person1}} and {{person2}} are viewing.',
|
||||
* 'one': '{{person1}}, {{person2}} and one other person are viewing.',
|
||||
* 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
|
||||
* </ng:pluralize>
|
||||
* </pre>
|
||||
*
|
||||
* Notice that we are still using two plural categories(one, other), but we added
|
||||
* three explicit number rules 0, 1 and 2.
|
||||
* When one person, perhaps John, views the document, "John is viewing" will be shown.
|
||||
* When three people view the document, no explicit number rule is found, so
|
||||
* an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
|
||||
* In this case, plural category 'one' is matched and "John, Marry and one other person are viewing"
|
||||
* is shown.
|
||||
*
|
||||
* Note that when you specify offsets, you must provide explicit number rules for
|
||||
* numbers from 0 up to and including the offset. If you use an offset of 3, for example,
|
||||
* you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
|
||||
* plural categories "one" and "other".
|
||||
*
|
||||
* @param {string|expression} count The variable to be bounded to.
|
||||
* @param {string} when The mapping between plural category to its correspoding strings.
|
||||
* @param {number=} offset Offset to deduct from the total number.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Person 1:<input type="text" name="person1" value="Igor" /><br/>
|
||||
Person 2:<input type="text" name="person2" value="Misko" /><br/>
|
||||
Number of People:<input type="text" name="personCount" value="1" /><br/>
|
||||
|
||||
<!--- Example with simple pluralization rules for en locale --->
|
||||
Without Offset:
|
||||
<ng:pluralize count="personCount"
|
||||
when="{'0': 'Nobody is viewing.',
|
||||
'one': '1 person is viewing.',
|
||||
'other': '{} people are viewing.'}">
|
||||
</ng:pluralize><br>
|
||||
|
||||
<!--- Example with offset --->
|
||||
With Offset(2):
|
||||
<ng:pluralize count="personCount" offset=2
|
||||
when="{'0': 'Nobody is viewing.',
|
||||
'1': '{{person1}} is viewing.',
|
||||
'2': '{{person1}} and {{person2}} are viewing.',
|
||||
'one': '{{person1}}, {{person2}} and one other person are viewing.',
|
||||
'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
|
||||
</ng:pluralize>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show correct pluralized string', function(){
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
toBe('1 person is viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
toBe('Igor is viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('0');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
toBe('Nobody is viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
toBe('Nobody is viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('2');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
toBe('2 people are viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
toBe('Igor and Misko are viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('3');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
toBe('3 people are viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and one other person are viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('4');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
toBe('4 people are viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and 2 other people are viewing.');
|
||||
});
|
||||
|
||||
it('should show data-binded names', function(){
|
||||
using('.doc-example-live').input('personCount').enter('4');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and 2 other people are viewing.');
|
||||
|
||||
using('.doc-example-live').input('person1').enter('Di');
|
||||
using('.doc-example-live').input('person2').enter('Vojta');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
toBe('Di, Vojta and 2 other people are viewing.');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('ng:pluralize', function(element) {
|
||||
var numberExp = element.attr('count'),
|
||||
whenExp = element.attr('when'),
|
||||
offset = element.attr('offset') || 0;
|
||||
|
||||
return annotate('$locale', function($locale, element) {
|
||||
var scope = this,
|
||||
whens = scope.$eval(whenExp),
|
||||
whensExpFns = {};
|
||||
|
||||
forEach(whens, function(expression, key) {
|
||||
whensExpFns[key] = compileBindTemplate(expression.replace(/{}/g,
|
||||
'{{' + numberExp + '-' + offset + '}}'));
|
||||
});
|
||||
|
||||
scope.$watch(function() {
|
||||
var value = parseFloat(scope.$eval(numberExp));
|
||||
|
||||
if (!isNaN(value)) {
|
||||
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
|
||||
//check it against pluralization rules in $locale service
|
||||
if (!whens[value]) value = $locale.pluralCat(value - offset);
|
||||
return whensExpFns[value](scope, element, true);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}, function(scope, newVal) {
|
||||
element.text(newVal);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1236,5 +1236,131 @@ describe("widget", function(){
|
|||
expect(rootScope.log).toEqual(['parent', 'init', 'child']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ng:pluralize', function() {
|
||||
describe('deal with pluralized strings without offset', function() {
|
||||
beforeEach(function() {
|
||||
compile('<ng:pluralize count="email"' +
|
||||
"when=\"{'0': 'You have no new email'," +
|
||||
"'one': 'You have one new email'," +
|
||||
"'other': 'You have {} new emails'}\">" +
|
||||
'</ng:pluralize>');
|
||||
});
|
||||
|
||||
it('should show single/plural strings', function() {
|
||||
scope.email = 0;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have no new email');
|
||||
|
||||
scope.email = '0';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have no new email');
|
||||
|
||||
scope.email = 1;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have one new email');
|
||||
|
||||
scope.email = 0.01;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have 0.01 new emails');
|
||||
|
||||
scope.email = '0.1';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have 0.1 new emails');
|
||||
|
||||
scope.email = 2;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have 2 new emails');
|
||||
|
||||
scope.email = -0.1;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have -0.1 new emails');
|
||||
|
||||
scope.email = '-0.01';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have -0.01 new emails');
|
||||
|
||||
scope.email = -2;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have -2 new emails');
|
||||
});
|
||||
|
||||
|
||||
it('should show single/plural strings with mal-formed inputs', function() {
|
||||
scope.email = '';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('');
|
||||
|
||||
scope.email = null;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('');
|
||||
|
||||
scope.email = undefined;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('');
|
||||
|
||||
scope.email = 'a3';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('');
|
||||
|
||||
scope.email = '011';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have 11 new emails');
|
||||
|
||||
scope.email = '-011';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have -11 new emails');
|
||||
|
||||
scope.email = '1fff';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have one new email');
|
||||
|
||||
scope.email = '0aa22';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have no new email');
|
||||
|
||||
scope.email = '000001';
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('You have one new email');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('deal with pluralized strings with offset', function() {
|
||||
it('should show single/plural strings with offset', function() {
|
||||
compile("<ng:pluralize count=\"viewCount\" offset=2 " +
|
||||
"when=\"{'0': 'Nobody is viewing.'," +
|
||||
"'1': '{{p1}} is viewing.'," +
|
||||
"'2': '{{p1}} and {{p2}} are viewing.'," +
|
||||
"'one': '{{p1}}, {{p2}} and one other person are viewing.'," +
|
||||
"'other': '{{p1}}, {{p2}} and {} other people are viewing.'}\">" +
|
||||
"</ng:pluralize>");
|
||||
scope.p1 = 'Igor';
|
||||
scope.p2 = 'Misko';
|
||||
|
||||
scope.viewCount = 0;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('Nobody is viewing.');
|
||||
|
||||
scope.viewCount = 1;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('Igor is viewing.');
|
||||
|
||||
scope.viewCount = 2;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('Igor and Misko are viewing.');
|
||||
|
||||
scope.viewCount = 3;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('Igor, Misko and one other person are viewing.');
|
||||
|
||||
scope.viewCount = 4;
|
||||
scope.$digest();
|
||||
expect(element.text()).toBe('Igor, Misko and 2 other people are viewing.');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue