Skip to content
Merged
Changes from 14 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
169 changes: 169 additions & 0 deletions spec/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,175 @@ together with the resolved options' values.

The _function_ `:currency` performs selection as described in [Number Selection](#number-selection) below.

### The `:unit` function

The _function_ `:unit` is a selector and formatter for unitized values,
that is, numeric values associated with a unit of measurement.
This is a specialized form of numeric selection and formatting.

> [!IMPORTANT]
> Implementation of this function is **_OPTIONAL_**.
> Any implementation of this function is strongly encouraged to follow this specification.

#### Operands

The _operand_ of the `:unit` function can be one of any number of
implementation-defined types,
each of which contains a numerical `value` plus a `unit`
or it can be a [Number Operand](#number-operands), as long as the _option_
`unit` is provided.
The _option_ `unit` MAY be used to override the units of an implementation-defined type,
provided the units are compatible and the implementation supports conversion.
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should make it clearer whether a function handler is allowed not to implement this conversion behaviour, of if the "MAY" is permitting a user to use it in such a fashion and expect it to work.

Copy link
Member

Choose a reason for hiding this comment

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

Hmmm. I missed this.

I have never thought of 'unit=x' as involving or requiring conversion, but rather simply using the numeric value of the operand with the specified unit x. I think that you are right; this should take a bit of rewording. Perhaps:

Suggested change
The _option_ `unit` MAY be used to override the units of an implementation-defined type,
provided the units are compatible and the implementation supports conversion.
The _option_ `unit` MAY be used to override the unit value of an implementation-defined type,
if the implementation supports unit conversion.
However, if the units cannot be converted, then the unit value is not overridden and a Bad Option error must be emitted.

Side issue

Frankly, currency is even worse. Suppose we have input of a implementation-defined parameter $n = {value=300, currency=EUR} and we format or select {$n :currency currency=JPY}. That is really nasty, displaying 300¥. However, there is no stable conversion possible.

So for :currency, we should make it really clear that if there is an implementation-defined operand that contains a currency value, the currency option is invalid, no matter its value.

Copy link
Member Author

Choose a reason for hiding this comment

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

Currency explicitly says that this is an error.

The unit example is a leftover from early on. I will make it use usage. We should do the same thing with the unit option that we do with the currency option: you can use it to create a unit value with a number operand. Otherwise it is an error.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds great.


The value of the _operand_'s `unit` SHOULD be either a string containing a
valid [Unit Identifier](https://www.unicode.org/reports/tr35/tr35-general.html#unit-identifiers)
or an implementation-defined unit type.

A [Number Operand](#number-operands) without a `unit` _option_ results in a _Bad Operand_ error.

> [!NOTE]
> For example, in ICU4J, the type `com.ibm.icu.util.Measure` might be used
> as an _operand_ for `:unit` because it contains the `value` and `unit`.

> [!NOTE]
> For runtime environments that do not provide a ready-made data structure,
> class, or type for unit values, the implementation ought to provide
> a data structure, convenience function, or documentation on how to encode
> the value and unit for formatting.
> For example, such an implementation might define a "unit operand"
> to include a key-value structure with specific keys to be the
> local unit operand, which might look like the following:
> ```
> {
> "value": 123.45,
> "unit": "kilometer-per-hour"
> }
> ```

#### Options

Some _options_ do not have default values defined in this specification.
The defaults for these _options_ are implementation-dependent.
In general, the default values for such _options_ depend on the locale,
the unit,
the value of other _options_, or all of these.

> [!NOTE]
> The option `select` does not accept the value `ordinal` because selecting
> unit values using ordinal rules makes no sense.

The following options and their values are required to be available on the function `:unit`:
- `select`
- `plural` (default)
- `exact`
- `unit`
- valid [Unit Identifier](https://www.unicode.org/reports/tr35/tr35-general.html#unit-identifiers)
(no default)
- `usage` \[OPTIONAL\]
- Well-formed and valid usage identifiers are defined in [Unicode Preferences](https://www.unicode.org/reports/tr35/tr35-info.html#unit-preferences).
- (no default)
- `unitDisplay`
- `short` (default)
- `narrow`
- `long`
- `compactDisplay` (this option only has meaning when combined with the option `notation=compact`)
- `short` (default)
- `long`
- `notation`
- `standard` (default)
- `compact`
- `numberingSystem`
- valid [Unicode Number System Identifier](https://cldr-smoke.unicode.org/spec/main/ldml/tr35.html#UnicodeNumberSystemIdentifier)
(default is locale-specific)
- `signDisplay`
- `auto` (default)
- `always`
- `exceptZero`
- `negative`
- `never`
- `useGrouping`
- `auto` (default)
- `always`
- `never`
- `min2`
- `minimumIntegerDigits`
- ([digit size option](#digit-size-options), default: `1`)
- `minimumFractionDigits`
- ([digit size option](#digit-size-options))
- `maximumFractionDigits`
- ([digit size option](#digit-size-options))
- `minimumSignificantDigits`
- ([digit size option](#digit-size-options))
- `maximumSignificantDigits`
- ([digit size option](#digit-size-options))
- `roundingPriority`
- `auto` (default)
- `morePrecision`
- `lessPrecision`
- `roundingIncrement`
- 1 (default), 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, and 5000
- `roundingMode`
- `ceil`
- `floor`
- `expand`
- `trunc`
- `halfCeil`
- `halfFloor`
- `halfExpand` (default)
- `halfTrunc`
- `halfEven`

If the _operand_ of the _expression_ is an implementation-defined type,
such as the _resolved value_ of an _expression_ with a `:unit` _annotation_,
it can include _option_ values.
Comment on lines +675 to +677
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's not clear here that not all "implementation-defined types" are necessarily supported as operands.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure I understand. I don't think this needs to indicate that some types are not supported as operands.

This text is not about the operand. It's about the handling of option values within a resolved value. It is nearly boilerplate, since most of our functions have a variation of this sentence. In practice, it might be better to say something like the following, which doesn't care what the operand is or is not:

Suggested change
If the _operand_ of the _expression_ is an implementation-defined type,
such as the _resolved value_ of an _expression_ with a `:unit` _annotation_,
it can include _option_ values.
The _resolved value_ of an _expression_ with a `:unit` _annotation_
includes the result of _option resolution_.

Such a boilerplate would be altered if some of the options were required to be filtered by the function. unit is candidate for this in :unit (since the unit presumably is now part of the resolved operand).

These are included in the resolved _option_ values of the _expression_,
with _options_ on the _expression_ taking priority over any _option_ values of the _operand_.

> For example, the _placeholder_ in this _message_:
> ```
> .input {$n :unit unit=furlong minimumFractionDigits=2}
> {{{$n :unit minimumIntegerDigits=1}}}
> ```
> would have the resolved options:
> `{ unit: 'furlong', minimumFractionDigits: '2', minimumIntegerDigits: '1' }`.

Some implementations support conversion to the locale's preferred units via the `usage` _option_.
Implementing this _option_ is optional.
Attempting to convert units produces an _Unsupported Operation_ error if such conversion is unsupported.
It produces a _Bad Option_ error if the specified units are incompatible.
> For example, trying to convert a `length` unit such as meters
> to a `volume` unit (such as "gallons") produces a _Bad Option_.

Implementations MUST NOT substitute the unit without performing the associated conversion.

> For example, consider the value:
> ```
> {
> "value": 123.5,
> "unit": "meter"
> }
> ```
> The following _message_ might convert the formatted result to U.S. customary units:
> ```
> You have {$v :unit unit=foot maximumFractionDigits=0} to go.
> ```
> This can produce "You have 405 feet to go."

Not all `usage` values are compatible with a given unit.
Implementations will produce a _Bad Option_ error for units
or combinations of units and usages that are not supported.

#### Resolved Value

The _resolved value_ of an _expression_ with a `:unit` _function_
consist of an implementation-defined unit value
of the _operand_ of the annotated _expression_,
together with the resolved _options_ and their resolved values.

#### Selection

The _function_ `:unit` performs selection as described in [Number Selection](#number-selection) below.

### Number Operands

The _operand_ of a number function is either an implementation-defined type or
Expand Down