SlexAxton/messageformat.js — pull request #68 by eemeli (2014-10-03 19:10:57+00:00)
Title: Icu simplearg functions

This is a follow-up to discussion in #65, and it implements ICU's [simpleArg syntax](http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html) in a rather liberal fashion. Effectively, it makes syntax like `{var, fn[, args]*}` call a user-definable function `fn`, passing it the value of `var`, the locale(s), and `args`.

Formatting functions for ICU's `number`, `date` and `time` are included, making use of `Intl.NumberFormat` and `Intl.DateTimeFormat`. They are a part of [ECMA-402](http://www.ecma-international.org/ecma-402/1.0/), but are not yet available in node by default (see joyent/node#7676), though their support in browsers is getting to be [pretty good](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat#Browser_Compatibility). There is a node [polyfill](https://www.npmjs.org/package/intl) available, though.

The `spellout`, `ordinal` and `duration` argTypes are not supported, as they're way more complicated and without support via `Intl` or in fact any JavaScript library that I could find. ICU uses the rules [defined here](http://source.icu-project.org/repos/icu/icu/tags/latest/source/data/rbnf/) using [this syntax](http://icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#details) to manage them, and implementing all that in JavaScript probably ought to stay outside the scope of messageformat.js.

As `number` includes the argStyle `currency`, it currently defaults to USD. To use something else, you should set `mf.currency` before calling `mf.compile()`, like so:
```js
> var mf = new MessageFormat('en');
> mf.currency = 'EUR';
> var mfunc = mf.compile("The total is {V,number,currency}.");
> mfunc({V:5.5})
"The total is €5.50."
```
Or by setting the formatter function directly:
```js
> var mf = new MessageFormat('fi');
> var mfunc = mf.compile("Yhteensä {V,number,currency}.");
> mf.runtime.fmt.number = MessageFormat.format.number({currency:'EUR'});
> mfunc({V:5.5})
"Yhteensä 5,50 €."
```

You can also define your own functions:
```js
> var mf = new MessageFormat( 'en' );
> var mfunc = mf.compile("This is {V,uppercase}.");
> mf.runtime.fmt.uppercase = function(v) { return v.toUpperCase(); };
> mfunc({V:"big"})
"This is BIG."
```
The `{V,fn,...}` format accepts any number of comma-delimited parameters that are passed on to `fn(v,lc,p)`. If there's just one parameter, it's passed as a string; otherwise `p` is an array, or `null` for none.

The default formatting functions are automatically included in `mf.runtime` when encountered by `mf.precompile()` (as seen in the first example above); additional auto-loading formatting functions may be added to `MakeFormat.format`.

----

To make all of the above work properly, I needed to redefine `mf.lc` to be an array, which gets passed on to the `Intl` methods. This meant that the MessageFormat constructor got reworked, and now accepts a string or an array as a first `locale` parameter. Semantics with string input are the same as before: fallback are successive stages of dropping `[-_][^-_]+` parts from its end. If `locale` is an array, though, it's taken to be an ordered list of locales to try, like [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat#Parameters).

There's also a new method `MessageFormat.pluralFunc(locale)` which wraps make-plural.js usage. It also tries to access `root.MakePlural`, which means that usage in a browser environment no longer requires manually setting `MessageFormat.locale` values, so this works:
```html

```

`mf.runtime` is also simplified a bit, getting rid of two of the utility functions and putting the formatting functions into their own namespace `mf.runtime.fmt`. I also renamed `mf.runtime.lc` to `mf.runtime.pf` as it holds plural functions. It's now a separate object from `MessageFormat.locale`, and only contains the plural functions that are explicitly included.

----

### Questions to consider

- [ ] Should `MessageFormat()` take as a third parameter an object of formatting functions?
- [ ] Should `MessageFormat.format` be renamed to something more specific?
- [ ] Should `MessageFormat.locale` also be renamed to more clearly refer to the plural functions it contains?
- [ ] Are we ok depending on `Intl`? Should that dependence be explicit, and/or checked at runtime?