Skip to content

Commit eebdfea

Browse files
committed
Refactor original REAME into index, build & guide pages
1 parent fcb2d32 commit eebdfea

3 files changed

Lines changed: 400 additions & 491 deletions

File tree

build.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
layout: page
3+
title: Build-time Compilation
4+
---
5+
6+
For a significant decrease in filesize and execution time, you should precompile your messages to JavaScript during your build phase. It works like this:
7+
8+
```js
9+
> var mf = new MessageFormat('en');
10+
> var messages = {
11+
simple: 'A simple message.',
12+
var: 'Message with {X}.',
13+
plural: 'You have {N, plural, =0{no messages} one{1 message} other{# messages}}.',
14+
select: '{GENDER, select, male{He has} female{She has} other{They have}} sent you a message.',
15+
ordinal: 'The {N, selectordinal, one{1st} two{2nd} few{3rd} other{#th}} message.' };
16+
17+
> var mfunc = mf.compile(messages);
18+
> mfunc().ordinal({N:3})
19+
'The 3rd message.'
20+
21+
> console.log(mfunc.toString())
22+
function anonymous() {
23+
var number = function (value, offset) {
24+
if (isNaN(value)) throw new Error("'" + value + "' isn't a number.");
25+
return value - (offset || 0);
26+
};
27+
var plural = function (value, offset, lcfunc, data, isOrdinal) {
28+
if ({}.hasOwnProperty.call(data, value)) return data[value]();
29+
if (offset) value -= offset;
30+
var key = lcfunc(value, isOrdinal);
31+
if (key in data) return data[key]();
32+
return data.other();
33+
};
34+
var select = function (value, data) {
35+
if ({}.hasOwnProperty.call(data, value)) return data[value]();
36+
return data.other()
37+
};
38+
var pluralFuncs = {
39+
en: function (n, ord) {
40+
var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n,
41+
n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
42+
if (ord) return (n10 == 1 && n100 != 11) ? 'one'
43+
: (n10 == 2 && n100 != 12) ? 'two'
44+
: (n10 == 3 && n100 != 13) ? 'few'
45+
: 'other';
46+
return (n == 1 && v0) ? 'one' : 'other';
47+
}
48+
};
49+
var fmt = {};
50+
51+
return {
52+
simple: function(d) { return "A simple message."; },
53+
var: function(d) { return "Message with " + d.X + "."; },
54+
plural: function(d) { return "You have " + plural(d.N, 0, pluralFuncs.en, { 0: function() { return "no messages";}, one: function() { return "1 message";}, other: function() { return number(d.N) + " messages";} }) + "."; },
55+
select: function(d) { return select(d.GENDER, { male: function() { return "He has";}, female: function() { return "She has";}, other: function() { return "They have";} }) + " sent you a message."; },
56+
ordinal: function(d) { return "The " + plural(d.N, 0, pluralFuncs.en, { one: function() { return "1st";}, two: function() { return "2nd";}, few: function() { return "3rd";}, other: function() { return number(d.N) + "th";} }, 1) + " message."; }
57+
}
58+
}
59+
```
60+
61+
62+
## CLI Compiler
63+
64+
A [CLI compiler](https://github.com/SlexAxton/messageformat.js/tree/master/bin/messageformat.js) is also included, available as `./node_modules/.bin/messageformat` or just `messageformat` when installed with `npm install -g`.
65+
66+
```
67+
$ messageformat --help
68+
Usage: messageformat -l [locale] [OPTIONS] [INPUT_DIR] [OUTPUT_DIR]
69+
70+
Available options:
71+
-l, --locale locale(s) to use [mandatory]
72+
-i, --inputdir directory containing messageformat files to compile
73+
-o, --output output where messageformat will be compiled
74+
-ns, --namespace global object in the output containing the templates
75+
-I, --include glob patterns for files to include from `inputdir`
76+
-m, --module create a commonJS module, instead of a global window variable
77+
-s, --stdout print the result in stdout instead of writing in a file
78+
-w, --watch watch `inputdir` for changes
79+
-v, --verbose print logs for debug
80+
```
81+
82+
`messageformat` will recursively read all JSON files in `inputdir` and compile them to `output`.
83+
84+
When using the CLI, the following commands will each produce the same results as included in the [examples](https://github.com/SlexAxton/messageformat.js/tree/master/example/en):
85+
86+
```
87+
$ messageformat --locale en --namespace i18n --inputdir ./example/en --output ./i18n.js
88+
$ messageformat --locale en ./example/en ./i18n.js
89+
$ messageformat -l en ./example/en
90+
```
91+
92+
93+
## Using compiled messageformat.js output
94+
95+
With default options, compiled messageformat functions are available through the `i18n` global object, with each source json having a corresponding subobject. Hence the compiled function corresponding to the `test` message defined in [`example/en/sub/folder/plural.json`](https://github.com/SlexAxton/messageformat.js/tree/master/example/en/sub/folder/plural.json) is available as [`i18n['sub/folder/plural'].plural`](https://github.com/SlexAxton/messageformat.js/tree/master/example/en/i18n.js):
96+
97+
```html
98+
<script src="path/to/bower_components/messageformat/example/en/i18n.js"></script>
99+
<script>
100+
console.log(i18n['sub/folder/plural'].plural({NUM: 3}));
101+
</script>
102+
```
103+
will log `"Your 3 messages go here."`
104+
105+
A working example is available [here](https://github.com/SlexAxton/messageformat.js/tree/master/example).

guide.md

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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

Comments
 (0)