# `Localize.HTML.Message`
[🔗](https://github.com/elixir-localize/localize_web/blob/main/lib/localize/html/message.ex#L2)

Renders an ICU MessageFormat 2 message into HEEx, including any
MF2 markup tags.

MF2 supports inline markup of the form `{#name attr=value}…{/name}`
(paired) and `{#name/}` (standalone). `Localize.Message.format/3`
strips these tags. This component preserves them by walking the
structured output of `Localize.Message.format_to_safe_list/3` and
dispatching each markup node to a registered renderer.

## Example

    <.message msgid={~t"Read our {#link href=|/terms|}terms{/link}"} />

The msgid attribute typically comes from `~t` (which performs the
Gettext lookup and binding interpolation), but any MF2 string is
accepted.

## Component registry

Markup names map to renderer functions. Three sources are consulted
in order:

1. The `:components` attribute on the component, if provided.

2. The `:components` key under `config :localize_web, :mf2_markup`.

3. The built-in defaults: `bold`/`strong` → `<strong>`,
   `italic`/`emphasis`/`em` → `<em>`, `code` → `<code>`,
   `link` → `<.link>` (Phoenix component — accepts `href`,
   `navigate`, or `patch` MF2 attributes), `br` → `<br>`.

Each renderer is a function of one argument
`%{attrs: map, children: safe_iodata}` returning either
`Phoenix.LiveView.Rendered.t()` (the recommended form, via `~H`)
or a `Phoenix.HTML.safe()` value (`{:safe, iodata}`). The `children`
value is already rendered and HTML-escaped — wrap or ignore it,
but do not pass it through `Phoenix.HTML.html_escape/1` again.

Unknown markup names raise `Localize.HTML.Message.UnknownMarkupError`
at render time.

## Bindings and locale

The `:bindings` attribute is forwarded to MF2 formatting. The
`:locale` attribute overrides `Localize.get_locale/0` for this
render only.

# `default_components`

```elixir
@spec default_components() :: %{
  required(String.t()) =&gt; (map() -&gt; Phoenix.LiveView.Rendered.t())
}
```

Returns the built-in markup component map.

Exposed so apps can selectively reuse defaults when building a
custom registry, e.g.:

    config :localize_web, :mf2_markup,
      components: Map.merge(
        Localize.HTML.Message.default_components(),
        %{"user" => &MyAppWeb.MF2.user/1}
      )

# `message`

Renders an MF2 message preserving its inline markup structure.

### Attributes

* `:msgid` — the MF2 message string. Required.

* `:bindings` — a map of variable bindings for MF2 placeholders.
  The default is `%{}`.

* `:locale` — a locale name or `t:Localize.LanguageTag.t/0`.
  The default is `Localize.get_locale/0`.

* `:components` — a map of `%{markup_name => renderer_fun}` that
  overrides defaults and app config for this render only.
  The default is `%{}`.

### Returns

* A HEEx-safe rendering of the message with markup nodes expanded
  and text nodes HTML-escaped.

### Examples

    <.message msgid="Hello {$name}!" bindings={%{"name" => "Kip"}} />
    <.message msgid={~t"Click {#link href=|/home|}here{/link}"} />

## Attributes

* `msgid` (`:string`) (required)
* `bindings` (`:map`) - Defaults to `%{}`.
* `locale` (`:any`) - Defaults to `nil`.
* `components` (`:map`) - Defaults to `%{}`.

# `render_to_safe`

```elixir
@spec render_to_safe(String.t(), map() | keyword(), keyword()) :: Phoenix.HTML.safe()
```

Renders an MF2 message to a `Phoenix.HTML.safe()` value without
going through HEEx. Useful for unit-testing markup output and for
composing rendered messages into Phoenix.HTML pipelines.

Accepts the same options as the component, passed as a keyword list.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
