Framework

Svelte

The official Svelte 5 package for Verbaly. Idiomatic stores over the same reactive core, store factories for app-level use, and a <Trans> component for rich text.

What you can accomplish:

Installation

Install the package with your preferred package manager.

 pnpm add @verbaly/svelte

Setup & stores

+layout.svelte · Inbox.svelte
<script>
  // root component / layout:
  import { provideVerbaly } from '@verbaly/svelte';
  import { verbaly } from 'virtual:verbaly';
  provideVerbaly(verbaly);
</script>

<script>
  // any child component:
  import { useT, useLocale } from '@verbaly/svelte';
  const t = useT();
  const locale = useLocale();  // writable store
</script>

<p>{$t('inbox', { count: 3 })}</p>
<select bind:value={$locale}>…</select>

Write the text in place

Since 0.20.0 the compiler reads your .svelte files too: write the source text with the store tag, in markup or script, and it is extracted, keyed and typed on save. No key to invent, no catalog to touch.

Inbox.svelte
<h1>{$t`Hola ${name}, tienes ${count} mensajes`}</h1>

<!-- extracted on save; the build rewrites it to: -->
<h1>{$t('x7Ka9q2f', { name, count })}</h1>

Prefer readable keys? $t.id('inbox.title') before the template extracts under your id instead of a content hash.

Rich text with <Trans>

Since 0.10.0 Svelte has a real component too: import it from the Trans.svelte subpath (raw component, compiled by your bundler):

Title.svelte
<script>
  import Trans from '@verbaly/svelte/Trans.svelte';
  import DocsLink from './DocsLink.svelte';
</script>

<Trans id='home.title' />
<Trans id='greet' values={{ name: 'Aron' }} />

<!-- message: 'Read the <docs>guide</docs>' → your component wraps the tag content -->
<Trans id='cta' components={{ docs: DocsLink }} />

SvelteKit

Server-rendered pages arrive already translated in the visitor's language, and the client hydrates with the same locale and catalog: no flash of untranslated text, no hydration mismatch.

SvelteKit needs the SSR package (@verbaly/sveltekit) alongside the Svelte bindings:

 pnpm add verbaly @verbaly/sveltekit @verbaly/svelte @verbaly/vite

Wire it up

Three wires on the server, one on the client. First the Vite plugin and the lang placeholder:

vite.config.ts · src/app.html
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import verbaly from '@verbaly/vite';

export default { plugins: [verbaly(), sveltekit()] };

<!-- src/app.html -->
<html lang="%verbaly.lang%">

The server hook resolves the locale per request (cookie, then Accept-Language, then your fallback) and fills the placeholder:

src/hooks.server.ts · src/app.d.ts
// src/hooks.server.ts
import { verbalyHandle } from '@verbaly/sveltekit';
import { locales } from 'virtual:verbaly';

export const handle = verbalyHandle({ locales });

// src/app.d.ts
declare namespace App {
  interface Locals { verbalyLocale: string }
}

Hand the locale to the client, then build one instance per render with the catalog awaited, the no-flash guarantee:

src/routes/+layout.server.ts · +layout.ts · +layout.svelte
// +layout.server.ts
export const load = ({ locals }) => ({ locale: locals.verbalyLocale });

// +layout.ts
import { createRequestInstance } from 'virtual:verbaly';

export const load = async ({ data }) => ({
  ...data,
  verbaly: await createRequestInstance(data.locale),  // catalog ready BEFORE render
});

<!-- +layout.svelte -->
<script>
  import { provideVerbaly } from '@verbaly/svelte';
  let { data, children } = $props();
  provideVerbaly(data.verbaly);
</script>

{@render children()}

From here it's plain @verbaly/svelte: useT() / useLocale() stores and Trans.svelte work unchanged in every component.

Switching languages

switchLocale loads the catalog first, switches live, then writes the cookie the server hook reads, so the next SSR request already matches:

LangPicker.svelte
<script>
  import { switchLocale } from '@verbaly/sveltekit';
  import { useVerbaly } from '@verbaly/svelte';

  const verbaly = useVerbaly();
</script>

<button onclick={() => switchLocale(verbaly, 'es')}>Español</button>
Copied to clipboard