From c50f5d910ace4bbfde431e4b794a80bae18cc56e Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Mon, 4 Aug 2025 21:02:10 +0300 Subject: [PATCH 1/8] Add :percent --- exploration/percent-format.md | 10 +-- spec/functions/README.md | 1 + spec/functions/number.md | 101 ++++++++++++++++++++++++++++++ test/README.md | 15 +++-- test/schemas/v0/tests.schema.json | 1 + test/tests/functions/percent.json | 45 +++++++++++++ 6 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 test/tests/functions/percent.json diff --git a/exploration/percent-format.md b/exploration/percent-format.md index 4dc958a816..09f9de466d 100644 --- a/exploration/percent-format.md +++ b/exploration/percent-format.md @@ -1,6 +1,6 @@ # Formatting Percent Values -Status: **Proposed** +Status: **Accepted**
Metadata @@ -11,7 +11,8 @@ Status: **Proposed**
First proposed
2025-04-07
Pull Requests
-
#1068
+
#1068
+
#1094
@@ -134,9 +135,10 @@ _What prior decisions and existing conditions limit the possible design?_ ## Proposed Design -_Describe the proposed solution. Consider syntax, formatting, errors, registry, tooling, interchange._ +Add a new, dedicated `:percent` function, +which scales the _resolved value_ of its _operand_ by 100. -TBD +Separately, allow `:unit unit=percent` formatting, without scaling. ## Alternatives Considered diff --git a/spec/functions/README.md b/spec/functions/README.md index f250306f95..126a8606cb 100644 --- a/spec/functions/README.md +++ b/spec/functions/README.md @@ -9,6 +9,7 @@ 1. [`:integer`](number.md#the-integer-function) 1. [`:offset`](number.md#the-offset-function) 1. [`:currency`](number.md#the-currency-function) + 1. [`:percent`](number.md#the-percent-function) 1. [`:unit`](number.md#the-unit-function) 1. [Date and Time Value Formatting](datetime.md) 1. [`:datetime`](datetime.md#the-datetime-function) diff --git a/spec/functions/number.md b/spec/functions/number.md index e2a7404e02..baab43056f 100644 --- a/spec/functions/number.md +++ b/spec/functions/number.md @@ -413,6 +413,107 @@ contains an implementation-defined currency value of the _operand_ of the annotated _expression_, together with the resolved options' values. +#### The `:percent` function + +> [!IMPORTANT] +> The _function_ `:percent` has a status of **Draft**. +> It is proposed for inclusion in a future release of this specification and is not Stable. + +The function `:percent` is a selector and formatter for percent values. + +##### Operands + +The function `:percent` requires a [Number Operand](#number-operands) as its _operand_. + +When either selecting or formatting the _expression_, +the _resolved value_ of the _operand_ is multiplied by 100. + +##### 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 value of other options, or both. + +> [!NOTE] +> The names of _options_ and their _option values_ were derived from the +> [options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options) +> in JavaScript's `Intl.NumberFormat`. + +The following _options_ are REQUIRED to be available on the function `:percent`: + +- `signDisplay` + - `auto` (default) + - `always` + - `exceptZero` + - `negative` + - `never` +- `useGrouping` + - `auto` (default) + - `always` + - `never` + - `min2` +- `minimumFractionDigits` + - _digit size option_, default: `0` +- `maximumFractionDigits` + - _digit size option_, default: `0` +- `minimumSignificantDigits` + - _digit size option_ +- `maximumSignificantDigits` + - _digit size option_ +- `trailingZeroDisplay` + - `auto` (default) + - `stripIfInteger` +- `roundingPriority` + - `auto` (default) + - `morePrecision` + - `lessPrecision` +- `roundingMode` + - `ceil` + - `floor` + - `expand` + - `trunc` + - `halfCeil` + - `halfFloor` + - `halfExpand` (default) + - `halfTrunc` + - `halfEven` + +When formatting or selecting, each of the options is applied +after the _resolved value_ of the _operand_ is multiplied by 100 + +> For example, this _placeholder_: +> +> ``` +> {0.1234 :percent maximumFractionDigits=1} +> ``` +> +> would be formatted as "12.3%" in an English locale. + +If the _operand_ of the _expression_ is an implementation-defined type, +such as the _resolved value_ of an _expression_ with a `:number` or `:integer` _annotation_, +it can include option values. +In general, these are included in the resolved option values of the _expression_, +with _options_ on the _expression_ taking priority over any options of the _operand_. +Options with the following names are however discarded if included in the _operand_: + +- `minimumIntegerDigits` +- `roundingIncrement` +- `select` + +##### Resolved Value + +The _resolved value_ of an _expression_ with a `:percent` _function_ +contains an implementation-defined numerical value +of the _operand_ of the annotated _expression_, +together with the resolved options' values. + +##### Selection + +The _function_ `:percent` performs selection as described in [Number Selection](#number-selection) below, +with selection always using `plural` selection mode, +and with the _resolved value_ of the _operand_ multiplied by 100. + #### The `:unit` function > [!IMPORTANT] diff --git a/test/README.md b/test/README.md index f936abf59f..352795f964 100644 --- a/test/README.md +++ b/test/README.md @@ -12,6 +12,7 @@ These test files are intended to be useful for testing multiple different _messa > If your implementation uses UTF-16 based strings (such as JavaScript `String` or Java `java.lang.String`) > or otherwise allows unpaired surrogates in text or literals, you will need to implement tests equivalent > to the following for syntax errors: +> > ```json > { > "locale": "en-US", @@ -72,11 +73,12 @@ Tests for such features have a `tags` array attached to them to mark the features that they rely on. This may include one or more of the following: -| Tag | Feature | -| ---------- | ----------------------------------------------------- | -| `u:dir` | The [u:dir](../spec/u-namespace.md#udir) option | -| `u:id` | The [u:id](../spec/u-namespace.md#uid) option | -| `u:locale` | The [u:locale](../spec/u-namespace.md#ulocale) option | +| Tag | Feature | +| ---------- | ------------------------------------------------------------------------- | +| `:percent` | The [:percent](../spec/functions/number.md#the-percent-function) function | +| `u:dir` | The [u:dir](../spec/u-namespace.md#udir) option | +| `u:id` | The [u:id](../spec/u-namespace.md#uid) option | +| `u:locale` | The [u:locale](../spec/u-namespace.md#ulocale) option | ## Test Functions @@ -199,8 +201,9 @@ emit a _Bad Option_ error. > Actual functions in your implementation might emit > an implementation-defined or platform-specific runtime error or exception > when the function handler is called. -> Your implementation might thus produce a _Message Function Error_ +> Your implementation might thus produce a _Message Function Error_ > not provided with a label in the JSON Schema of this test suite. + ### `:test:select` This _function_ accepts the same _operands_ and _options_, diff --git a/test/schemas/v0/tests.schema.json b/test/schemas/v0/tests.schema.json index d0215dae33..11a0d8ca2f 100644 --- a/test/schemas/v0/tests.schema.json +++ b/test/schemas/v0/tests.schema.json @@ -204,6 +204,7 @@ "type": "array", "items": { "enum": [ + ":percent", "u:dir", "u:id", "u:locale" diff --git a/test/tests/functions/percent.json b/test/tests/functions/percent.json new file mode 100644 index 0000000000..69b91d7c98 --- /dev/null +++ b/test/tests/functions/percent.json @@ -0,0 +1,45 @@ +{ + "$schema": "../../schemas/v0/tests.schema.json", + "scenario": "Percent function", + "description": "The built-in formatter and selector for percent values.", + "defaultTestProperties": { + "tags": [":percent"], + "bidiIsolation": "none", + "locale": "en-US", + "expErrors": [] + }, + "tests": [ + { + "src": "{:percent}", + "expErrors": [{ "type": "bad-operand" }], + "exp": "{:percent}" + }, + { + "src": "{foo :percent}", + "expErrors": [{ "type": "bad-operand" }], + "exp": "{|foo|}" + }, + { "src": "{1 :percent}" }, + { "src": ".local $n = {0.42 :number} {{{$n :percent}}}" }, + { "src": ".local $n = {42 :integer} {{{$n :percent}}}" }, + { "src": ".local $n = {0.01 :percent} {{{$n :percent}}}" }, + { "src": "{0.12345678 :percent}" }, + { "src": "{0.12345678 :percent maximumFractionDigits=1}" }, + { "src": "{0.12 :percent minimumFractionDigits=1}" }, + { "src": "{0.12 :percent minimumSignificantDigits=1}" }, + { + "src": "{$x :percent}", + "params": [{ "name": "x", "value": 0.99 }] + }, + { + "src": ".input {$n :percent} .match $n one {{one}} * {{other}}", + "params": [{ "name": "n", "value": 0.01 }], + "exp": "one" + }, + { + "src": ".input {$n :percent} .match $n one {{one}} * {{other}}", + "params": [{ "name": "n", "value": 1 }], + "exp": "other" + } + ] +} From c64d781f2eb0bfac5fb35533b69a8cf7e6b2b996 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Tue, 5 Aug 2025 10:37:58 +0300 Subject: [PATCH 2/8] Apply suggestions from code review Co-authored-by: Addison Phillips --- spec/functions/number.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/functions/number.md b/spec/functions/number.md index baab43056f..95773bcca2 100644 --- a/spec/functions/number.md +++ b/spec/functions/number.md @@ -479,8 +479,10 @@ The following _options_ are REQUIRED to be available on the function `:percent`: - `halfTrunc` - `halfEven` -When formatting or selecting, each of the options is applied -after the _resolved value_ of the _operand_ is multiplied by 100 +The _resolved value_ of the _operand_ is multiplied by 100 +at the start of formatting or selection. +Each _option_ is applied to the formatted (or selected) value +rather than the unaltered value of the _operand_. > For example, this _placeholder_: > @@ -488,7 +490,7 @@ after the _resolved value_ of the _operand_ is multiplied by 100 > {0.1234 :percent maximumFractionDigits=1} > ``` > -> would be formatted as "12.3%" in an English locale. +> might be formatted as "12.3%" in an English locale. If the _operand_ of the _expression_ is an implementation-defined type, such as the _resolved value_ of an _expression_ with a `:number` or `:integer` _annotation_, @@ -505,7 +507,8 @@ Options with the following names are however discarded if included in the _opera The _resolved value_ of an _expression_ with a `:percent` _function_ contains an implementation-defined numerical value -of the _operand_ of the annotated _expression_, +of the _operand_ of the annotated _expression_ +(not multiplied by 100), together with the resolved options' values. ##### Selection @@ -514,6 +517,17 @@ The _function_ `:percent` performs selection as described in [Number Selection]( with selection always using `plural` selection mode, and with the _resolved value_ of the _operand_ multiplied by 100. +> For example, this _message_: +> ``` +> .local $pct = {1 :percent} +> .match $pct +> 1 {{Would match with 0.01 as the operand}} +> 100 {{Matches 💯}} +> * {{Otherwise}} +> ``` +> +> would be formatted as "Matches 💯". + #### The `:unit` function > [!IMPORTANT] From 2cfa500b4bb06b82be6978734c5cda4984d1eef5 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Tue, 5 Aug 2025 17:26:37 +0300 Subject: [PATCH 3/8] Apply suggestions from code review Co-authored-by: Addison Phillips --- spec/functions/number.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/functions/number.md b/spec/functions/number.md index 95773bcca2..38ebde7acb 100644 --- a/spec/functions/number.md +++ b/spec/functions/number.md @@ -508,14 +508,16 @@ Options with the following names are however discarded if included in the _opera The _resolved value_ of an _expression_ with a `:percent` _function_ contains an implementation-defined numerical value of the _operand_ of the annotated _expression_ -(not multiplied by 100), together with the resolved options' values. +The _resolved value_ is not altered by `:percent`, +that is, it is not multiplied by 100. ##### Selection -The _function_ `:percent` performs selection as described in [Number Selection](#number-selection) below, -with selection always using `plural` selection mode, -and with the _resolved value_ of the _operand_ multiplied by 100. +The _function_ `:percent` performs selection as described in [Number Selection](#number-selection) below. +This selection always uses the `plural` selection mode, +and is performed on the _resolved value_ of the _operand_ +multiplied by 100. > For example, this _message_: > ``` From 277649d45c8fe3fcda6882ca90ac34a440d13f2d Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Tue, 5 Aug 2025 21:40:02 +0300 Subject: [PATCH 4/8] Update exploration/percent-format.md Co-authored-by: Addison Phillips --- exploration/percent-format.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exploration/percent-format.md b/exploration/percent-format.md index 09f9de466d..49930b4f39 100644 --- a/exploration/percent-format.md +++ b/exploration/percent-format.md @@ -138,7 +138,9 @@ _What prior decisions and existing conditions limit the possible design?_ Add a new, dedicated `:percent` function, which scales the _resolved value_ of its _operand_ by 100. -Separately, allow `:unit unit=percent` formatting, without scaling. +This design could allow a function such as `:unit unit=percent` +for formatting values without scaling +when the working group tackles units. ## Alternatives Considered From 2fc35acbcb1598c81b36092b974f355423d80ce6 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Wed, 6 Aug 2025 09:42:14 +0300 Subject: [PATCH 5/8] Use "numerical value" instead of "resolved value" where appropriate --- spec/functions/number.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/functions/number.md b/spec/functions/number.md index 38ebde7acb..8e55c815d8 100644 --- a/spec/functions/number.md +++ b/spec/functions/number.md @@ -426,7 +426,7 @@ The function `:percent` is a selector and formatter for percent values. The function `:percent` requires a [Number Operand](#number-operands) as its _operand_. When either selecting or formatting the _expression_, -the _resolved value_ of the _operand_ is multiplied by 100. +the numeric value of the _operand_ is multiplied by 100. ##### Options @@ -479,7 +479,7 @@ The following _options_ are REQUIRED to be available on the function `:percent`: - `halfTrunc` - `halfEven` -The _resolved value_ of the _operand_ is multiplied by 100 +The numeric value of the _operand_ is multiplied by 100 at the start of formatting or selection. Each _option_ is applied to the formatted (or selected) value rather than the unaltered value of the _operand_. @@ -510,13 +510,13 @@ contains an implementation-defined numerical value of the _operand_ of the annotated _expression_ together with the resolved options' values. The _resolved value_ is not altered by `:percent`, -that is, it is not multiplied by 100. +that is, its numerical value is not multiplied by 100. ##### Selection The _function_ `:percent` performs selection as described in [Number Selection](#number-selection) below. This selection always uses the `plural` selection mode, -and is performed on the _resolved value_ of the _operand_ +and is performed on the numerical value of the _operand_ multiplied by 100. > For example, this _message_: From a1dccbfeca3aa62828b1e306b71a925daccce9f6 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Thu, 7 Aug 2025 12:15:43 +0300 Subject: [PATCH 6/8] Update spec/functions/number.md --- spec/functions/number.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/functions/number.md b/spec/functions/number.md index 8e55c815d8..f6434945f4 100644 --- a/spec/functions/number.md +++ b/spec/functions/number.md @@ -509,8 +509,9 @@ The _resolved value_ of an _expression_ with a `:percent` _function_ contains an implementation-defined numerical value of the _operand_ of the annotated _expression_ together with the resolved options' values. -The _resolved value_ is not altered by `:percent`, -that is, its numerical value is not multiplied by 100. +The numerical value of the _resolved value_ of the _expression_ +is the same as the numerical value of its _operand_; +it is not multiplied by 100. ##### Selection From 54ed7b71ab0e099e5970faf059379cb3eb67eec1 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Sun, 10 Aug 2025 16:43:55 +0300 Subject: [PATCH 7/8] Fix section titles to match the style of PR #1096 --- spec/functions/number.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/functions/number.md b/spec/functions/number.md index f6434945f4..7fc5179163 100644 --- a/spec/functions/number.md +++ b/spec/functions/number.md @@ -421,14 +421,14 @@ together with the resolved options' values. The function `:percent` is a selector and formatter for percent values. -##### Operands +##### `:percent` Operands The function `:percent` requires a [Number Operand](#number-operands) as its _operand_. When either selecting or formatting the _expression_, the numeric value of the _operand_ is multiplied by 100. -##### Options +##### `:percent` Options Some options do not have default values defined in this specification. The defaults for these options are implementation-dependent. @@ -503,7 +503,7 @@ Options with the following names are however discarded if included in the _opera - `roundingIncrement` - `select` -##### Resolved Value +##### `:percent` Resolved Value The _resolved value_ of an _expression_ with a `:percent` _function_ contains an implementation-defined numerical value @@ -513,7 +513,7 @@ The numerical value of the _resolved value_ of the _expression_ is the same as the numerical value of its _operand_; it is not multiplied by 100. -##### Selection +##### Selection with `:percent` The _function_ `:percent` performs selection as described in [Number Selection](#number-selection) below. This selection always uses the `plural` selection mode, From 90f1b87de3b7ded16c46190c958f0c8e8c0a616d Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Mon, 11 Aug 2025 19:43:52 +0300 Subject: [PATCH 8/8] Refer to _numeric operand_ term --- spec/functions/number.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/functions/number.md b/spec/functions/number.md index 7fc5179163..4504d572f9 100644 --- a/spec/functions/number.md +++ b/spec/functions/number.md @@ -423,7 +423,7 @@ The function `:percent` is a selector and formatter for percent values. ##### `:percent` Operands -The function `:percent` requires a [Number Operand](#number-operands) as its _operand_. +The function `:percent` requires a _numeric operand_ as its _operand_. When either selecting or formatting the _expression_, the numeric value of the _operand_ is multiplied by 100.