docs(chord): i18n proposal, messages as object state

This commit is contained in:
Mario Zechner 2026-11-02 17:03:46 +00:00
parent 7dffeaf521
commit 48f9f1b423
2 changed files with 25 additions and 8 deletions

View file

@ -145,13 +145,22 @@ both look at the same object state.
So objects carry messages as data, and each client renders them:
```ts
// plugins/notes/worker.ts: state and notices carry a key and its parameters
self.notify({ message: { namespace: "notes", key: "saved", params: [note.title, new Date(note.at)] } });
// plugins/notes/contract.ts: a member of the Notes object's state holds a message as data
lastSaved: opt(obj({ namespace: str(), key: str(), params: arr(unknown()) })),
// plugins/notes/web.tsx: rendered in the viewer's language
const text = t.render(notice.message); // typed against the notes catalog
// plugins/notes/worker.ts: the object writes the key and its parameters, never a sentence
await self.tx(async (tx) => {
(await tx.draft(status)).lastSaved = { namespace: "notes", key: "saved", params: [note.title, note.at] };
}, ctx);
// plugins/notes/web.tsx: each viewer renders it in its own language
const notes = useHold(Notes, { session });
const text = notes.lastSaved && t.render(notes.lastSaved); // typed against the notes catalog
```
The same holds for anything else that reaches people: an app's notices object stores `{ namespace, key, params }`
per notice, a tool card's summary carries one, an error shown to a user carries a code and parameters.
Parameters are plain data (strings, numbers, ISO dates), so they cross links like any state. What is never
translated: the model's prompts and the model's own answers (the conversation's language is the user's business with
the model), log lines (for developers, in English), error codes. Error *messages* shown to people follow the same rule

View file

@ -239,11 +239,19 @@ with themes.</p>
summary, a status line, an error. If the worker formatted &quot;Notiz gespeichert&quot; for Lena, Tom would read German too:
both look at the same object state.</p>
<p>So objects carry messages as data, and each client renders them:</p>
<pre><code><span class="tk-c">// plugins/notes/worker.ts: state and notices carry a key and its parameters</span>
self.<span class="tk-f">notify</span>({ message: { namespace: <span class="tk-s">"notes"</span>, key: <span class="tk-s">"saved"</span>, params: [note.title, <span class="tk-k">new</span> <span class="tk-t">Date</span>(note.at)] } });
<pre><code><span class="tk-c">// plugins/notes/contract.ts: a member of the Notes object's state holds a message as data</span>
lastSaved: <span class="tk-f">opt</span>(<span class="tk-f">obj</span>({ namespace: <span class="tk-f">str</span>(), key: <span class="tk-f">str</span>(), params: <span class="tk-f">arr</span>(<span class="tk-k">unknown</span>()) })),
<span class="tk-c">// plugins/notes/web.tsx: rendered in the viewer's language</span>
<span class="tk-k">const</span> text = t.<span class="tk-f">render</span>(notice.message); <span class="tk-c">// typed against the notes catalog</span></code></pre>
<span class="tk-c">// plugins/notes/worker.ts: the object writes the key and its parameters, never a sentence</span>
<span class="tk-k">await</span> self.<span class="tk-f">tx</span>(<span class="tk-k">async</span> (tx) =&gt; {
(<span class="tk-k">await</span> tx.<span class="tk-f">draft</span>(status)).lastSaved = { namespace: <span class="tk-s">"notes"</span>, key: <span class="tk-s">"saved"</span>, params: [note.title, note.at] };
}, ctx);
<span class="tk-c">// plugins/notes/web.tsx: each viewer renders it in its own language</span>
<span class="tk-k">const</span> notes = <span class="tk-f">useHold</span>(<span class="tk-t">Notes</span>, { session });
<span class="tk-k">const</span> text = notes.lastSaved &amp;&amp; t.<span class="tk-f">render</span>(notes.lastSaved); <span class="tk-c">// typed against the notes catalog</span></code></pre>
<p>The same holds for anything else that reaches people: an app&#39;s notices object stores <code>{ namespace, key, params }</code>
per notice, a tool card&#39;s summary carries one, an error shown to a user carries a code and parameters.</p>
<p>Parameters are plain data (strings, numbers, ISO dates), so they cross links like any state. What is never
translated: the model&#39;s prompts and the model&#39;s own answers (the conversation&#39;s language is the user&#39;s business with
the model), log lines (for developers, in English), error codes. Error <em>messages</em> shown to people follow the same rule