As mentioned by @Reboot, part of the confusion with these classes is that ChoiceFormat
is treated specially by MessageFormat.subformat()
here:
subFormatter = formats[i];
if (subFormatter instanceof ChoiceFormat) {
arg = formats[i].format(obj);
if (arg.indexOf('{') >= 0) {
subFormatter = new MessageFormat(arg, locale);
obj = arguments;
arg = null;
}
}
This hack is what allows a MessageFormat
to contain a ChoiceFormat
which itself contains a MessageFormat
:
new ChoiceFormat("0#none|1#one|1<{0}").format(3); // "{0}"
new MessageFormat("{0,choice,0#none|1#one|1<{0}}").format(new Object[] { 3 }); // "3"
Of course then, as a special case then, a ChoiceFormat
nested within a MessageFormat
may contain a nested ChoiceFormat
, as long as you escape/quote properly.
These classes get away with a lot of looseness in the syntax/parsing. Unlike Java or bash where parsing/escaping/quoting is "nested", the parsing here is "eager"... but it works.
I wrote some classes to help fight the madness. These classes don't try to reinvent the wheel; they simply make visible the underlying structure and nesting that already exists. But they allow you to bypass all the quoting issues, and they support arbitrary nesting depth.
In my own project I've wired them for XML. This lets me define messages like this:
<message key="how.many.items">
<text>There </text>
<choice argnum="0">
<option limit="0">
<text>are no items</text>
</option>
<option limit="1">
<text>is one item</text>
</option>
<option limit="1<">
<text>are </text>
<choice argnum="0">
<option limit="0">
<number argnum="0"/>
</option>
<option limit="100">
<text>way too many</text>
</option>
</choice>
<text>items</text>
</option>
</choice>
<text>.</text>
</message>
See MessageFmt for details.