feat(i18n): tag controls with data-i18n + add language dropdown

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-29 18:47:03 -07:00
parent 00534e116c
commit 49b77e0700
2 changed files with 35 additions and 12 deletions
+16 -12
View File
@@ -9,11 +9,11 @@
<body>
<div id="loading">
<div class="loading-inner">
<div class="loading-title">Loading Universe<span class="loading-dots"></span></div>
<div class="loading-title"><span data-i18n="loading.title">Loading Universe</span><span class="loading-dots"></span></div>
<div class="loading-bar"><div id="loading-fill"></div></div>
</div>
</div>
<header><h1>Human Experience Filter — Alteration Preview</h1></header>
<header><h1 data-i18n="app.title">Human Experience Filter — Alteration Preview</h1></header>
<main>
<section class="stage" id="stage">
<div class="screen">
@@ -31,39 +31,42 @@
<section class="panel">
<fieldset>
<legend>Output</legend>
<legend data-i18n="output.legend">Output</legend>
<label class="lang-pick">🌐
<select id="lang-select" aria-label="Language"></select>
</label>
<label class="dev-switch" for="visual">
<input type="checkbox" id="visual" />
<span class="dev-switch-track"><span class="dev-switch-thumb"></span></span>
<span class="dev-switch-label">Video</span>
<span class="dev-switch-label" data-i18n="output.video">Video</span>
</label>
<label class="audio-level">Audio
<label class="audio-level"><span data-i18n="output.audio">Audio</span>
<input type="range" id="audio" min="0" max="10" value="0" step="1" />
<span id="audio-level-val">0</span>/10
</label>
</fieldset>
<fieldset>
<legend>Altitude</legend>
<legend data-i18n="altitude.legend">Altitude</legend>
<div class="dial-wrap">
<svg id="dial" viewBox="0 0 100 100" aria-label="Altitude knob (turn to change scale)"></svg>
</div>
<span id="scale-name" class="scale-name"></span>
<p class="hint">Turn the knob (drag it, or scroll) to change altitude — endless: past the deepest it wraps back up to the highest. Click a label to jump there.</p>
<p class="hint" data-i18n="altitude.hint">Turn the knob (drag it, or scroll) to change altitude — endless: past the deepest it wraps back up to the highest. Click a label to jump there.</p>
</fieldset>
<fieldset>
<legend>Experience knobs (04)</legend>
<label>Think <input type="range" id="left" min="0" max="4" value="0" /></label>
<label>Feel <input type="range" id="right" min="0" max="4" value="0" /></label>
<label>Mood — dark ◀ 0 ▶ light <input type="range" id="mood" min="-4" max="4" value="0" /></label>
<legend data-i18n="knobs.legend">Experience knobs (04)</legend>
<label><span data-i18n="knobs.think">Think</span> <input type="range" id="left" min="0" max="4" value="0" /></label>
<label><span data-i18n="knobs.feel">Feel</span> <input type="range" id="right" min="0" max="4" value="0" /></label>
<label><span data-i18n="knobs.mood">Mood — dark ◀ 0 ▶ light</span> <input type="range" id="mood" min="-4" max="4" value="0" /></label>
</fieldset>
<!-- Dev Mode: a single toggle; everything dev (pool picker + analysis) lives below it. -->
<label class="dev-switch" for="dev-mode">
<input type="checkbox" id="dev-mode" />
<span class="dev-switch-track"><span class="dev-switch-thumb"></span></span>
<span class="dev-switch-label">Dev Mode</span>
<span class="dev-switch-label" data-i18n="devmode.label">Dev Mode</span>
</label>
<div id="dev-panel" class="dev-panel hidden">
@@ -105,6 +108,7 @@
</section>
</main>
<script src="/scrub.js"></script>
<script src="/i18n.js"></script>
<script src="/app.js"></script>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const I = require("../static/i18n.js");
const html = fs.readFileSync(path.join(__dirname, "../static/index.html"), "utf8");
test("index.html includes the i18n script and a language select", () => {
assert.match(html, /<script src="\/i18n\.js">/);
assert.match(html, /id="lang-select"/);
});
test("every data-i18n key in index.html exists in UI_STRINGS", () => {
const keys = [...html.matchAll(/data-i18n="([^"]+)"/g)].map((m) => m[1]);
assert.ok(keys.length >= 12, `expected many tagged nodes, found ${keys.length}`);
for (const k of keys) assert.ok(I.UI_STRINGS[k], `data-i18n key not in catalog: ${k}`);
});