Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cbfd8f6

Browse files
committedMar 16, 2015
feat($interpolate): MessageFormat extensions
Extend interpolation with MessageFormat like syntax. Ref: <https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit> Example: ```html {{recipients.length, plural, offset:1 =0 {You gave no gifts} =1 { {{ recipients[0].gender, select, male {You gave him a gift.} female {You gave her a gift.} other {You gave them a gift.} }} } one { {{ recipients[0].gender, select, male {You gave him and one other person a gift.} female {You gave her and one other person a gift.} other {You gave them and one other person a gift.} }} } other {You gave {{recipients[0].gender}} and # other people gifts. } }} ``` This is a SEPARATE module so you MUST include angular-messageformat.min.js. In addition, your application module should depend on the "ngMessageFormat". (e.g. angular.module('myApp', ['ngMessageFormat']);) $interpolate automatically gets the new behavior. Quick note on syntax differences from MessageFormat: - MessageFormat directives are always inside {{ }} instead of single { }. This ensures a consistent interpolation syntax (else you could interpolate in more than one way and have to pick one based on feature availability for that syntax.) - The first word inside such syntax can be an arbitrary Angular expression instead of a single identifier. - You can nest them as deep as you want. As mentioned earlier, you would use {{ }} to start the nested interpolation that may optionally include select/plural extensions. - Only "select" and "plural" keywords are currently recognized. - Quoting support is coming in a future commit. - Positional arguments/placeholders are not supported. They don't make sense in Angular templates anyway (they are only helpful when using API calls from a programming language.) - Redefining of the startSymbol and endSymbol used for interpolation is not currently supported yet.
1 parent d8492f4 commit cbfd8f6

26 files changed

+1916
-12
lines changed
 

‎Gruntfile.js

