@@ -18,8 +18,10 @@ application means providing translations and localized formats for the abstracte
1818Angular supports i18n/l10n for {@link ng.filter:date date}, {@link ng.filter:number number} and
1919{@link ng.filter:currency currency} filters.
2020
21- Additionally, Angular supports localizable pluralization support through the {@link
22- ng.directive:ngPluralize `ngPluralize` directive}.
21+ Localizable pluralization is supported via the {@link ng.directive:ngPluralize `ngPluralize`
22+ directive}. Additionally, you can use <a href="#MessageFormat">MessageFormat extensions</a> to
23+ `$interpolate` for localizable pluralization and gender support in all interpolations via the
24+ `ngMessageFormat` module.
2325
2426All localizable Angular components depend on locale-specific rule sets managed by the {@link
2527ng.$locale `$locale` service}.
@@ -142,96 +144,200 @@ displaying the date with a timezone specified by the developer.
142144<a name="MessageFormat"></a>
143145## MessageFormat extensions
144146
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.
147+ You can write localizable plural and gender based messages in Angular interpolation expressions and
148+ `$interpolate` calls.
149+
150+ This syntax extension is provided by way of the `ngMessageFormat` module that your application can
151+ depend upon (shipped separately as `angular-messageFormat.min.js` and `angular-messageFormat.js`.)
152+ A current limitation of the `ngMessageFormat` module, is that it does not support redefining the
153+ `$interpolate` start and end symbols. Only the default `{{` and `}}` are allowed.
154+
155+ The syntax extension is based on a subset of the ICU MessageFormat syntax that covers plurals and
156+ gender selections. Please refer to the links in the “Further Reading” section at the bottom of this
157+ section.
158+
159+ You may find it helpful to play with our [Plnkr Example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9RyR?p=preview)
160+ as you read the examples below.
161+
162+ ### Plural Syntax
163+
164+ The syntax for plural based message selection looks like this following:
165+
166+ ```text
167+ {{NUMERIC_EXPRESSION, plural,
168+ =0 {MESSAGE_WHEN_VALUE_IS_0}
169+ =1 {MESSAGE_WHEN_VALUE_IS_1}
170+ =2 {MESSAGE_WHEN_VALUE_IS_2}
171+ =3 {MESSAGE_WHEN_VALUE_IS_3}
172+ ...
173+ zero {MESSAGE_WHEN_PLURAL_CATEGORY_IS_ZERO}
174+ one {MESSAGE_WHEN_PLURAL_CATEGORY_IS_ONE}
175+ two {MESSAGE_WHEN_PLURAL_CATEGORY_IS_TWO}
176+ few {MESSAGE_WHEN_PLURAL_CATEGORY_IS_FEW}
177+ many {MESSAGE_WHEN_PLURAL_CATEGORY_IS_MANY}
178+ other {MESSAGE_WHEN_THERE_IS_NO_MATCH}
179+ }}
180+ ```
148181
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).
182+ Please note that whitespace (including newline) is generally insignificant except as part of the
183+ actual message text that occurs in curly braces. Whitespace is generally used to aid readability.
184+
185+ Here, `NUMERIC_EXPRESSION` is an expression that evaluates to a numeric value based on which the
186+ displayed message should change based on pluralization rules.
187+
188+ Following the Angular expression, you would denote the plural extension syntax by the `, plural,`
189+ syntax element. The spaces there are optional.
190+
191+ This is followed by a list of selection keyword and corresponding message pairs. The "other"
192+ keyword and corresponding message are **required** but you may have as few or as many of the other
193+ categories as you need.
194+
195+ #### Selection Keywords
196+
197+ The selection keywords can be either exact matches or language dependant [plural
198+ categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
199+
200+ Exact matches are written as the equal sign followed by the exact value. `=0`, `=1`, `=2` and
201+ `=123` are all examples of exact matches. Note that there should be no space between the equal sign
202+ and the numeric value.
203+
204+ Plural category matches are single words corresponding to the [plural
205+ categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) of
206+ the CLDR plural category spec. These categories vary by locale. The "en" (English) locale, for
207+ example, defines just "one" and "other" while the "ga" (Irish) locale defines "one", "two", "few",
208+ "many" and "other". Typically, you would just write the categories for your language. During
209+ translation, the translators will add or remove more categories depending on the target locale.
210+
211+ Exact matches always win over keyword matches. Therefore, if you define both `=0` and `zero`, when
212+ the value of the expression is zero, the `=0` message is the one that will be selected. (The
213+ duplicate keyword categories are helpful when used with the optional `offset` syntax described
214+ later.)
151215
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).
154216
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.
217+ #### Messages
186218
219+ Messages immediately follow a selection keyword and are optionally preceded by whitespace. They are
220+ written in single curly braces (`{}`). They may contain Angular interpolation syntax inside them.
221+ In addition, the `#` symbol is a placeholder for the actual numeric value of the expression.
187222
188223### Simple plural example
189224
190- ```
225+ ```text
191226{{numMessages, plural,
192- =0 { You have no new messages }
193- =1 { You have one new message }
194- other { You have # new messages }
227+ =0 {You have no new messages}
228+ =1 {You have one new message}
229+ other {You have # new messages}
195230}}
196231```
197232
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:
233+ Because these messages can themselves contain Angular expressions, you could also write this as
234+ follows:
201235
202- ```
236+ ```text
203237{{numMessages, plural,
204- =0 { You have no new messages }
205- =1 { You have one new message }
206- other { You have {{numMessages}} new messages }
238+ =0 {You have no new messages}
239+ =1 {You have one new message}
240+ other {You have {{numMessages}} new messages}
207241}}
208242```
209243
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.
214244
215- Please note that **other** is a **required** category (for
216- both the plural syntax and the select syntax that is shown
217- later.)
245+ ### Plural syntax with optional `offset`
246+
247+ The plural syntax supports an optional `offset` syntax that is used in matching. It's simpler to
248+ explain this with an example.
249+
250+ ```text
251+ {{recipients.length, plural, offset:1
252+ =0 {You gave no gifts}
253+ =1 {You gave {{recipients[0].name}} a gift}
254+ one {You gave {{recipients[0].name}} and one other person a gift}
255+ other {You gave {{recipients[0].name}} and # other people a gift}
256+ }}
257+ ```
258+
259+ When an `offset` is specified, the matching works as follows. First, the exact value of the Angular
260+ expression is matched against the exact matches (i.e. `=N` selectors) to find a match. If there is
261+ one, that message is used. If there was no match, then the offset value is subtracted from the
262+ value of the expression and locale specific pluralization rules are applied to this new value to
263+ obtain its plural category (such as “one”, “few”, “many”, etc.) and a match is attempted against the
264+ keyword selectors and the matching message is used. If there was no match, then the “other”
265+ category (required) is used. The value of the `#` character inside a message is the value of
266+ original expression reduced by the offset value that was specified.
267+
268+ ### Escaping / Quoting
269+
270+ You might need to quote the curly braces or the `#` character inside the message texts so that they
271+ are treated literally with no special meaning. To support such cases, you may escape any character
272+ by preceding it with a `\` (backslash) character. The backslash character removes any special
273+ meaning to the character that immediately follows it. Therefore, you can escape or quote the
274+ backslash itself by preceding it with another backslash character.
218275
219276
220- ### Simple select (for gender) example
277+ ### Gender (aka select) Syntax
221278
279+ The gender support is provided by the more generic "select" syntax that is more akin to a switch
280+ statement. It is general enough to support use for gender based messages.
281+
282+ The syntax for gender based message selection looks like this following:
283+
284+ ```text
285+ {{EXPRESSION, select,
286+ male {MESSAGE_WHEN_EXPRESSION_IS_MALE}
287+ female {MESSAGE_WHEN_EXPRESSION_IS_FEMALE}
288+ ...
289+ other {MESSAGE_WHEN_THERE_IS_NO_GENDER_MATCH}
290+ }}
222291```
292+
293+ Please note that whitespace (including newline) is generally insignificant except as part of the
294+ actual message text that occurs in curly braces. Whitespace is generally used to aid readability.
295+
296+ Here, `EXPRESSION` is an Angular expression that evaluates to the gender of the person that
297+ is used to select the message that should be displayed.
298+
299+ The Angular expression is followed by `, select,` where the spaces are optional.
300+
301+ This is followed by a list of selection keyword and corresponding message pairs. The "other"
302+ keyword and corresponding message are **required** but you may have as few or as many of the other
303+ gender values as you need (i.e. it isn't restricted to male/female.) Note however, that the
304+ matching is **case-sensitive**.
305+
306+ #### Selection Keywords
307+
308+ Selection keywords are simple words like "male" and "female". The keyword, "other", and it's
309+ corresponding message are required while others are optional. It is used when the Angular
310+ expression does not match (case-insensitively) any of the other keywords specified.
311+
312+ #### Messages
313+
314+ Messages immediately follow a selection keyword and are optionally preceded by whitespace. They are
315+ written in single curly braces (`{}`). They may contain Angular interpolation syntax inside them.
316+
317+ ### Simple gender example
318+
319+ ```text
223320{{friendGender, select,
224321 male { Invite him }
225322 female { Invite her }
226323 other { Invite them }
227324}}
228325```
229326
230- ### More complex example that combines some of these
327+ ### Nesting
328+
329+ As mentioned in the syntax for plural and select, the embedded messages can contain Angular
330+ interpolation syntax. Since you can use MessageFormat extensions in Angular interpolation, this
331+ allows you to nest plural and gender expressions in any order.
332+
333+ Please note that if these are intended to reach a translator and be translated, it is recommended
334+ that the messages appear as a while and not be split up.
335+
336+ ### More complex example that demonstrates nesting
231337
232338This is taken from the [plunker example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9RyR?p=preview) linked to earlier.
233339
234- ```
340+ ```text
235341{{recipients.length, plural, offset:1
236342 =0 {You ({{sender.name}}) gave no gifts}
237343 =1 { {{ recipients[0].gender, select,
@@ -249,3 +355,27 @@ This is taken from the [plunker example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9R
249355 other {You ({{sender.name}}) gave {{recipients.length}} people gifts. }
250356}}
251357```
358+
359+ ### Differences from the ICU MessageFormat syntax
360+
361+ This section is useful to you if you're already familiar with the ICU MessageFormat syntax.
362+
363+ This syntax extension, while based on MessageFormat, has been designed to be backwards compatible
364+ with existing AngularJS interpolation expressions. The key rule is simply this: **All
365+ interpolations are done inside double curlies.** The top level comma operator after an expression
366+ inside the double curlies causes MessageFormat extensions to be recognized. Such a top level comma
367+ is otherwise illegal in an Angular expression and is used by MessageFormat to specify the function
368+ (such as plural/select) and it's related syntax.
369+
370+ To understand the extension, take a look at the ICU MessageFormat syntax as specified by the ICU
371+ documentation. Anywhere in that MessageFormat that you have regular message text and you want to
372+ substitute an expression, just put it in double curlies instead of single curlies that MessageFormat
373+ dictates. This has a huge advantage. **You are no longer limited to simple identifiers for
374+ substitutions**. Because you are using double curlies, you can stick in any arbitrary interpolation
375+ syntax there, including nesting more MessageFormat expressions! Some examples will make this clear.
376+ In the following example, I will only be showing you the AngularJS syntax.
377+
378+ ### Further Reading
379+ For more details, please refer to our [design doc](https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit)
380+ for a lot more details. You can read more about the ICU MessageFormat syntax at
381+ [Formatting Messages | ICU User Guide](http://userguide.icu-project.org/formatparse/messages#TOC-MessageFormat).
0 commit comments