Skip to content

Commit a5d2d89

Browse files
eemeliaphillipsstasmgibson042
authored
Add markup as designed (#574)
* Add markup as designed * Apply suggestions from code review Co-authored-by: Addison Phillips <[email protected]> Co-authored-by: Stanisław Małolepszy <[email protected]> * Apply suggestions from code review * Add separate markup-standalone schema in messages.json * Apply suggestions from code review Co-authored-by: Richard Gibson <[email protected]> Co-authored-by: Addison Phillips <[email protected]> * Apply suggestions from code review Co-authored-by: Addison Phillips <[email protected]> --------- Co-authored-by: Addison Phillips <[email protected]> Co-authored-by: Stanisław Małolepszy <[email protected]> Co-authored-by: Richard Gibson <[email protected]>
1 parent e1f48ed commit a5d2d89

File tree

6 files changed

+215
-82
lines changed

6 files changed

+215
-82
lines changed

spec/data-model/README.md

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,31 +117,40 @@ String values include all processing of the underlying _text_ values,
117117
including escape sequence processing.
118118
`Expression` values wrap each of the _expression_ shapes.
119119

120-
Implementations MUST NOT rely on the set of `Expression` interfaces being exhaustive,
121-
as future versions of this specification MAY define additional expressions.
120+
Implementations MUST NOT rely on the set of `Expression` and
121+
`Markup` interfaces defined in this document being exhaustive.
122+
Future versions of this specification might define additional
123+
expressions or markup.
122124

123125
```ts
124126
type Pattern = Array<string | Expression>;
125127

126-
type Expression = LiteralExpression | VariableExpression | FunctionExpression |
127-
UnsupportedExpression;
128+
type Expression =
129+
| LiteralExpression
130+
| VariableExpression
131+
| FunctionExpression
132+
| UnsupportedExpression;
128133

129134
interface LiteralExpression {
135+
type: "expression";
130136
arg: Literal;
131137
annotation?: FunctionAnnotation | UnsupportedAnnotation;
132138
}
133139

134140
interface VariableExpression {
141+
type: "expression";
135142
arg: VariableRef;
136143
annotation?: FunctionAnnotation | UnsupportedAnnotation;
137144
}
138145

139146
interface FunctionExpression {
147+
type: "expression";
140148
arg?: never;
141149
annotation: FunctionAnnotation;
142150
}
143151

144152
interface UnsupportedExpression {
153+
type: "expression";
145154
arg?: never;
146155
annotation: UnsupportedAnnotation;
147156
}
@@ -172,17 +181,13 @@ interface VariableRef {
172181
```
173182

174183
A `FunctionAnnotation` represents a _function_ _annotation_.
175-
In a `FunctionAnnotation`,
176-
the `kind` corresponds to the starting sigil of a _function_:
177-
`'open'` for `+`, `'close'` for `-`, and `'value'` for `:`.
178-
The `name` does not include this starting sigil.
184+
The `name` does not include the `:` starting sigil.
179185

180186
Each _option_ is represented by an `Option`.
181187

182188
```ts
183189
interface FunctionAnnotation {
184190
type: "function";
185-
kind: "open" | "close" | "value";
186191
name: string;
187192
options?: Option[];
188193
}
@@ -214,11 +219,44 @@ that the implementation attaches to that _annotation_.
214219
```ts
215220
interface UnsupportedAnnotation {
216221
type: "unsupported-annotation";
217-
sigil: "!" | "@" | "#" | "%" | "^" | "&" | "*" | "<" | ">" | "/" | "?" | "~";
222+
sigil: "!" | "@" | "%" | "^" | "&" | "*" | "+" | "<" | ">" | "?" | "~";
218223
source: string;
219224
}
220225
```
221226

227+
## Markup
228+
229+
A `Markup` object is either `MarkupOpen`, `MarkupStandalone`, or `MarkupClose`,
230+
which are differentiated by `kind`.
231+
The `name` in these does not include the starting sigils `#` and `/`
232+
or the ending sigil `/`.
233+
The optional `options` for open and standalone markup use the same `Option`
234+
as `FunctionAnnotation`.
235+
236+
```ts
237+
type Markup = MarkupOpen | MarkupStandalone | MarkupClose;
238+
239+
interface MarkupOpen {
240+
type: "markup";
241+
kind: "open";
242+
name: string;
243+
options?: Option[];
244+
}
245+
246+
interface MarkupStandalone {
247+
type: "markup";
248+
kind: "standalone";
249+
name: string;
250+
options?: Option[];
251+
}
252+
253+
interface MarkupClose {
254+
type: "markup";
255+
kind: "close";
256+
name: string;
257+
}
258+
```
259+
222260
## Extensions
223261

224262
Implementations MAY extend this data model with additional interfaces,

spec/data-model/message.dtd

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<!ELEMENT key (#PCDATA)>
2222
<!ATTLIST key default (true | false) "false">
2323

24-
<!ELEMENT pattern (#PCDATA | expression)*>
24+
<!ELEMENT pattern (#PCDATA | expression | markup)*>
2525

2626
<!ELEMENT expression (
2727
((literal | variable), (functionAnnotation | unsupportedAnnotation)?) |
@@ -35,12 +35,17 @@
3535
<!ATTLIST variable name NMTOKEN #REQUIRED>
3636

3737
<!ELEMENT functionAnnotation (option)*>
38-
<!ATTLIST functionAnnotation
39-
kind (open | close | value) #REQUIRED
40-
name NMTOKEN #REQUIRED
41-
>
38+
<!ATTLIST functionAnnotation name NMTOKEN #REQUIRED>
39+
4240
<!ELEMENT option (literal | variable)>
4341
<!ATTLIST option name NMTOKEN #REQUIRED>
4442

4543
<!ELEMENT unsupportedAnnotation (#PCDATA)>
4644
<!ATTLIST unsupportedAnnotation sigil CDATA #REQUIRED>
45+
46+
<!-- A <markup kind="close"> MUST NOT contain any <option> elements -->
47+
<!ELEMENT markup (option)*>
48+
<!ATTLIST markup
49+
kind (open | standalone | close) #REQUIRED
50+
name NMTOKEN #REQUIRED
51+
>

spec/data-model/message.json

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,8 @@
4040
"type": "object",
4141
"properties": {
4242
"type": { "const": "function" },
43-
"kind": { "enum": ["open", "close", "value"] },
4443
"name": { "type": "string" },
45-
"options": {
46-
"type": "array",
47-
"items": { "$ref": "#/$defs/option" }
48-
}
44+
"options": { "$ref": "#/$defs/options" }
4945
},
5046
"required": ["type", "kind", "name"]
5147
},
@@ -70,32 +66,36 @@
7066
"literal-expression": {
7167
"type": "object",
7268
"properties": {
69+
"type": { "const": "expression" },
7370
"arg": { "$ref": "#/$defs/literal" },
7471
"annotation": { "$ref": "#/$defs/annotation" }
7572
},
76-
"required": ["arg"]
73+
"required": ["type", "arg"]
7774
},
7875
"variable-expression": {
7976
"type": "object",
8077
"properties": {
78+
"type": { "const": "expression" },
8179
"arg": { "$ref": "#/$defs/variable" },
8280
"annotation": { "$ref": "#/$defs/annotation" }
8381
},
84-
"required": ["arg"]
82+
"required": ["type", "arg"]
8583
},
8684
"function-expression": {
8785
"type": "object",
8886
"properties": {
87+
"type": { "const": "expression" },
8988
"annotation": { "$ref": "#/$defs/function-annotation" }
9089
},
91-
"required": ["annotation"]
90+
"required": ["type", "annotation"]
9291
},
9392
"unsupported-expression": {
9493
"type": "object",
9594
"properties": {
95+
"type": { "const": "expression" },
9696
"annotation": { "$ref": "#/$defs/unsupported-annotation" }
9797
},
98-
"required": ["annotation"]
98+
"required": ["type", "annotation"]
9999
},
100100
"expression": {
101101
"oneOf": [
@@ -106,6 +106,43 @@
106106
]
107107
},
108108

109+
"markup-open": {
110+
"type": "object",
111+
"properties": {
112+
"type": { "const": "markup" },
113+
"kind": { "const": "open" },
114+
"name": { "type": "string" },
115+
"options": { "$ref": "#/$defs/options" }
116+
},
117+
"required": ["type", "kind", "name"]
118+
},
119+
"markup-standalone": {
120+
"type": "object",
121+
"properties": {
122+
"type": { "const": "markup" },
123+
"kind": { "const": "standalone" },
124+
"name": { "type": "string" },
125+
"options": { "$ref": "#/$defs/options" }
126+
},
127+
"required": ["type", "kind", "name"]
128+
},
129+
"markup-close": {
130+
"type": "object",
131+
"properties": {
132+
"type": { "const": "markup" },
133+
"kind": { "const": "close" },
134+
"name": { "type": "string" }
135+
},
136+
"required": ["type", "kind", "name"]
137+
},
138+
"markup": {
139+
"oneOf": [
140+
{ "$ref": "#/$defs/markup-open" },
141+
{ "$ref": "#/$defs/markup-standalone" },
142+
{ "$ref": "#/$defs/markup-close" }
143+
]
144+
},
145+
109146
"pattern": {
110147
"type": "array",
111148
"items": {

spec/formatting.md

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ their handling during formatting is specified here as well.
1212

1313
Formatting of a _message_ is defined by the following operations:
1414

15-
- **_Expression Resolution_** determines the value of an _expression_,
15+
- **_Expression and Markup Resolution_** determines the value of an _expression_ or _markup_,
1616
with reference to the current _formatting context_.
1717
This can include multiple steps,
1818
such as looking up the value of a variable and calling formatting functions.
@@ -83,9 +83,10 @@ At a minimum, it includes:
8383

8484
Implementations MAY include additional fields in their _formatting context_.
8585

86-
## Expression Resolution
86+
## Expression and Markup Resolution
8787

8888
_Expressions_ are used in _declarations_, _selectors_, and _patterns_.
89+
_Markup_ is only used in _patterns_.
8990

9091
In a _declaration_, the resolved value of the _expression_ is bound to a _variable_,
9192
which is available for use by later _expressions_.
@@ -98,7 +99,7 @@ but also the _variable_ to which the resolved value of the _variable-expression_
9899

99100
In _selectors_, the resolved value of an _expression_ is used for _pattern selection_.
100101

101-
In a _pattern_, the resolved value of an _expression_ is used in its _formatting_.
102+
In a _pattern_, the resolved value of an _expression_ or _markup_ is used in its _formatting_.
102103

103104
The shapes of resolved values are implementation-dependent,
104105
and different implementations MAY choose to perform different levels of resolution.
@@ -210,17 +211,7 @@ the following steps are taken:
210211
Implementations are not required to implement _namespaces_ or installable
211212
_function registries_.
212213
213-
3. Resolve the _options_ to a mapping of string identifiers to values.
214-
If _options_ is missing, the mapping will be empty.
215-
For each _option_:
216-
- Resolve the _identifier_ of the _option_.
217-
- If the _option_'s _identifier_ already exists in the resolved mapping of _options_,
218-
emit a Duplicate Option Name error.
219-
- If the _option_'s right-hand side successfully resolves to a value,
220-
bind the _identifier_ of the _option_ to the resolved value in the mapping.
221-
- Otherwise, bind the _identifier_ of the _option_ to an unresolved value in the mapping.
222-
Implementations MAY later remove this value before calling the _function_.
223-
(Note that an Unresolved Variable error will have been emitted.)
214+
3. Perform _option resolution_.
224215
225216
4. Call the function implementation with the following arguments:
226217
@@ -247,6 +238,37 @@ the following steps are taken:
247238
If the call fails or does not return a valid value,
248239
emit a Resolution error and use a _fallback value_ for the _expression_.
249240
241+
#### Option Resolution
242+
243+
The result of resolving _option_ values is a mapping of string identifiers to values.
244+
245+
For each _option_:
246+
247+
- Resolve the _identifier_ of the _option_.
248+
- If the _option_'s _identifier_ already exists in the resolved mapping of _options_,
249+
emit a Duplicate Option Name error.
250+
- If the _option_'s right-hand side successfully resolves to a value,
251+
bind the _identifier_ of the _option_ to the resolved value in the mapping.
252+
- Otherwise, bind the _identifier_ of the _option_ to an unresolved value in the mapping.
253+
Implementations MAY later remove this value before calling the _function_.
254+
(Note that an Unresolved Variable error will have been emitted.)
255+
256+
Errors MAY be emitted during _option resolution_,
257+
but it always resolves to some mapping of string identifiers to values.
258+
This mapping can be empty.
259+
260+
### Markup Resolution
261+
262+
Unlike _functions_, the resolution of _markup_ is not customizable.
263+
264+
The resolved value of _markup_ includes the following fields:
265+
266+
- The type of the markup: open, standalone, or close
267+
- The _identifier_ of the _markup_
268+
- For _markup-open_ and _markup_standalone_,
269+
the resolved _options_ values after _option resolution_.
270+
271+
The resolution of _markup_ MUST always succeed.
250272
251273
### Fallback Resolution
252274
@@ -304,7 +326,7 @@ The _fallback value_ depends on the contents of the _expression_:
304326
> the message formats to `{$arg}`.
305327
306328
- _function_ _expression_ with no _operand_:
307-
the _function_ starting sigil followed by its _identifier_
329+
U+003A COLON `:` followed by the _function_ _identifier_
308330
309331
> Examples:
310332
> In a context where `:func` fails to resolve, `{:func}` resolves to the _fallback value_ `:func`.
@@ -599,7 +621,7 @@ one {{Category match}}
599621
## Formatting
600622
601623
After _pattern selection_,
602-
each _text_ and _expression_ part of the selected _pattern_ is resolved and formatted.
624+
each _text_ and _placeholder_ part of the selected _pattern_ is resolved and formatted.
603625
604626
_Formatting_ is a mostly implementation-defined process,
605627
as it depends on the implementation's shape for resolved values
@@ -618,13 +640,18 @@ appropriate data type or structure. Some examples of these include:
618640
- A string with associated attributes for portions of its text.
619641
- A flat sequence of objects corresponding to each resolved value.
620642
- A hierarchical structure of objects that group spans of resolved values,
621-
such as sequences delimited by "open" and "close" _function_ _annotations_.
643+
such as sequences delimited by _markup-open_ and _markup-close_ _placeholders_.
622644
623645
Implementations SHOULD provide _formatting_ result types that match user needs,
624646
including situations that require further processing of formatted messages.
625647
Implementations SHOULD encourage users to consider a formatted localised string
626648
as an opaque data structure, suitable only for presentation.
627649
650+
When formatting to a string, the default representation of all _markup_
651+
MUST be an empty string.
652+
Implementations MAY offer functionality for customizing this,
653+
such as by emitting XML-ish tags for each _markup_.
654+
628655
### Examples
629656
630657
_This section is non-normative._

0 commit comments

Comments
 (0)