Here are several additions I am making to the GWT Messages functionality.
Before finalizing it for review, I would like to get some feedback.
*Extended Plurals
*
Some applications want to have messages like "JohnT, JeffH, and 3 others
have recommended this movie", instead of trying to list all users.
0 - No one has recommended this movie
1 - {1} has recommended this movie
2 - {1} and {2} have recommended this movie
3 - {1}, {2}, and one other have recommended this movie
4+ - {1}, {2}, and {0,number} others have recommended this movie
(with the trick being that the total count needs to be altered by the number
of explicit entries, and that adjusted number is used to drive plural rule
selection, which gets more complicated for languages like Arabic with 6
plural forms).
There have already been requests to allow customizing the form to use even
if the language doesn't require it. For example, "You have no messages" when
the count is 0 even though English doesn't grammatically require it. I
propose the following to accomplish both goals:
@DefaultMessage("{1}, {2}, and {0,number} others have recommended this
movie")
@PluralText({
"=0", "No one has recommended this movie",
"=1", "{1} has recommended this movie",
"=2", "{1} and {2} have recommended this movie",
"one", "{1}, {2}, and one other have recommended this movie"})
String recommenders(@Optional @Example("3") @PluralCount @Offset(2) int
count, String name1, String name2);
The new pieces are @Offset(2) which means to subtract 2 from the value
before applying normal plural rules, and plural forms "=n", which means if
the original value matches the specified value that form is used instead of
any normal plural lookups. If the value doesn't match any "=n" plural forms,
then the offset (if any) is subtracted and the normal plural rules for the
locale apply.
I am not sure if there is a use case for it, but I was thinking of
supporting forms like "=1..3" as well.
*Static Placeholders
*
Translators shouldn't see things like HTML markup, because it isn't clear to
them whether they should translate portions inside the placeholder or can be
confused by it. The recommendation is to use placeholders to hide those
parts from the translator, and put them back into the translated string.
UiBinder already takes care of a similar situation, but if you using
Messages directly you have to do it yourself, using something like the
following:
private static final String BEGIN_BOLD = "<b>";
private static final String END_BOLD = "</b>";
@DefaultMessage("This is {0}important{1}")
String important(@Example(BEGIN_BOLD) String beginBold, @Example(END_BOLD)
String endBold);
...
msg.important(BEGIN_BOLD, END_BOLD);
It works, but is a lot of boilerplate that is easy to get wrong. I propose
the following:
@DefaultMessage("This is {beginBold,<b>}important{endBold,</b>}")
String important();
...
msg.important();
For escaping, I propose using single quote like elsewhere in Messages, such
as "This is {beginLink,<a href="url¶m='{0}'">}..{endLink,</a>}" -- the
single quotes protect the braces from ending the parameter, and any single
quotes in the static content would be doubled. Note that we still have work
to do getting these out of the generated and translated property files, just
like we currently expect the full {0,number,currency} in the translated
files. That will probably be a task for next quarter, when I intend to
improve support for integration with external translation tools.
*List Rendering
*
Right now, if you want to render a list, you have to do all the work
yourself and have some way of getting locale-specific separators. CLDR
defines the data needed to do this, so I propose adding:
@DefaultMessage("The values are {0,list,number}")
@PluralText({"=0", "There are no values",
"one", "The value is {0,list,number}"})
String values(@PluralCount List<Integer> list);
or
String values(@PluralCount int... list);
or
String values(@PluralCount int[] list);
...
Example results for English (using the varargs method):
msg.values() => "There are no values"
msg.values(1001) => "The value is 1,001"
msg.values(1,2) => "The values are 1 and 2"
msg.values(1,2,3) => "The values are 1, 2, and 3"
Basically, if the format is list, then the remainder of the argument tag is
a format applied to each element of the list.
*Format Arguments, for Selectable Currency Codes / Timezones
*
If you format a currency value with {0,number,currency}, you get the locales
default currency. It would be nice to be able to specify the currency in the
message (if for example it is always in USD regardless of the locale) or in
a dynamic parameter. I propose adding a general way to add parameters to
formats and subformats:
*Examples:*
- {0,number:curcode=USD,currency}
- always uses USD as the currency, regardless of the locale
- {0,number:curcode=$curCode,\xA4 #,##0}
- refers to a String parameter named curCode
- {1,localdatetime:tz=0,yMd hms}
- uses a timezone constructed from just an hour offset from GMT
- {1,time:tz=$tz,short}
- uses a TimeZone instance passed as the tz argument to the method
I am particularly interested in feedback about the general format for adding
arguments to formats as well as the syntax for referencing parameters by
name.
--
John A. Tamplin
Software Engineer (GWT), Google
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors