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..ba6d83a7e4 100644 --- a/spec/formatting.md +++ b/spec/formatting.md @@ -652,6 +652,10 @@ MUST be an empty string. Implementations MAY offer functionality for customizing this, such as by emitting XML-ish tags for each _markup_. +_Attributes_ are reserved for future standardization. +Other than checking for valid syntax, they SHOULD NOT +affect the processing or output of a _message_. + ### 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..96af226216 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,42 @@ _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 + +**_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_ +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_.