Release v0.10.0
[0.10.0] — 2026-07-07
Static sites ship translated + Verbaly beyond Vite. verbaly render pre-fills built HTML per locale (the SSG FOUC fix, flagship), new @verbaly/unplugin package brings the compiler to webpack/Rollup/esbuild/Rspack, <Trans> lands in Svelte, and verbaly pseudo adds i18n QA. Ready to publish. No breaking changes; core runtime untouched in behavior (~3KB intact).
Added
- SSG per-locale output —
verbaly render(@verbaly/compilerCLI): walks the built site (--site <path>, defaultdist) and pre-fills everydata-verbalyelement per locale with the real runtime (createVerbalyper locale — plurals,Intlformatting,data-verbaly-args, attribute translation viadata-verbaly-attrwith theon*block,data-verbaly-richwith the same phrasing whitelist). Source locale is filled in place; every other locale is mirrored todist/<locale>/…with<html lang>set. Static HTML ships already translated — no flash of untranslated content — and the runtime attributes stay, so client-side switching keeps working. Exported API:renderHtml(html, opts)/renderSite(cfg, opts)(+ option/result types). Safety: message text is HTML-escaped (no injection),""entries fall back to source, missing keys are reported and left untouched, comments/<script>/<style>bodies are opaque to the scanner, nested same-name elements handled. Idempotent — re-runs exclude locale subdirs. Zero new deps (magic-string + tinyglobby already there). - New package
@verbaly/unplugin: the compiler wrapped with unplugin — samevirtual:verbalymodule, tagged-template transform and missing-translation build gate (failOnMissing: falseopts out) on webpack 5, Rollup, esbuild and Rspack. Build-focused: runverbaly extractin the dev loop/CI; live extraction + HMR remain@verbaly/vite's value. ESM-only (like compiler/vite — usewebpack.config.mjs). Deps:unplugin ^3.3.0,@verbaly/compiler; peerverbaly. <Trans>for Svelte (@verbaly/svelte): raw.sveltecomponents shipped indistunder the subpath@verbaly/svelte/Trans.svelte(svelteexport condition + hand-written.d.ts— no svelte-package), legacy syntax compatible with Svelte 4 and 5. Renders message tags as real elements via<svelte:element>against the same whitelist asdata-verbaly-rich(unknown tags unwrap to inert text;richTagsprop overrides;instanceprop orprovideVerbalycontext;valuesfor params; re-renders on locale change, unsubscribes on unmount). Nocomponentsmap in v1 — passing components across Svelte 4/5 is fragile (roadmap note).- Pseudo-localization —
verbaly pseudo(@verbaly/compilerCLI): regenerates a QA catalog (defaulten-XA,--locale <id>to change) from the source locale: accented letters,⟦…⟧markers, ~33%~padding — exposes hardcoded strings, clipped layouts and concatenation bugs. Params, variant blocks, tags and escape sequences survive verbatim, guaranteed by the samestructureMatchesvalidation astranslate. Exported:pseudoLocalize,pseudoCatalogs,PSEUDO_LOCALE. - Coverage (repo): root
pnpm coverage(vitest projects +@vitest/coverage-v8, lcov). CI now runs coverage instead of plain tests and uploads to Codecov; coverage badge in the README. (~90% lines at cut.)
Changed
- Packaging: node16-clean dual packages (
verbaly,@verbaly/react,@verbaly/vue):exportssplit intoimport/requireconditions, each with its owntypes(.d.ctsfor CJS — tsup already emitted it). Fixes publint's "types interpreted as ESM under require" warning and arethetypeswrong's "Masquerading as ESM" (node16-from-CJS now 🟢). No runtime change. verbaly(core):RICH_TAGS(the phrasing whitelist behinddata-verbaly-rich) is now exported — single source of truth reused by the compiler's static renderer. Additive.- README (repo): "How it compares" table (vs i18next/Lingui/Paraglide/typesafe-i18n), CI + coverage badges, unplugin row, SSG/QA bullets. Core
package.jsonkeywords expanded (npm SEO).
Notes
- 238 tests (core 88 · compiler 99 · svelte 18 · vue 10 · react 8 · unplugin 8 · vite 7) — was 197.
- Bench re-run (ritual): lookup 36.2×, interpolation 16.8×, plural 5.5×, currency 5.2× vs i18next 26.
- publint + arethetypeswrong green on core/react/vue (dual) and compiler/unplugin (ESM-only).
@verbaly/svelte/Trans.svelteshows attw node10 "resolution failed" — expected:.sveltefiles resolve via bundler (🟢), which is the only way Svelte components are consumed. - New devDeps:
@vitest/coverage-v8(root),@sveltejs/vite-plugin-svelte+happy-dom(svelte tests). Svelte tests needresolve.conditions: ['browser'](else Svelte 5 resolves its server build under vitest). - Codecov: the badge/upload go live once Aron authorizes the repo on codecov.io (GitHub login; tokenless upload works for public repos).
- Seven aligned packages now —
@verbaly/unpluginjoins at 0.10.0.