Skip to content

Add case selection specification #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions spec/case-selection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
## Variant Case Selection

> This is a fragment of the full MessageFormat 2 specification,
> intended for later merge into a complete spec document.

### Prerequisites

The following assumptions are made about the **data model** and **function registry**,
as prerequisites for the following algorithm:

1. The data model supports a message type that allows for selecting
one variant **case** of a message based on the value of one or more **selectors**.

2. The value of a selector may be determined by a literal value,
a function defined in the function registry,
a dynamic variable provided at runtime,
the formatted value of another message,
or some subset of these.
The process for determining the value of any of the above is defined elsewhere.

3. For each selector, a default or **fallback** value is defined.
This may be a fixed value such as `"other"`,
or a string or integer value defined separately for each selector.

4. In the data model, the selectors are defined by an ordered list.

5. In the data model, the variant cases are defined by an ordered list of entries.
In addition to the case's actual message **value**,
each entry contains the **key** for the case as an ordered list of string and integer values.
These key values are aligned with the selectors.
The length of any key list is not greater than the length of the selector list.

6. If the function registry contains a `plural` selector function,
that function may be called with one number argument,
and be expected to return a list of string and integer values.
The returned values correspond to the numerical value of the argument as well as
the identifier of the cardinal plural category of the argument.

7. The cases are sorted, such that numerical keys are before string keys and
default or fallback key values are after all other key values.
If a fallback key has a number value, it is sorted after all non-fallback keys.

### Algorithm

1. The value `s` of each selector is determined:

1. The initial value `v` of the selector is resolved (see prereq. 2)
2. If `v` is a string value or a list of string and integer values, `s = v` is set.
3. Else if `v` is a number and a `plural` function is defined, `s = plural(v)` is set.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer the data model to be more explicit, i.e. the selector should already encode that it will use the plural function for resolution.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intent with this is to handle cases where the data model representation does not make it clear whether the input value might or might not be a number. For instance, take the default message used by the Fluent Playground:

shared-photos =
    {$userName} {$photoCount ->
        [one] added a new photo
       *[other] added {$photoCount} new photos
    } to {$userGender ->
        [male] his stream
        [female] her stream
       *[other] their stream
    }.

Specifically, consider the $photoCount selector: at what point can it be known whether it represents a numeric value vs. e.g. the literal string 'one'? The implicit plural allows us to ask that question only when we actually have the value; any earlier, we'd need additional input on its data type, or we'd need to add an explicit plural wrapper on every single selector.

As I mention in the discussion, the algorithm does of course allow for explicit plural wrappers, and that is in fact required when doing anything other than simple cardinal plural selection.

4. Else if `v` is a number, `s = v` is set.
5. Else, `v` is forcibly stringified: `s = String(v)`.

At this point, we have a list `S` of selector values,
each of which is a string, number, or a list of a string and integer values.

2. For each case (in order), its key `K` is compared to the selector values.
Each string or integer `k` in `K` is compared to its corresponding selector value `s`
as well that selector's fallback value `d`:

1. If `s` is a string or number and `k == s`, the match succeeds.
2. Else if `s` if a list of string and integer values and `k` is in `s`, the match succeeds.
3. Else if `k == d`, the match succeds.
4. Else, the match fails.

If the match succeeds for all `k` in `K`,
the case is selected and its value is selected as the value of the whole message.

3. If no case matches the selector value, an empty string is selected as the message value.