This document defines the behaviour of a MessageFormat 2.0 implementation when formatting a message for display in a user interface, or for some later processing.
To start, we presume that a message has either been parsed from its syntax or created from a data model description. If this construction has encountered any Syntax Errors or Data Model Errors, an appropriate error MUST be emitted and a fallback value MAY be used as the formatting result.
Formatting of a message is defined by the following operations:
-
Expression and Markup Resolution determines the value of an expression or markup, with reference to the current formatting context. This can include multiple steps, such as looking up the value of a variable and calling formatting functions. The form of the resolved value is implementation defined and the value might not be evaluated or formatted yet. However, it needs to be "formattable", i.e. it contains everything required by the eventual formatting.
The resolution of text is rather straightforward, and is detailed under literal resolution.
Important
This specification does not require either eager or lazy expression resolution of message parts; do not construe any requirement in this document as requiring either.
Implementations are not required to evaluate all parts of a message when parsing, processing, or formatting. In particular, an implementation MAY choose not to evaluate or resolve the value of a given expression until it is actually used by a selection or formatting process. However, when an expression is resolved, it MUST behave as if all preceding declarations and selectors affecting variables referenced by that expression have already been evaluated in the order in which the relevant declarations and selectors appear in the message.
-
Pattern Selection determines which of a message's patterns is formatted. For a message with no selectors, this is simple as there is only one pattern. With selectors, this will depend on their resolution.
At the start of pattern selection, if the message contains any reserved statements, emit an Unsupported Statement error.
-
Formatting takes the resolved values of the selected pattern, and produces the formatted result for the message. Depending on the implementation, this result could be a single concatenated string, an array of objects, an attributed string, or some other locally appropriate data type.
Formatter implementations are not required to expose the expression resolution and pattern selection operations to their users, or even use them in their internal processing, as long as the final formatting result is made available to users and the observable behavior of the formatter matches that described here.
A message's formatting context represents the data and procedures that are required for the message's expression resolution, pattern selection and formatting.
At a minimum, it includes:
-
Information on the current locale, potentially including a fallback chain of locales. This will be passed on to formatting functions.
-
Information on the base directionality of the message and its text tokens. This will be used by strategies for bidirectional isolation, and can be used to set the base direction of the message upon display.
-
An input mapping of string identifiers to values, defining variable values that are available during variable resolution. This is often determined by a user-provided argument of a formatting function call.
-
The function registry, providing the implementations of the functions referred to by message functions.
-
Optionally, a fallback string to use for the message if it contains any Syntax Errors or Data Model Errors.
Implementations MAY include additional fields in their formatting context.
Expressions are used in declarations, selectors, and patterns. Markup is only used in patterns.
In a declaration, the resolved value of the expression is bound to a variable, which is available for use by later expressions. Since a variable can be referenced in different ways later, implementations SHOULD NOT immediately fully format the value for output.
In an input-declaration, the variable operand of the variable-expression identifies not only the name of the external input value, but also the variable to which the resolved value of the variable-expression is bound.
In selectors, the resolved value of an expression is used for pattern selection.
In a pattern, the resolved value of an expression or markup is used in its formatting.
The form that resolved values take is implementation-dependent, and different implementations MAY choose to perform different levels of resolution.
For example, the resolved value of the expression
{|0.40| :number style=percent}
could be an object such as{ value: Number('0.40'), formatter: NumberFormat(locale, { style: 'percent' }) }
Alternatively, it could be an instance of an ICU4J
FormattedNumber
, or some other locally appropriate value.
Depending on the presence or absence of a variable or literal operand and a function, private-use annotation, or reserved annotation, the resolved value of the expression is determined as follows:
If the expression contains a reserved annotation, an Unsupported Expression error is emitted and a fallback value is used as the resolved value of the expression.
Else, if the expression contains a private-use annotation, its resolved value is defined according to the implementation's specification.
Else, if the expression contains an annotation, its resolved value is defined by function resolution.
Else, if the expression consists of a variable, its resolved value is defined by variable resolution. An implementation MAY perform additional processing when resolving the value of an expression that consists only of a variable.
For example, it could apply function resolution using a function and a set of options chosen based on the value or type of the variable. So, given a message like this:
Today is {$date}
If the value passed in the variable were a date object, such as a JavaScript
Date
or a Javajava.util.Date
orjava.time.Temporal
, the implementation could interpret the placeholder{$date}
as if the pattern included the function:datetime
with some set of default options.
Else, the expression consists of a literal. Its resolved value is defined by literal resolution.
Note This means that a literal value with no annotation is always treated as a string. To represent values that are not strings as a literal, an annotation needs to be provided:
.local $aNumber = {1234 :number} .local $aDate = {|2023-08-30| :datetime} .local $aFoo = {|some foo| :foo} {{You have {42 :number}}}
The resolved value of a text or a literal is the character sequence of the text or literal after any character escape has been converted to the escaped character.
When a literal is used as an operand or on the right-hand side of an option, the formatting function MUST treat its resolved value the same whether its value was originally quoted or unquoted.
For example, the option
foo=42
and the optionfoo=|42|
are treated as identical.
The resolution of a text or literal MUST resolve to a string.
To resolve the value of a variable, its name is used to identify either a local variable or an input variable. If a declaration exists for the variable, its resolved value is used. Otherwise, the variable is an implicit reference to an input value, and its value is looked up from the formatting context input mapping.
The resolution of a variable MAY fail if no value is identified for its name. If this happens, an Unresolved Variable error MUST be emitted. If a variable would resolve to a fallback value, this MUST also be considered a failure.
To resolve an expression with a function annotation, the following steps are taken:
-
If the expression includes an operand, resolve its value. If this fails, use a fallback value for the expression.
-
Resolve the identifier of the function and, based on the starting sigil, find the appropriate function implementation to call. If the implementation cannot find the function, or if the identifier includes a namespace that the implementation does not support, emit an Unknown Function error and use a fallback value for the expression.
Implementations are not required to implement namespaces or installable function registries.
-
Perform option resolution.
-
Call the function implementation with the following arguments:
- The current locale.
- The resolved mapping of options.
- If the expression includes an operand, its resolved value.
The form that resolved operand and option values take is implementation-defined.
A declaration binds the resolved value of an expression to a variable. Thus, the result of one function is potentially the operand of another function, or the value of one of the options for another function. For example, in
.input {$n :number minimumIntegerDigits=3} .local $n1 = {$n :number maximumFractionDigits=3}
the value bound to
$n
is the resolved value used as the operand of the:number
function when resolving the value of the variable$n1
.Implementations that provide a means for defining custom functions SHOULD provide a means for function implementations to return values that contain enough information (e.g. a representation of the resolved operand and option values that the function was called with) to be used as arguments to subsequent calls to the function implementations. For example, an implementation might define an interface that allows custom function implementation. Such an interface SHOULD define an implementation-specific argument type
T
and return typeU
for implementations of functions such thatU
can be coerced toT
. Implementations of a function SHOULD emit an Invalid Expression error for operands whose resolved value or type is not supported.
Note
The behavior of the previous example is
currently implementation-dependent. Supposing that
the external input variable n
is bound to the string "1"
,
and that the implementation formats to a string,
the formatted result of the following message:
.input {$n :number minimumIntegerDigits=3}
.local $n1 = {$n :number maximumFractionDigits=3}
{{$n1}}
is currently implementation-dependent.
Depending on whether the options are preserved
between the resolution of the first :number
annotation
and the resolution of the second :number
annotation,
a conformant implementation
could produce either "001.000" or "1.000"
Each function specification MAY have its own rules to preserve some options in the returned structure and discard others. In instances where a function specification does not determine whether an option is preserved or discarded, each function implementation of that specification MAY have its own rules to preserve some options in the returned structure and discard others.
Note
During the Technical Preview, feedback on how the registry describes the flow of resolved values and options from one function to another, and on what requirements this specification should impose, is highly desired.
An implementation MAY pass additional arguments to the function, as long as reasonable precautions are taken to keep the function interface simple and minimal, and avoid introducing potential security vulnerabilities.
An implementation MAY define its own functions. An implementation MAY allow custom functions to be defined by users.
Function access to the formatting context MUST be minimal and read-only, and execution time SHOULD be limited.
Implementation-defined functions SHOULD use an implementation-defined namespace.
-
If the call succeeds, resolve the value of the expression as the result of that function call.
If the call fails or does not return a valid value, emit a Invalid Expression error.
Implementations MAY provide a mechanism for the function to provide additional detail about internal failures. Specifically, if the cause of the failure was that the datatype, value, or format of the operand did not match that expected by the function, the function might cause an Operand Mismatch Error to be emitted.
In all failure cases, use the fallback value for the expression as the resolved value.
The result of resolving option values is an unordered mapping of string identifiers to values.
For each option:
- Resolve the identifier of the option.
- If the option's right-hand side successfully resolves to a value, bind the identifier of the option to the resolved value in the mapping.
- Otherwise, bind the identifier of the option to an unresolved value in the mapping. Implementations MAY later remove this value before calling the function. (Note that an Unresolved Variable error will have been emitted.)
Errors MAY be emitted during option resolution, but it always resolves to some mapping of string identifiers to values. This mapping can be empty.
Unlike functions, the resolution of markup is not customizable.
The resolved value of markup includes the following fields:
- The type of the markup: open, standalone, or close
- The identifier of the markup
- The resolved options values after option resolution.
The resolution of markup MUST always succeed.
A fallback value is the resolved value for an expression that fails to resolve.
An expression fails to resolve when:
- A variable used as an operand (with or without an annotation) fails to resolve.
- Note that this does not include a variable used as an option value.
- A function annotation fails to resolve.
- A private-use annotation is unsupported by the implementation or if a private-use annotation fails to resolve.
- The expression has a reserved annotation.
The fallback value depends on the contents of the expression:
-
expression with literal operand (quoted or unquoted): U+007C VERTICAL LINE
|
followed by the value of the literal with escaping applied to U+005C REVERSE SOLIDUS\
and U+007C VERTICAL LINE|
, and then by U+007C VERTICAL LINE|
.Examples: In a context where
:func
fails to resolve,{42 :func}
resolves to the fallback value|42|
and{|C:\\| :func}
resolves to the fallback value|C:\\|
. In any context,{|| @reserved}
resolves to the fallback value||
. -
expression with variable operand referring to a local declaration (with or without an annotation): the value to which it resolves (which may already be a fallback value)
Examples: In a context where
:func
fails to resolve, the pattern's expression in.local $var={|val|} {{{$val :func}}}
resolves to the fallback value|val|
and the message formats to{|val|}
. In a context where:now
fails to resolve but:datetime
does not, the pattern's expression in.local $t = {:now format=iso8601} .local $pretty_t = {$t :datetime} {{{$pretty_t}}}
(transitively) resolves to the fallback value
:now
and the message formats to{:now}
. -
expression with variable operand not referring to a local declaration (with or without an annotation): U+0024 DOLLAR SIGN
$
followed by the name of the variableExamples: In a context where
$var
fails to resolve,{$var}
and{$var :number}
and{$var @reserved}
all resolve to the fallback value$var
. In a context where:func
fails to resolve, the pattern's expression in.input $arg {{{$arg :func}}}
resolves to the fallback value$arg
and the message formats to{$arg}
. -
function expression with no operand: U+003A COLON
:
followed by the function identifierExamples: In a context where
:func
fails to resolve,{:func}
resolves to the fallback value:func
. In a context where:ns:func
fails to resolve,{:ns:func}
resolves to the fallback value:ns:func
. -
unsupported private-use annotation or reserved annotation with no operand: the annotation starting sigil
Examples: In any context,
{@reserved}
and{@reserved |...|}
both resolve to the fallback value@
. -
supported private-use annotation with no operand: the annotation starting sigil, optionally followed by implementation-defined details conforming with patterns in the other cases (such as quoting literals). If details are provided, they SHOULD NOT leak potentially private information.
Examples: In a context where
^
expressions are used for comments,{^▽^}
might resolve to the fallback value^
. In a context where&
expressions are function-like macro invocations,{&foo |...|}
might resolve to the fallback value&foo
. -
Otherwise: the U+FFFD REPLACEMENT CHARACTER
�
This is not currently used by any expression, but may apply in future revisions.
Option identifiers and values are not included in the fallback value.
Pattern selection is not supported for fallback values.
When a message contains a matcher with one or more selectors, the implementation needs to determine which variant will be used to provide the pattern for the formatting operation. This is done by ordering and filtering the available variant statements according to their key values and selecting the first one.
Note
At least one variant is required to have all of its keys consist of
the fallback value *
.
Some selectors might be implemented in a way that the key value *
cannot be selected in a valid message.
In other cases, this key value might be unreachable only in certain locales.
This could result in the need in some locales to create
one or more variants that do not make sense grammatically for that language.
For example, in the
pl
(Polish) locale, this message cannot reach the*
variant:.match {$num :integer} 0 {{ }} one {{ }} few {{ }} many {{ }} * {{Only used by fractions in Polish.}}
In the Tech Preview, feedback from users and implementers is desired about whether to relax the requirement that such a "fallback variant" appear in every message, versus the potential for a message to fail at runtime because no matching variant is available.
The number of keys in each variant MUST equal the number of selectors.
Each key corresponds to a selector by its position in the variant.
For example, in this message:
.match {:one} {:two} {:three} 1 2 3 {{ ... }}
The first key
1
corresponds to the first selector ({:one}
), the second key2
to the second selector ({:two}
), and the third key3
to the third selector ({:three}
).
To determine which variant best matches a given set of inputs, each selector is used in turn to order and filter the list of variants.
Each variant with a key that does not match its corresponding selector is omitted from the list of variants. The remaining variants are sorted according to the selector's key-ordering preference. Earlier selectors in the matcher's list of selectors have a higher priority than later ones.
When all of the selectors have been processed, the earliest-sorted variant in the remaining list of variants is selected.
Note
A selector is not a declaration.
Even when the same function can be used for both formatting and selection
of a given operand
the annotation that appears in a selector has no effect on subsequent
selectors nor on the formatting used in placeholders.
To use the same value for selection and formatting,
set its value with a .input
or .local
declaration.
This selection method is defined in more detail below. An implementation MAY use any pattern selection method, as long as its observable behavior matches the results of the method defined here.
If the message being formatted has any Syntax Errors or Data Model Errors,
the result of pattern selection MUST be a pattern resolving to a single fallback value
using the message's fallback string defined in the formatting context
or if this is not available or empty, the U+FFFD REPLACEMENT CHARACTER �
.
First, resolve the values of each selector:
- Let
res
be a new empty list of resolved values that support selection. - For each selector
sel
, in source order,- Let
rv
be the resolved value ofsel
. - If selection is supported for
rv
:- Append
rv
as the last element of the listres
.
- Append
- Else:
- Let
nomatch
be a resolved value for which selection always fails. - Append
nomatch
as the last element of the listres
. - Emit a Selection Error.
- Let
- Let
The form of the resolved values is determined by each implementation, along with the manner of determining their support for selection.
Next, using res
, resolve the preferential order for all message keys:
- Let
pref
be a new empty list of lists of strings. - For each index
i
inres
:- Let
keys
be a new empty list of strings. - For each variant
var
of the message:- Let
key
be thevar
key at positioni
. - If
key
is not the catch-all key'*'
:- Assert that
key
is a literal. - Let
ks
be the resolved value ofkey
. - Append
ks
as the last element of the listkeys
.
- Assert that
- Let
- Let
rv
be the resolved value at indexi
ofres
. - Let
matches
be the result of calling the method MatchSelectorKeys(rv
,keys
) - Append
matches
as the last element of the listpref
.
- Let
The method MatchSelectorKeys is determined by the implementation.
It takes as arguments a resolved selector value rv
and a list of string keys keys
,
and returns a list of string keys in preferential order.
The returned list MUST contain only unique elements of the input list keys
.
The returned list MAY be empty.
The most-preferred key is first,
with each successive key appearing in order by decreasing preference.
Then, using the preferential key orders pref
,
filter the list of variants to the ones that match with some preference:
- Let
vars
be a new empty list of variants. - For each variant
var
of the message:- For each index
i
inpref
:- Let
key
be thevar
key at positioni
. - If
key
is the catch-all key'*'
:- Continue the inner loop on
pref
.
- Continue the inner loop on
- Assert that
key
is a literal. - Let
ks
be the resolved value ofkey
. - Let
matches
be the list of strings at indexi
ofpref
. - If
matches
includesks
:- Continue the inner loop on
pref
.
- Continue the inner loop on
- Else:
- Continue the outer loop on message variants.
- Let
- Append
var
as the last element of the listvars
.
- For each index
Finally, sort the list of variants vars
and select the pattern:
- Let
sortable
be a new empty list of (integer, variant) tuples. - For each variant
var
ofvars
:- Let
tuple
be a new tuple (-1,var
). - Append
tuple
as the last element of the listsortable
.
- Let
- Let
len
be the integer count of items inpref
. - Let
i
belen
- 1. - While
i
>= 0:- Let
matches
be the list of strings at indexi
ofpref
. - Let
minpref
be the integer count of items inmatches
. - For each tuple
tuple
ofsortable
:- Let
matchpref
be an integer with the valueminpref
. - Let
key
be thetuple
variant key at positioni
. - If
key
is not the catch-all key'*'
:- Assert that
key
is a literal. - Let
ks
be the resolved value ofkey
. - Let
matchpref
be the integer position ofks
inmatches
.
- Assert that
- Set the
tuple
integer value asmatchpref
.
- Let
- Set
sortable
to be the result of calling the methodSortVariants(sortable)
. - Set
i
to bei
- 1.
- Let
- Let
var
be the variant element of the first element ofsortable
. - Select the pattern of
var
.
SortVariants
is a method whose single argument is
a list of (integer, variant) tuples.
It returns a list of (integer, variant) tuples.
Any implementation of SortVariants
is acceptable
as long as it satisfies the following requirements:
- Let
sortable
be an arbitrary list of (integer, variant) tuples. - Let
sorted
beSortVariants(sortable)
. sorted
is the result of sortingsortable
using the following comparator:(i1, v1)
<=(i2, v2)
if and only ifi1 <= i2
.
- The sort is stable (pairs of tuples from
sortable
that are equal in their first element have the same relative order insorted
).
This section is non-normative.
Presuming a minimal implementation which only supports :string
annotation
which matches keys by using string comparison,
and a formatting context in which
the variable reference $foo
resolves to the string 'foo'
and
the variable reference $bar
resolves to the string 'bar'
,
pattern selection proceeds as follows for this message:
.match {$foo :string} {$bar :string}
bar bar {{All bar}}
foo foo {{All foo}}
* * {{Otherwise}}
-
For the first selector:
The value of the selector is resolved to be'foo'
.
The available keys «'bar'
,'foo'
» are compared to'foo'
,
resulting in a list «'foo'
» of matching keys. -
For the second selector:
The value of the selector is resolved to be'bar'
.
The available keys «'bar'
,'foo'
» are compared to'bar'
,
resulting in a list «'bar'
» of matching keys. -
Creating the list
vars
of variants matching all keys:
The first variantbar bar
is discarded as its first key does not match the first selector.
The second variantfoo foo
is discarded as its second key does not match the second selector.
The catch-all keys of the third variant* *
always match, and this is added tovars
,
resulting in a list «* *
» of variants. -
As the list
vars
only has one entry, it does not need to be sorted.
The patternOtherwise
of the third variant is selected.
Alternatively, with the same implementation and formatting context as in Example 1, pattern selection would proceed as follows for this message:
.match {$foo :string} {$bar :string}
* bar {{Any and bar}}
foo * {{Foo and any}}
foo bar {{Foo and bar}}
* * {{Otherwise}}
-
For the first selector:
The value of the selector is resolved to be'foo'
.
The available keys «'foo'
» are compared to'foo'
,
resulting in a list «'foo'
» of matching keys. -
For the second selector:
The value of the selector is resolved to be'bar'
.
The available keys «'bar'
» are compared to'bar'
,
resulting in a list «'bar'
» of matching keys. -
Creating the list
vars
of variants matching all keys:
The keys of all variants either match each selector exactly, or via the catch-all key,
resulting in a list «* bar
,foo *
,foo bar
,* *
» of variants. -
Sorting the variants:
The listsortable
is first set with the variants in their source order and scores determined by the second selector:
« ( 0,* bar
), ( 1,foo *
), ( 0,foo bar
), ( 1,* *
) »
This is then sorted as:
« ( 0,* bar
), ( 0,foo bar
), ( 1,foo *
), ( 1,* *
) ».
To sort according to the first selector, the scores are updated to:
« ( 1,* bar
), ( 0,foo bar
), ( 0,foo *
), ( 1,* *
) ».
This is then sorted as:
« ( 0,foo bar
), ( 0,foo *
), ( 1,* bar
), ( 1,* *
) ». -
The pattern
Foo and bar
of the most preferredfoo bar
variant is selected.
A more-complex example is the matching found in selection APIs
such as ICU's PluralFormat
.
Suppose that this API is represented here by the function :number
.
This :number
function can match a given numeric value to a specific number literal
and also to a plural category (zero
, one
, two
, few
, many
, other
)
according to locale rules defined in CLDR.
Given a variable reference $count
whose value resolves to the number 1
and an en
(English) locale,
the pattern selection proceeds as follows for this message:
.input {$count :number}
.match {$count}
one {{Category match for {$count}}}
1 {{Exact match for {$count}}}
* {{Other match for {$count}}}
-
For the selector:
The value of the selector is resolved to an implementation-defined value that is capable of performing English plural category selection on the value1
.
The available keys «'one'
,'1'
» are passed to the implementation's MatchSelectorKeys method,
resulting in a list «'1'
,'one'
» of matching keys. -
Creating the list
vars
of variants matching all keys:
The keys of all variants are included in the list of matching keys, or use the catch-all key,
resulting in a list «one
,1
,*
» of variants. -
Sorting the variants:
The listsortable
is first set with the variants in their source order and scores determined by the selector key order:
« ( 1,one
), ( 0,1
), ( 2,*
) »
This is then sorted as:
« ( 0,1
), ( 1,one
), ( 2,*
) » -
The pattern
Exact match for {$count}
of the most preferred1
variant is selected.
After pattern selection, each text and placeholder part of the selected pattern is resolved and formatted.
Resolved values cannot always be formatted by a given implementation. When such an error occurs during formatting, an implementation SHOULD emit a Formatting Error and produce a fallback value for the placeholder that produced the error. A formatting function MAY substitute a value to use instead of a fallback value.
Implementations MAY represent the result of formatting using the most appropriate data type or structure. Some examples of these include:
- A single string concatenated from the parts of the resolved pattern.
- A string with associated attributes for portions of its text.
- A flat sequence of objects corresponding to each resolved value.
- A hierarchical structure of objects that group spans of resolved values, such as sequences delimited by markup-open and markup-close placeholders.
Implementations SHOULD provide formatting result types that match user needs, including situations that require further processing of formatted messages. Implementations SHOULD encourage users to consider a formatted localised string as an opaque data structure, suitable only for presentation.
When formatting to a string, the default representation of all markup 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.
This section is non-normative.
-
An implementation might choose to return an interstitial object so that the caller can "decorate" portions of the formatted value. In ICU4J, the
NumberFormatter
class returns aFormattedNumber
object, so a pattern such asThis is my number {42 :number}
might return the character sequenceThis is my number
followed by aFormattedNumber
object representing the value42
in the current locale. -
A formatter in a web browser could format a message as a DOM fragment rather than as a representation of its HTML source.
If the resolved pattern includes any fallback values
and the formatting result is a concatenated string or a sequence of strings,
the string representation of each fallback value MUST be the concatenation of
a U+007B LEFT CURLY BRACKET {
,
the fallback value as a string,
and a U+007D RIGHT CURLY BRACKET }
.
For example, a message with a Syntax Error and no fallback string defined in the formatting context would format to a string as
{�}
.
Messages contain text. Any text can be bidirectional text. That is, the text can can consist of a mixture of left-to-right and right-to-left spans of text. The display of bidirectional text is defined by the Unicode Bidirectional Algorithm [UAX9].
The directionality of the message as a whole is provided by the formatting context.
When a message is formatted, placeholders are replaced with their formatted representation. Applying the Unicode Bidirectional Algorithm to the text of a formatted message (including its formatted parts) can result in unexpected or undesirable spillover effects. Applying bidi isolation to each affected formatted value helps avoid this spillover in a formatted message.
Note that both the message and, separately, each placeholder need to have direction metadata for this to work. If an implementation supports formatting to something other than a string (such as a sequence of parts), the directionality of each formatted placeholder needs to be available to the caller.
If a formatted expression itself contains spans with differing directionality, its formatter SHOULD perform any necessary processing, such as inserting controls or isolating such parts to ensure that the formatted value displays correctly in a plain text context.
For example, an implementation could provide a
:currency
formatting function which inserts strongly directional characters, such as U+200F RIGHT-TO-LEFT MARK (RLM), U+200E LEFT-TO-RIGHT MARK (LRM), or U+061C ARABIC LETTER MARKER (ALM), to coerce proper display of the sign and currency symbol next to a formatted number. An example of this is formatting the value-1234.56
as the currencyAED
in thear-AE
locale. The formatted value appears like this:-1,234.56 د.إ.
The code point sequence for this string, as produced by the ICU4J
NumberFormat
function, includes U+200F U+200E at the start and U+200F at the end of the string. If it did not do this, the same string would appear like this instead:
A bidirectional isolation strategy is functionality in the formatter's processing of a message that produces bidirectional output text that is ready for display.
The Default Bidi Strategy is a bidirectional isolation strategy that uses isolating Unicode control characters around placeholder's formatted values. It is primarily intended for use in plain-text strings, where markup or other mechanisms are not available. Implementations MUST provide the Default Bidi Strategy as one of the bidirectional isolation strategies.
Implementations MAY provide other bidirectional isolation strategies.
Implementations MAY supply a bidirectional isolation strategy that performs no processing.
The Default Bidi Strategy is defined as follows:
- Let
msgdir
be the directionality of the whole message, one of «'LTR'
,'RTL'
,'unknown'
». These correspond to the message having left-to-right directionality, right-to-left directionality, and to the message's directionality not being known. - For each expression
exp
in pattern:- Let
fmt
be the formatted string representation of the resolved value ofexp
. - Let
dir
be the directionality offmt
, one of «'LTR'
,'RTL'
,'unknown'
», with the same meanings as formsgdir
. - If
dir
is'LTR'
:- If
msgdir
is'LTR'
in the formatted output, letfmt
be itself - Else, in the formatted output,
prefix
fmt
with U+2066 LEFT-TO-RIGHT ISOLATE and postfix it with U+2069 POP DIRECTIONAL ISOLATE.
- If
- Else, if
dir
is'RTL'
:- In the formatted output,
prefix
fmt
with U+2067 RIGHT-TO-LEFT ISOLATE and postfix it with U+2069 POP DIRECTIONAL ISOLATE.
- In the formatted output,
prefix
- Else:
- In the formatted output,
prefix
fmt
with U+2068 FIRST STRONG ISOLATE and postfix it with U+2069 POP DIRECTIONAL ISOLATE.
- In the formatted output,
prefix
- Let