Intl.MessageFormat
I have some questions.
Eemeli Aro, Mozilla / OpenJS Foundation
interface MessageFormatOptions {
bidiIsolation?: 'default' | 'none';
dir?: 'ltr' | 'rtl' | 'auto';
functions?:� { [key: string]: MessageFunction };
localeMatcher?: 'best fit' | 'lookup';
}
��type MessageFunction = (
source: string,
locales: string[],
options: { [key: string]: unknown },
input?: unknown
) => MessageValue;
class Intl.MessageFormat {
constructor(
source: MessageData | string,
locales?: string | string[],
options?: MessageFormatOptions
);
format(
values?: { [key: string]: unknown },
onError?: (error: Error) => void
): string;
formatToParts(
values?: { [key: string]: unknown },
onError?: (error: Error) => void
): MessagePart[];
resolvedOptions():
ResolvedMessageFormatOptions;
}
const msg = 'Total price: {$price :number style=currency currency=EUR}';
const mf = new Intl.MessageFormat(msg, 'en');
mf.format({ price: 42 }); // 'Total price: €42.00'
mf.formatToParts({ price: 42 });
/* [
{ type: 'literal', value: 'Total price: ' },
{ type: 'number', source: '$price',
parts: [
{ type: 'currency', value: '€' }, { type: 'integer', value: '42' },
{ type: 'decimal', value: '.' }, { type: 'fraction', value: '00' }
] }
] */
Developments since the 2023.09 update presentation
Q1: When can we consider a MessageFormat 2 parser in JS?
Option A
When the MF2 specification is finalized in Unicode CLDR, and has a sufficient stability guarantee.
Option B
When the stability of the MF2 specification has been proven by years of experience with it.
Timeline
2013.04 First strawman proposal presented & discussed
2016.07 Discussion re-started under tc39/ecma402#92
2019.07 MessageFormat WG first organised under TC39 TG2
2020.01 WG reorganised as a subgroup of the Unicode CLDR-TC
2022.03 Intl.MessageFormat accepted for Stage 1
2022.11 Message resources spun off as a separate proposal
2023.09 Stage 1 update, presenting current API
2023.10 Follow-up incubator call confirming no API changes
If we build it, they will come.
If we build it, they will come.
“For some time I know thought about the next version of i18next [...] but with the current uncertainty what the decision here is - I better do nothing before picking the one that gets not adopted by browsers. [...] If there is a chance that one format gets the defacto standard for web [...] my bet is the tooling will improve. Currently, as a vendor, you just have too many formats for web you need to support (and the web is only one piece of what you need to support)”
– Jan Mühlemann, i18next maintainer, 2019
Motivation, as presented for Stage 1 in 2022.03
The MessageFormat 2 Stability Policy
Q1: When can we consider a MessageFormat 2 parser in JS?
Option A
When the MF2 specification is finalized in Unicode CLDR, and has a sufficient stability guarantee.
Option B
When the stability of the MF2 specification has been proven by years of experience with it.
Pause for discussion
What if we left out the MF2 syntax parser?
class Intl.MessageFormat {
constructor(
source: MessageData,
locales?: string | string[],
options?: MessageFormatOptions
);
...
}
class Intl.MessageFormat {
constructor(
source: MessageData | string,
locales?: string | string[],
options?: MessageFormatOptions
);
...
}
→
Messages as Data
ICU MessageFormat: 'Total price: {price, number, currency}' →
{ type: 'message',
pattern: [
'Total price: ',
{ type: 'expression',
arg: { type: 'variable', name: 'price' },
annotation: { type: 'function', name: 'number', options: [
{ name: 'style', value: { type: 'literal', value: 'currency' } }
] } }
] }
Messages as Data
Fluent: 'Total price: { NUMBER($price, style: "currency") }' →
{ type: 'message',
pattern: [
'Total price: ',
{ type: 'expression',
arg: { type: 'variable', name: 'price' },
annotation: { type: 'function', name: 'number', options: [
{ name: 'style', value: { type: 'literal', value: 'currency' } }
] } }
] }
Messages as Data
MessageFormat 2: 'Total price: {$price :number style=currency}' →
{ type: 'message',
pattern: [
'Total price: ',
{ type: 'expression',
arg: { type: 'variable', name: 'price' },
annotation: { type: 'function', name: 'number', options: [
{ name: 'style', value: { type: 'literal', value: 'currency' } }
] } }
] }
type Message =
| PatternMessage
| SelectMessage
interface PatternMessage {
type: "message"
declarations?: Declaration[]
pattern: Pattern
}
interface SelectMessage {
type: "select"
declarations?: Declaration[]
selectors: Expression[]
variants: Variant[]
}
interface Declaration {
type: "input" | "local"
name: string
value: Expression
}
interface Variant {
keys: (Literal | CatchallKey)[]
value: Pattern
}
interface CatchallKey {
type: "*"
}
interface Literal {
type: "literal"
value: string
}
interface VariableRef {
type: "variable"
name: string
}
interface FunctionAnnotation {
type: "function"
name: string
options?: Option[]
}
interface Option {
name: string
value: Literal | VariableRef
}
type Pattern =
(string | Expression | Markup)[]
interface Expression {
type: "expression"
arg?: Literal | VariableRef
annotation?: FunctionAnnotation
}
interface Markup {
type: "markup"
kind: "open" | "standalone" | "close"
name: string
options?: Option[]
}
Q2: Without syntax, is what remains motivated enough� for inclusion in the language?
The proposal includes answers to the following questions:
Links
npm i messageformat@next