You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/web/routes/uix/components/trans/+page.svelte

459 lines
16 KiB

<script lang="ts">
import { Trans } from '$uix/eidos/components/trans';
import { compileMorfo } from '$uix/morfo';
import { transMorfo } from '@/uix/morfo/components/trans';
import { getActiveUix } from '$active-uix';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
let tab = $state<Tab>('live');
const uix = getActiveUix();
const compiled = compileMorfo(transMorfo);
const partsList = $derived([...compiled.parts.byKebab.values()]);
// ── Live state ────────────────────────────────────────────────────────
let valueProp = $state('#?components.badge.remove|Remove');
let pathProp = $state('components.badge.remove');
let asProp = $state<keyof HTMLElementTagNameMap>('span');
let trim = $state(true);
const eidosSnippet = $derived(
[
"<script lang='ts'>",
" import { Trans } from '$uix/eidos/components/trans';",
'</' + 'script>',
'',
'<!-- mode 1 — idlangref via children -->',
'<Trans>#?common.save|Save</Trans>',
'',
'<!-- mode 2 — idlangref via value prop -->',
`<Trans value="${valueProp}" />`,
'',
'<!-- mode 3 — path + fallback children -->',
`<Trans path="${pathProp}">Fallback</Trans>`,
'',
'<!-- mode 4 — path with interpolation params -->',
`<Trans path="common.greet" params={{ name: 'Juan' }} />`,
'',
'<!-- mode 5 — literal pass-through (no #? prefix) -->',
'<Trans>Just a literal string</Trans>',
'',
'<!-- tag override -->',
`<Trans as="${asProp}">#?ui.required|Required</Trans>`
].join('\n')
);
</script>
<div data-uix-canvas-inner>
<header>
<div data-uix-eyebrow>Typography · Trans</div>
<h1 data-uix-page-title>Trans</h1>
<p data-uix-page-lede>
i18n primitive that resolves an idlangref or path through the active langs catalog and
renders the translated string. Wraps the
<code>getActiveUix().langs.ts/t</code> API in a greppable component — searching
<code>&lt;Trans</code> finds every site that touches translations, much easier to audit
than inline <code>langs.ts(...)</code> calls. Reactive to locale changes: when the active
locale flips, every <code>&lt;Trans&gt;</code> on screen updates without consumer
plumbing.
</p>
<div data-uix-page-meta>
<span data-uix-meta-pill>
<span data-uix-meta-key>parts</span>{compiled.parts.order.length}
</span>
<span data-uix-meta-pill>
<span data-uix-meta-key>events</span>0
</span>
<span data-uix-meta-pill>
<span data-uix-meta-key>modes</span>5
</span>
<span data-uix-meta-pill>
<span data-uix-meta-key>current locale</span>
<code>{uix.langs.getLocale()}</code>
</span>
<span data-uix-meta-pill>
<span data-uix-meta-key>scope</span>eidos
</span>
</div>
</header>
<!-- Live preview -->
<div data-uix-stage>
<div data-uix-stage-area>
<div style="display: flex; flex-direction: column; gap: var(--uix-space-3); inline-size: 100%;">
<div data-uix-trace-row>
<code>&lt;Trans&gt;{valueProp}&lt;/Trans&gt;</code>
<span style="margin-inline-start: var(--uix-space-3); font-weight: 600;"
>→ <Trans>{valueProp}</Trans></span
>
</div>
<div data-uix-trace-row>
<code>&lt;Trans value="{valueProp}" /&gt;</code>
<span style="margin-inline-start: var(--uix-space-3); font-weight: 600;"
>→ <Trans value={valueProp} /></span
>
</div>
<div data-uix-trace-row>
<code>&lt;Trans path="{pathProp}"&gt;Fallback&lt;/Trans&gt;</code>
<span style="margin-inline-start: var(--uix-space-3); font-weight: 600;"
>→ <Trans path={pathProp}>Fallback</Trans></span
>
</div>
<div data-uix-trace-row>
<code>&lt;Trans as="{asProp}"&gt;#?ui.required|Required&lt;/Trans&gt;</code>
<span style="margin-inline-start: var(--uix-space-3); font-weight: 600;"
>→ <Trans as={asProp}>#?ui.required|Required</Trans></span
>
</div>
<div data-uix-trace-row>
<code>&lt;Trans&gt;Literal pass-through (no #?)&lt;/Trans&gt;</code>
<span style="margin-inline-start: var(--uix-space-3); font-weight: 600;"
>→ <Trans>Literal pass-through (no #?)</Trans></span
>
</div>
</div>
</div>
<div data-uix-stage-trace>
<span data-uix-stage-trace-key>locale</span>
<code>{uix.langs.getLocale()}</code>
<span style="color: var(--uix-text-faint)">·</span>
<span data-uix-stage-trace-key>cambia el locale en el header</span>
</div>
</div>
<div data-uix-tabs role="tablist">
<button data-uix-tab data-active={tab === 'live'} onclick={() => (tab = 'live')}>Live</button>
<button data-uix-tab data-active={tab === 'api'} onclick={() => (tab = 'api')}>
API <span data-uix-tab-count>6</span>
</button>
<button data-uix-tab data-active={tab === 'morfo'} onclick={() => (tab = 'morfo')}>
<span data-uix-layer-badge="morfo">morfo</span>
<span data-uix-tab-count>{partsList.length}p · 0e</span>
</button>
<button data-uix-tab data-active={tab === 'sema'} onclick={() => (tab = 'sema')}>
<span data-uix-layer-badge="sema">sema</span>
<span data-uix-tab-count>0</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button
>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
{#if tab === 'live'}
<section data-uix-section>
<h2 data-uix-section-title>Controls</h2>
<div data-uix-controls>
<label data-uix-control>
<span data-uix-control-label>value (idlangref)</span>
<input
type="text"
bind:value={valueProp}
style="inline-size: 28rem; font-family: var(--uix-font-mono); font-size: var(--uix-font-size-sm);"
/>
</label>
<label data-uix-control>
<span data-uix-control-label>path</span>
<input
type="text"
bind:value={pathProp}
style="inline-size: 22rem; font-family: var(--uix-font-mono); font-size: var(--uix-font-size-sm);"
/>
</label>
<label data-uix-control>
<span data-uix-control-label>as</span>
<span data-uix-chips role="radiogroup">
{#each ['span', 'div', 'p', 'strong', 'em', 'small', 'h2', 'h3'] as t}
<button
data-uix-chip
data-active={asProp === t}
onclick={() => (asProp = t as keyof HTMLElementTagNameMap)}>{t}</button
>
{/each}
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>trim</span>
<span data-uix-switch>
<input type="checkbox" bind:checked={trim} />
<span data-uix-switch-label>{trim ? 'on' : 'off'}</span>
</span>
</label>
</div>
<div data-uix-code style="margin-top: var(--uix-space-4);">
<div data-uix-code-head>
<span data-uix-layer-badge="eidos">eidos</span>
<span>composition · 5 resolution modes</span>
<span data-uix-code-lang>svelte</span>
</div>
<pre><code>{eidosSnippet}</code></pre>
</div>
<div data-uix-subsection-head>Patterns · common shapes</div>
<div data-uix-pattern-stack>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Resolución por modo — comparativa con idlangref existente</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead>
<tr><th>Forma</th><th>Output (current locale)</th></tr>
</thead>
<tbody>
<tr>
<td><code>&lt;Trans&gt;#?components.badge.remove|Remove&lt;/Trans&gt;</code></td>
<td><strong><Trans>#?components.badge.remove|Remove</Trans></strong></td>
</tr>
<tr>
<td><code>&lt;Trans value="#?components.image.loading|Loading image" /&gt;</code></td>
<td><strong><Trans value="#?components.image.loading|Loading image" /></strong></td>
</tr>
<tr>
<td><code>&lt;Trans path="components.image.error"&gt;Failed&lt;/Trans&gt;</code></td>
<td><strong><Trans path="components.image.error">Failed</Trans></strong></td>
</tr>
<tr>
<td><code>&lt;Trans&gt;Literal text&lt;/Trans&gt;</code></td>
<td><strong><Trans>Literal text</Trans></strong></td>
</tr>
</tbody>
</table>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Tag override — Trans dentro de cualquier elemento HTML</h3>
<div data-uix-pattern-row>
<div style="display: flex; flex-direction: column; gap: var(--uix-space-2);">
<div>span: <Trans>#?components.badge.remove|Remove</Trans></div>
<div>strong: <Trans as="strong">#?components.badge.remove|Remove</Trans></div>
<div>em: <Trans as="em">#?components.badge.remove|Remove</Trans></div>
<div>
<Trans as="h3" style="margin: 0;">#?components.image.loading|Loading image</Trans>
</div>
</div>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>
Reactividad al locale — cambia el idioma en el header de la página y los Trans se
re-renderizan automáticamente
</h3>
<div data-uix-pattern-row>
<p style="margin: 0;">
Locale activo: <code style="font-weight: 600;">{uix.langs.getLocale()}</code>
</p>
<p style="margin: 0;">
Remove: <Trans>#?components.badge.remove|Remove</Trans>
</p>
<p style="margin: 0;">
Loading image: <Trans>#?components.image.loading|Loading image</Trans>
</p>
<p style="margin: 0;">
Image failed: <Trans>#?components.image.error|Image failed to load</Trans>
</p>
</div>
</div>
</div>
</section>
{/if}
{#if tab === 'api'}
<section data-uix-section>
<h2 data-uix-section-title>API reference</h2>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Prop</th><th>Type</th><th>Default</th><th>Notes</th></tr></thead>
<tbody>
<tr>
<td class="name">value</td>
<td class="type">string</td>
<td class="default empty">—</td>
<td>
Idlangref (<code>'#?path|fallback'</code>) or literal string. Wins over
<code>path</code> and over captured children.
</td>
</tr>
<tr>
<td class="name">path</td>
<td class="type">string</td>
<td class="default empty">—</td>
<td>
Dotted path (no <code>#?</code> prefix). Uses <code>langs.t()</code> for
interpolation. Children become the fallback when the path is missing from
the catalog.
</td>
</tr>
<tr>
<td class="name">params</td>
<td class="type">LangParams</td>
<td class="default empty">—</td>
<td>Interpolation params for <code>path</code>-based translations.</td>
</tr>
<tr>
<td class="name">locale</td>
<td class="type">SupportedLocale</td>
<td class="default empty">— uix.langs.getLocale()</td>
<td>Optional locale override.</td>
</tr>
<tr>
<td class="name">as</td>
<td class="type">keyof HTMLElementTagNameMap</td>
<td class="default">'span'</td>
<td>HTML tag for the rendered element.</td>
</tr>
<tr>
<td class="name">trim</td>
<td class="type">boolean</td>
<td class="default">true</td>
<td>Trim whitespace from captured children text.</td>
</tr>
<tr>
<td class="name">children</td>
<td class="type">Snippet</td>
<td class="default empty">—</td>
<td>
Source idlangref / literal text when neither <code>value</code> nor
<code>path</code> is set; fallback text when <code>path</code> is set and
missing.
</td>
</tr>
</tbody>
</table>
</div>
<div data-uix-subsection-head>Resolution precedence</div>
<ol style="line-height: var(--uix-leading-prose);">
<li><code>value</code> prop (if set) → resolved via <code>langs.ts()</code></li>
<li>
<code>path</code> prop (if set) → resolved via <code>langs.t(path, params, locale)</code>
</li>
<li>Captured children text → resolved via <code>langs.ts()</code></li>
<li>If everything is empty, renders the fallback snippet</li>
</ol>
</section>
{/if}
{#if tab === 'morfo'}
<section data-uix-section>
<h2 data-uix-section-title>
<span data-uix-layer-badge="morfo">morfo</span> · declarative contract
</h2>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Field</th><th>Value</th></tr></thead>
<tbody>
<tr><td class="name">name</td><td>{transMorfo.name}</td></tr>
<tr><td class="name">kebab</td><td><code>{transMorfo.kebab}</code></td></tr>
<tr><td class="name">scope</td><td>{transMorfo.scope.join(', ')}</td></tr>
<tr><td class="name">parts</td><td>{partsList.length}</td></tr>
<tr><td class="name">events</td><td>0</td></tr>
</tbody>
</table>
</div>
</section>
{/if}
{#if tab === 'sema'}
<section data-uix-section>
<h2 data-uix-section-title>
<span data-uix-layer-badge="sema">sema</span> · events
</h2>
<p data-uix-section-desc>
Trans declares <strong>no semantic events</strong>. Translation is a passive
transformation, not an evaluative act.
</p>
</section>
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>
<p data-uix-section-desc>
Trans deliberately ships <strong>no CSS recipe</strong>. The rendered element is a
plain `<span>` (or whatever the consumer sets via <code>as</code>) and inherits its
styling from the surrounding context. The <code>[data-trans]</code> marker exists
only for tooling — i18n audits can target it without affecting visual output.
</p>
</section>
{/if}
{#if tab === 'a11y'}
<section data-uix-section>
<h2 data-uix-section-title>Accessibility</h2>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Concern</th><th>Contract</th></tr></thead>
<tbody>
<tr>
<td class="name">Element semantics</td>
<td>
Inherits the semantics of the chosen <code>as</code> tag. Default
<code>&lt;span&gt;</code> is inline and announces with the surrounding
context. For headings, set <code>as="h2"</code> (etc.); for emphasis, use
<code>as="strong"</code> / <code>as="em"</code>.
</td>
</tr>
<tr>
<td class="name">Live updates</td>
<td>
When the locale flips, the text content inside <code>&lt;Trans&gt;</code>
updates. Screen readers reading the page at that moment may or may not
re-announce — wrap critical announcements in
<a href="/uix/components/announce">&lt;Announce&gt;</a> for explicit
<code>aria-live</code> coverage.
</td>
</tr>
<tr>
<td class="name">Fallback strings</td>
<td>
Every idlangref carries its English fallback (<code>#?path|Fallback</code>).
If the catalog lookup fails the fallback renders — never an empty string.
</td>
</tr>
</tbody>
</table>
</div>
</section>
{/if}
</div>
<style>
[data-uix-trace-row] {
display: flex;
align-items: center;
gap: var(--uix-space-2);
padding: var(--uix-space-2) var(--uix-space-3);
border-radius: var(--uix-radius-sm);
background: var(--uix-bg-subtle, var(--color-surface-raised));
font-family: var(--uix-font-mono);
font-size: var(--uix-font-size-sm);
}
[data-uix-trace-row] > code {
color: var(--uix-text-faint);
}
[data-uix-pattern-stack] {
display: flex;
flex-direction: column;
gap: var(--uix-space-5);
}
[data-uix-pattern-row] {
display: flex;
flex-direction: column;
gap: var(--uix-space-3);
padding: var(--uix-space-4);
border: 1px solid var(--uix-border);
border-radius: var(--uix-radius-md);
}
h3[data-uix-pattern-title] {
margin: 0 0 var(--uix-space-3);
font-family: var(--uix-font-ui);
font-size: var(--uix-font-size-sm);
font-weight: 600;
color: var(--uix-text-faint);
text-transform: uppercase;
letter-spacing: 0.04em;
}
</style>

Powered by TurnKey Linux.