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

Commit

Permalink
feat($interpolate): MessageFormat extensions
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chirayuk committed Mar 16, 2015
1 parent bfd7b22 commit c37f18d
Show file tree
Hide file tree
Showing 26 changed files with 1,916 additions and 12 deletions.
8 changes: 8 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ module.exports = function(grunt) {
ngLocale: {
files: { src: 'src/ngLocale/**/*.js' },
},
ngMessageFormat: {
files: { src: 'src/ngMessageFormat/**/*.js' },
},
ngMessages: {
files: { src: 'src/ngMessages/**/*.js' },
},
Expand Down Expand Up @@ -200,6 +203,10 @@ module.exports = function(grunt) {
dest: 'build/angular-resource.js',
src: util.wrap(files['angularModules']['ngResource'], 'module')
},
messageformat: {
dest: 'build/angular-messageFormat.js',
src: util.wrap(files['angularModules']['ngMessageFormat'], 'module')
},
messages: {
dest: 'build/angular-messages.js',
src: util.wrap(files['angularModules']['ngMessages'], 'module')
Expand Down Expand Up @@ -232,6 +239,7 @@ module.exports = function(grunt) {
animate: 'build/angular-animate.js',
cookies: 'build/angular-cookies.js',
loader: 'build/angular-loader.js',
messageformat: 'build/angular-messageFormat.js',
messages: 'build/angular-messages.js',
touch: 'build/angular-touch.js',
resource: 'build/angular-resource.js',
Expand Down
9 changes: 9 additions & 0 deletions angularFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ var angularFiles = {
'ngCookies': [
'src/ngCookies/cookies.js'
],
'ngMessageFormat': [
'src/ngMessageFormat/messageFormatCommon.js',
'src/ngMessageFormat/messageFormatSelector.js',
'src/ngMessageFormat/messageFormatInterpolationParts.js',
'src/ngMessageFormat/messageFormatParser.js',
'src/ngMessageFormat/messageFormatService.js'
],
'ngMessages': [
'src/ngMessages/messages.js'
],
Expand Down Expand Up @@ -181,6 +188,7 @@ var angularFiles = {
'@angularSrcModules',
'src/ngScenario/browserTrigger.js',
'test/helpers/*.js',
'test/ngMessageFormat/*.js',
'test/ngMock/*.js',
'test/ngCookies/*.js',
'test/ngRoute/**/*.js',
Expand Down Expand Up @@ -209,6 +217,7 @@ var angularFiles = {

angularFiles['angularSrcModules'] = [].concat(
angularFiles['angularModules']['ngAnimate'],
angularFiles['angularModules']['ngMessageFormat'],
angularFiles['angularModules']['ngMessages'],
angularFiles['angularModules']['ngCookies'],
angularFiles['angularModules']['ngResource'],
Expand Down
6 changes: 6 additions & 0 deletions docs/content/error/$interpolate/badexpr.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@ngdoc error
@name $interpolate:badexpr
@fullName Expecting end operator
@description

The Angular expression is missing the corresponding closing operator.
11 changes: 11 additions & 0 deletions docs/content/error/$interpolate/dupvalue.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@ngdoc error
@name $interpolate:dupvalue
@fullName Duplicate choice in plural/select
@description

You have repeated a match selection for your plural or select MessageFormat
extension in your interpolation expression. The different choices have to be unique.

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
12 changes: 12 additions & 0 deletions docs/content/error/$interpolate/logicbug.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@ngdoc error
@name $interpolate:logicbug
@fullName Bug in ngMessageFormat module
@description

You've just hit a bug in the ngMessageFormat module provided by angular-messageFormat.min.js.
Please file a github issue for this and provide the interpolation text that caused you to hit this
bug mentioning the exact version of AngularJS used and we will fix it!

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
17 changes: 17 additions & 0 deletions docs/content/error/$interpolate/nochgmustache.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@ngdoc error
@name $interpolate:nochgmustache
@fullName Redefinition of start/endSymbol incompatible with MessageFormat extensions
@description

You have redefined `$interpolate.startSymbol`/`$interpolate.endSymbol` and also
loaded the `ngMessageFormat` module (provided by angular-messageFormat.min.js)
while creating your injector.

`ngMessageFormat` currently does not support redefinition of the
startSymbol/endSymbol used by `$interpolate`. If this is affecting you, please
file an issue and mention @chirayuk on it. This is intended to be fixed in a
future commit and the github issue will help gauge urgency.

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
12 changes: 12 additions & 0 deletions docs/content/error/$interpolate/reqarg.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@ngdoc error
@name $interpolate:reqarg
@fullName Missing required argument for MessageFormat
@description

You must specify the MessageFormat function that you're using right after the
comma following the Angular expression. Currently, the supported functions are
"plural" and "select" (for gender selections.)

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
11 changes: 11 additions & 0 deletions docs/content/error/$interpolate/reqcomma.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@ngdoc error
@name $interpolate:reqcomma
@fullName Missing comma following MessageFormat plural/select keyword
@description

The MessageFormat syntax requires a comma following the "plural" or "select"
extension keyword in the extended interpolation syntax.

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
11 changes: 11 additions & 0 deletions docs/content/error/$interpolate/reqendbrace.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@ngdoc error
@name $interpolate:reqendbrace
@fullName Unterminated message for plural/select value
@description

The plural or select message for a value or keyword choice has no matching end
brace to mark the end of the message.

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
6 changes: 6 additions & 0 deletions docs/content/error/$interpolate/reqendinterp.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@ngdoc error
@name $interpolate:reqendinterp
@fullName Unterminated interpolation
@description

The interpolation text does not have an ending `endSymbol` ("}}" by default) and is unterminated.
12 changes: 12 additions & 0 deletions docs/content/error/$interpolate/reqopenbrace.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@ngdoc error
@name $interpolate:reqopenbrace
@fullName An opening brace was expected but not found
@description

The plural or select extension keyword or values (such as "other", "male",
"female", "=0", "one", "many", etc.) MUST be followed by a message enclosed in
braces.

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
13 changes: 13 additions & 0 deletions docs/content/error/$interpolate/reqother.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@ngdoc error
@name $interpolate:reqother
@fullName Required choice "other" for select/plural in MessageFormat
@description

Your interpolation expression with a MessageFormat extension for either
"plural" or "select" (typically used for gender selection) does not contain a
message for the choice "other". Using either select or plural MessageFormat
extensions require that you provide a message for the selection "other".

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
12 changes: 12 additions & 0 deletions docs/content/error/$interpolate/unknarg.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@ngdoc error
@name $interpolate:unknarg
@fullName Unrecognized MessageFormat extension
@description

The MessageFormat extensions provided by `ngMessageFormat` are currently
limited to "plural" and "select". The extension that you have used is either
unsupported or invalid.

For more information about the MessageFormat syntax in interpolation
expressions, please refer to MessageFormat extensions section at
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
10 changes: 10 additions & 0 deletions docs/content/error/$interpolate/unsafe.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@ngdoc error
@name $interpolate:unsafe
@fullName MessageFormat extensions not allowed in secure context
@description

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.

Read more about secure contexts at {@link ng.$sce Strict Contextual Escaping
(SCE)} and about the MessageFormat extensions at {@link
guide/i18n#MessageFormat Angular i18n MessageFormat}.
6 changes: 6 additions & 0 deletions docs/content/error/$interpolate/untermstr.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@ngdoc error
@name $interpolate:untermstr
@fullName Unterminated string literal
@description

The string literal was not terminated in your Angular expression.
8 changes: 8 additions & 0 deletions docs/content/error/$interpolate/wantstring.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@ngdoc error
@name $interpolate:wantstring
@fullName Expected the beginning of a string
@description

We expected to see the beginning of a string (either a single quote or a double
quote character) in the expression but it was not found. The expression is
invalid. If this is incorrect, please file an issue on github.
112 changes: 112 additions & 0 deletions docs/content/guide/i18n.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,115 @@ The Angular datetime filter uses the time zone settings of the browser. The same
application will show different time information depending on the time zone settings of the
computer that the application is running on. Neither JavaScript nor Angular currently supports
displaying the date with a timezone specified by the developer.


<a name="MessageFormat"></a>
## MessageFormat extensions

AngularJS interpolations via `$interpolate` and in templates
support an extended syntax based on a subset of the ICU
MessageFormat that covers plurals and gender selections.

Please refer to our [design doc](https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit)
for a lot more details. You may find it helpful to play with our [Plnkr Example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9RyR?p=preview).

You can read more about the ICU MessageFormat syntax at
[Formatting Messages | ICU User Guide](http://userguide.icu-project.org/formatparse/messages#TOC-MessageFormat).

This extended syntax is provided by way of the
`ngMessageFormat` module that your application can depend
upon (shipped separately as `angular-messageFormat.min.js` and
`angular-messageFormat.js`.) A current limitation of the
`ngMessageFormat` module, is that it does not support
redefining the `$interpolate` start and end symbols. Only the
default `{{` and `}}` are allowed.

This syntax extension, while based on MessageFormat, has
been designed to be backwards compatible with existing
AngularJS interpolation expressions. The key rule is simply
this: **All interpolations are done inside double curlies.**
The top level comma operator after an expression inside the
double curlies causes MessageFormat extensions to be
recognized. Such a top level comma is otherwise illegal in
an Angular expression and is used by MessageFormat to
specify the function (such as plural/select) and it's
related syntax.

To understand the extension, take a look at the ICU
MessageFormat syntax as specified by the ICU documentation.
Anywhere in that MessageFormat that you have regular message
text and you want to substitute an expression, just put it
in double curlies instead of single curlies that
MessageFormat dictates. This has a huge advantage. **You
are no longer limited to simple identifiers for
substitutions**. Because you are using double curlies, you
can stick in any arbitrary interpolation syntax there,
including nesting more MessageFormat expressions! Some
examples will make this clear. In the following example, I
will only be showing you the AngularJS syntax.


### Simple plural example

```
{{numMessages, plural,
=0 { You have no new messages }
=1 { You have one new message }
other { You have # new messages }
}}
```

While I won't be teaching you MessageFormat here, you will
note that the `#` symbol works as expected. You could have
also written it as:

```
{{numMessages, plural,
=0 { You have no new messages }
=1 { You have one new message }
other { You have {{numMessages}} new messages }
}}
```

where you explicitly typed in `numMessages` for "other"
instead of using `#`. They are nearly the same except if
you're using "offset". Refer to the ICU MessageFormat
documentation to learn about offset.

Please note that **other** is a **required** category (for
both the plural syntax and the select syntax that is shown
later.)


### Simple select (for gender) example

```
{{friendGender, select,
male { Invite him }
female { Invite her }
other { Invite them }
}}
```

### More complex example that combines some of these

This is taken from the [plunker example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9RyR?p=preview) linked to earlier.

```
{{recipients.length, plural, offset:1
=0 {You ({{sender.name}}) gave no gifts}
=1 { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) a gift.}
}}
}
one { {{ recipients[0].gender, select,
male {You ({{sender.name}}) gave him ({{recipients[0].name}}) and one other person a gift.}
female {You ({{sender.name}}) gave her ({{recipients[0].name}}) and one other person a gift.}
other {You ({{sender.name}}) gave them ({{recipients[0].name}}) and one other person a gift.}
}}
}
other {You ({{sender.name}}) gave {{recipients.length}} people gifts. }
}}
```
4 changes: 3 additions & 1 deletion lib/grunt/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,16 @@ module.exports = {
var mapFileName = mapFile.match(/[^\/]+$/)[0];
var errorFileName = file.replace(/\.js$/, '-errors.json');
var versionNumber = grunt.config('NG_VERSION').full;
var compilationLevel = (file === 'build/angular-messageFormat.js') ?
'ADVANCED_OPTIMIZATIONS' : 'SIMPLE_OPTIMIZATIONS';
shell.exec(
'java ' +
this.java32flags() + ' ' +
'-Xmx2g ' +
'-cp bower_components/closure-compiler/compiler.jar' + classPathSep +
'bower_components/ng-closure-runner/ngcompiler.jar ' +
'org.angularjs.closurerunner.NgClosureRunner ' +
'--compilation_level SIMPLE_OPTIMIZATIONS ' +
'--compilation_level ' + compilationLevel + ' ' +
'--language_in ECMASCRIPT5_STRICT ' +
'--minerr_pass ' +
'--minerr_errors ' + errorFileName + ' ' +
Expand Down
Loading

0 comments on commit c37f18d

Please sign in to comment.