Skip to content

Commit

Permalink
Add markup support (#29)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Minor <[email protected]>
  • Loading branch information
eemeli and dminor authored Jan 17, 2024
1 parent 1243189 commit add4851
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,57 @@ using the constructor's `functions` option,
which takes `MessageFunction` function values that are applied when
the annotation's name (without the `:`) corresponds to the `functions` key.

#### Markup

In addition to expressions, placeholders may also be markup;
content corresponding to HTML elements or other markup syntax.
Unlike expressions, markup does not accept a positional input argument
and its resolution is not customizable by the `functions` option.

Markup placeholders take three different forms:

- "standalone" markup for non-textual content such as inline images,
- "open" markup that starts a markup span, and
- "close" markup that end a markup span.

The syntax used by markup is somewhat similar to that of XML,
though with curly braces `{}` instead of angle brackets `<>` and
with `#` as a prefix for "standalone" and "open": `{#img /}`, `{#b}`, `{/b}`.

Markup placeholders are not required to be paired or nest cleanly;
within the formatter each is only considered by itself,
and any higher-level validation is the responsibility of the caller.

A markup placeholder cannot be used as a selector.
In `format()`, all markup is ignored, with each being formatted to an empty string.
In `formatToParts()`, each markup placeholder is formatted to a single part:

```ts
interface MessageMarkupPart {
type: 'markup';
kind: 'open' | 'standalone' | 'close';
source: string;
name: string;
options?: { [key: string]: unknown };
}
```

The `type` of the part is always `"markup"`,
and its `kind` is one of `"open"`, `"standalone"`, or `"close"`.
The `name` matches the name of the markup,
without the `#` or `/` prefixes or suffixes.
The `source` matches the `name` of the markup placeholder,
prefixed and suffixed with the appropriate `#` and `/` characters.

The `options` correspond to the resolved literal and variable values
of the options included in the placeholder.
For example, when formatting `{#open foo=42 bar=$baz}` with `formatToParts({ baz: 13 })`,
the formatted part's `options` would be `{ foo: '42', bar: 13 }`.
For options with variable reference values,
if the resolved value is an object with a `valueOf()` method, the returned value is used.
The `options` are only supported for "open" and "standalone" markup placeholders
and are never included for a "close" markup placeholder.

### MessageFunction

Fundamentally, messages are formed by concatenating values together.
Expand Down Expand Up @@ -631,7 +682,7 @@ interface MessageFallbackPart {
```

This representation is also used when resolving MF2 expressions
that include "markup", "reserved" or "private-use" annotations.
that include "reserved" or "private-use" annotations.

The `source` of the `MessageFallback` corresponds to the `source` of the `MessageValue`.
When `MessageFallback` is formatted to a string,
Expand Down
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2983,7 +2983,7 @@
</ul></div><div id="menu-toggle"><svg xmlns="http://www.w3.org/2000/svg" style="width:100%; height:100%; stroke:currentColor" viewBox="0 0 120 120">
<title>Menu</title>
<path stroke-width="10" stroke-linecap="round" d="M30,60 h60 M30,30 m0,5 h60 M30,90 m0,-5 h60"></path>
</svg></div><div id="menu-spacer" class="menu-spacer"></div><div id="menu"><div id="menu-search"><input type="text" id="menu-search-box" placeholder="Search..."><div id="menu-search-results" class="inactive"></div></div><div id="menu-pins"><div class="menu-pane-header">Pins<button class="unpin-all">clear</button></div><ul id="menu-pins-list"></ul></div><div class="menu-pane-header">Table of Contents</div><div id="menu-toc"><ol class="toc"><li><span class="item-toggle">+</span><a href="#messageformat-objects" title="MessageFormat Objects"><span class="secnum">1</span> MessageFormat Objects</a><ol class="toc"><li><span class="item-toggle">+</span><a href="#sec-intl-messageformat-constructor" title="The Intl.MessageFormat Constructor"><span class="secnum">1.1</span> The Intl.MessageFormat Constructor</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat" title="Intl.MessageFormat ( source, [ locales [ , options ] ] )"><span class="secnum">1.1.1</span> Intl.MessageFormat ( <var>source</var>, [ <var>locales</var> [ , <var>options</var> ] ] )</a></li><li><span class="item-toggle-none"></span><a href="#sec-initializemessageformat" title="InitializeMessageFormat ( messageFormat, source, locales, options )"><span class="secnum">1.1.2</span> InitializeMessageFormat ( <var>messageFormat</var>, <var>source</var>, <var>locales</var>, <var>options</var> )</a></li></ol></li><li><span class="item-toggle">+</span><a href="#sec-properties-of-intl-messageformat-constructor" title="Properties of the Intl.MessageFormat Constructor"><span class="secnum">1.2</span> Properties of the Intl.MessageFormat Constructor</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype" title="Intl.MessageFormat.prototype"><span class="secnum">1.2.1</span> Intl.MessageFormat.prototype</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat-internal-slots" title="Internal slots"><span class="secnum">1.2.2</span> Internal slots</a></li></ol></li><li><span class="item-toggle">+</span><a href="#sec-properties-of-intl-messageformat-prototype-object" title="Properties of the Intl.MessageFormat Prototype Object"><span class="secnum">1.3</span> Properties of the Intl.MessageFormat Prototype Object</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.constructor" title="Intl.MessageFormat.prototype.constructor"><span class="secnum">1.3.1</span> Intl.MessageFormat.prototype.constructor</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype-tostringtag" title="Intl.MessageFormat.prototype [ @@toStringTag ]"><span class="secnum">1.3.2</span> Intl.MessageFormat.prototype [ @@toStringTag ]</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.format" title="Intl.MessageFormat.prototype.format ( [ values [ , onError ] ] )"><span class="secnum">1.3.3</span> Intl.MessageFormat.prototype.format ( [ <var>values</var> [ , <var>onError</var> ] ] )</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.formatToParts" title="Intl.MessageFormat.prototype.formatToParts ( [ values [ , onError ] ] )"><span class="secnum">1.3.4</span> Intl.MessageFormat.prototype.formatToParts ( [ <var>values</var> [ , <var>onError</var> ] ] )</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.resolvedoptions" title="Intl.MessageFormat.prototype.resolvedOptions ( )"><span class="secnum">1.3.5</span> Intl.MessageFormat.prototype.resolvedOptions ( )</a></li></ol></li><li><span class="item-toggle-none"></span><a href="#sec-properties-of-intl-messageformat-instances" title="Properties of Intl.MessageFormat Instances"><span class="secnum">1.4</span> Properties of Intl.MessageFormat Instances</a></li><li><span class="item-toggle">+</span><a href="#sec-intl-messageformat-abstracts" title="Abstract Operations for MessageFormat Objects"><span class="secnum">1.5</span> Abstract Operations for MessageFormat Objects</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-getmessagedata" title="GetMessageData ( source )"><span class="secnum">1.5.1</span> GetMessageData ( <var>source</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-getmessagefunctions" title="GetMessageFunctions ( userFunctions )"><span class="secnum">1.5.2</span> GetMessageFunctions ( <var>userFunctions</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-handlemessageformaterror" title="HandleMessageFormatError ( onError, completionRecord )"><span class="secnum">1.5.3</span> HandleMessageFormatError ( <var>onError</var>, <var>completionRecord</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-messagefallbackpart" title="MessageFallbackPart ( mv )"><span class="secnum">1.5.4</span> MessageFallbackPart ( <var>mv</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-messagefallbackstring" title="MessageFallbackString ( mv )"><span class="secnum">1.5.5</span> MessageFallbackString ( <var>mv</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-messagevaluesource" title="MessageValueSource ( mv )"><span class="secnum">1.5.6</span> MessageValueSource ( <var>mv</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-resolveexpression" title="ResolveExpression ( mf, values, onError, expression )"><span class="secnum">1.5.7</span> ResolveExpression ( <var>mf</var>, <var>values</var>, <var>onError</var>, <var>expression</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-resolvemessage" title="ResolveMessage ( mf, values, onError )"><span class="secnum">1.5.8</span> ResolveMessage ( <var>mf</var>, <var>values</var>, <var>onError</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-selectpattern" title="SelectPattern ( mf, values, onError )"><span class="secnum">1.5.9</span> SelectPattern ( <var>mf</var>, <var>values</var>, <var>onError</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-selectvariant" title="SelectVariant ( selectors, variants, onError )"><span class="secnum">1.5.10</span> SelectVariant ( <var>selectors</var>, <var>variants</var>, <var>onError</var> )</a></li></ol></li></ol></li><li><span class="item-toggle-none"></span><a href="#sec-copyright-and-software-license" title="Copyright &amp; Software License"><span class="secnum">A</span> Copyright &amp; Software License</a></li></ol></div></div><div id="spec-container"><h1 class="version">Stage 1 Draft / December 16, 2023</h1><h1 class="title">Intl.MessageFormat</h1>
</svg></div><div id="menu-spacer" class="menu-spacer"></div><div id="menu"><div id="menu-search"><input type="text" id="menu-search-box" placeholder="Search..."><div id="menu-search-results" class="inactive"></div></div><div id="menu-pins"><div class="menu-pane-header">Pins<button class="unpin-all">clear</button></div><ul id="menu-pins-list"></ul></div><div class="menu-pane-header">Table of Contents</div><div id="menu-toc"><ol class="toc"><li><span class="item-toggle">+</span><a href="#messageformat-objects" title="MessageFormat Objects"><span class="secnum">1</span> MessageFormat Objects</a><ol class="toc"><li><span class="item-toggle">+</span><a href="#sec-intl-messageformat-constructor" title="The Intl.MessageFormat Constructor"><span class="secnum">1.1</span> The Intl.MessageFormat Constructor</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat" title="Intl.MessageFormat ( source, [ locales [ , options ] ] )"><span class="secnum">1.1.1</span> Intl.MessageFormat ( <var>source</var>, [ <var>locales</var> [ , <var>options</var> ] ] )</a></li><li><span class="item-toggle-none"></span><a href="#sec-initializemessageformat" title="InitializeMessageFormat ( messageFormat, source, locales, options )"><span class="secnum">1.1.2</span> InitializeMessageFormat ( <var>messageFormat</var>, <var>source</var>, <var>locales</var>, <var>options</var> )</a></li></ol></li><li><span class="item-toggle">+</span><a href="#sec-properties-of-intl-messageformat-constructor" title="Properties of the Intl.MessageFormat Constructor"><span class="secnum">1.2</span> Properties of the Intl.MessageFormat Constructor</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype" title="Intl.MessageFormat.prototype"><span class="secnum">1.2.1</span> Intl.MessageFormat.prototype</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat-internal-slots" title="Internal slots"><span class="secnum">1.2.2</span> Internal slots</a></li></ol></li><li><span class="item-toggle">+</span><a href="#sec-properties-of-intl-messageformat-prototype-object" title="Properties of the Intl.MessageFormat Prototype Object"><span class="secnum">1.3</span> Properties of the Intl.MessageFormat Prototype Object</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.constructor" title="Intl.MessageFormat.prototype.constructor"><span class="secnum">1.3.1</span> Intl.MessageFormat.prototype.constructor</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype-tostringtag" title="Intl.MessageFormat.prototype [ @@toStringTag ]"><span class="secnum">1.3.2</span> Intl.MessageFormat.prototype [ @@toStringTag ]</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.format" title="Intl.MessageFormat.prototype.format ( [ values [ , onError ] ] )"><span class="secnum">1.3.3</span> Intl.MessageFormat.prototype.format ( [ <var>values</var> [ , <var>onError</var> ] ] )</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.formatToParts" title="Intl.MessageFormat.prototype.formatToParts ( [ values [ , onError ] ] )"><span class="secnum">1.3.4</span> Intl.MessageFormat.prototype.formatToParts ( [ <var>values</var> [ , <var>onError</var> ] ] )</a></li><li><span class="item-toggle-none"></span><a href="#sec-intl.messageformat.prototype.resolvedoptions" title="Intl.MessageFormat.prototype.resolvedOptions ( )"><span class="secnum">1.3.5</span> Intl.MessageFormat.prototype.resolvedOptions ( )</a></li></ol></li><li><span class="item-toggle-none"></span><a href="#sec-properties-of-intl-messageformat-instances" title="Properties of Intl.MessageFormat Instances"><span class="secnum">1.4</span> Properties of Intl.MessageFormat Instances</a></li><li><span class="item-toggle">+</span><a href="#sec-intl-messageformat-abstracts" title="Abstract Operations for MessageFormat Objects"><span class="secnum">1.5</span> Abstract Operations for MessageFormat Objects</a><ol class="toc"><li><span class="item-toggle-none"></span><a href="#sec-getmessagedata" title="GetMessageData ( source )"><span class="secnum">1.5.1</span> GetMessageData ( <var>source</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-getmessagefunctions" title="GetMessageFunctions ( userFunctions )"><span class="secnum">1.5.2</span> GetMessageFunctions ( <var>userFunctions</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-handlemessageformaterror" title="HandleMessageFormatError ( onError, completionRecord )"><span class="secnum">1.5.3</span> HandleMessageFormatError ( <var>onError</var>, <var>completionRecord</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-messagefallbackpart" title="MessageFallbackPart ( mv )"><span class="secnum">1.5.4</span> MessageFallbackPart ( <var>mv</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-messagefallbackstring" title="MessageFallbackString ( mv )"><span class="secnum">1.5.5</span> MessageFallbackString ( <var>mv</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-messagevaluesource" title="MessageValueSource ( mv )"><span class="secnum">1.5.6</span> MessageValueSource ( <var>mv</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-resolveexpression" title="ResolveExpression ( mf, values, onError, expression )"><span class="secnum">1.5.7</span> ResolveExpression ( <var>mf</var>, <var>values</var>, <var>onError</var>, <var>expression</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-resolvemessage" title="ResolveMessage ( mf, values, onError )"><span class="secnum">1.5.8</span> ResolveMessage ( <var>mf</var>, <var>values</var>, <var>onError</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-selectpattern" title="SelectPattern ( mf, values, onError )"><span class="secnum">1.5.9</span> SelectPattern ( <var>mf</var>, <var>values</var>, <var>onError</var> )</a></li><li><span class="item-toggle-none"></span><a href="#sec-selectvariant" title="SelectVariant ( selectors, variants, onError )"><span class="secnum">1.5.10</span> SelectVariant ( <var>selectors</var>, <var>variants</var>, <var>onError</var> )</a></li></ol></li></ol></li><li><span class="item-toggle-none"></span><a href="#sec-copyright-and-software-license" title="Copyright &amp; Software License"><span class="secnum">A</span> Copyright &amp; Software License</a></li></ol></div></div><div id="spec-container"><h1 class="version">Stage 1 Draft / January 12, 2024</h1><h1 class="title">Intl.MessageFormat</h1>
<emu-clause id="messageformat-objects">
<h1><span class="secnum">1</span> MessageFormat Objects</h1>
Expand Down Expand Up @@ -3287,7 +3287,7 @@ <h1><span class="secnum">1.5.10</span> SelectVariant ( <var>selectors</var>, <va
<h1><span class="secnum">A</span> Copyright &amp; Software License</h1>
<h2>Copyright Notice</h2>
<p>© 2023 Eemeli Aro</p>
<p>© 2024 Eemeli Aro</p>
<h2>Software License</h2>
<p>All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT <a href="https://ecma-international.org/memento/codeofconduct.htm">https://ecma-international.org/memento/codeofconduct.htm</a> FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.</p>
Expand Down

0 comments on commit add4851

Please sign in to comment.