Framework

Plain HTML

No framework? Verbaly treats plain HTML as a first-class citizen, something no other compiler-based i18n tool does. Mark elements, call bindDom, done.

Usage

index.html
<h1 data-verbaly="home.title"></h1>
<p  data-verbaly="inbox" data-verbaly-args='{"count":3}'></p>
<input data-verbaly-attr='{"placeholder":"search.hint"}' />
main.ts
import { bindDom, createVerbaly } from 'verbaly';

const v = createVerbaly({ locale: 'es', messages });
const unbind = bindDom(v);   // render + observe

v.setLocale('en');            // every bound node re-renders

Attributes

AttributePurpose
data-verbalyKey whose translation becomes the element's text
data-verbaly-argsJSON params shared by text and attribute translations
data-verbaly-attrJSON map of attribute → key, e.g. placeholder, title, aria-label. Since 0.17.0: URL attributes (href, src…) are sanitized against unsafe schemes; on*, style and srcdoc are blocked, so a catalog can't inject scripts
data-verbaly-richOpt-in: render tags in the message as real elements (see Rich text)
data-verbaly-linksJSON map of link name → href for named links (see Named links)

Rich text

By default translations render via textContent, so tags stay literal. Mark the element with data-verbaly-rich and tagged messages become real elements:

locales/en.json
{ "gate.title": "The build <em>gate</em>" }
index.html
<h3 data-verbaly="gate.title" data-verbaly-rich></h3>
<!-- renders: The build <em>gate</em> -->

One key per sentence, so translators see the whole message and each locale keeps its own word order. The safety model:

Links are deliberately excluded from the whitelist: an <a> without attributes is useless, and attributes never come from messages. Since 0.11.0 you name the link in the message and provide the href from code:

locales/en.json
{ "cta": "Read the <docs>guide</docs> now" }
main.ts
bindDom(v, {
  richLinks: { docs: { href: '/docs', target: '_blank', rel: 'noopener' } },
});
// renders: Read the <a href="/docs" target="_blank" rel="noopener">guide</a> now
index.html: per-element override
<p data-verbaly="cta" data-verbaly-rich data-verbaly-links='{"docs":"/es/docs"}'></p>

Locale bootstrap

Two helpers cover the boilerplate every app repeats, picking the initial locale and remembering the user's choice:

main.ts
import { bindDom, createVerbaly, persistLocale, resolveLocale } from 'verbaly';

const v = createVerbaly({
  locale: resolveLocale({ supported: ['en', 'es', 'pt'] }),  // storage → navigator → fallback
  messages: { en },  // bundle only the source
  loaders: { es: () => import('./locales/es.json'), pt: () => import('./locales/pt.json') },
});
bindDom(v);

// on user switch:
await v.loadLocale('es');  // flash-free (setLocale alone also auto-loads)
v.setLocale('es');
persistLocale('es');  // localStorage + <html lang>

Behavior

Copied to clipboard