This section defines the behavior of a MessageFormat 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 the resulting message is not well-formed, a Syntax Error is emitted. If the resulting message is well-formed but is not valid, a Data Model Error is emitted.
The formatting of a message is defined by the following operations:
-
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.
-
Formatting takes the resolved values of the text and placeholder parts 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.
-
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.
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 formatting matches that described here.
Attributes MUST NOT have any effect on the formatted output of a message, nor be made available to function handlers.
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 affecting variables referenced by that expression have already been evaluated in the order in which the relevant declarations appear in the message. An implementation MUST ensure that every expression in a message is evaluated at most once.
Important
Implementations with lazy evaluation MUST NOT use a call-by-name evaluation strategy. Instead, they must evaluate expressions at most once ("call-by-need"). This is to prevent expressions from having different values when used in different parts of a given message. Function handlers are not necessarily pure: they can access external mutable state such as the current system clock time. Thus, evaluating the same expression more than once could yield different results. That behavior violates this specification.
Important
Implementations and users SHOULD NOT create function handlers that mutate external program state, particularly since such a function handler can present a remote execution hazard.
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.
-
A mapping of string identifiers to the function handlers that are available during function resolution.
-
Optionally, a fallback string to use for the message if it is not valid.
Implementations MAY include additional fields in their formatting context.
A resolved value is the result of resolving a text, literal, variable, expression, or markup. The resolved value is determined using the formatting context. The form of the resolved value is implementation-defined.
In a declaration, the resolved value of an expression is bound to a variable, which makes it available for use in later expressions and markup options.
For example, in
.input {$a :number minimumFractionDigits=3} .local $b = {$a :integer useGrouping=never} .match $a 0 {{The value is zero.}} * {{Without grouping separators, the value {$a} is rendered as {$b}.}}the resolved value bound to
$ais used as the operand of the:integerfunction when resolving the value of the variable$b, as a selector in the.matchstatement, as well as for formatting the placeholder{$a}.
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 a pattern, the resolved value of an expression or markup is used in its formatting. To support the Default Bidi Strategy, the resolved value of each expression SHOULD include information about the directionality of its formatted string representation, as well as a flag to indicate whether its formatted representation requires isolation from the surrounding text. (See "Handling Bidirectional Text".)
For each option value, the resolved value MUST indicate if the value was directly set with a literal, as opposed to being resolved from a variable. This is to allow function handlers to require specific options to be set using literals.
For example, the default functions
:numberand:integerrequire that the optionselectbe set with a literal option value (plural,ordinal, orexact).
The form that resolved values take is implementation-dependent, and different implementations MAY choose to perform different levels of resolution.
While this specification does not require it, a resolved value could be implemented by requiring each function handler to return a value matching the following interface:
interface MessageValue { formatToString(): string formatToX(): X // where X is an implementation-defined type unwrap(): unknown resolvedOptions(): { [key: string]: MessageValue } match(key: string): boolean betterThan(key1: string, key2: string): boolean directionality(): 'LTR' | 'RTL' | 'unknown' isolate(): boolean isLiteralOptionValue(): boolean }With this approach:
- An expression could be used as a placeholder if calling the
formatToString()orformatToX()method of its resolved value did not emit an error.- A variable could be used as a selector if calling the
match(key)andbetterThan(key1, key2)methods of its resolved value did not emit an error.- The resolved value of an expression could be used as an operand or option value if calling the
unwrap()method of its resolved value did not emit an error. (This requires an intermediate variable declaration.) In this use case, theresolvedOptions()method could also provide a set of option values that could be taken into account by the called function.
- The
unwrap()method returns the function-specific result of the function's operation. For example, the handlers for the following functions might behave as follows:
- The handler for the default function
:numberreturns a value whoseunwrap()method returns the implementation-defined numeric value of the operand.- The handler for a custom
:uppercasefunction might return a value whoseunwrap()method returns an uppercase string in place of the original operand value.- The handler for a custom function that extracts a field from a data structure might return a value whose
unwrap()method returns the extracted value.- Other functions' handlers might return a value whose
unwrap()method returns the original operand value.- The
directionality(),isolate(), andisLiteralOptionValue()methods fulfill requirements and recommendations mentioned elsewhere in this specification.Extensions of the base
MessageValueinterface could be provided for different data types, such as numbers or strings, for which theunknownreturn type ofunwrap()and the genericMessageValuetype used inresolvedOptions()could be narrowed appropriately. An implementation could also allowMessageValuevalues to be passed in as input variables, or automatically wrap each variable as aMessageValueto provide a uniform interface for custom functions.
Expressions are used in declarations and patterns. Markup is only used in patterns. Options are used in expressions and markup.
Expression resolution determines the value of an expression. Depending on the presence or absence of a variable or literal operand and a function, the resolved value of the expression is determined as follows:
If the expression contains a function, 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
Dateor a Javajava.util.Dateorjava.time.Temporal, the implementation could interpret the placeholder{$date}as if the pattern included the function:datetimewith 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 function is always treated as a string. To represent values that are not strings as a literal, a function needs to be provided:
.local $aNumber = {1234 :number}
.local $aDate = {|2023-08-30| :datetime}
.local $aFoo = {|some foo| :ns:foo}
{{You have {42 :number}}}
Literal resolution : The resolved value of a text or a literal contains 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 as an option value, the formatting function MUST treat its resolved value the same whether its value was originally a quoted literal or an unquoted literal.
For example, the option
foo=42and the optionfoo=|42|are treated as identical.
For example, in a JavaScript formatter, the resolved value of a text or a literal could have the following implementation:
class MessageLiteral implements MessageValue { constructor(value: string) { this.formatToString = () => value; this.getValue = () => value; } resolvedOptions: () => ({}); match(_key: string) { throw Error("Selection on unannotated literals is not supported"); } }
Variable resolution : 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 fails if no value is identified for its name. If this happens, an Unresolved Variable error is emitted and a fallback value is used as the resolved value of the variable.
If the resolved value identified for the variable name is a fallback value, a fallback value is used as the resolved value of the variable.
The fallback value representation of a variable has a string representation
consisting of the U+0024 DOLLAR SIGN $ followed by the name of the variable.
Function resolution : To resolve an expression with a function, the following steps are taken:
-
If the expression includes an operand, resolve its value. If this is a fallback value, return a fallback value as the resolved value of the expression.
-
Resolve the identifier of the function and find the appropriate function handler to call. If the implementation cannot find the function handler, or if the identifier includes a namespace that the implementation does not support, emit an Unknown Function error and return a fallback value as the resolved value of the expression.
Implementations are not required to implement namespaces or support functions other than the default functions.
-
Perform option resolution.
-
Determine the function context for calling the function handler.
The function context contains the context necessary for the function handler to resolve the expression. This includes:
- The current locale, potentially including a fallback chain of locales.
- The base directionality of the expression. By default, this is undefined or empty.
If the resolved mapping of options includes any
u:options supported by the implementation, process them as specified. Suchu:options MAY be removed from the resolved mapping of options. -
Call the function handler with the following arguments:
- The function context.
- 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.
An implementation MAY pass additional arguments to the function handler, as long as reasonable precautions are taken to keep the function interface simple and minimal, and avoid introducing potential security vulnerabilities.
-
If the call succeeds, resolve the value of the expression as the result of that function call. The value MUST NOT be marked as a literal option value.
If the call fails or does not return a valid value, emit the appropriate Message Function Error for the failure.
Implementations MAY provide a mechanism for the function handler 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 SHOULD cause a Bad Operand error to be emitted.
In all failure cases, return a fallback value as the resolved value of the expression.
A function handler is an implementation-defined process such as a function or method which accepts a set of arguments and returns a resolved value. A function handler is required to resolve a function.
An implementation MAY define its own functions and their handlers. An implementation MAY allow custom functions to be defined by users.
Implementations that provide a means for defining custom functions MUST provide a means for function handlers to return resolved values that contain enough information to be used as operands or option values in subsequent expressions.
The resolved value returned by a function handler MAY be different from the value of the operand of the function. It MAY be an implementation specified type. It is not required to be the same type as the operand.
A function handler MAY include resolved options in its resolved value. The resolved options MAY be different from the options of the function.
A function handler SHOULD emit a Bad Operand error for operands whose resolved value or type is not supported.
Function handler 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.
Markup resolution determines the value of markup. 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 mapping of options after option resolution.
If the resolved mapping of options includes any u: options
supported by the implementation, process them as specified.
Such u: options MAY be removed from the resolved mapping of options.
The resolution of markup MUST always succeed. (Any errors emitted by option resolution are non-fatal.)
Option resolution is the process of computing the options for a given expression or markup. Option resolution results in a mapping of string identifiers to resolved values. The order of options MUST NOT be significant.
For example, the following message treats both both placeholders identically:
{$x :ns:func option1=foo option2=bar} {$x :ns:func option2=bar option1=foo}
For each option:
- Let
resbe a new empty mapping. - For each option:
- Let
idbe the string value of the identifier of the option. - Let
rvbe the resolved value of the option value. - If
rvis a fallback value:- Emit a Bad Option error, if supported.
- Else:
- If the option value consists of a literal:
- Mark
rvas a literal option value.
- Mark
- Set
res[id]to berv.
- If the option value consists of a literal:
- Let
- Return
res.
Note
If the resolved value of an option value is a fallback value, the option is intentionally omitted from the mapping of resolved options.
The result of option resolution MUST be a (possibly empty) mapping of string identifiers to values; that is, errors MAY be emitted, but such errors MUST NOT be fatal. This mapping can be empty.
Note
The resolved value of a function operand can also include resolved option values. These are not included in the option resolution result, and need to be processed separately by a function handler.
A fallback value is the resolved value for an expression or variable when that expression or variable fails to resolve. It contains a string representation that is used for its formatting. All options are removed.
The resolved value of text, literal, and markup MUST NOT be a fallback value.
A variable fails to resolve when no value is identified for its name.
The string representation of its fallback value is
U+0024 DOLLAR SIGN $ followed by the name of the variable.
An expression fails to resolve when:
- A variable used as its operand resolves to a fallback value. Note that an expression does not necessarily fail to resolve if an option value resolves with a fallback value.
- No function handler is found for a function identifier.
- Calling a function handler fails or does not return a valid value.
The string representation of the fallback value of an expression depends on its contents:
-
expression with a literal operand (either 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
:ns:funcfails to resolve,{42 :ns:func}resolves to a fallback value with a string representation|42|and{|C:\\| :ns:func}resolves to a fallback value with a string representation|C:\\|. -
expression with variable operand: the fallback value representation of that variable, U+0024 DOLLAR SIGN
$followed by the name of the variableExamples: In a context where
$varfails to resolve,{$var}and{$var :number}both resolve to a fallback value with a string representation$var(even if:numberfails to resolve).In a context where
:ns:funcfails to resolve, the placeholder in.local $var = {|val| :ns:func} {{{$var}}}resolves to a fallback value with a string representation$var.In a context where either
:ns:nowor:ns:prettyfails to resolve, the placeholder in.local $time = {:ns:now format=iso8601} {{{$time :ns:pretty}}}resolves to a fallback value with a string representation
$time. -
function expression with no operand: U+003A COLON
:followed by the function identifierExample: In a context where
:ns:funcfails to resolve,{:ns:func}resolves to a fallback value with a string representation:ns:func. -
Otherwise: the U+FFFD REPLACEMENT CHARACTER
�This is not currently used by any expression, but may apply in future revisions.
Options and attributes are not included in the fallback value.
Pattern selection is not supported for fallback values.
For example, in a JavaScript formatter the fallback value could have the following implementation, where
sourceis one of the above-defined strings:class MessageFallback implements MessageValue { constructor(source: string) { this.formatToString = () => `{${source}}`; this.getValue = () => undefined; } resolvedOptions: () => ({}); match(_key: string) { throw Error("Selection on fallback values is not supported"); } }
If the message being formatted is not well-formed and valid,
the result of pattern selection is a pattern consisting of 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 �.
If the message being formatted does not contain a matcher, the result of pattern selection is its pattern value.
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 traversing the list of available variant statements and maintaining a provisional "best variant". Each subsequent variant is compared to the previous best variant according to its key values, yielding a single best variant.
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:.input {$num :integer} .match $num 0 {{ }} one {{ }} few {{ }} many {{ }} * {{Only used by fractions in Polish.}}
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:
.input {$one :number} .input {$two :number} .input {$three :number} .match $one $two $three 1 2 3 {{ ... }}The first key
1corresponds to the first selector ($one), the second key2to the second selector ($two), and the third key3to the third selector ($three).
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.
For a resolved value to support selection, the operations Match and BetterThan need to be defined on it.
If rv is a resolved value that supports selection,
then Match(rv, k) returns true for any key k that matches rv
and returns false otherwise.
BetterThan(rv, k1, k2) returns true
for any keys k1 and k2 for which Match(rv, k1) is true,
Match(rv, k2) is true, and k1 is a better match than k2,
and returns false otherwise.
On any error, both operations return false.
Other than the Match(rv, k) and BetterThan(rv, k1, k2) operations
on resolved values,
the form of the resolved values is determined by each implementation,
along with the manner of determining their support for selection.
First, resolve the values of each selector:
- Let
resbe a new empty list of resolved values that support selection. - For each selector
sel, in source order,- Let
rvbe the resolved value ofsel. - If selection is supported for
rv:- Append
rvas the last element of the listres.
- Append
- Else:
- Let
nomatchbe a resolved value for which Match(rv,k) is false for any keyk. - Append
nomatchas the last element of the listres. - Emit a Bad Selector error.
- Let
- Let
Next, using res:
- Let
bestVariantbeUNSET. - For each variant
varof the message, in source order:- Let
keysbe the keys ofvar. - Let
matchbe SelectorsMatch(res,keys). - If
matchis false:- Continue the loop.
- If
bestVariantisUNSET.- Set
bestVarianttovar.
- Set
- Else:
- Let
bestVariantKeysbe the keys ofbestVariant. - If SelectorsCompare(
res,keys,bestVariantKeys) is true:- Set
bestVarianttovar.
- Set
- Let
- Let
- Assert that
bestVariantis notUNSET. - Select the pattern of
bestVariant.
SelectorsMatch(selectors, keys) is defined as follows, where
selectors is a list of resolved values
and keys is a list of keys:
- Let
ibe 0. - For each key
keyinkeys:- If
keyis not the catch-all key'*'- Let
kbe NormalizeKey(key). - Let
selbe theith element ofselectors. - If Match(
sel,k) is false:- Return false.
- Let
- Set
itoi+ 1.
- If
- Return true.
SelectorsCompare(selectors, keys1, keys2) is defined as follows, where
selectors is a list of resolved values
and keys1 and keys2 are lists of keys.
- Let
ibe 0. - For each key
key1inkeys1:- Let
key2be theith element ofkeys2. - If
key1is the catch-all key'*'andkey2is not the catch-all key:- Return false.
- If
key1is not the catch-all key'*'andkey2is the catch-all key:- Return true.
- If
key1andkey2are both the catch-all key'*'- Set
itoi + 1. - Continue the loop.
- Set
- Let
k1be NormalizeKey(key1). - Let
k2be NormalizeKey(key2). - If
k1andk2consist of the same sequence of Unicode code points, then:- Set
itoi + 1. - Continue the loop.
- Set
- Let
selbe theith element ofselectors. - Let
resultbe BetterThan(sel,k1,k2). - Return
result.
- Let
- Return false.
NormalizeKey(key) is defined as follows, where
key is a key.
- Let
rvbe the resolved value ofkey(see Literal Resolution.) - Let
kbe the string value ofrv. - Let
k1be the result of applying Unicode Normalization Form C [UAX#15] tok. - Return
k1.
For examples of how the algorithms work, see the appendix.
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 appropriate Message Function Error is emitted and a fallback value is used for the placeholder with the error.
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.
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
NumberFormatterclass returns aFormattedNumberobject, so a pattern such asThis is my number {42 :number}might return the character sequenceThis is my numberfollowed by aFormattedNumberobject representing the value42in 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 that is not well-formed would format to a string as
{�}, unless a fallback string is defined in the formatting context, in which case that string would be used instead.
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 formatted message as a whole is provided by the formatting context.
Note
Keep in mind the difference between the formatted output of a message, which is the topic of this section, and the syntax of message prior to formatting. The processing of a message depends on the logical sequence of Unicode code points, not on the presentation of the message. Affordances to allow users appropriate control over the appearance of the message's syntax have been provided.
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
:currencyformatting 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.56as the currencyAEDin thear-AElocale. The formatted value appears like this:-1,234.56 د.إ.The code point sequence for this string, as produced by the ICU4J
NumberFormatfunction, 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. The Default Bidi Strategy MUST be the default bidirectional isolation strategy when formatting a message as a single string.
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
outbe the empty string. - Let
msgdirbe 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 part
partin pattern:- If
partis a plain literal (text) part, appendparttoout. - Else if
partis a markup placeholder:- Let
fmtbe the formatted string representation of the resolved value ofpart. Note that this is normally the empty string. - Append
fmttoout.
- Let
- Else:
- Let
resvalbe the resolved value ofpart. - Let
fmtbe the formatted string representation ofresval. - Let
dirbe the directionality ofresval, one of «'LTR','RTL','unknown'», with the same meanings as formsgdir. - Let the boolean value
isolatebe True if theu:diroption ofresvalhas a value other than'inherit', or False otherwise. - If
diris'LTR':- If
msgdiris'LTR'andisolateis False:- Append
fmttoout.
- Append
- Else:
- Append U+2066 LEFT-TO-RIGHT ISOLATE to
out. - Append
fmttoout. - Append U+2069 POP DIRECTIONAL ISOLATE to
out.
- Append U+2066 LEFT-TO-RIGHT ISOLATE to
- If
- Else if
diris'RTL':- Append U+2067 RIGHT-TO-LEFT ISOLATE to
out. - Append
fmttoout. - Append U+2069 POP DIRECTIONAL ISOLATE to
out.
- Append U+2067 RIGHT-TO-LEFT ISOLATE to
- Else:
- Append U+2068 FIRST STRONG ISOLATE to
out. - Append
fmttoout. - Append U+2069 POP DIRECTIONAL ISOLATE to
out.
- Append U+2068 FIRST STRONG ISOLATE to
- Let
- If
- Emit
outas the formatted output of the message.
Note
As mentioned in the "Resolved Values" section, the representation of a resolved value can track everything needed to determine the directionality of the formatted string representation of a resolved value. Each function handler can have its own means for determining the directionality annotation on the resolved value it returns. Alternately, an implementation could simply determine directionality based on the locale.
Important
Directionality SHOULD NOT be determined by introspecting
the character sequence in the formatted string representation
of resval.
