Plugs into df$
Load it after defuss-shadcn and call df$.i18n. Without shadcn, defuss-query and defuss-morph provide the same runtime.
defuss-i18n is the localization module of the defuss runtime. Every translation is plain HTML in a <template> beside your markup, and a switch morphs only what a component owns and has rendered before: focus, typed input and state-related rendering like open dialogs stay exactly as they were.
<section id="welcome">
<h1>Translate the page.</h1>
<button aria-label="Close">×</button>
</section>
<section id="welcome">
<section id="welcome" data-i18n-component>
<h1>Translate the page.</h1>
<h1 data-i18n-target="title">Translate the page.</h1>
<template data-i18n-for="title" data-i18n-locale="en">Translate the page.</template>
<template data-i18n-for="title" data-i18n-locale="de">Seite übersetzen.</template>
<button aria-label="Close">×</button>
<button aria-label="Close" data-i18n-aria-label-de="Schließen">×</button>
</section>
The translations live in the markup: one <template> per language, attributes as data-i18n-*. The cursor marks the line that is live right now.
defuss-i18n is the localization module of the defuss runtime. Every translation is plain HTML in a <template> beside your markup, and a switch morphs only what a component owns and has rendered before: focus, typed input and state-related rendering like open dialogs stay exactly as they were.
defuss-i18n ist das Lokalisierungsmodul der defuss-Runtime. Jede Übersetzung ist schlichtes HTML in einem <template> neben deinem Markup, und ein Switch morpht nur, was eine Komponente managed und zuvor gerendert hat: Fokus, Eingaben und Zustand-bedingtes Rendering wie offene Dialoge bleiben genau so erhalten, wie sie zuvor waren.
Try the live demo
Live-Demo ausprobieren
Install
Installieren
Standard HTML
Standard-HTML
defuss-i18n HTML
defuss-i18n-HTML
The translations live in the markup: one <template> per language, attributes as data-i18n-*. The cursor marks the line that is live right now.
Die Übersetzungen stehen im Markup: ein <template> pro Sprache, Attribute als data-i18n-*. Der Cursor markiert die Zeile, die gerade gilt.
defuss-i18n implements the df$.i18n API with no framework dependencies, no build step and no DOM reconciler.
Load it after defuss-shadcn and call df$.i18n. Without shadcn, defuss-query and defuss-morph provide the same runtime.
Each language is a <template> next to the default markup. The page reads without JavaScript, and a translation may change structure, not only words.
Regions are morphed in place: elements, listeners, focus, text selection, typed input and open dialogs stay.
BCP 47 fallback chains such as de-AT → de → en, CLDR plural rules, and number and date formats from the browser, with no bundled locale data.
Sixteen content attributes, from alt and aria-label to href and src, through data-i18n-{attribute}-{locale}.
validateI18n returns structured diagnostics for missing languages, overlapping regions and broken ID references.
Ship one language and fetch the others with loadLocale. Only the newest request commits; a superseded one resolves as superseded.
Interpolated values are written as text, and value slots inside script, style or iframe are rejected before any write.
defuss-i18n implements the df$.i18n API with no framework dependencies, no build step and no DOM reconciler.
defuss-i18n implementiert die df$.i18n-API, ohne Framework-Abhängigkeiten, ohne Build-Step und ohne DOM-Reconciler.
Plugs into df$
Steckt in df$
Load it after defuss-shadcn and call df$.i18n. Without shadcn, defuss-query and defuss-morph provide the same runtime.
Nach defuss-shadcn laden und df$.i18n aufrufen. Ohne shadcn liefern defuss-query und defuss-morph dieselbe Runtime.
HTML is the catalog
HTML ist der Katalog
Each language is a <template> next to the default markup. The page reads without JavaScript, and a translation may change structure, not only words.
Jede Sprache ist ein <template> neben dem Standard-Markup. Die Seite ist ohne JavaScript lesbar, und eine Übersetzung darf auch die Struktur ändern, nicht nur Wörter.
State survives a switch
State überlebt den Switch
Regions are morphed in place: elements, listeners, focus, text selection, typed input and open dialogs stay.
Regionen werden in-place gemorpht: Elemente, Listener, Fokus, Textauswahl, Eingaben und offene Dialoge bleiben.
Native Intl
Natives Intl
BCP 47 fallback chains such as de-AT → de → en, CLDR plural rules, and number and date formats from the browser, with no bundled locale data.
BCP-47-Fallback-Ketten wie de-AT → de → en, CLDR-Pluralregeln sowie Zahlen- und Datumsformate aus dem Browser, ohne mitgelieferte Locale-Daten.
Attributes, too
Auch Attribute
Sixteen content attributes, from alt and aria-label to href and src, through data-i18n-{attribute}-{locale}.
Sechzehn Content-Attribute, von alt und aria-label bis href und src, über data-i18n-{attribute}-{locale}.
Checked before it writes
Geprüft, bevor es schreibt
validateI18n returns structured diagnostics for missing languages, overlapping regions and broken ID references.
validateI18n liefert strukturierte Diagnosen zu fehlenden Sprachen, überlappenden Regionen und kaputten ID-Referenzen.
Languages on demand
Sprachen bei Bedarf
Ship one language and fetch the others with loadLocale. Only the newest request commits; a superseded one resolves as superseded.
Shippe eine Sprache und lade die anderen mit loadLocale nach. Nur der neueste Request setzt sich durch; ein überholter endet als superseded.
Values stay text
Werte bleiben Text
Interpolated values are written as text, and value slots inside script, style or iframe are rejected before any write.
Interpolierte Werte werden als Text geschrieben, und Value-Slots in script, style oder iframe werden vor jedem Schreiben abgelehnt.
A language switch is synchronous: when setLocale returns, every bound component shows the new language.
Write the default language as plain HTML and mark the parts that change with data-i18n-target. Each translation goes into a <template> beside it.
bind(root, locale) captures the templates once and checks ownership: every region belongs to exactly one component.
setLocale('de') commits a frozen snapshot. Each binding picks one template per region through the fallback chain and plural rules, and fills values and attributes on detached copies.
defuss-morph patches each live region through df$. Matched elements stay, lang and dir follow, and defuss-i18n:change fires.
setLocale returns, every bound component shows the new language.
Ein Sprachwechsel ist synchron: Wenn setLocale zurückkehrt, zeigt jede gebundene Komponente die neue Sprache.
Author
Schreiben
Write the default language as plain HTML and mark the parts that change with data-i18n-target. Each translation goes into a <template> beside it.
Schreibe die Standardsprache als schlichtes HTML und markiere die veränderlichen Teile mit data-i18n-target. Jede Übersetzung kommt in ein <template> daneben.
Bind
Binden
bind(root, locale) captures the templates once and checks ownership: every region belongs to exactly one component.
bind(root, locale) erfasst die Templates einmal und prüft die Ownership: Jede Region gehört genau einer Komponente.
Select
Auswählen
setLocale('de') commits a frozen snapshot. Each binding picks one template per region through the fallback chain and plural rules, and fills values and attributes on detached copies.
setLocale('de') legt einen eingefrorenen Snapshot fest. Jedes Binding wählt pro Region ein Template über Fallback-Kette und Pluralregeln und füllt Werte und Attribute an losgelösten Kopien.
Morph
Morphen
defuss-morph patches each live region through df$. Matched elements stay, lang and dir follow, and defuss-i18n:change fires.
defuss-morph patcht jede Live-Region über df$. Gematchte Elemente bleiben, lang und dir ziehen mit, und defuss-i18n:change feuert.
Every sentence on this page comes from a defuss-i18n template. The window below holds what usually breaks: typed input in an open dialog, plural forms, markup rendered from state and translated attributes.
In the carton
1 egg
CLDR picks the plural form, Intl formats the number.
Open the dialog, type a name, then switch the language inside it.
render() builds this markup from state, so the checkbox keeps its value through every switch.
render() builds this markup from state, so the checkbox keeps its value through every switch.
render() baut dieses Markup aus dem State, darum behält die Checkbox ihren Wert bei jedem Switch.
Toggle checked state
Häkchen umschalten
The image’s src and alt switch with the language, through data-i18n-src-de and data-i18n-alt-de.
src and alt switch with the language, through data-i18n-src-de and data-i18n-alt-de.
src und alt des Bildes wechseln mit der Sprache, über data-i18n-src-de und data-i18n-alt-de.
Event log
One line per render, written by a defuss-i18n:change listener.
defuss-i18n:change listener.
Eine Zeile pro Render, geschrieben von einem defuss-i18n:change-Listener.
defuss-i18n works best with defuss-shadcn. From jsDelivr next to defuss-shadcn, from npm for a bundler, or with the two peers on their own.
Pick how the page loads its runtime, then copy the snippet.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/defuss-shadcn@0.9.8/dist/components/core.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/defuss-shadcn@0.9.8/dist/components/all.min.css"><script type="module" src="https://cdn.jsdelivr.net/npm/defuss-shadcn@0.9.8/dist/components/all.min.js"></script><script type="module" src="https://cdn.jsdelivr.net/npm/defuss-i18n@0.1.0/dist/all.min.js"></script><script type="module"> const locale = df$.i18n.createI18n({ locale: 'en', fallback: ['en'] }); for (const root of document.querySelectorAll('[data-i18n-component]')) df$.i18n.bind(root, locale); locale.setLocale('de'); // every bound component now shows German</script>
bun add defuss-i18n defuss-query@^0.1.0 defuss-morph@^0.1.1
import { createI18n, bindI18n } from 'defuss-i18n'; const locale = createI18n({ locale: 'de-DE', fallback: ['en'] });const welcome = bindI18n(document.getElementById('welcome'), locale);welcome.dispose(); // when the component leaves the page
<script type="module"> await import('https://cdn.jsdelivr.net/npm/defuss-morph@0.1.1/dist/all.min.js'); await import('https://cdn.jsdelivr.net/npm/defuss-query@0.1.0/dist/all.min.js'); await import('https://cdn.jsdelivr.net/npm/defuss-i18n@0.1.0/dist/all.min.js');</script>
Paste this prompt into your coding agent. It links the repository, so the agent works from the current README and the authoring contract in documentation/component-skill.md.
Integrate defuss-i18n into this app: https://github.com/kyr0/defuss-i18n
Read its README.md and documentation/component-skill.md first. Install it with bun add defuss-i18n defuss-query@^0.1.0 defuss-morph@^0.1.1, or load https://cdn.jsdelivr.net/npm/defuss-i18n@0.1.0/dist/all.min.js after the all.min.js of defuss-shadcn.
Mark each translated component with data-i18n-component and the parts that change with data-i18n-target. Put one <template data-i18n-for data-i18n-locale> per language next to each part, never inside it, and translate attributes with data-i18n-{attribute}-{locale}.
Create one controller with createI18n({ locale, fallback: ['en'] }), bind() every component, resolve every validateI18n diagnostic, pass dynamic values as text through data-i18n-value slots, and dispose() a binding when its component leaves the page.
⏎ send
Give your agent the defuss-vae skills too: it then plans, tests and reviews against a verifier instead of declaring itself done. In Claude Code, the plugin adds the commit and Stop-hook gate. Needs python3 ≥ 3.9, git and make.
npx skills add kyr0/defuss-vae --skill '*'
claude plugin marketplace add kyr0/defuss-vae
claude plugin install defuss-vae@defuss-vae
defuss-i18n works best with defuss-shadcn. From jsDelivr next to defuss-shadcn, from npm for a bundler, or with the two peers on their own.
defuss-i18n funktioniert am besten mit defuss-shadcn. Über jsDelivr neben defuss-shadcn, über npm für einen Bundler oder nur mit den beiden Peers.
By hand
Von Hand
Pick how the page loads its runtime, then copy the snippet.
Wähle, wie die Seite ihre Runtime lädt, und kopiere das Snippet.
With defuss-shadcn
Mit defuss-shadcn
Without defuss-shadcn
Ohne defuss-shadcn
Vibe coding
Vibe Coding
Paste this prompt into your coding agent. It links the repository, so the agent works from the current README and the authoring contract in documentation/component-skill.md.
Füge diesen Prompt in deinen Coding-Agenten ein. Er verlinkt das Repository, damit der Agent mit der aktuellen README und dem Authoring-Contract in documentation/component-skill.md arbeitet.
Integrate defuss-i18n into this app: https://github.com/kyr0/defuss-i18n
Integriere defuss-i18n in diese App: https://github.com/kyr0/defuss-i18n
Read its README.md and documentation/component-skill.md first. Install it with bun add defuss-i18n defuss-query@^0.1.0 defuss-morph@^0.1.1, or load https://cdn.jsdelivr.net/npm/defuss-i18n@0.1.0/dist/all.min.js after the all.min.js of defuss-shadcn.
Lies zuerst README.md und documentation/component-skill.md. Installiere es mit bun add defuss-i18n defuss-query@^0.1.0 defuss-morph@^0.1.1 oder lade https://cdn.jsdelivr.net/npm/defuss-i18n@0.1.0/dist/all.min.js nach dem all.min.js von defuss-shadcn.
Mark each translated component with data-i18n-component and the parts that change with data-i18n-target. Put one <template data-i18n-for data-i18n-locale> per language next to each part, never inside it, and translate attributes with data-i18n-{attribute}-{locale}.
Markiere jede übersetzte Komponente mit data-i18n-component und die veränderlichen Teile mit data-i18n-target. Lege pro Sprache ein <template data-i18n-for data-i18n-locale> neben jeden Teil, nie hinein, und übersetze Attribute mit data-i18n-{attribute}-{locale}.
Create one controller with createI18n({ locale, fallback: ['en'] }), bind() every component, resolve every validateI18n diagnostic, pass dynamic values as text through data-i18n-value slots, and dispose() a binding when its component leaves the page.
Erzeuge einen Controller mit createI18n({ locale, fallback: ['en'] }), binde jede Komponente mit bind(), behebe jede validateI18n-Diagnose, übergib dynamische Werte als Text über data-i18n-value-Slots und rufe dispose() auf, wenn eine Komponente die Seite verlässt.
⏎ send
⏎ senden
Copy prompt
Prompt kopieren
Give your agent the defuss-vae skills too: it then plans, tests and reviews against a verifier instead of declaring itself done. In Claude Code, the plugin adds the commit and Stop-hook gate. Needs python3 ≥ 3.9, git and make.
Gib deinem Agenten auch die Skills von defuss-vae: Dann plant, testet und reviewt er gegen einen Verifier, statt sich selbst für fertig zu erklären. In Claude Code ergänzt das Plugin das Commit- und Stop-Hook-Gate. Benötigt python3 ≥ 3.9, git und make.
Any agent
Jeder Agent
Claude Code plugin
Claude-Code-Plugin

Build more consistent agents with defuss-vae, Aron Homberg’s Verified Agentic Engineering method. Aron is a freelance AI researcher, O’Reilly author (2011), conference speaker, and longtime mentor to software engineering teams.
…if you’re looking for a remote training or mentoring session for you or your engineering team.
Guides to state and ownership, lazy loading and the security model, plus the full API and error reference.
MIT licensed · Source on GitHub