|
| 1 | +--- |
| 2 | +layout: page |
| 3 | +title: Format Guide |
| 4 | +--- |
| 5 | + |
| 6 | +## No Frills |
| 7 | + |
| 8 | +The most simple case of MessageFormat would involve no formatting. Just a string passthrough. This sounds silly, but often it's nice to always use the same i18n system when doing translations, and not everything takes variables. |
| 9 | + |
| 10 | +```javascript |
| 11 | +// Insantiate a MessageFormat object on your locale |
| 12 | +var mf = new MessageFormat('en'); |
| 13 | + |
| 14 | +// Compile a message |
| 15 | +var message = mf.compile( 'This is a message.' ); // returns a function |
| 16 | + |
| 17 | +// You can call the function to get data out |
| 18 | +> message(); |
| 19 | +"This is a message." |
| 20 | + |
| 21 | +// NOTE:: if a message _does_ require data to be passed in, an error is thrown if you do not. |
| 22 | + |
| 23 | +``` |
| 24 | + |
| 25 | +## Simple Variable Replacement |
| 26 | + |
| 27 | +The second most simple way to use MessageFormat is for simple variable replacement. MessageFormat looks odd at first, but it's actually fairly simple. One way to think about the `{` and `}` is that every level of them bring you into and out-of `literal` and `code` mode. |
| 28 | + |
| 29 | +By default (like in the previous example), you are just writing a literal. Then the first level of brackets brings you into one of several data-driven situations. The most simple is variable replacement. |
| 30 | + |
| 31 | +Simply putting a variable name in between `{` and `}` will place that variable there in the output. |
| 32 | + |
| 33 | +```javascript |
| 34 | +// Instantiate new MessageFormat object for your locale |
| 35 | +var mf = new MessageFormat('en'); |
| 36 | + |
| 37 | +// Compile a message |
| 38 | +var message = mf.compile('His name is {NAME}.'); |
| 39 | + |
| 40 | +// Then send that data into the function |
| 41 | +> message({ "NAME" : "Jed" }); |
| 42 | +"His name is Jed." |
| 43 | + |
| 44 | +// NOTE:: it's best to try and stick to keys that would be natively |
| 45 | +// tolerant in your JS runtimes (think valid JS variable names). |
| 46 | +``` |
| 47 | + |
| 48 | +## SelectFormat |
| 49 | + |
| 50 | +`SelectFormat` is a lot like a switch statement for your messages. Most often it's used to select gender in a string. Here's an example: |
| 51 | + |
| 52 | +```javascript |
| 53 | +// Insantiate an instance with your language settings |
| 54 | +var mf = new MesssageFormat('en'); |
| 55 | +// Compile a message - returns a function |
| 56 | +var message = mf.compile('{GENDER, select, male{He} female{She} other{They}} liked this.'); |
| 57 | + |
| 58 | +// Run your message function with your data |
| 59 | +> message({"GENDER" : "male"}); |
| 60 | +"He liked this." |
| 61 | + |
| 62 | +> message({"GENDER" : "female"}); |
| 63 | +"She liked this." |
| 64 | + |
| 65 | +// The 'other' key is **required** and in the case of GENDER |
| 66 | +// it should be phrased as if you are too far away to tell the gender of the subject. |
| 67 | +> message({}); |
| 68 | +"They liked this." |
| 69 | + |
| 70 | +``` |
| 71 | + |
| 72 | +## PluralFormat |
| 73 | + |
| 74 | +`PluralFormat` is a similar mechanism to `SelectFormat` (especially syntax wise), but it's specific to numbers, and the key that is chosen is generated by a _Plural Function_. |
| 75 | + |
| 76 | +```javascript |
| 77 | +// Insantiate a new MessageFormat object |
| 78 | +var mf = new MessageFormat('en'); |
| 79 | + |
| 80 | +// You can use the provided locales in the `/locale` folder |
| 81 | +// (include the file directly after including messageformat.js |
| 82 | +var mf = new MessageFormat( 'sl' ); |
| 83 | + |
| 84 | +// OR - you can pass a custom plural function to the MessageFormat constructor function. |
| 85 | +var mf = new Message( 'requiredCustomName', function (n) { |
| 86 | + if ( n === 42 ) { |
| 87 | + return 'many'; |
| 88 | + } |
| 89 | + return 'other'; |
| 90 | +}); |
| 91 | + |
| 92 | +// Then the numbers that are passed into a compiled message will run through this function to select |
| 93 | +// the keys. This is for the 'en' locale: |
| 94 | +var message = mf.compile('There {NUM_RESULTS, plural, one{is one result} other{are # results}}.'); |
| 95 | + |
| 96 | +// Then the data causes the function to output: |
| 97 | + |
| 98 | +> message({"NUM_RESULTS" : 0}); |
| 99 | +"There are 0 results." |
| 100 | + |
| 101 | +> message({"NUM_RESULTS" : 1}); |
| 102 | +"There is one result." |
| 103 | + |
| 104 | +> message({"NUM_RESULTS" : 100}); |
| 105 | +"There are 100 results." |
| 106 | + |
| 107 | +``` |
| 108 | + |
| 109 | +### Named Keys |
| 110 | + |
| 111 | +ICU declares the 6 named keys that CLDR defines for their plural form data. Those are: |
| 112 | + |
| 113 | +* zero |
| 114 | +* one |
| 115 | +* two |
| 116 | +* few |
| 117 | +* many |
| 118 | +* other (**required**) |
| 119 | + |
| 120 | +All of them are fairly straight-forward, but do remember, that for some languages, they are more loose "guidelines" than they are exact. |
| 121 | + |
| 122 | +The only **required** key is `other`. Your compilation will throw an error if you forget this. In english, and many other languages, the logic is simple: |
| 123 | + |
| 124 | +`If N equals 1, then ONE, otherwise OTHER` |
| 125 | + |
| 126 | +Other languages (take a peak at `ar.js` or `sl.js`) can get much more complicated. |
| 127 | + |
| 128 | +**Note:** English only uses `one` and `other` for cardinal plurals, so including `zero` will never get called, even when the number is 0 |
| 129 | + |
| 130 | +The most simple (to pluralize) languages have no pluralization rules an rely solely on the `other` named key. |
| 131 | + |
| 132 | +``` |
| 133 | +{NUM, plural, |
| 134 | + zero {There are zero - in a lang that needs it.} |
| 135 | + one {There is one - in a lang that has it.} |
| 136 | + two {There is two - in a lang that has it.} |
| 137 | + few {There are a few - in a lang that has it.} |
| 138 | + many {There are many - in a lang that has it.} |
| 139 | + other {There is a different amount than all the other stuff above.} |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +### Literal Numeric Keys |
| 144 | + |
| 145 | +There also exists the capability to put literal numbers as keys in a select statement. These are delimited by prefixing them with the `=` character. These will match single, specific numbers. If there is a match, that branch will immediately run, and the corresponding named key **will not** also run. |
| 146 | + |
| 147 | +There are plenty of legitimate uses for this, especially when considering base cases and more pleasant language. But if you're a Douglas Adams fan, might use it like so: |
| 148 | + |
| 149 | +``` |
| 150 | +You have {NUM_TASKS, plural, |
| 151 | + one {one task} |
| 152 | + other {# tasks} |
| 153 | + =42 {the answer to the life, the universe and everything tasks} |
| 154 | + } remaining. |
| 155 | +``` |
| 156 | + |
| 157 | +When `NUM_TASKS` is 42, this outputs smiles. Remember, these have priority over the named keys. |
| 158 | + |
| 159 | +### PluralFormat - offset extension |
| 160 | + |
| 161 | +ICU provided the ability to extend existing select and plural functionality, and the only official extension (that I could find) is the `offset` extension. |
| 162 | + |
| 163 | +It goes after the `plural` declaration, and is used to generate sentences that break up a number into multiple sections. For instance: |
| 164 | + |
| 165 | +> You and 4 others added this to their profiles. |
| 166 | +
|
| 167 | +In this case, the total number of people who added 'this' to their profiles is actually 5. We can use the `offset` extension to help us with this. |
| 168 | + |
| 169 | +```javascript |
| 170 | +var mf = new MessageFormat('en'); |
| 171 | + |
| 172 | +// For simplicity's sake, let's assume the base case here isn't silly. |
| 173 | +// The test suite has a bigger offset example at the bottom |
| 174 | +// Let's also assume neutral gender for the same reason |
| 175 | + |
| 176 | +// Set the offset to 1 |
| 177 | +var message = mf.compile( |
| 178 | + 'You {NUM_ADDS, plural, offset:1' + |
| 179 | + '=0{didnt add this to your profile}' + // Number literals, with a `=` do **NOT** use |
| 180 | + '=1{added this to your profile}' + // the offset value |
| 181 | + 'one{and one other person added this to their profile}' + |
| 182 | + 'other{and # others added this to their profiles}' + |
| 183 | + '}.' |
| 184 | +); |
| 185 | + |
| 186 | +// Tip: I like to consider the `=` prefixed number literals as more of an "inductive step" |
| 187 | +// e.g. in this case, since (0 - 1) is _negative_ 1, we want to handle that base case. |
| 188 | + |
| 189 | +> message({"NUM_ADDS" : 0 }); |
| 190 | +"You didnt add this to your profile." |
| 191 | + |
| 192 | +> message({"NUM_ADDS" : 1 }); |
| 193 | +"You added this to your profile." |
| 194 | + |
| 195 | +> message({"NUM_ADDS" : 2 }); |
| 196 | +"You and one other person added this to their profile." |
| 197 | + |
| 198 | +> message({"NUM_ADDS" : 3 }); |
| 199 | +"You and 2 others added this to their profile." |
| 200 | + |
| 201 | +``` |
| 202 | + |
| 203 | +## Nesting |
| 204 | + |
| 205 | +Very simply, you can nest both `SelectFormat` blocks into `PluralFormat` blocks, and visa-versa, as deeply as you'd like. Simply start the new block directly inside: |
| 206 | + |
| 207 | +``` |
| 208 | +{SEL1, select, |
| 209 | + other { |
| 210 | + {PLUR1, plural, |
| 211 | + one {1} |
| 212 | + other { |
| 213 | + {SEL2, select, |
| 214 | + other {deep in the heart.} |
| 215 | + } |
| 216 | + } |
| 217 | + } |
| 218 | + } |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +## Escaping |
| 223 | + |
| 224 | +messageformat.js tries to a good job of being tolerant of as much as possible, but some characters, like the ones used the actual MessageFormat spec itself, must be escaped to be a part of your string. |
| 225 | + |
| 226 | +For `{`, `}` and `#` (only inside of a select value) literals, just escape them with a backslash. (If you are in a JS string, you'll need to escape the escape backslash so it'll look like two). |
| 227 | + |
| 228 | +```javascript |
| 229 | +// Technically, it's just: |
| 230 | + |
| 231 | +\{\}\# |
| 232 | + |
| 233 | +// But in practice, since you're often dealing with string literals, it looks more like |
| 234 | + |
| 235 | +var msg = mf.compile("\\{ {S, select, other{# is a \\#}} \\}"); |
| 236 | + |
| 237 | +> msg({S:5}); |
| 238 | +"{ 5 is a # }" |
| 239 | +``` |
| 240 | + |
0 commit comments