+8
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ module.exports = function(grunt) {
126126
ngLocale: {
127127
files: { src: 'src/ngLocale/**/*.js' },
128128
},
129+
ngMessageFormat: {
130+
files: { src: 'src/ngMessageFormat/**/*.js' },
131+
},
129132
ngMessages: {
130133
files: { src: 'src/ngMessages/**/*.js' },
131134
},
@@ -200,6 +203,10 @@ module.exports = function(grunt) {
200203
dest: 'build/angular-resource.js',
201204
src: util.wrap(files['angularModules']['ngResource'], 'module')
202205
},
206+
messageformat: {
207+
dest: 'build/angular-messageFormat.js',
208+
src: util.wrap(files['angularModules']['ngMessageFormat'], 'module')
209+
},
203210
messages: {
204211
dest: 'build/angular-messages.js',
205212
src: util.wrap(files['angularModules']['ngMessages'], 'module')
@@ -232,6 +239,7 @@ module.exports = function(grunt) {
232239
animate: 'build/angular-animate.js',
233240
cookies: 'build/angular-cookies.js',
234241
loader: 'build/angular-loader.js',
242+
messageformat: 'build/angular-messageFormat.js',
235243
messages: 'build/angular-messages.js',
236244
touch: 'build/angular-touch.js',
237245
resource: 'build/angular-resource.js',

‎angularFiles.js

+9
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ var angularFiles = {
9494
'src/ngCookies/cookieStore.js',
9595
'src/ngCookies/cookieWriter.js'
9696
],
97+
'ngMessageFormat': [
98+
'src/ngMessageFormat/messageFormatCommon.js',
99+
'src/ngMessageFormat/messageFormatSelector.js',
100+
'src/ngMessageFormat/messageFormatInterpolationParts.js',
101+
'src/ngMessageFormat/messageFormatParser.js',
102+
'src/ngMessageFormat/messageFormatService.js'
103+
],
97104
'ngMessages': [
98105
'src/ngMessages/messages.js'
99106
],
@@ -184,6 +191,7 @@ var angularFiles = {
184191
'@angularSrcModules',
185192
'src/ngScenario/browserTrigger.js',
186193
'test/helpers/*.js',
194+
'test/ngMessageFormat/*.js',
187195
'test/ngMock/*.js',
188196
'test/ngCookies/*.js',
189197
'test/ngRoute/**/*.js',
@@ -212,6 +220,7 @@ var angularFiles = {
212220

213221
angularFiles['angularSrcModules'] = [].concat(
214222
angularFiles['angularModules']['ngAnimate'],
223+
angularFiles['angularModules']['ngMessageFormat'],
215224
angularFiles['angularModules']['ngMessages'],
216225
angularFiles['angularModules']['ngCookies'],
217226
angularFiles['angularModules']['ngResource'],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@ngdoc error
2+
@name $interpolate:badexpr
3+
@fullName Expecting end operator
4+
@description
5+
6+
The Angular expression is missing the corresponding closing operator.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@ngdoc error
2+
@name $interpolate:dupvalue
3+
@fullName Duplicate choice in plural/select
4+
@description
5+
6+
You have repeated a match selection for your plural or select MessageFormat
7+
extension in your interpolation expression. The different choices have to be unique.
8+
9+
For more information about the MessageFormat syntax in interpolation
10+
expressions, please refer to MessageFormat extensions section at
11+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@ngdoc error
2+
@name $interpolate:logicbug
3+
@fullName Bug in ngMessageFormat module
4+
@description
5+
6+
You've just hit a bug in the ngMessageFormat module provided by angular-messageFormat.min.js.
7+
Please file a github issue for this and provide the interpolation text that caused you to hit this
8+
bug mentioning the exact version of AngularJS used and we will fix it!
9+
10+
For more information about the MessageFormat syntax in interpolation
11+
expressions, please refer to MessageFormat extensions section at
12+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@ngdoc error
2+
@name $interpolate:nochgmustache
3+
@fullName Redefinition of start/endSymbol incompatible with MessageFormat extensions
4+
@description
5+
6+
You have redefined `$interpolate.startSymbol`/`$interpolate.endSymbol` and also
7+
loaded the `ngMessageFormat` module (provided by angular-messageFormat.min.js)
8+
while creating your injector.
9+
10+
`ngMessageFormat` currently does not support redefinition of the
11+
startSymbol/endSymbol used by `$interpolate`. If this is affecting you, please
12+
file an issue and mention @chirayuk on it. This is intended to be fixed in a
13+
future commit and the github issue will help gauge urgency.
14+
15+
For more information about the MessageFormat syntax in interpolation
16+
expressions, please refer to MessageFormat extensions section at
17+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@ngdoc error
2+
@name $interpolate:reqarg
3+
@fullName Missing required argument for MessageFormat
4+
@description
5+
6+
You must specify the MessageFormat function that you're using right after the
7+
comma following the Angular expression. Currently, the supported functions are
8+
"plural" and "select" (for gender selections.)
9+
10+
For more information about the MessageFormat syntax in interpolation
11+
expressions, please refer to MessageFormat extensions section at
12+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@ngdoc error
2+
@name $interpolate:reqcomma
3+
@fullName Missing comma following MessageFormat plural/select keyword
4+
@description
5+
6+
The MessageFormat syntax requires a comma following the "plural" or "select"
7+
extension keyword in the extended interpolation syntax.
8+
9+
For more information about the MessageFormat syntax in interpolation
10+
expressions, please refer to MessageFormat extensions section at
11+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@ngdoc error
2+
@name $interpolate:reqendbrace
3+
@fullName Unterminated message for plural/select value
4+
@description
5+
6+
The plural or select message for a value or keyword choice has no matching end
7+
brace to mark the end of the message.
8+
9+
For more information about the MessageFormat syntax in interpolation
10+
expressions, please refer to MessageFormat extensions section at
11+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@ngdoc error
2+
@name $interpolate:reqendinterp
3+
@fullName Unterminated interpolation
4+
@description
5+
6+
The interpolation text does not have an ending `endSymbol` ("}}" by default) and is unterminated.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@ngdoc error
2+
@name $interpolate:reqopenbrace
3+
@fullName An opening brace was expected but not found
4+
@description
5+
6+
The plural or select extension keyword or values (such as "other", "male",
7+
"female", "=0", "one", "many", etc.) MUST be followed by a message enclosed in
8+
braces.
9+
10+
For more information about the MessageFormat syntax in interpolation
11+
expressions, please refer to MessageFormat extensions section at
12+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@ngdoc error
2+
@name $interpolate:reqother
3+
@fullName Required choice "other" for select/plural in MessageFormat
4+
@description
5+
6+
Your interpolation expression with a MessageFormat extension for either
7+
"plural" or "select" (typically used for gender selection) does not contain a
8+
message for the choice "other". Using either select or plural MessageFormat
9+
extensions require that you provide a message for the selection "other".
10+
11+
For more information about the MessageFormat syntax in interpolation
12+
expressions, please refer to MessageFormat extensions section at
13+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@ngdoc error
2+
@name $interpolate:unknarg
3+
@fullName Unrecognized MessageFormat extension
4+
@description
5+
6+
The MessageFormat extensions provided by `ngMessageFormat` are currently
7+
limited to "plural" and "select". The extension that you have used is either
8+
unsupported or invalid.
9+
10+
For more information about the MessageFormat syntax in interpolation
11+
expressions, please refer to MessageFormat extensions section at
12+
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@ngdoc error
2+
@name $interpolate:unsafe
3+
@fullName MessageFormat extensions not allowed in secure context
4+
@description
5+
6+
You have attempted to use a MessageFormat extension in your interpolation expression that is marked as a secure context. For security purposes, this is not supported.
7+
8+
Read more about secure contexts at {@link ng.$sce Strict Contextual Escaping
9+
(SCE)} and about the MessageFormat extensions at {@link
10+
guide/i18n#MessageFormat Angular i18n MessageFormat}.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@ngdoc error
2+
@name $interpolate:untermstr
3+
@fullName Unterminated string literal
4+
@description
5+
6+
The string literal was not terminated in your Angular expression.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@ngdoc error
2+
@name $interpolate:wantstring
3+
@fullName Expected the beginning of a string
4+
@description
5+
6+
We expected to see the beginning of a string (either a single quote or a double
7+
quote character) in the expression but it was not found. The expression is
8+
invalid. If this is incorrect, please file an issue on github.

‎docs/content/guide/i18n.ngdoc

+112
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,115 @@ The Angular datetime filter uses the time zone settings of the browser. The same
137137
application will show different time information depending on the time zone settings of the
138138
computer that the application is running on. Neither JavaScript nor Angular currently supports
139139
displaying the date with a timezone specified by the developer.
140+
141+
142+
<a name="MessageFormat"></a>
143+
## MessageFormat extensions
144+
145+
AngularJS interpolations via `$interpolate` and in templates
146+
support an extended syntax based on a subset of the ICU
147+
MessageFormat that covers plurals and gender selections.
148+
149+
Please refer to our [design doc](https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit)
150+
for a lot more details. You may find it helpful to play with our [Plnkr Example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9RyR?p=preview).
151+
152+
You can read more about the ICU MessageFormat syntax at
153+
[Formatting Messages | ICU User Guide](http://userguide.icu-project.org/formatparse/messages#TOC-MessageFormat).
154+
155+
This extended syntax is provided by way of the
156+
`ngMessageFormat` module that your application can depend
157+
upon (shipped separately as `angular-messageFormat.min.js` and
158+
`angular-messageFormat.js`.) A current limitation of the
159+
`ngMessageFormat` module, is that it does not support
160+
redefining the `$interpolate` start and end symbols. Only the
161+
default `{{` and `}}` are allowed.
162+
163+
This syntax extension, while based on MessageFormat, has
164+
been designed to be backwards compatible with existing
165+
AngularJS interpolation expressions. The key rule is simply
166+
this: **All interpolations are done inside double curlies.**
167+
The top level comma operator after an expression inside the
168+
double curlies causes MessageFormat extensions to be
169+
recognized. Such a top level comma is otherwise illegal in
170+
an Angular expression and is used by MessageFormat to
171+
specify the function (such as plural/select) and it's
172+
related syntax.
173+
174+
To understand the extension, take a look at the ICU
175+
MessageFormat syntax as specified by the ICU documentation.
176+
Anywhere in that MessageFormat that you have regular message
177+
text and you want to substitute an expression, just put it
178+
in double curlies instead of single curlies that
179+
MessageFormat dictates. This has a huge advantage. **You
180+
are no longer limited to simple identifiers for
181+
substitutions**. Because you are using double curlies, you
182+
can stick in any arbitrary interpolation syntax there,
183+
including nesting more MessageFormat expressions! Some
184+
examples will make this clear. In the following example, I
185+
will only be showing you the AngularJS syntax.
186+
187+
188+
### Simple plural example
189+
190+
```
191+
{{numMessages, plural,
192+
=0 { You have no new messages }
193+
=1 { You have one new message }
194+
other { You have # new messages }
195+
}}
196+
```
197+
198+
While I won't be teaching you MessageFormat here, you will
199+
note that the `#` symbol works as expected. You could have
200+
also written it as:
201+
202+
```
203+
{{numMessages, plural,
204+
=0 { You have no new messages }
205+
=1 { You have one new message }
206+
other { You have {{numMessages}} new messages }
207+
}}
208+
```
209+
210+
where you explicitly typed in `numMessages` for "other"
211+
instead of using `#`. They are nearly the same except if
212+
you're using "offset". Refer to the ICU MessageFormat
213+
documentation to learn about offset.
214+
215+
Please note that **other** is a **required** category (for
216+
both the plural syntax and the select syntax that is shown
217+
later.)
218+
219+
220+
### Simple select (for gender) example
221+
222+
```
223+
{{friendGender, select,
224+
male { Invite him }
225+
female { Invite her }
226+
other { Invite them }
227+
}}
228+
```
229+
230+
### More complex example that combines some of these
231+
232+
This is taken from the [plunker example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9RyR?p=preview) linked to earlier.
233+
234+
```
235+
{{recipients.length, plural, offset:1
236+
=0 {You ({{sender.name}}) gave no gifts}
237+
=1 { {{ recipients[0].gender, select,
238+
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) a gift.}
239+
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) a gift.}
240+
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) a gift.}
241+
}}
242+
}
243+
one { {{ recipients[0].gender, select,
244+
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) and one other person a gift.}
245+
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) and one other person a gift.}
246+
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) and one other person a gift.}
247+
}}
248+
}
249+
other {You ({{sender.name}}) gave {{recipients.length}} people gifts. }
250+
}}
251+
```

‎lib/grunt/utils.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,16 @@ module.exports = {
185185
var mapFileName = mapFile.match(/[^\/]+$/)[0];
186186
var errorFileName = file.replace(/\.js$/, '-errors.json');
187187
var versionNumber = grunt.config('NG_VERSION').full;
188+
var compilationLevel = (file === 'build/angular-messageFormat.js') ?
189+
'ADVANCED_OPTIMIZATIONS' : 'SIMPLE_OPTIMIZATIONS';
188190
shell.exec(
189191
'java ' +
190192
this.java32flags() + ' ' +
191193
'-Xmx2g ' +
192194
'-cp bower_components/closure-compiler/compiler.jar' + classPathSep +
193195
'bower_components/ng-closure-runner/ngcompiler.jar ' +
194196
'org.angularjs.closurerunner.NgClosureRunner ' +
195-
'--compilation_level SIMPLE_OPTIMIZATIONS ' +
197+
'--compilation_level ' + compilationLevel + ' ' +
196198
'--language_in ECMASCRIPT5_STRICT ' +
197199
'--minerr_pass ' +
198200
'--minerr_errors ' + errorFileName + ' ' +

0 commit comments

Comments
 (0)
This repository has been archived.