-
-
Notifications
You must be signed in to change notification settings - Fork 35
Implement :unit as Proposed RECOMMENDED in the registry
#922
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
Changes from 15 commits
f05d0ba
9a5b46a
4a623f9
aaba292
8786990
b5ef049
59ae657
8d83a8f
a76eedc
d0eb3e3
57582ee
0ce878b
8fe64c9
0c7beb2
b90a720
13e33f0
b5cf412
94c0dec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -559,6 +559,173 @@ 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 **Proposed** for inclusion in the next release of this specification but has not yet been finalized. | ||||||||||||
| The _function_ `:unit` is proposed to be a **RECOMMENDED** selector and formatter for unitized values, | ||||||||||||
| that is, for numeric values associated with a unit of measurement. | ||||||||||||
| This is a specialized form of numeric selection and formatting. | ||||||||||||
|
|
||||||||||||
| #### 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. | ||||||||||||
|
|
||||||||||||
| 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, see [Unit Conversion](#unit-conversion) below) | ||||||||||||
| - `usage` \[RECOMMENDED\] | ||||||||||||
| - valid [Unicode Unit Preference](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` | ||||||||||||
|
|
||||||||||||
aphillips marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Such a boilerplate would be altered if some of the options were required to be filtered by the function. |
||||||||||||
| 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' }`. | ||||||||||||
|
|
||||||||||||
| #### 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. | ||||||||||||
|
Comment on lines
+691
to
+698
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These don't really work right together. If unit conversion is applied, the formatted value can be completely different from the "unit value of the operand of the annotated expression". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take 2 cases: Suppose $n is an implementation-defined unit value, and the locale is en-US.
So what is happening in both cases is that the numeric value for selection is equivalent to what the formatted value has. And that is what needs to be selected on, namely 66. The wording we have for that in selection as described in
So in both cases, the resolved value has to be 66. There are of course other cases: n$ = {number=66.1387} and we format {$n :number maximumSignificantDigits=1} we'll get "70" (In English), and "٧٠" in Arabic. And that must match a selection key of 70. So in this case, the resolved value has to be 70. I think we haven't yet nailed 'resolved value', because the resolved value for selection and formatting ends up needing to be different than the resolved value for functional composition. But that we cannot 'resolve' at this point in the release. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that what you describe is likely the intent here, but the resolved value description talks of "an implementation-defined unit value of the operand of the annotated expression", with no reference to any conversion possibly being made to it, and so it's reasonable to assume that e.g. your case 1 example would end up with the numerical value Note how in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we permitted There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That works for me, and is safe and far simpler. It would still be possible for a future version to allow that, but I think one would have to make a good case for doing so. |
||||||||||||
|
|
||||||||||||
| #### Unit Conversion | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to the text above, unit conversion is also possible via the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. |
||||||||||||
|
|
||||||||||||
| Implementations MAY support conversion to the locale's preferred units via the `usage` _option_. | ||||||||||||
| Implementing this _option_ is optional. | ||||||||||||
| Not all `usage` values are compatible with a given unit. | ||||||||||||
| Implementations SHOULD emit an _Unsupported Operation_ error if the requestion conversion is not supported. | ||||||||||||
macchiati marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
|
|
||||||||||||
| > For example, trying to convert a `length` unit such as meters | ||||||||||||
| > to a `volume` unit (such as "gallons") could produce an _Unsupported Operation_ error. | ||||||||||||
|
|
||||||||||||
macchiati marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
| 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." | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| ### Number Operands | ||||||||||||
|
|
||||||||||||
| The _operand_ of a number function is either an implementation-defined type or | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
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.
There was a problem hiding this comment.
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 theunitoption that we do with thecurrencyoption: you can use it to create a unit value with a number operand. Otherwise it is an error.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds great.