Framework
Svelte
El paquete oficial de Svelte 5 para Verbaly. Stores idiomáticos sobre el mismo core reactivo, factories de stores para uso a nivel app, y un componente <Trans> para rich text.
Qué puedes lograr:
- Traduce con stores:
$tre-renderiza en cada cambio de idioma, sin cableado - Enlaza un switcher de idioma en una línea:
bind:valuesobre el store del locale simplemente funciona - Usa contexto o factories de stores, con o sin árbol de componentes
- Mapea un tag de un mensaje a tu propio componente: <Trans> gana el prop components en 0.23.0
Instalación
Instala el paquete con tu gestor de paquetes preferido.
pnpm add @verbaly/svelteSetup y stores
<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>- Stores idiomáticos:
$tre-renderiza en cada cambio de idioma;bind:value={$locale}en un select funciona directo. - ¿Sin árbol de componentes?
tStore(instance)/localeStore(instance)construyen los mismos stores sin context, como singletons a nivel app. - Rich text: el intérprete del DOM del core funciona en cualquier app Svelte vía
data-verbaly-rich+bindDom(ver HTML plano → Rich text).
Escribe el texto en su lugar
Desde 0.20.0 el compilador también lee tus archivos .svelte: escribe el texto fuente con el tag del store, en el markup o en el script, y se extrae, recibe su clave y sus tipos al guardar. Sin inventar claves, sin tocar catálogos.
<h1>{$t`Hola ${name}, tienes ${count} mensajes`}</h1>
<!-- extracted on save; the build rewrites it to: -->
<h1>{$t('x7Ka9q2f', { name, count })}</h1>¿Prefieres claves legibles? $t.id('inbox.title') antes del template extrae bajo tu id en vez de un hash del contenido.
Rich text con <Trans>
Desde 0.10.0 Svelte también tiene componente real: impórtalo del subpath Trans.svelte (componente crudo, lo compila tu bundler):
<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 }} />- Los tags del mensaje (
The <em>build</em> gate) se renderizan como elementos reales contra la misma whitelist phrasing dedata-verbaly-rich. Los tags desconocidos se desenvuelven a texto inerte,richTagsla sobreescribe. - Usa la instancia de
provideVerbaly; pasainstance={verbaly}explícito cuando no hay contexto. Se re-renderiza en cada cambio de locale. - Links nombrados (
0.11.0): el proplinksrenderiza tags nombrados como elementos<a>reales, con hrefs desde tu código, nunca desde los mensajes;javascript:bloqueado. - Tus propios componentes (
0.23.0): el propcomponentsmapea un tag a un componente Svelte que recibe el contenido del tag como children, y gana sobre links y whitelist. Requiere Svelte 5; en Svelte 4 quédate en0.22.0.
SvelteKit
Las páginas renderizadas en el servidor llegan ya traducidas al idioma del visitante, y el cliente hidrata con el mismo locale y el mismo catálogo, sin flash de texto sin traducir ni mismatch de hidratación.
- Cada página se renderiza en el idioma del visitante, elegido en cada request desde su cookie o su header
Accept-Language - Sin flash de texto sin traducir: el HTML que envía el servidor y la página ya hidratada son idénticos, catálogo incluido
- Cada request tiene su propia instancia, así que dos visitantes simultáneos nunca ven el idioma del otro
- Un switcher de idioma en una llamada:
switchLocalecarga el catálogo y persiste la elección para el próximo render del servidor
SvelteKit necesita el paquete de SSR (@verbaly/sveltekit) junto a los bindings de Svelte:
pnpm add verbaly @verbaly/sveltekit @verbaly/svelte @verbaly/viteConéctalo
Tres cables en el servidor, uno en el cliente. Primero el plugin de Vite y el placeholder de lang:
// 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%">El hook del servidor resuelve el locale por request (cookie, luego Accept-Language, luego tu fallback) y rellena el placeholder:
// 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 }
}Pásale el locale al cliente y construye una instancia por render con el catálogo esperado, la garantía sin flash:
// +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()}De aquí en adelante es @verbaly/svelte normal: los stores useT() / useLocale() y Trans.svelte funcionan sin cambios en cada componente.
Cambiar de idioma
switchLocale carga el catálogo primero, cambia en vivo y escribe la cookie que el hook del servidor lee, así el próximo request SSR ya coincide:
<script>
import { switchLocale } from '@verbaly/sveltekit';
import { useVerbaly } from '@verbaly/svelte';
const verbaly = useVerbaly();
</script>
<button onclick={() => switchLocale(verbaly, 'es')}>Español</button>- La cookie por defecto es
verbaly-locale(la misma identidad que la key de storage del core);cookie: falseen cualquiera de los dos lados la desactiva, paraAccept-Languagepuro. fallbackpor defecto es tu primer locale; la negociación entiende q-values y estrecha regiones (es-PEmatcheaes).- Sin dependencia de SvelteKit en sí: el hook está tipado estructuralmente y se mantiene compatible a lo largo de Kit 2.x.