This is a follow-up to discussion in #65, and it implements ICU's simpleArg syntax 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, but are not yet available in node by default (see joyent/node#7676), though their support in browsers is getting to be pretty good. There is a node polyfill 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 using this syntax 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:
> 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:
> 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:
> 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.
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:
<script src="path/to/messageformat.js"></script>
<script src="path/to/make-plural.js"></script>
<script>
var mf = new MessageFormat('en');
var mfunc = mf.compile("The total is {V,number,currency}.");
console.log(mfunc({V:5.5})); // "The total is $5.50."
</script>
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
- [x] Should
MessageFormat()
take as a third parameter an object of formatting functions?
- [x] Should
MessageFormat.format
be renamed to something more specific?
- [x] Should
MessageFormat.locale
also be renamed to more clearly refer to the plural functions it contains?
- [x] Are we ok depending on
Intl
? Should that dependence be explicit, and/or checked at runtime?