@@ -18,8 +18,10 @@ application means providing translations and localized formats for the abstracte
18
18
Angular supports i18n/l10n for {@link ng.filter:date date}, {@link ng.filter:number number} and
19
19
{@link ng.filter:currency currency} filters.
20
20
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.
23
25
24
26
All localizable Angular components depend on locale-specific rule sets managed by the {@link
25
27
ng.$locale `$locale` service}.
@@ -142,96 +144,200 @@ displaying the date with a timezone specified by the developer.
142
144
<a name="MessageFormat"></a>
143
145
## MessageFormat extensions
144
146
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 the 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
+ ```
148
181
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 .
151
184
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).
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 dependent [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.)
154
215
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
216
217
+ #### Messages
218
+
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.
187
222
188
223
### Simple plural example
189
224
190
- ```
225
+ ```text
191
226
{{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}
195
230
}}
196
231
```
197
232
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:
201
235
202
- ```
236
+ ```text
203
237
{{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}
207
241
}}
208
242
```
209
243
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
244
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 will need to escape curly braces or the `#` character inside message texts if you want them to
271
+ be treated literally with no special meaning. You may quote/escape any character in your message
272
+ text 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.
218
275
219
276
220
- ### Simple select (for gender) example
277
+ ### Gender (aka select) Syntax
221
278
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 the 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
+ }}
222
291
```
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
223
320
{{friendGender, select,
224
- male { Invite him }
225
- female { Invite her }
226
- other { Invite them }
321
+ male {Invite him}
322
+ female {Invite her}
323
+ other {Invite them}
227
324
}}
228
325
```
229
326
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 whole and not be split up.
335
+
336
+ ### More complex example that demonstrates nesting
231
337
232
338
This is taken from the [plunker example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9RyR?p=preview) linked to earlier.
233
339
234
- ```
340
+ ```text
235
341
{{recipients.length, plural, offset:1
236
342
=0 {You ({{sender.name}}) gave no gifts}
237
343
=1 { {{ recipients[0].gender, select,
@@ -249,3 +355,26 @@ This is taken from the [plunker example](http://plnkr.co/edit/QBVRQ70dvKZDWmHW9R
249
355
other {You ({{sender.name}}) gave {{recipients.length}} people gifts. }
250
356
}}
251
357
```
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!
376
+
377
+ ### Further Reading
378
+ For more details, please refer to our [design doc](https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit).
379
+ You can read more about the ICU MessageFormat syntax at
380
+ [Formatting Messages | ICU User Guide](http://userguide.icu-project.org/formatparse/messages#TOC-MessageFormat).
0 commit comments