From 9654e2510102acbaac3f41d92d048f53ef949cd3 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Fri, 12 Jan 2024 20:34:06 +0200 Subject: [PATCH 1/4] Add @attributes as reserved --- spec/data-model/README.md | 14 ++++++++- spec/data-model/message.dtd | 9 ++++-- spec/data-model/message.json | 39 +++++++++++++++++++----- spec/formatting.md | 5 ++++ spec/message.abnf | 21 +++++++------ spec/syntax.md | 57 ++++++++++++++++++++++++++++++------ 6 files changed, 115 insertions(+), 30 deletions(-) diff --git a/spec/data-model/README.md b/spec/data-model/README.md index 421854aae9..08e511167e 100644 --- a/spec/data-model/README.md +++ b/spec/data-model/README.md @@ -135,24 +135,33 @@ interface LiteralExpression { type: "expression"; arg: Literal; annotation?: FunctionAnnotation | UnsupportedAnnotation; + attributes?: Attribute[]; } interface VariableExpression { type: "expression"; arg: VariableRef; annotation?: FunctionAnnotation | UnsupportedAnnotation; + attributes?: Attribute[]; } interface FunctionExpression { type: "expression"; arg?: never; annotation: FunctionAnnotation; + attributes?: Attribute[]; } interface UnsupportedExpression { type: "expression"; arg?: never; annotation: UnsupportedAnnotation; + attributes?: Attribute[]; +} + +interface Attribute { + name: string; + value?: Literal | VariableRef; } ``` @@ -219,7 +228,7 @@ that the implementation attaches to that _annotation_. ```ts interface UnsupportedAnnotation { type: "unsupported-annotation"; - sigil: "!" | "@" | "%" | "^" | "&" | "*" | "+" | "<" | ">" | "?" | "~"; + sigil: "!" | "%" | "^" | "&" | "*" | "+" | "<" | ">" | "?" | "~"; source: string; } ``` @@ -241,6 +250,7 @@ interface MarkupOpen { kind: "open"; name: string; options?: Option[]; + attributes?: Attribute[]; } interface MarkupStandalone { @@ -248,12 +258,14 @@ interface MarkupStandalone { kind: "standalone"; name: string; options?: Option[]; + attributes?: Attribute[]; } interface MarkupClose { type: "markup"; kind: "close"; name: string; + attributes?: Attribute[]; } ``` diff --git a/spec/data-model/message.dtd b/spec/data-model/message.dtd index 35a3fc6290..c6cb86c324 100644 --- a/spec/data-model/message.dtd +++ b/spec/data-model/message.dtd @@ -24,8 +24,8 @@ @@ -43,8 +43,11 @@ + + + - + ", "/", "?", "~"] + "enum": ["!", "#", "%", "^", "&", "*", "<", ">", "/", "?", "~"] }, "source": { "type": "string" } }, @@ -68,7 +84,8 @@ "properties": { "type": { "const": "expression" }, "arg": { "$ref": "#/$defs/literal" }, - "annotation": { "$ref": "#/$defs/annotation" } + "annotation": { "$ref": "#/$defs/annotation" }, + "attributes": { "$ref": "#/$defs/attributes" } }, "required": ["type", "arg"] }, @@ -77,7 +94,8 @@ "properties": { "type": { "const": "expression" }, "arg": { "$ref": "#/$defs/variable" }, - "annotation": { "$ref": "#/$defs/annotation" } + "annotation": { "$ref": "#/$defs/annotation" }, + "attributes": { "$ref": "#/$defs/attributes" } }, "required": ["type", "arg"] }, @@ -85,7 +103,8 @@ "type": "object", "properties": { "type": { "const": "expression" }, - "annotation": { "$ref": "#/$defs/function-annotation" } + "annotation": { "$ref": "#/$defs/function-annotation" }, + "attributes": { "$ref": "#/$defs/attributes" } }, "required": ["type", "annotation"] }, @@ -93,7 +112,8 @@ "type": "object", "properties": { "type": { "const": "expression" }, - "annotation": { "$ref": "#/$defs/unsupported-annotation" } + "annotation": { "$ref": "#/$defs/unsupported-annotation" }, + "attributes": { "$ref": "#/$defs/attributes" } }, "required": ["type", "annotation"] }, @@ -112,7 +132,8 @@ "type": { "const": "markup" }, "kind": { "const": "open" }, "name": { "type": "string" }, - "options": { "$ref": "#/$defs/options" } + "options": { "$ref": "#/$defs/options" }, + "attributes": { "$ref": "#/$defs/attributes" } }, "required": ["type", "kind", "name"] }, @@ -122,7 +143,8 @@ "type": { "const": "markup" }, "kind": { "const": "standalone" }, "name": { "type": "string" }, - "options": { "$ref": "#/$defs/options" } + "options": { "$ref": "#/$defs/options" }, + "attributes": { "$ref": "#/$defs/attributes" } }, "required": ["type", "kind", "name"] }, @@ -131,7 +153,8 @@ "properties": { "type": { "const": "markup" }, "kind": { "const": "close" }, - "name": { "type": "string" } + "name": { "type": "string" }, + "attributes": { "$ref": "#/$defs/attributes" } }, "required": ["type", "kind", "name"] }, diff --git a/spec/formatting.md b/spec/formatting.md index e65547d623..8a932191f9 100644 --- a/spec/formatting.md +++ b/spec/formatting.md @@ -652,6 +652,11 @@ MUST be an empty string. Implementations MAY offer functionality for customizing this, such as by emitting XML-ish tags for each _markup_. +_Attributes_ MUST NOT have any impact on message formatting, +and MUST NOT be available in the formatted output. +_Variables_ in _attribute_ _values_ are not resolved, +and no errors are emitted for them. + ### Examples _This section is non-normative._ diff --git a/spec/message.abnf b/spec/message.abnf index 6ed784a284..15383232aa 100644 --- a/spec/message.abnf +++ b/spec/message.abnf @@ -19,16 +19,18 @@ selector = expression variant = key *(s key) [s] quoted-pattern key = literal / "*" -expression = literal-expression / variable-expression / annotation-expression -literal-expression = "{" [s] literal [s annotation] [s] "}" -variable-expression = "{" [s] variable [s annotation] [s] "}" -annotation-expression = "{" [s] annotation [s] "}" +expression = literal-expression + / variable-expression + / annotation-expression +literal-expression = "{" [s] literal [s annotation] *(s attribute) [s] "}" +variable-expression = "{" [s] variable [s annotation] *(s attribute) [s] "}" +annotation-expression = "{" [s] annotation *(s attribute) [s] "}" annotation = function / private-use-annotation / reserved-annotation -markup = "{" [s] markup-open [s] ["/"] "}" - / "{" [s] markup-close [s] "}" +markup = "{" [s] markup-open *(s attribute) [s] ["/"] "}" + / "{" [s] markup-close *(s attribute) [s] "}" markup-open = "#" identifier *(s option) markup-close = "/" identifier @@ -36,6 +38,7 @@ literal = quoted / unquoted variable = "$" name function = ":" identifier *(s option) option = identifier [s] "=" [s] (literal / variable) +attribute = "@" identifier [[s] "=" [s] (literal / variable)] input = %s".input" local = %s".local" @@ -70,14 +73,14 @@ reserved-keyword = "." name ; Reserve additional sigils for use by future versions of this specification. reserved-annotation = reserved-annotation-start reserved-body -reserved-annotation-start = "!" / "@" / "%" / "*" / "+" - / "<" / ">" / "?" / "~" +reserved-annotation-start = "!" / "%" / "*" / "+" / "<" / ">" / "?" / "~" reserved-body = *([s] 1*(reserved-char / reserved-escape / quoted)) reserved-char = %x00-08 ; omit HTAB and LF / %x0B-0C ; omit CR / %x0E-19 ; omit SP - / %x21-5B ; omit \ + / %x21-3F ; omit @ + / %x41-5B ; omit \ / %x5D-7A ; omit { | } / %x7E-2FFF ; omit IDEOGRAPHIC SPACE / %x3001-D7FF ; omit surrogates diff --git a/spec/syntax.md b/spec/syntax.md index dbfa1c4c13..44d7f05051 100644 --- a/spec/syntax.md +++ b/spec/syntax.md @@ -427,6 +427,7 @@ An _expression_ MUST begin with U+007B LEFT CURLY BRACKET `{` and end with U+007D RIGHT CURLY BRACKET `}`. An _expression_ MUST NOT be empty. An _expression_ cannot contain another _expression_. +An _expression_ MAY contain one more _attributes_. A **_literal-expression_** contains a _literal_, optionally followed by an _annotation_. @@ -438,9 +439,9 @@ An **_annotation-expression_** contains an _annotation_ without an _o ```abnf expression = literal-expression / variable-expression / annotation-expression -literal-expression = "{" [s] literal [s annotation] [s] "}" -variable-expression = "{" [s] variable [s annotation] [s] "}" -annotation-expression = "{" [s] annotation [s] "}" +literal-expression = "{" [s] literal [s annotation] *(s attribute) [s] "}" +variable-expression = "{" [s] variable [s annotation] *(s attribute) [s] "}" +annotation-expression = "{" [s] annotation *(s attribute) [s] "}" ``` There are several types of _expression_ that can appear in a _message_. @@ -638,14 +639,14 @@ unrecognized _reserved-annotations_ or _private-use-annotations_ have no meaning ```abnf reserved-annotation = reserved-annotation-start reserved-body -reserved-annotation-start = "!" / "@" / "%" / "*" / "+" - / "<" / ">" / "?" / "~" +reserved-annotation-start = "!" / "%" / "*" / "+" / "<" / ">" / "?" / "~" reserved-body = *([s] 1*(reserved-char / reserved-escape / quoted)) reserved-char = %x00-08 ; omit HTAB and LF / %x0B-0C ; omit CR / %x0E-19 ; omit SP - / %x21-5B ; omit \ + / %x21-3F ; omit @ + / %x41-5B ; omit \ / %x5D-7A ; omit { | } / %x7E-D7FF ; omit surrogates / %xE000-10FFFF @@ -656,7 +657,12 @@ reserved-char = %x00-08 ; omit HTAB and LF **_Markup_** _placeholders_ are _pattern_ parts that can be used to represent non-language parts of a _message_, such as inline elements or styling that should apply to a span of parts. -Markup comes in three forms: + +_Markup_ MUST begin with U+007B LEFT CURLY BRACKET `{` +and end with U+007D RIGHT CURLY BRACKET `}`. +_Markup_ MAY contain one more _attributes_. + +_Markup_ comes in three forms: **_Markup-open_** starts with U+0023 NUMBER SIGN `#` and represents an opening element within the _message_, @@ -673,8 +679,8 @@ is a _pattern_ part ending a span. Unlike the other forms, it does not include _options_. ```abnf -markup = "{" [s] markup-open [s] ["/"] "}" - / "{" [s] markup-close [s] "}" +markup = "{" [s] markup-open *(s attribute) [s] ["/"] "}" + / "{" [s] markup-close *(s attribute) [s] "}" markup-open = "#" identifier *(s option) markup-close = "/" identifier ``` @@ -691,6 +697,39 @@ _Markup_ _placeholders_ can appear in any order without making the _message_ inv However, specifications or implementations defining _markup_ might impose requirements on the pairing, ordering, or contents of _markup_ during _formatting_. +## Attributes + +An **_attribute_** is a key with an optional value that +is attached to the _expression_ or _markup_ as a whole. +_Attributes_ are reserved for use by future specification versions. + +_Attributes_ are prefixed by a U+0040 COMMERCIAL AT `@` sign, +followed by an _identifier_. +An _attribute_ MAY have a _value_ separated from the _identifier_ +by an U+003D EQUALS SIGN `=` along with optional whitespace. +The _value_ of an _attribute_ can be either a _literal_ or a _variable_. + +Multiple _attributes_ are permitted in an _expression_ or _markup_. +Each _attribute_ is separated by whitespace. + +```abnf +attribute = "@" identifier [[s] "=" [s] (literal / variable)] +``` + +> Examples of _expressions_ and _markup_ with _attributes_: +> +> A _message_ including a _literal_ that should not be translated: +> +> ``` +> In French, "{|bonjour| @translate=no}" is a greeting +> ``` +> +> A _message_ with _markup_ that should not be copied: +> +> ``` +> Have a {+span @can-copy}great and wonderful{-span @can-copy} birthday! +> ``` + ## Other Syntax Elements This section defines common elements used to construct _messages_. From 03e24e9ad3c8bbec625cebde00d0bdb333711671 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Sat, 13 Jan 2024 11:00:11 +0200 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Addison Phillips --- spec/syntax.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/syntax.md b/spec/syntax.md index 44d7f05051..937fce4812 100644 --- a/spec/syntax.md +++ b/spec/syntax.md @@ -699,13 +699,16 @@ on the pairing, ordering, or contents of _markup_ during _formatting_. ## Attributes -An **_attribute_** is a key with an optional value that -is attached to the _expression_ or _markup_ as a whole. -_Attributes_ are reserved for use by future specification versions. +An **_attribute_** is an _identifier_ with an optional value +that appears in an _expression_ or in _markup_. + +**_Attributes_ are reserved for standardization by future versions of this specification.** +Examples in this section are meant to be illustrative and +might not match future requirements or usage. _Attributes_ are prefixed by a U+0040 COMMERCIAL AT `@` sign, followed by an _identifier_. -An _attribute_ MAY have a _value_ separated from the _identifier_ +An _attribute_ MAY have a _value_ which is separated from the _identifier_ by an U+003D EQUALS SIGN `=` along with optional whitespace. The _value_ of an _attribute_ can be either a _literal_ or a _variable_. From bcf50ed738360d4fd1f7c1a8fb65280c3231d08f Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Sat, 13 Jan 2024 11:01:11 +0200 Subject: [PATCH 3/4] Apply suggestions from code review --- spec/syntax.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/syntax.md b/spec/syntax.md index 937fce4812..96af226216 100644 --- a/spec/syntax.md +++ b/spec/syntax.md @@ -699,13 +699,13 @@ on the pairing, ordering, or contents of _markup_ during _formatting_. ## Attributes -An **_attribute_** is an _identifier_ with an optional value -that appears in an _expression_ or in _markup_. - **_Attributes_ are reserved for standardization by future versions of this specification.** Examples in this section are meant to be illustrative and might not match future requirements or usage. +An **_attribute_** is an _identifier_ with an optional value +that appears in an _expression_ or in _markup_. + _Attributes_ are prefixed by a U+0040 COMMERCIAL AT `@` sign, followed by an _identifier_. An _attribute_ MAY have a _value_ which is separated from the _identifier_ From 71388a6dee7ebff49b7589fbf0ca4c6bd9b9a51c Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Sat, 13 Jan 2024 20:07:29 +0200 Subject: [PATCH 4/4] Relax formatting restriction from MUST to SHOULD Co-authored-by: Addison Phillips --- spec/formatting.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/formatting.md b/spec/formatting.md index 8a932191f9..ba6d83a7e4 100644 --- a/spec/formatting.md +++ b/spec/formatting.md @@ -652,10 +652,9 @@ MUST be an empty string. Implementations MAY offer functionality for customizing this, such as by emitting XML-ish tags for each _markup_. -_Attributes_ MUST NOT have any impact on message formatting, -and MUST NOT be available in the formatted output. -_Variables_ in _attribute_ _values_ are not resolved, -and no errors are emitted for them. +_Attributes_ are reserved for future standardization. +Other than checking for valid syntax, they SHOULD NOT +affect the processing or output of a _message_. ### Examples