Skip to content

Commit cb02ef0

Browse files
eemelistasmmihnita
authored
Use keywords in syntax (#287)
Co-authored-by: Stanisław Małolepszy <[email protected]> Co-authored-by: Mihai Nita <[email protected]>
1 parent 11c3d5d commit cb02ef0

File tree

2 files changed

+73
-100
lines changed

2 files changed

+73
-100
lines changed

spec/message.ebnf

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
Message ::= Plain | Pattern | Preamble Variant+
1+
Message ::= Declaration* ( Pattern | Selector Variant+ )
22

33
/* Preamble */
4-
Preamble ::= Selector+
5-
Selector ::= (Variable '=')? '{' Expression '}'
4+
Declaration ::= 'let' WhiteSpace Variable '=' '{' Expression '}'
5+
Selector ::= 'match' ( '{' Expression '}' )+
66

77
/* Variants and Patterns */
8-
Variant ::= VariantKey* Pattern
8+
Variant ::= 'when' ( WhiteSpace VariantKey )+ Pattern
99
VariantKey ::= Literal | Nmtoken | '*'
1010
Pattern ::= '[' (Text | Placeholder)* ']' /* ws: explicit */
1111

@@ -23,12 +23,6 @@ Markup ::= MarkupStart Option*
2323

2424
<?TOKENS?>
2525

26-
/* Plain */
27-
Plain ::= PlainStart (PlainChar* PlainEnd)? /* ws: explicit */
28-
PlainChar ::= AnyChar - ('{' | '}')
29-
PlainStart ::= PlainChar - ('[' | '$' | WhiteSpace)
30-
PlainEnd ::= PlainChar - WhiteSpace
31-
3226
/* Text */
3327
Text ::= (TextChar | TextEscape)+
3428
TextChar ::= AnyChar - ('[' | ']' | '{' | '}' | Esc)

spec/syntax.md

Lines changed: 69 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
1. [Complex Messages](#complex-messages)
1616
1. [Productions](#productions)
1717
1. [Message](#message)
18-
1. [Plain](#plain)
19-
1. [Preamble](#preamble)
18+
1. [Variable Declarations](#variable-declarations)
19+
1. [Selectors](#selectors)
2020
1. [Variants](#variants)
2121
1. [Patterns](#patterns)
2222
1. [Placeholders](#placeholders)
@@ -66,9 +66,6 @@ The design goals of the syntax specification are as follows:
6666
`.properties`, YAML, XML, inlined as string literals in programming languages, etc.
6767
This includes a future _MessageResource_ specification.
6868

69-
1. Simple messages that do not use any placeholders or selectors should (as far as possible)
70-
be represented in the syntax with no additional characters than their actual contents.
71-
7269
### Design Restrictions
7370

7471
The syntax specification takes into account the following design restrictions:
@@ -77,52 +74,36 @@ The syntax specification takes into account the following design restrictions:
7774
It should be possible to define a message entirely on a single line with no ambiguity,
7875
as well as to format it over multiple lines for clarity.
7976

80-
1. The syntax should not use nor reserve any keywords in any natural language,
81-
such as `if`, `match`, or `let`.
82-
8377
1. The syntax should define as few special characters and sigils as possible.
8478

8579
## Overview & Examples
8680

8781
### Simple Messages
8882

89-
A simple message without any variables does not need any syntax:
83+
All messages, including simple ones, need `[…]` delimiters:
9084

91-
Hello, world!
85+
[Hello, world!]
9286

9387
The same message defined in a `.properties` file:
9488

9589
```properties
96-
app.greetings.hello = Hello, world!
90+
app.greetings.hello = [Hello, world!]
9791
```
9892

9993
The same message defined inline in JavaScript:
10094

10195
```js
102-
let hello = new MessageFormat('Hello, world!')
96+
let hello = new MessageFormat('[Hello, world!]')
10397
hello.format()
10498
```
10599

106100
### Simple Placeholders
107101

108-
A message with an interpolated variable needs to be interpreted as a pattern,
109-
which uses `[…]` delimiters:
102+
Messages may contain placeholders within `{…}` delimiters,
103+
such as variables that are expected to be passed in as format paramters:
110104

111105
[Hello, {$userName}!]
112106

113-
The same message defined in a `.properties` file:
114-
115-
```properties
116-
app.greetings.hello = [Hello, {$userName}!]
117-
```
118-
119-
The same message defined inline in JavaScript:
120-
121-
```js
122-
let hello = new MessageFormat('[Hello, {$userName}!]')
123-
hello.format({ userName: 'Anne' })
124-
```
125-
126107
### Formatting Functions
127108

128109
A message with an interpolated `$date` variable formatted with the `:datetime` function:
@@ -152,73 +133,73 @@ which the runtime can use to construct a document tree structure for a UI framew
152133

153134
A message with a single selector:
154135

155-
{$count :number}
156-
1 [You have one notification.]
157-
* [You have {$count} notifications.]
136+
match {$count :number}
137+
when 1 [You have one notification.]
138+
when * [You have {$count} notifications.]
158139

159140
A message with a single selector which is an invocation of
160141
a custom function `:platform`, formatted on a single line:
161142

162-
{:platform} windows [Settings] * [Preferences]
143+
match {:platform} when windows [Settings] when * [Preferences]
163144

164145
A message with a single selector and a custom `:hasCase` function
165146
which allows the message to query for presence of grammatical cases required for each variant:
166147

167-
{$userName :hasCase}
168-
vocative [Hello, {$userName :person case=vocative}!]
169-
accusative [Please welcome {$userName :person case=accusative}!]
170-
* [Hello!]
148+
match {$userName :hasCase}
149+
when vocative [Hello, {$userName :person case=vocative}!]
150+
when accusative [Please welcome {$userName :person case=accusative}!]
151+
when * [Hello!]
171152

172153
A message with 2 selectors:
173154

174-
{$photoCount :number} {$userGender :equals}
175-
1 masculine [{$userName} added a new photo to his album.]
176-
1 feminine [{$userName} added a new photo to her album.]
177-
1 * [{$userName} added a new photo to their album.]
178-
* masculine [{$userName} added {$photoCount} photos to his album.]
179-
* feminine [{$userName} added {$photoCount} photos to her album.]
180-
* * [{$userName} added {$photoCount} photos to their album.]
155+
match {$photoCount :number} {$userGender :equals}
156+
when 1 masculine [{$userName} added a new photo to his album.]
157+
when 1 feminine [{$userName} added a new photo to her album.]
158+
when 1 * [{$userName} added a new photo to their album.]
159+
when * masculine [{$userName} added {$photoCount} photos to his album.]
160+
when * feminine [{$userName} added {$photoCount} photos to her album.]
161+
when * * [{$userName} added {$photoCount} photos to their album.]
181162

182163
### Local Variables
183164

184165
A message defining a local variable `$whom` which is then used twice inside the pattern:
185166

186-
$whom = {$monster :noun case=accusative}
167+
let $whom = {$monster :noun case=accusative}
187168
[You see {$quality :adjective article=indefinite accord=$whom} {$whom}!]
188169

189170
A message defining two local variables:
190171
`$itemAcc` and `$countInt`, and using `$countInt` as a selector:
191172

192-
$countInt = {$count :number maximumFractionDigits=0}
193-
$itemAcc = {$item :noun count=$count case=accusative}
194-
one [You bought {$color :adjective article=indefinite accord=$itemAcc} {$itemAcc}.]
195-
* [You bought {$countInt} {$color :adjective accord=$itemAcc} {$itemAcc}.]
173+
let $countInt = {$count :number maximumFractionDigits=0}
174+
let $itemAcc = {$item :noun count=$count case=accusative}
175+
match {$countInt}
176+
when one [You bought {$color :adjective article=indefinite accord=$itemAcc} {$itemAcc}.]
177+
when * [You bought {$countInt} {$color :adjective accord=$itemAcc} {$itemAcc}.]
196178

197179
### Complex Messages
198180

199181
A complex message with 2 selectors and 3 local variable definitions:
200182

201-
{$host :gender}
202-
{$guestOther :number}
183+
let $hostName = {$host :person firstName=long}
184+
let $guestName = {$guest :person firstName=long}
185+
let $guestsOther = {$guestCount :number offset=1}
203186

204-
$hostName = {$host :person firstName=long}
205-
$guestName = {$guest :person firstName=long}
206-
$guestsOther = {$guestCount :number offset=1}
187+
match {$host :gender} {$guestOther :number}
207188

208-
female 0 [{$hostName} does not give a party.]
209-
female 1 [{$hostName} invites {$guestName} to her party.]
210-
female 2 [{$hostName} invites {$guestName} and one other person to her party.]
211-
female * [{$hostName} invites {$guestName} and {$guestsOther} other people to her party.]
189+
when female 0 [{$hostName} does not give a party.]
190+
when female 1 [{$hostName} invites {$guestName} to her party.]
191+
when female 2 [{$hostName} invites {$guestName} and one other person to her party.]
192+
when female * [{$hostName} invites {$guestName} and {$guestsOther} other people to her party.]
212193

213-
male 0 [{$hostName} does not give a party.]
214-
male 1 [{$hostName} invites {$guestName} to his party.]
215-
male 2 [{$hostName} invites {$guestName} and one other person to his party.]
216-
male * [{$hostName} invites {$guestName} and {$guestsOther} other people to his party.]
194+
when male 0 [{$hostName} does not give a party.]
195+
when male 1 [{$hostName} invites {$guestName} to his party.]
196+
when male 2 [{$hostName} invites {$guestName} and one other person to his party.]
197+
when male * [{$hostName} invites {$guestName} and {$guestsOther} other people to his party.]
217198

218-
* 0 [{$hostName} does not give a party.]
219-
* 1 [{$hostName} invites {$guestName} to their party.]
220-
* 2 [{$hostName} invites {$guestName} and one other person to their party.]
221-
* * [{$hostName} invites {$guestName} and {$guestsOther} other people to their party.]
199+
when * 0 [{$hostName} does not give a party.]
200+
when * 1 [{$hostName} invites {$guestName} to their party.]
201+
when * 2 [{$hostName} invites {$guestName} and one other person to their party.]
202+
when * * [{$hostName} invites {$guestName} and {$guestsOther} other people to their party.]
222203

223204
## Productions
224205

@@ -229,63 +210,61 @@ if it meets additional semantic requirements about its structure, defined below.
229210

230211
### Message
231212

232-
A single message is either a plain message, a single pattern, or has a preamble
213+
A single message is either a single pattern, or has a `match` statement
233214
followed by one or more variants which represent the translatable body of the message.
234215

235216
```ebnf
236-
Message ::= Plain | Pattern | Preamble Variant+
217+
Message ::= Declaration* ( Pattern | Selector Variant+ )
237218
```
238219

239-
### Plain
220+
### Variable Declarations
240221

241-
A plain message only contains translatable content;
242-
placeholders or their delimiters are not allowed inside a plain message.
243-
Plain messages must not start with one of the syntax characters `[`, `{` or `$`,
244-
as those would indicate that the message has a more complex structure.
245-
Any whitespace at the beginning or end of a plain message is ignored.
246-
A plain message cannot represent an empty string;
247-
for that, use an empty pattern `[]` instead.
222+
A variable declaration is an expression binding a variable identifier
223+
within the scope of the message to the value of an expression.
224+
This local variable may then be used in other expressions within the same message.
248225

249226
```ebnf
250-
Plain ::= PlainStart (PlainChar* PlainEnd)? /* ws: explicit */
251-
PlainChar ::= AnyChar - ('{' | '}')
252-
PlainStart ::= PlainChar - ('[' | '$' | WhiteSpace)
253-
PlainEnd ::= PlainChar - WhiteSpace
227+
Declaration ::= 'let' WhiteSpace Variable '=' '{' Expression '}'
254228
```
255229

256-
### Preamble
230+
### Selectors
257231

258-
The preamble is where selectors and local variables can be defined.
259-
A selector is an expression which will be used to choose one of the variants during formatting.
260-
A selector can be optionally bound to a local variable, which may then be used in other expressions.
232+
A selector is a statement containing one or more expressions
233+
which will be used to choose one of the variants during formatting.
261234

262235
```ebnf
263-
Preamble ::= Selector+
264-
Selector ::= (Variable '=')? '{' Expression '}'
236+
Selector ::= 'match' ( '{' Expression '}' )+
265237
```
266238

267239
Examples:
268240

269241
```
270-
$frac = {$count: number minFractionDigits=2}
271-
1 [One apple]
272-
* [{$frac} apples]
242+
match {$count :plural}
243+
when 1 [One apple]
244+
when * [{$count} apples]
245+
```
246+
247+
```
248+
let $frac = {$count: number minFractionDigits=2}
249+
match {$frac}
250+
when 1 [One apple]
251+
when * [{$frac} apples]
273252
```
274253

275254
### Variants
276255

277256
A variant is a keyed pattern.
278-
The keys are used to match against the selectors defined in the preamble.
257+
The keys are used to match against the selectors defined in the `match` statement.
279258
The key `*` is a "catch-all" key, matching all selector values.
280259

281260
```ebnf
282-
Variant ::= VariantKey* Pattern
261+
Variant ::= 'when' ( WhiteSpace VariantKey )+ Pattern
283262
VariantKey ::= Literal | Nmtoken | '*'
284263
```
285264

286265
A well-formed message is considered valid if the following requirements are satisfied:
287266

288-
- The number of keys on each variant must be fewer or equal to the number of selectors defined in the preamble.
267+
- The number of keys on each variant must be equal to the number of selectors.
289268
- At least one variant's keys must all be equal to the catch-all key (`*`).
290269

291270
### Patterns

0 commit comments

Comments
 (0)