Matchers
A matcher is a feature in MessageFormat that lets you group together different variants of a message, in which one variant is chosen based on runtime data.
.input {$count :number}
.match $count
one {{You have {$count} week.}}
* {{You have {$count} weeks.}}
{ "count": 15 }
Matchers match based on values of variables. Variables must be declared before the matcher, in a .local
or .input
declaration. This annotation determines how selection is done. In this case, the annotation :number
means that $count
is examined as a numerical value based on its plural category, which happens to be the default selection category for numbers (the other options include ordinal categories for instance). :number
is an example of a selector function. You might remember that :number
is also a formatting function. Some functions are both a selector and a formatter, while others can only be one or the other.
The :number
function has the built-in ability to map values onto plural categories. This mapping is based on CLDR data and it doesn't have to be provided to the formatter explicitly.
The .match
keyword has to be followed by a variable: in this case, $count
. We call $count
the selector of a matcher.
one
and *
are both keys. The key one
matches the runtime value of $count
based on that value's plural category. The key *
is special and matches any value.
The quoted pattern that follows each key is the pattern to use if the key matches.
Let's work through how this message is formatted depending on the runtime value of $count
. Suppose $count
is 1
.
- The
:number
selector looks at the value (1
) and the keys (one
and*
). It determines thatone
is the best match. - The pattern
{{You have {$count} week.}}
is chosen. - The variable is replaced with its value, and the result is
You have 1 week.
Now let's suppose $count
is 42
.
- The
:number
selector looks at the value (42
) and the keys (one
and*
). The only key that can match in this case is the wildcard,*
. - The pattern
{{You have {$count} weeks.}}
is chosen. - The variable is replaced with its value, and the result is
You have 42 weeks.
The details of how values are matched against keys depend on the annotation of the selector. :number
is just one example.
Multiple selectors Jump to heading
More complicated matchers can have multiple keys and multiple selectors. The following example matches on two expressions: one for a gender string, and one for a number (of guests).
.input {$hostGender :string}
.input {$guestCount :number}
.match $hostGender $guestCount
female 0 {{{$hostName} does not give a party.}}
female 1 {{{$hostName} invites {$guestName} to her party.}}
female 2 {{{$hostName} invites {$guestName} and one other person to her party.}}
female * {{{$hostName} invites {$guestCount} people, including {$guestName}, to her party.}}
male 0 {{{$hostName} does not give a party.}}
male 1 {{{$hostName} invites {$guestName} to his party.}}
male 2 {{{$hostName} invites {$guestName} and one other person to his party.}}
male * {{{$hostName} invites {$guestCount} people, including {$guestName}, to his party.}}
* 0 {{{$hostName} does not give a party.}}
* 1 {{{$hostName} invites {$guestName} to their party.}}
* 2 {{{$hostName} invites {$guestName} and one other person to their party.}}
* * {{{$hostName} invites {$guestCount} people, including {$guestName}, to their party.}}
{ "hostGender": "female", "hostName": "Alice", "guestCount": 2, "guestName": "Bob" }
In this example, hostGender
is passed in as a variable separately from hostName
.
In a more realistic example, they might be fields of the same object, which are
extracted using custom selectors, like:
.local $hostGender = {$host :gender}
.local $hostName = {$host :name}
.match $hostGender $guestCount
female 0 {{{$hostName} does not give a party.}}
(etc.) In this example, the external input value $host
would be an object
with gender and name fields, and perhaps others.
Number selection in languages with more than two plural categories Jump to heading
While English only has two plural categories, there are other natural languages that have more categories. Consider translating the example into Polish:
.input {$count :number}
.match $count
one {{Masz {$count} tydzień.}}
few {{Masz {$count} tygodnie.}}
many {{Masz {$count} tygodni.}}
other {{Masz {$count} tygodnia.}}
* {{Masz {$count} tygodnia.}}
{ "count": 5 }
This shows why the :number
selector is useful: when translating the first example
from English to Polish, the translator knows how to create a Polish version
that covers all the plural categories. For more information, see the
CLDR page on plural rules.
(By the way, the *
variant is always required. This example could be rewritten
without the other
variant, but we showed it for expository reasons.)