Angular integration for **MessageFormat 2 (MF2)** with a pipe, a tiny store, and DI-based configuration. It renders MF2 strings to **sanitized HTML** and supports a small, explicit subset of inline and block markup.
npm install angular-mf2Angular integration for MessageFormat 2 (MF2) with a pipe, a tiny store, and DI-based configuration. It renders MF2 strings to sanitized HTML and supports a small, explicit subset of inline and block markup.
You bring the catalogs and the arguments; the library handles locale selection, MF2 parsing, and safe HTML.
---
* ⚙️ DI-first config via MF2_CONFIG and a useMF2Config(...) helper
* 🧠 Store service for locale management and formatting (impure pipe reacts to it)
* 🧵 | mf2 pipe (standalone, impure) for templates
* 🔒 HTML sanitization with a strict allowlist (customizable via DI)
* 🏷️ Minimal markup, now including:
* Inline: {#bold}, {#italic}, {#underline}, {#code}, {#kbd}, {#mark}, {#sup}, {#sub}
* Block: {#p}, {#quote}, {#ul}, {#ol}, {#li}, {#pre}
* 🧰 Sanitizer policy token: MF2_SANITIZE_OPTIONS lets apps extend/tighten the sanitize-html policy
``bash`
npm i angular-mf2 messageformat sanitize-html
---
`ts
// app.config.ts (Angular standalone bootstrap)
import { ApplicationConfig, provideHttpClient } from '@angular/core';
import { useMF2Config } from 'angular-mf2';
const catalogs = {
en: {
greeting: 'Hello {$name}, how are you?',
rich: 'This is {#bold}bold{/bold}, {#italic}italic{/italic}, {#underline}underlined{/underline}, inline {#code}code(){/code}.',
block: '{#p}Paragraph one.{/p}{#p}Paragraph two with a {#mark}highlight{/mark}.{/p}',
quote: '{#quote}“Simplicity is the soul of efficiency.” — Austin Freeman{/quote}',
list: '{#p}List:{/p}{#ul}{#li}First{/li}{#li}Second{/li}{#li}Third{/li}{/ul}',
ordlist: '{#p}Steps:{/p}{#ol}{#li}Plan{/li}{#li}Do{/li}{#li}Review{/li}{/ol}',
supSub: 'H{#sub}2{/sub}O and 2{#sup}10{/sup}=1024',
codeBlock: '{#pre}npm i angular-mf2{/pre}',
},
no: {
greeting: 'Hei {$name}, hvordan går det?',
rich: 'Dette er {#bold}fet{/bold}, {#italic}kursiv{/italic}, {#underline}understreket{/underline}, inline {#code}kode(){/code}.',
block: '{#p}Avsnitt én.{/p}{#p}Avsnitt to med en {#mark}utheving{/mark}.{/p}',
quote: '{#quote}«Enkelhet er effektivitetens sjel.» — Austin Freeman{/quote}',
list: '{#p}Liste:{/p}{#ul}{#li}Første{/li}{#li}Andre{/li}{#li}Tredje{/li}{/ul}',
ordlist: '{#p}Steg:{/p}{#ol}{#li}Plan{/li}{#li}Gjør{/li}{#li}Evaluer{/li}{/ol}',
supSub: 'H{#sub}2{/sub}O og 2{#sup}10{/sup}=1024',
codeBlock: '{#pre}npm i angular-mf2{/pre}',
}
} as const;
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
...useMF2Config({
defaultLocale: 'en',
catalogs,
}),
],
};
`
` html`
---
Holds the runtime configuration:
`ts`
export type MF2Config = {
defaultLocale: string;
catalogs: Record
};
Helper that returns providers to register both the config and the store:
`ts`
providers: [
...useMF2Config({ defaultLocale: 'en', catalogs }),
]
* Signature: {{ key | mf2 : args? }}
* Standalone: can be imported directly into components
* Impure: re-evaluates when the Store changes (e.g., locale swap)
* Returns: a sanitized HTML string
A small service used by the pipe (and available for direct use):
`ts`
@Injectable()
class Store {
setLocale(locale: string): void;
getLocale(): string;
format(key: string, args?: Record
}
Example (programmatic use):
`ts`
@Component({/ ... /})
export class LocaleSwitcher {
constructor(private store: Store) {}
set(lang: string) { this.store.setLocale(lang); }
}
---
This library uses sanitize-html with a conservative default policy. You can adjust that policy via DI:
`ts
// app.config.ts (adding attributes/styles for links, etc.)
import { MF2_SANITIZE_OPTIONS } from 'angular-mf2';
providers: [
...useMF2Config({ defaultLocale: 'en', catalogs }),
{
provide: MF2_SANITIZE_OPTIONS,
useValue: {
allowedAttributes: {
a: ['href', 'target', 'rel', 'title', 'role', 'tabindex'],
},
allowedStyles: { '': { color: [/^.$/], 'background-color': [/^.*$/] } },
},
},
];
`
Default allowlist (subset): strong, em, u, s, code, pre, kbd, mark, sup, sub, a, span, br, p, ul, ol, li, blockquote with a minimal set of attributes. This keeps messages safe while still expressive.
---
| MF2 Markup | Renders as |
| --------------------------- | ---------------------------- |
| {#bold}x{/bold} | x |{#italic}x{/italic}
| | x |{#underline}x{/underline}
| | x |{#s}x{/s}
| | x |{#code}x{/code}
| | x |{#kbd}⌘K{/kbd}
| | ⌘K |{#mark}x{/mark}
| | x |{#sup}x{/sup}
| | x |{#sub}x{/sub}
| | x |{#p}x{/p}
| |
x
|
| {#quote}x{/quote} | x
|
| {#ul}{#li}x{/li}{/ul} | |
| {#ol}{#li}x{/li}{/ol} | |
| {#pre}x{/pre} | x` |
> All output is sanitized. Only the above tags (and a minimal set of attributes) are allowed by default.
MIT