Skip to content
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

Add u: options namespace #846

Merged
merged 12 commits into from
Oct 21, 2024
1 change: 1 addition & 0 deletions spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
1. [Resolution Errors](errors.md#resolution-errors)
1. [Message Function Errors](errors.md#message-function-errors)
1. [Default Function Registry](registry.md)
1. [`u:` Namespace](u-namespace.md)
1. [Formatting](formatting.md)
1. [Interchange data model](data-model/README.md)

Expand Down
21 changes: 18 additions & 3 deletions spec/formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,20 @@ the following steps are taken:

3. Perform _option resolution_.

4. Call the _function handler_ with the following arguments:
4. Determine the **_<dfn>function context</dfn>_** for calling the function implementation.
This includes:

- The current _locale_.
- The current _locale_,
potentially including a fallback chain of locales.
- The base directionality of the _message_ and its _text_ tokens.

If the resolved mapping of _options_ includes any _`u:` options_
supported by the implementation, process them as specified.
Such `u:` options MAY be removed from the resolved mapping of _options_.
eemeli marked this conversation as resolved.
Show resolved Hide resolved
aphillips marked this conversation as resolved.
Show resolved Hide resolved

5. Call the function implementation with the following arguments:

- The _function context_.
- The resolved mapping of _options_.
- If the _expression_ includes an _operand_, its _resolved value_.

Expand All @@ -272,7 +283,7 @@ the following steps are taken:
as long as reasonable precautions are taken to keep the function interface
simple and minimal, and avoid introducing potential security vulnerabilities.

5. If the call succeeds,
6. If the call succeeds,
resolve the value of the _expression_ as the result of that function call.

If the call fails or does not return a valid value,
Expand Down Expand Up @@ -345,6 +356,10 @@ The _resolved value_ of _markup_ includes the following fields:
- The _identifier_ of the _markup_
- The resolved _options_ values after _option resolution_.

If the resolved mapping of _options_ includes any _`u:` options_
supported by the implementation, process them as specified.
Such `u:` options MAY be removed from the resolved mapping of _options_.

The resolution of _markup_ MUST always succeed.

### Fallback Resolution
Expand Down
65 changes: 65 additions & 0 deletions spec/u-namespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# MessageFormat 2.0 Unicode Namespace

The `u:` namespace is reserved for use by the Unicode Consortium.
eemeli marked this conversation as resolved.
Show resolved Hide resolved

## Options

This section describes common **_<dfn>`u:` options</dfn>_** which each implementation SHOULD support
for all _functions_ and _markup_.

### `u:id`

A string value that is included as an `id` or other suitable value
in the formatted parts for the _placeholder_,
or any other structured formatted results.

Ignored when formatting a message to a string.

Accepts string values, or values which can be stringified without error.
eemeli marked this conversation as resolved.
Show resolved Hide resolved
For other values, a _Bad Option_ error is emitted
and the `u:id` option is ignored.

### `u:locale`

A comma-delimited list of BCP 47 language tags,
or an implementation-defined list of such tags.
eemeli marked this conversation as resolved.
Show resolved Hide resolved

Replaces the _locale_ defined in the _function context_ for this _expression_.
aphillips marked this conversation as resolved.
Show resolved Hide resolved
The value is ignored when set on _markup_.

During processing, the `u:locale` option
is always removed from the resolved mapping of _options_.
eemeli marked this conversation as resolved.
Show resolved Hide resolved

Values matching the following ABNF are always accepted:
```abnf
u-locale-option = langtag *([s] "," [s] langtag)
eemeli marked this conversation as resolved.
Show resolved Hide resolved
```
using `langtag` as defined in [BCP 47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt).
Note that `langtag` is the rule for "normal language tags",
and does not include private-use or grandfathered tags.

Implementations MAY support additional language tags,
such as private-use or grandfathered tags,
or tags using `_` instead of `-` as a separator.
When the value of `u:locale` is set by a _variable_,
implementations MAY support non-string values otherwise representing locales.

For unsupported values, a _Bad Option_ error is emitted
and the value of the `u:locale` option is ignored.
eemeli marked this conversation as resolved.
Show resolved Hide resolved

### `u:dir`

Replaces the base directionality defined in
the _function context_ for this _expression_.
The value is ignored when set on _markup_.
eemeli marked this conversation as resolved.
Show resolved Hide resolved

During processing, the `u:dir` option
is always removed from the resolved mapping of _options_.

Accepts the following string values:
- `ltr`: left-to-right directionality
- `rtl`: right-to-left directionality
- `auto`: directionality determined from _expression_ contents
eemeli marked this conversation as resolved.
Show resolved Hide resolved

For other values, a _Bad Option_ error is emitted
and the value of the `u:dir` option is ignored.
3 changes: 3 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ These test files are intended to be useful for testing multiple different messag
- `data-model-errors.json` - Strings that should produce a Data Model Error when processed.
Error names are defined in ["MessageFormat 2.0 Errors"](../spec/errors.md) in the spec.

- `u-options.md` — Test cases for the `u:` options, using built-in functions.
eemeli marked this conversation as resolved.
Show resolved Hide resolved

- `functions/` — Test cases that correspond to built-in functions.
The behaviour of the built-in formatters is implementation-specific so the `exp` field is often
omitted and assertions are made on error cases.
Expand All @@ -21,6 +23,7 @@ Some examples of test harnesses using these tests, from the source repository:
- [Formatting tests](https://github.com/messageformat/messageformat/blob/11c95dab2b25db8454e49ff4daadb817e1d5b770/packages/mf2-messageformat/src/messageformat.test.ts)

A [JSON schema](./schemas/) is included for the test files in this repository.

## Error Codes

The following table relates the error names used in the [JSON schema](./schemas/)
Expand Down
3 changes: 3 additions & 0 deletions test/schemas/v0/tests.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@
"name": {
"type": "string"
},
"id": {
"type": "string"
},
"options": {
"type": "object"
}
Expand Down
105 changes: 105 additions & 0 deletions test/tests/u-options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json",
"scenario": "u: Options",
"description": "Common options affecting the function context",
"defaultTestProperties": {
"locale": "en-US"
},
"tests": [
{
"src": "{#tag u:id=x u:dir=rtl u:locale=ar}content{/ns:tag u:id=x}",
"exp": "content",
"expParts": [
{
"type": "markup",
"kind": "open",
"id": "x",
"name": "tag"
},
{
"type": "literal",
"value": "content"
},
{
"type": "markup",
"kind": "close",
"id": "x",
"name": "tag"
}
]
},
{
"src": "hello {4.2 :number u:locale=fr}",
"exp": "hello 4,2"
},
{
"src": "hello {world :string u:dir=ltr u:id=foo}",
"exp": "hello world",
"expParts": [
{
"type": "literal",
"value": "hello "
},
{
"type": "string",
"source": "|world|",
"dir": "ltr",
"id": "foo",
"value": "world"
}
]
},
{
"src": "hello {world :string u:dir=rtl}",
"exp": "hello \u2067world\u2069",
"expParts": [
{
"type": "literal",
"value": "hello "
},
{
"type": "string",
"source": "|world|",
"dir": "rtl",
"value": "world"
}
]
},
{
"src": "hello {world :string u:dir=auto}",
"exp": "hello \u2068world\u2069",
"expParts": [
{
"type": "literal",
"value": "hello "
},
{
"type": "string",
"source": "|world|",
"dir": "auto",
"value": "world"
}
]
},
{
"locale": "ar",
"src": "أهلاً {بالعالم :string u:dir=rtl}",
"exp": "أهلاً \u2067بالعالم\u2069"
},
{
"locale": "ar",
"src": "أهلاً {بالعالم :string u:dir=auto}",
"exp": "أهلاً \u2068بالعالم\u2069"
},
{
"locale": "ar",
"src": "أهلاً {world :string u:dir=ltr}",
"exp": "أهلاً \u2066world\u2069"
},
{
"locale": "ar",
"src": "أهلاً {بالعالم :string}",
"exp": "أهلاً \u2067بالعالم\u2069"
}
]
}