Skip to content

Commit bb8323d

Browse files
eemeliaphillips
andauthored
Add :percent (#1094)
Co-authored-by: Addison Phillips <[email protected]>
1 parent e298f59 commit bb8323d

File tree

6 files changed

+182
-10
lines changed

6 files changed

+182
-10
lines changed

exploration/percent-format.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Formatting Percent Values
22

3-
Status: **Proposed**
3+
Status: **Accepted**
44

55
<details>
66
<summary>Metadata</summary>
@@ -11,7 +11,8 @@ Status: **Proposed**
1111
<dt>First proposed</dt>
1212
<dd>2025-04-07</dd>
1313
<dt>Pull Requests</dt>
14-
<dd>#1068</dd>
14+
<dd><a href="https://github.com/unicode-org/message-format-wg/pull/1068">#1068</a></dd>
15+
<dd><a href="https://github.com/unicode-org/message-format-wg/pull/1094">#1094</a></dd>
1516
</dl>
1617
</details>
1718

@@ -134,9 +135,12 @@ _What prior decisions and existing conditions limit the possible design?_
134135

135136
## Proposed Design
136137

137-
_Describe the proposed solution. Consider syntax, formatting, errors, registry, tooling, interchange._
138+
Add a new, dedicated `:percent` function,
139+
which scales the _resolved value_ of its _operand_ by 100.
138140

139-
TBD
141+
This design could allow a function such as `:unit unit=percent`
142+
for formatting values without scaling
143+
when the working group tackles units.
140144

141145
## Alternatives Considered
142146

spec/functions/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
1. [`:integer`](number.md#the-integer-function)
1010
1. [`:offset`](number.md#the-offset-function)
1111
1. [`:currency`](number.md#the-currency-function)
12+
1. [`:percent`](number.md#the-percent-function)
1213
1. [`:unit`](number.md#the-unit-function)
1314
1. [Date and Time Value Formatting](datetime.md)
1415
1. [`:datetime`](datetime.md#the-datetime-function)

spec/functions/number.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,124 @@ contains an implementation-defined currency value
413413
of the _operand_ of the annotated _expression_,
414414
together with the resolved options' values.
415415
416+
#### The `:percent` function
417+
418+
> [!IMPORTANT]
419+
> The _function_ `:percent` has a status of **Draft**.
420+
> It is proposed for inclusion in a future release of this specification and is not Stable.
421+
422+
The function `:percent` is a selector and formatter for percent values.
423+
424+
##### `:percent` Operands
425+
426+
The function `:percent` requires a _numeric operand_ as its _operand_.
427+
428+
When either selecting or formatting the _expression_,
429+
the numeric value of the _operand_ is multiplied by 100.
430+
431+
##### `:percent` Options
432+
433+
Some options do not have default values defined in this specification.
434+
The defaults for these options are implementation-dependent.
435+
In general, the default values for such options depend on the locale,
436+
the value of other options, or both.
437+
438+
> [!NOTE]
439+
> The names of _options_ and their _option values_ were derived from the
440+
> [options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options)
441+
> in JavaScript's `Intl.NumberFormat`.
442+
443+
The following _options_ are REQUIRED to be available on the function `:percent`:
444+
445+
- `signDisplay`
446+
- `auto` (default)
447+
- `always`
448+
- `exceptZero`
449+
- `negative`
450+
- `never`
451+
- `useGrouping`
452+
- `auto` (default)
453+
- `always`
454+
- `never`
455+
- `min2`
456+
- `minimumFractionDigits`
457+
- _digit size option_, default: `0`
458+
- `maximumFractionDigits`
459+
- _digit size option_, default: `0`
460+
- `minimumSignificantDigits`
461+
- _digit size option_
462+
- `maximumSignificantDigits`
463+
- _digit size option_
464+
- `trailingZeroDisplay`
465+
- `auto` (default)
466+
- `stripIfInteger`
467+
- `roundingPriority`
468+
- `auto` (default)
469+
- `morePrecision`
470+
- `lessPrecision`
471+
- `roundingMode`
472+
- `ceil`
473+
- `floor`
474+
- `expand`
475+
- `trunc`
476+
- `halfCeil`
477+
- `halfFloor`
478+
- `halfExpand` (default)
479+
- `halfTrunc`
480+
- `halfEven`
481+
482+
The numeric value of the _operand_ is multiplied by 100
483+
at the start of formatting or selection.
484+
Each _option_ is applied to the formatted (or selected) value
485+
rather than the unaltered value of the _operand_.
486+
487+
> For example, this _placeholder_:
488+
>
489+
> ```
490+
> {0.1234 :percent maximumFractionDigits=1}
491+
> ```
492+
>
493+
> might be formatted as "12.3%" in an English locale.
494+
495+
If the _operand_ of the _expression_ is an implementation-defined type,
496+
such as the _resolved value_ of an _expression_ with a `:number` or `:integer` _annotation_,
497+
it can include option values.
498+
In general, these are included in the resolved option values of the _expression_,
499+
with _options_ on the _expression_ taking priority over any options of the _operand_.
500+
Options with the following names are however discarded if included in the _operand_:
501+
502+
- `minimumIntegerDigits`
503+
- `roundingIncrement`
504+
- `select`
505+
506+
##### `:percent` Resolved Value
507+
508+
The _resolved value_ of an _expression_ with a `:percent` _function_
509+
contains an implementation-defined numerical value
510+
of the _operand_ of the annotated _expression_
511+
together with the resolved options' values.
512+
The numerical value of the _resolved value_ of the _expression_
513+
is the same as the numerical value of its _operand_;
514+
it is not multiplied by 100.
515+
516+
##### Selection with `:percent`
517+
518+
The _function_ `:percent` performs selection as described in [Number Selection](#number-selection) below.
519+
This selection always uses the `plural` selection mode,
520+
and is performed on the numerical value of the _operand_
521+
multiplied by 100.
522+
523+
> For example, this _message_:
524+
> ```
525+
> .local $pct = {1 :percent}
526+
> .match $pct
527+
> 1 {{Would match with 0.01 as the operand}}
528+
> 100 {{Matches 💯}}
529+
> * {{Otherwise}}
530+
> ```
531+
>
532+
> would be formatted as "Matches 💯".
533+
416534
#### The `:unit` function
417535
418536
> [!IMPORTANT]

test/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ These test files are intended to be useful for testing multiple different _messa
1212
> If your implementation uses UTF-16 based strings (such as JavaScript `String` or Java `java.lang.String`)
1313
> or otherwise allows unpaired surrogates in text or literals, you will need to implement tests equivalent
1414
> to the following for syntax errors:
15+
>
1516
> ```json
1617
> {
1718
> "locale": "en-US",
@@ -72,11 +73,12 @@ Tests for such features have a `tags` array attached to them
7273
to mark the features that they rely on.
7374
This may include one or more of the following:
7475
75-
| Tag | Feature |
76-
| ---------- | ----------------------------------------------------- |
77-
| `u:dir` | The [u:dir](../spec/u-namespace.md#udir) option |
78-
| `u:id` | The [u:id](../spec/u-namespace.md#uid) option |
79-
| `u:locale` | The [u:locale](../spec/u-namespace.md#ulocale) option |
76+
| Tag | Feature |
77+
| ---------- | ------------------------------------------------------------------------- |
78+
| `:percent` | The [:percent](../spec/functions/number.md#the-percent-function) function |
79+
| `u:dir` | The [u:dir](../spec/u-namespace.md#udir) option |
80+
| `u:id` | The [u:id](../spec/u-namespace.md#uid) option |
81+
| `u:locale` | The [u:locale](../spec/u-namespace.md#ulocale) option |
8082
8183
## Test Functions
8284
@@ -199,8 +201,9 @@ emit a _Bad Option_ error.
199201
> Actual functions in your implementation might emit
200202
> an implementation-defined or platform-specific runtime error or exception
201203
> when the function handler is called.
202-
> Your implementation might thus produce a _Message Function Error_
204+
> Your implementation might thus produce a _Message Function Error_
203205
> not provided with a label in the JSON Schema of this test suite.
206+
204207
### `:test:select`
205208
206209
This _function_ accepts the same _operands_ and _options_,

test/schemas/v0/tests.schema.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@
204204
"type": "array",
205205
"items": {
206206
"enum": [
207+
":percent",
207208
"u:dir",
208209
"u:id",
209210
"u:locale"

test/tests/functions/percent.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"$schema": "../../schemas/v0/tests.schema.json",
3+
"scenario": "Percent function",
4+
"description": "The built-in formatter and selector for percent values.",
5+
"defaultTestProperties": {
6+
"tags": [":percent"],
7+
"bidiIsolation": "none",
8+
"locale": "en-US",
9+
"expErrors": []
10+
},
11+
"tests": [
12+
{
13+
"src": "{:percent}",
14+
"expErrors": [{ "type": "bad-operand" }],
15+
"exp": "{:percent}"
16+
},
17+
{
18+
"src": "{foo :percent}",
19+
"expErrors": [{ "type": "bad-operand" }],
20+
"exp": "{|foo|}"
21+
},
22+
{ "src": "{1 :percent}" },
23+
{ "src": ".local $n = {0.42 :number} {{{$n :percent}}}" },
24+
{ "src": ".local $n = {42 :integer} {{{$n :percent}}}" },
25+
{ "src": ".local $n = {0.01 :percent} {{{$n :percent}}}" },
26+
{ "src": "{0.12345678 :percent}" },
27+
{ "src": "{0.12345678 :percent maximumFractionDigits=1}" },
28+
{ "src": "{0.12 :percent minimumFractionDigits=1}" },
29+
{ "src": "{0.12 :percent minimumSignificantDigits=1}" },
30+
{
31+
"src": "{$x :percent}",
32+
"params": [{ "name": "x", "value": 0.99 }]
33+
},
34+
{
35+
"src": ".input {$n :percent} .match $n one {{one}} * {{other}}",
36+
"params": [{ "name": "n", "value": 0.01 }],
37+
"exp": "one"
38+
},
39+
{
40+
"src": ".input {$n :percent} .match $n one {{one}} * {{other}}",
41+
"params": [{ "name": "n", "value": 1 }],
42+
"exp": "other"
43+
}
44+
]
45+
}

0 commit comments

Comments
 (0)