This library provides FormatJS internationalization bindings for Svelte 5.
Installation#
npm i -S @formatjs/svelte-intl
Usage#
Create an intl instance and provide it to the component tree using Svelte's Context API.
Provider setup#
In your root component (e.g. App.svelte):
<script>
import {createIntl, provideIntl} from '@formatjs/svelte-intl'
const intl = createIntl({
locale: 'en',
defaultLocale: 'en',
messages: {
greeting: 'Hello, {name}!',
},
})
provideIntl(intl)
</script>
<slot />
Consuming intl#
In any descendant component, use useIntl() to access the intl object:
<script>
import {useIntl} from '@formatjs/svelte-intl'
const intl = useIntl()
</script>
<p>
{intl.formatMessage({id: 'greeting'}, {name: 'World'})}
</p>
<p>
{intl.formatNumber(1000, {style: 'currency', currency: 'USD'})}
</p>
<p>
{intl.formatDate(new Date(), {dateStyle: 'full'})}
</p>
See @formatjs/intl for the full list of IntlShape methods.
Standalone usage (without context)#
You can also use createIntl directly without Svelte context, useful for utility files or server-side code:
import {createIntl, createIntlCache} from '@formatjs/svelte-intl'
const cache = createIntlCache()
const intl = createIntl(
{
locale: 'en',
defaultLocale: 'en',
messages: {greeting: 'Hello, {name}!'},
},
cache
)
intl.formatMessage({id: 'greeting'}, {name: 'World'})
Rich Text / HTML Formatting#
Unlike react-intl which uses React elements for rich text, Svelte does not have a VNode-like type. There are two approaches:
Using {@html} (simple)#
Use {@html} to render HTML returned by formatMessage:
<script>
import {useIntl} from '@formatjs/svelte-intl'
const intl = useIntl()
// Message: "Welcome, <b>{name}</b>!"
</script>
<p>
{@html intl.formatMessage(
{id: 'welcome', defaultMessage: 'Welcome, <b>{name}</b>!'},
{name: userName}
)}
</p>
Caution
{@html} renders unescaped HTML. Make sure interpolated values are trusted or
sanitized to avoid XSS vulnerabilities.
Using rich text callbacks (safe)#
For XSS-safe rich text, use formatMessage's tag callbacks to split the message into parts, then render each part with Svelte markup:
<script>
import {useIntl} from '@formatjs/svelte-intl'
const intl = useIntl()
// Message: "Read the <link>docs</link> for more info."
const parts = intl.formatMessage(
{
id: 'info',
defaultMessage: 'Read the <link>docs</link> for more info.',
},
{
link: (chunks) => `<a href="/docs">${chunks}</a>`,
}
)
</script>
<p>{@html parts}</p>
Since T = string in @formatjs/svelte-intl, rich text tag callbacks receive and return strings. You still need {@html} to render the result, but the message structure is preserved for translators.
Message Declaration#
Use defineMessages and defineMessage for static analysis and extraction:
<script>
import {defineMessages, useIntl} from '@formatjs/svelte-intl'
const messages = defineMessages({
greeting: {
id: 'app.greeting',
defaultMessage: 'Hello, {name}!',
description: 'Greeting shown on homepage',
},
farewell: {
id: 'app.farewell',
defaultMessage: 'Goodbye!',
},
})
const intl = useIntl()
</script>
<h1>{intl.formatMessage(messages.greeting, {name: 'World'})}</h1>
<p>{intl.formatMessage(messages.farewell)}</p>
API Reference#
| Export | Description |
|---|---|
provideIntl | Set intl context via Svelte's setContext. Call in a parent component. |
useIntl | Get intl context via Svelte's getContext. Call in a descendant component. |
intlKey | The Symbol used as the context key. |
createIntl | Create an IntlShape instance. Re-exported from @formatjs/intl. |
createIntlCache | Create a cache for createIntl. Re-exported from @formatjs/intl. |
defineMessages | Identity function for message extraction tooling. |
defineMessage | Identity function for single message extraction. |
All types and error classes from @formatjs/intl are also re-exported.
Tooling#
formatjs toolchain supports .svelte files:
- eslint-plugin-formatjs: Works with
.sveltefiles viaeslint-plugin-svelte. - @formatjs/cli: Supports extracting messages from
.svelteSFC files (both<script>blocks and template expressions). - babel-plugin-formatjs: Compile messages during bundling for
babel. - @formatjs/ts-transformer: Compile messages during bundling for
TypeScript.
Extracting messages#
formatjs extract "src/**/*.{ts,svelte}" --out-file lang.json
The CLI extracts messages from:
<script>and<script module>blocks- Template expressions:
{intl.formatMessage(...)}, attribute bindings,{#if},{#each},{@const}, etc.
Typed messages and migration#
Version 2 requires TypeScript 5.4 or newer. Message helpers now share the
@formatjs/intl contracts. Returned descriptors and catalog entries are readonly;
construct a new descriptor instead of mutating one.
import {defineMessage} from '@formatjs/svelte-intl'
const message = defineMessage<{readonly count: number}>(
{id: 'items', defaultMessage: '{count, number} items'},
{typed: true}
)
// intl.formatMessage(message, {count: 3})
defineMessages<Contracts>(catalog, {typed: true}) attaches a separate values
contract to each catalog entry. Helpers return the original objects at runtime.
The generic declares the contract; it does not parse ICU literals.
For formatMessage and $t, the first generic is the values contract and the
second is the rich output type. Move an existing output generic to the second
position. Without generics, typed descriptors retain their checks; ordinary
unregistered descriptors remain permissive. Vue uses its configured VNode output;
Svelte uses strings.