[TBD]
This section defines the formal grammar describing the syntax of a single message.
This section is non-normative.
The design goals of the syntax specification are as follows:
-
The syntax should leverage the familiarity with ICU MessageFormat 1.0 in order to lower the barrier to entry and increase the chance of adoption. At the same time, the syntax should fix the pain points of ICU MessageFormat 1.0.
- Non-Goal: Be backwards-compatible with the ICU MessageFormat 1.0 syntax.
-
The syntax inside translatable content should be easy to understand for humans. This includes making it clear which parts of the message body are translatable content, which parts inside it are placeholders for expressions, as well as making the selection logic predictable and easy to reason about.
- Non-Goal: Make the syntax intuitive enough for non-technical translators to hand-edit. Instead, we assume that most translators will work with MessageFormat 2 by means of GUI tooling, CAT workbenches etc.
-
The syntax surrounding translatable content should be easy to write and edit for developers, localization engineers, and easy to parse by machines.
-
The syntax should make a single message easily embeddable inside many container formats:
.properties
, YAML, XML, inlined as string literals in programming languages, etc. This includes a future MessageResource specification.- Non-Goal: Support unnecessary escape sequences, which would theirselves require
additional escaping when embedded. Instead, we tolerate direct use of nearly all
characters (including line breaks, control characters, etc.) and rely upon escaping
in those outer formats to aid human comprehension (e.g., depending upon container
format, a U+000A LINE FEED might be represented as
\n
,\012
,\x0A
,\u000A
,\U0000000A
,

,

,%0A
,<LF>
, or something else entirely).
- Non-Goal: Support unnecessary escape sequences, which would theirselves require
additional escaping when embedded. Instead, we tolerate direct use of nearly all
characters (including line breaks, control characters, etc.) and rely upon escaping
in those outer formats to aid human comprehension (e.g., depending upon container
format, a U+000A LINE FEED might be represented as
This section is non-normative.
The syntax specification takes into account the following design restrictions:
-
Whitespace outside the translatable content should be insignificant. It should be possible to define a message entirely on a single line with no ambiguity, as well as to format it over multiple lines for clarity.
-
The syntax should define as few special characters and sigils as possible. Note that this necessitates extra care when presenting messages for human consumption, because they may contain invisible characters such as U+200B ZERO WIDTH SPACE, control characters such as U+0000 NULL and U+0009 TAB, permanently reserved noncharacters (U+FDD0 through U+FDEF and U+nFFFE and U+nFFFF where n is 0x0 through 0x10), private-use code points (U+E000 through U+F8FF, U+F0000 through U+FFFFD, and U+100000 through U+10FFFD), unassigned code points, and other potentially confusing content.
The purpose of MessageFormat is to allow content to vary at runtime. This variation might be due to placing a value into the content or it might be due to selecting a different bit of content based on some data value or it might be due to a combination of the two.
MessageFormat calls the template for a given formatting operation a message.
The values passed in at runtime (which are to be placed into the content or used to select between different content items) are called external variables. The author of a message can also assign local variables, including variables that modify external variables.
This part of the MessageFormat specification defines the syntax for a message, along with the concepts and terminology needed when processing a message during the formatting of a message at runtime.
The complete formal syntax of a message is described by the ABNF.
A message is well-formed if it satisfies all the rules of the grammar. Attempting to parse a message that is not well-formed will result in a Syntax Error.
A message is valid if it is well-formed and also meets the additional content restrictions and semantic requirements about its structure defined below for declarations, matcher and options. Attempting to parse a message that is not valid will result in a Data Model Error.
A message is the complete template for a specific message formatting request.
Note
This syntax is designed to be embeddable into many different programming languages and formats. As such, it avoids constructs, such as character escapes, that are specific to any given file format or processor. In particular, it avoids using quote characters common to many file formats and formal languages so that these do not need to be escaped in the body of a message.
Note
In general (and except where required by the syntax), whitespace carries no meaning in the structure of a message. While many of the examples in this spec are written on multiple lines, the formatting shown is primarily for readability.
Example This message:
.local $foo = { |horse| } {{You have a {$foo}!}}
Can also be written as:
.local $foo={|horse|}{{You have a {$foo}!}}
An exception to this is: whitespace inside a pattern is always significant.
Note
The syntax assumes that each message will be displayed with a left-to-right display order and be processed in the logical character order. The syntax also permits the use of right-to-left characters in identifiers, literals, and other values. This can result in confusion when viewing the message.
Additional restrictions or requirements, such as permitting the use of certain bidirectional control characters in the syntax, might be added during the Tech Preview to better manage bidirectional text. Feedback on the creation and management of messages containing bidirectional tokens is strongly desired.
A message can be a simple message or it can be a complex message.
message = simple-message / complex-message
A simple message contains a single pattern, with restrictions on its first character. An empty string is a valid simple message.
simple-message = [simple-start pattern]
simple-start = simple-start-char / text-escape / placeholder
A complex message is any message that contains declarations,
a matcher, or both.
A complex message always begins with either a keyword that has a .
prefix or a quoted pattern
and consists of:
- an optional list of declarations, followed by
- a complex body
complex-message = *(declaration [s]) complex-body
A declaration binds a variable identifier to a value within the scope of a message. This variable can then be used in other expressions within the same message. Declarations are optional: many messages will not contain any declarations.
An input-declaration binds a variable to an external input value. The variable-expression of an input-declaration MAY include an annotation that is applied to the external value.
A local-declaration binds a variable to the resolved value of an expression.
For compatibility with later MessageFormat 2 specification versions, declarations MAY also include reserved statements.
declaration = input-declaration / local-declaration / reserved-statement
input-declaration = input [s] variable-expression
local-declaration = local s variable [s] "=" [s] expression
Variables, once declared, MUST NOT be redeclared. A message that does any of the following is not valid and will produce a Duplicate Declaration error during processing:
- A declaration MUST NOT bind a variable that appears as a variable anywhere within a previous declaration.
- An input-declaration MUST NOT bind a variable that appears anywhere within the annotation of its variable-expression.
- A local-declaration MUST NOT bind a variable that appears in its expression.
A local-declaration MAY overwrite an external input value as long as the external input value does not appear in a previous declaration.
Note
These restrictions only apply to declarations. A placeholder or selector can apply a different annotation to a variable than one applied to the same variable named in a declaration. For example, this message is valid:
.input {$var :number maximumFractionDigits=0}
.match {$var :number maximumFractionDigits=2}
0 {{The selector can apply a different annotation to {$var} for the purposes of selection}}
* {{A placeholder in a pattern can apply a different annotation to {$var :number maximumFractionDigits=3}}}
(See the Errors section for examples of invalid messages)
A reserved statement reserves additional .keywords
for use by future versions of this specification.
Any such future keyword must start with .
,
followed by two or more lower-case ASCII characters.
The rest of the statement supports a similarly wide range of content as reserved annotations, but it MUST end with one or more expressions.
reserved-statement = reserved-keyword [s reserved-body] 1*([s] expression)
reserved-keyword = "." name
Note
The reserved-keyword
ABNF rule is a simplification,
as it MUST NOT be considered to match any of the existing keywords
.input
, .local
, or .match
.
This allows flexibility in future standardization, as future definitions MAY define additional semantics and constraints on the contents of these reserved statements.
Implementations MUST NOT assign meaning or semantics to a reserved statement: these are reserved for future standardization. Implementations MUST NOT remove or alter the contents of a reserved statement.
The complex body of a complex message is the part that will be formatted. The complex body consists of either a quoted pattern or a matcher.
complex-body = quoted-pattern / matcher
A pattern contains a sequence of text and placeholders to be formatted as a unit. Unless there is an error, resolving a message always results in the formatting of a single pattern.
pattern = *(text-char / text-escape / placeholder)
A pattern MAY be empty.
A pattern MAY contain an arbitrary number of placeholders to be evaluated during the formatting process.
A quoted pattern is a pattern that is "quoted" to prevent
interference with other parts of the message.
A quoted pattern starts with a sequence of two U+007B LEFT CURLY BRACKET {{
and ends with a sequence of two U+007D RIGHT CURLY BRACKET }}
.
quoted-pattern = "{{" pattern "}}"
A quoted pattern MAY be empty.
An empty quoted pattern:
{{}}
text is the translateable content of a pattern.
Any Unicode code point is allowed, except for U+0000 NULL
and the surrogate code points U+D800 through U+DFFF inclusive.
The characters U+005C REVERSE SOLIDUS \
,
U+007B LEFT CURLY BRACKET {
, and U+007D RIGHT CURLY BRACKET }
MUST be escaped as \\
, \{
, and \}
respectively.
In the ABNF, text is represented by non-empty sequences of
simple-start-char
, text-char
, and text-escape
.
The first of these is used at the start of a simple message,
and matches text-char
except for not allowing U+002E FULL STOP .
.
The ABNF uses content-char
as a shared base for text and quoted literal characters.
Whitespace in text, including tabs, spaces, and newlines is significant and MUST be preserved during formatting.
simple-start-char = content-char / s / "@" / "|"
text-char = content-char / s / "." / "@" / "|"
quoted-char = content-char / s / "." / "@" / "{" / "}"
reserved-char = content-char / "."
content-char = %x01-08 ; omit NULL (%x00), HTAB (%x09) and LF (%x0A)
/ %x0B-0C ; omit CR (%x0D)
/ %x0E-1F ; omit SP (%x20)
/ %x21-2D ; omit . (%x2E)
/ %x2F-3F ; omit @ (%x40)
/ %x41-5B ; omit \ (%x5C)
/ %x5D-7A ; omit { | } (%x7B-7D)
/ %x7E-2FFF ; omit IDEOGRAPHIC SPACE (%x3000)
/ %x3001-D7FF ; omit surrogates
/ %xE000-10FFFF
When a pattern is quoted by embedding the pattern in curly brackets, the resulting message can be embedded into various formats regardless of the container's whitespace trimming rules. Otherwise, care must be taken to ensure that pattern-significant whitespace is preserved.
Example In a Java
.properties
file, the valueshello
andhello2
both contain an identical message which consists of a single pattern. This pattern consists of text with exactly three spaces before and after the word "Hello":hello = {{ Hello }} hello2=\ Hello \
A placeholder is an expression or markup that appears inside of a pattern and which will be replaced during the formatting of a message.
placeholder = expression / markup
A matcher is the complex body of a message that allows runtime selection of the pattern to use for formatting. This allows the form or content of a message to vary based on values determined at runtime.
A matcher consists of the keyword .match
followed by at least one selector
and at least one variant.
When the matcher is processed, the result will be a single pattern that serves as the template for the formatting process.
A message can only be considered valid if the following requirements are satisfied:
- The number of keys on each variant MUST be equal to the number of selectors.
- At least one variant MUST exist whose keys are all equal to the "catch-all" key
*
. - Each selector MUST have an annotation, or contain a variable that directly or indirectly references a declaration with an annotation.
matcher = match-statement 1*([s] variant)
match-statement = match 1*([s] selector)
A message with a matcher:
.input {$count :number} .match {$count} one {{You have {$count} notification.}} * {{You have {$count} notifications.}}
A message containing a matcher formatted on a single line:
.match {:platform} windows {{Settings}} * {{Preferences}}
A selector is an expression that ranks or excludes the variants based on the value of the corresponding key in each variant. The combination of selectors in a matcher thus determines which pattern will be used during formatting.
selector = expression
There MUST be at least one selector in a matcher. There MAY be any number of additional selectors.
A message with a single selector that uses a custom function
:hasCase
which is a selector that allows the message to choose a pattern based on grammatical case:.match {$userName :hasCase} vocative {{Hello, {$userName :person case=vocative}!}} accusative {{Please welcome {$userName :person case=accusative}!}} * {{Hello!}}
A message with two selectors:
.input {$numLikes :integer} .input {$numShares :integer} .match {$numLikes} {$numShares} 0 0 {{Your item has no likes and has not been shared.}} 0 one {{Your item has no likes and has been shared {$numShares} time.}} 0 * {{Your item has no likes and has been shared {$numShares} times.}} one 0 {{Your item has {$numLikes} like and has not been shared.}} one one {{Your item has {$numLikes} like and has been shared {$numShares} time.}} one * {{Your item has {$numLikes} like and has been shared {$numShares} times.}} * 0 {{Your item has {$numLikes} likes and has not been shared.}} * one {{Your item has {$numLikes} likes and has been shared {$numShares} time.}} * * {{Your item has {$numLikes} likes and has been shared {$numShares} times.}}
A variant is a quoted pattern associated with a set of keys in a matcher. Each variant MUST begin with a sequence of keys, and terminate with a valid quoted pattern. The number of keys in each variant MUST match the number of selectors in the matcher.
Each key is separated from each other by whitespace. Whitespace is permitted but not required between the last key and the quoted pattern.
variant = key *(s key) [s] quoted-pattern
key = literal / "*"
A key is a value in a variant for use by a selector when ranking
or excluding variants during the matcher process.
A key can be either a literal value or the "catch-all" key *
.
The catch-all key is a special key, represented by *
,
that matches all values for a given selector.
An expression is a part of a message that will be determined during the message's formatting.
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.
A variable-expression contains a variable, optionally followed by an annotation.
An annotation-expression contains an annotation without an operand.
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] "}"
There are several types of expression that can appear in a message. All expressions share a common syntax. The types of expression are:
- The value of a local-declaration
- A selector
- A kind of placeholder in a pattern
Additionally, an input-declaration can contain a variable-expression.
Examples of different types of expression
Declarations:
.input {$x :function option=value} .local $y = {|This is an expression|}
Selectors:
.match {$selector :functionRequired}
Placeholders:
This placeholder contains a literal expression: {|literal|} This placeholder contains a variable expression: {$variable} This placeholder references a function on a variable: {$variable :function with=options} This placeholder contains a function expression with a variable-valued option: {:function option=$variable}
An annotation is part of an expression containing either a function together with its associated options, or a private-use annotation or a reserved annotation.
annotation = function
/ private-use-annotation
/ reserved-annotation
An operand is the literal of a literal-expression or the variable of a variable-expression.
An annotation can appear in an expression by itself or following a single operand. When following an operand, the operand serves as input to the annotation.
A function is named functionality in an annotation. Functions are used to evaluate, format, select, or otherwise process data values during formatting.
Each function is defined by the runtime's function registry. A function's entry in the function registry will define whether the function is a selector or formatter (or both), whether an operand is required, what form the values of an operand can take, what options and option values are valid, and what outputs might result. See function registry for more information.
A function starts with a prefix sigil :
followed by an identifier.
The identifier MAY be followed by one or more options.
Options are not required.
function = ":" identifier *(s option)
A message with a function operating on the variable
$now
:It is now {$now :datetime}.
An option is a key-value pair containing a named argument that is passed to a function.
An option has an identifier and a value.
The identifier is separated from the value by an U+003D EQUALS SIGN =
along with
optional whitespace.
The value of an option can be either a literal or a variable.
Multiple options are permitted in an annotation. Options are separated from the preceding function identifier and from each other by whitespace. Each option's identifier MUST be unique within the annotation: an annotation with duplicate option identifiers is not valid.
The order of options is not significant.
option = identifier [s] "=" [s] (literal / variable)
Examples of functions with options
A message using the
:datetime
function. The optionweekday
has the literallong
as its value:Today is {$date :datetime weekday=long}!
A message using the
:datetime
function. The optionweekday
has a variable$dateStyle
as its value:Today is {$date :datetime weekday=$dateStyle}!
A private-use annotation is an annotation whose syntax is reserved for use by a specific implementation or by private agreement between multiple implementations. Implementations MAY define their own meaning and semantics for private-use annotations.
A private-use annotation starts with either U+0026 AMPERSAND &
or U+005E CIRCUMFLEX ACCENT ^
.
Characters, including whitespace, are assigned meaning by the implementation.
The definition of escapes in the reserved-body
production, used for the body of
a private-use annotation is an affordance to implementations that
wish to use a syntax exactly like other functions. Specifically:
- The characters
\
,{
, and}
MUST be escaped as\\
,\{
, and\}
respectively when they appear in the body of a private-use annotation. - The character
|
is special: it SHOULD be escaped as\|
in a private-use annotation, but can appear unescaped as long as it is paired with another|
. This is an affordance to allow literals to appear in the private use syntax.
A private-use annotation MAY be empty after its introducing sigil.
private-use-annotation = private-start [[s] reserved-body]
private-start = "^" / "&"
Note
Users are cautioned that private-use annotations cannot be reliably exchanged and can result in errors during formatting. It is generally a better idea to use the function registry to define additional formatting or annotation options.
Here are some examples of what private-use sequences might look like:
Here's private use with an operand: {$foo &bar} Here's a placeholder that is entirely private-use: {&anything here} Here's a private-use function that uses normal function syntax: {$operand ^foo option=|literal|} The character \| has to be paired or escaped: {&private || |something between| or isolated: \| } Stop {& "translate 'stop' as a verb" might be a translator instruction or comment } Protect stuff in {^ph}<a>{^/ph}private use{^ph}</a>{^/ph}
A reserved annotation is an annotation whose syntax is reserved for future standardization.
A reserved annotation starts with a reserved character. The remaining part of a reserved annotation, called a reserved body, MAY be empty or contain arbitrary text that starts and ends with a non-whitespace character.
This allows maximum flexibility in future standardization, as future definitions MAY define additional semantics and constraints on the contents of these annotations.
Implementations MUST NOT assign meaning or semantics to
an annotation starting with reserved-annotation-start
:
these are reserved for future standardization.
Whitespace before or after a reserved body is not part of the reserved body.
Implementations MUST NOT remove or alter the contents of a reserved body,
including any interior whitespace,
but MAY remove or alter whitespace before or after the reserved body.
While a reserved sequence is technically "well-formed", unrecognized reserved-annotations or private-use-annotations have no meaning.
reserved-annotation = reserved-annotation-start [[s] reserved-body]
reserved-annotation-start = "!" / "%" / "*" / "+" / "<" / ">" / "?" / "~"
reserved-body = reserved-body-part *([s] reserved-body-part)
reserved-body-part = reserved-char / reserved-escape / quoted
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 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,
such as markup used to start a span.
It MAY include options.
Markup-standalone starts with U+0023 NUMBER SIGN #
and has a U+002F SOLIDUS /
immediately before its closing }
representing a self-closing or standalone element within the message.
It MAY include options.
Markup-close starts with U+002F SOLIDUS /
and
is a pattern part ending a span.
markup = "{" [s] "#" identifier *(s option) *(s attribute) [s] ["/"] "}" ; open and standalone
/ "{" [s] "/" identifier *(s option) *(s attribute) [s] "}" ; close
A message with one
button
markup span and a standaloneimg
markup element:{#button}Submit{/button} or {#img alt=|Cancel| /}.
A message with attributes in the closing tag:
{#ansi attr=|bold,italic|}Bold and italic{/ansi attr=|bold|} italic only {/ansi attr=|italic|} no formatting.}
A markup-open can appear without a corresponding markup-close. A markup-close can appear without a corresponding markup-open. Markup placeholders can appear in any order without making the message invalid. However, specifications or implementations defining markup might impose requirements on the pairing, ordering, or contents of markup during formatting.
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.
Note
The Tech Preview does not provide a built-in mechanism for overriding
values in the formatting context (most notably the locale)
Nor does it provide a mechanism for identifying specific expressions
such as by assigning a name or id.
The utility of these types of mechanisms has been debated.
There are at least two proposed mechanisms for implementing support for
these.
Specifically, one mechanism would be to reserve specifically-named options,
possibly using a Unicode namespace (i.e. locale=xxx
or u:locale=xxx
).
Such options would be reserved for use in any and all functions or markup.
The other mechanism would be to use the reserved "expression attribute" syntax
for this purpose (i.e. @locale=xxx
or @id=foo
)
Neither mechanism was included in this Tech Preview.
Feedback on the preferred mechanism for managing these features
is strongly desired.
In the meantime, function authors and other implementers are cautioned to avoid creating function-specific or implementation-specific option values for this purpose. One workaround would be to use the implementation's namespace for these features to insure later interoperability when such a mechanism is finalized during the Tech Preview period. Specifically:
- Avoid specifying an option for setting the locale of an expression as different from that of the overall message locale, or use a namespace that later maps to the final mechanism.
- Avoid specifying options for the purpose of linking placeholders (such as to pair opening markup to closing markup). If such an option is created, the implementer should use an implementation-specific namespace. Users and implementers are cautioned that such options might be replaced with a standard mechanism in a future version.
- Avoid specifying generic options to communicate with translators and translation tooling (i.e. implementation-specific options that apply to all functions. The above are all desirable features. We welcome contributions to and proposals for such features during the Technical Preview.
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.
The order of attributes is not significant.
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!
This section defines common elements used to construct messages.
A keyword is a reserved token that has a unique meaning in the message syntax.
The following three keywords are defined: .input
, .local
, and .match
.
Keywords are always lowercase and start with U+002E FULL STOP .
.
input = %s".input"
local = %s".local"
match = %s".match"
A literal is a character sequence that appears outside of text in various parts of a message. A literal can appear as a key value, as the operand of a literal-expression, or in the value of an option. A literal MAY include any Unicode code point except for U+0000 NULL or the surrogate code points U+D800 through U+DFFF.
All code points are preserved.
A quoted literal begins and ends with U+005E VERTICAL BAR |
.
The characters \
and |
within a quoted literal MUST be
escaped as \\
and \|
.
An unquoted literal is a literal that does not require the |
quotes around it to be distinct from the rest of the message syntax.
An unquoted MAY be used when the content of the literal
contains no whitespace and otherwise matches the unquoted
production.
Any unquoted literal MAY be quoted.
Implementations MUST NOT distinguish between quoted and unquoted literals
that have the same sequence of code points.
Unquoted literals can contain a name or consist of a number-literal. A number-literal uses the same syntax as JSON and is intended for the encoding of number values in operands or options, or as keys for variants.
literal = quoted / unquoted
quoted = "|" *(quoted-char / quoted-escape) "|"
unquoted = name / number-literal
number-literal = ["-"] (%x30 / (%x31-39 *DIGIT)) ["." 1*DIGIT] [%i"e" ["-" / "+"] 1*DIGIT]
An identifier is a character sequence that
identifies a function, markup, or option.
Each identifier consists of a name optionally preceeded by
a namespace.
When present, the namespace is separated from the name by a
U+003A COLON :
.
Built-in functions and their options do not have a namespace identifier.
The namespace u
(U+0075 LATIN SMALL LETTER U)
is reserved for future standardization.
Function identifiers are prefixed with :
.
Markup identifiers are prefixed with #
or /
.
Option identifiers have no prefix.
A name is a character sequence used in an identifier or as the name for a variable or the value of an unquoted literal.
Variable names are prefixed with $
.
Valid content for names is based on Namespaces in XML 1.0's
NCName.
This is different from XML's Name
in that it MUST NOT contain a U+003A COLON :
.
Otherwise, the set of characters allowed in a name is large.
Note
External variables can be passed in that are not valid names. Such variables cannot be referenced in a message, but are not otherwise errors.
Examples:
A variable:
This has a {$variable}
A function:
This has a {:function}
An add-on function from the
icu
namespace:This has a {:icu:function}
An option and an add-on option:
This has {:options option=value icu:option=add_on}
Support for namespaces and their interpretation is implementation-defined in this release.
variable = "$" name
option = identifier [s] "=" [s] (literal / variable)
identifier = [namespace ":"] name
namespace = name
name = name-start *name-char
name-start = ALPHA / "_"
/ %xC0-D6 / %xD8-F6 / %xF8-2FF
/ %x370-37D / %x37F-1FFF / %x200C-200D
/ %x2070-218F / %x2C00-2FEF / %x3001-D7FF
/ %xF900-FDCF / %xFDF0-FFFC / %x10000-EFFFF
name-char = name-start / DIGIT / "-" / "."
/ %xB7 / %x300-36F / %x203F-2040
An escape sequence is a two-character sequence starting with
U+005C REVERSE SOLIDUS \
.
An escape sequence allows the appearance of lexically meaningful characters in the body of text, quoted, or reserved (which includes, in this case, private-use) sequences respectively:
text-escape = backslash ( backslash / "{" / "}" )
quoted-escape = backslash ( backslash / "|" )
reserved-escape = backslash ( backslash / "{" / "|" / "}" )
backslash = %x5C ; U+005C REVERSE SOLIDUS "\"
Whitespace is defined as one or more of U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (new line), U+000D CARRIAGE RETURN, U+3000 IDEOGRAPHIC SPACE, or U+0020 SPACE.
Inside patterns and quoted literals, whitespace is part of the content and is recorded and stored verbatim. Whitespace is not significant outside translatable text, except where required by the syntax.
Note
The character U+3000 IDEOGRAPHIC SPACE is included in whitespace for compatibility with certain East Asian keyboards and input methods, in which users might accidentally create these characters in a message.
s = 1*( SP / HTAB / CR / LF / %x3000 )
The grammar is formally defined in message.abnf
using the ABNF notation [STD68],
including the modifications found in RFC 7405.
RFC7405 defines a variation of ABNF that is case-sensitive.
Some ABNF tools are only compatible with the specification found in
RFC 5234.
To make message.abnf
compatible with that version of ABNF, replace
the rules of the same name with this block:
input = %x2E.69.6E.70.75.74 ; ".input"
local = %x2E.6C.6F.63.61.6C ; ".local"
match = %x2E.6D.61.74.63.68 ; ".match"