Posts

i18n, a11y, and Shareable Lab State in the Browser

How Bio-Dynamics supports English, German, and Persian, keeps keyboard and screen-reader paths usable, and encodes lab checkpoints in the URL.
Table of Contents

Shipping an educational lab to a global audience means more than translation strings. Bio-Dynamics adds RTL layout for Persian, keyboard region shortcuts, ARIA live announcements, touch gesture hints, and URL-encoded lab checkpoints so teachers can share a mid-simulation state without a backend.

Lightweight i18n without a framework

Locales live in src/i18n/en.ts, de.ts, and fa.ts. A small t() helper resolves dot-path keys with parameter interpolation:

1
export function t(path: string, params?: Record<string, string | number>): string

Locale selection order:

  1. ?lang=en|de|fa query param
  2. navigator.language heuristic (de, fa / per prefixes)
  3. Default en

applyDocumentLocale() sets document.documentElement.lang and dir="rtl" for Persian — the dashboard CSS uses logical properties where needed so RTL does not require a duplicate stylesheet.

Event log lines from the engine are English internally; translateEvent() maps known prefixes to localized strings on display.

User guide (sharing and URL params): docs/user-guide.md

Accessibility choices that survived scope pressure

FeatureImplementation
Keyboard region selectKeys 17 map to region list order; Esc returns to macro body
Focusable actionsCatalog and trigger buttons carry aria-labels
Live announcementsaria-live="polite" region updates on region change, preset switch, and major sim events
Touch hintsDismissible gesture card on coarse pointers; hidden on fine-pointer desktops
Reduced motion respectCamera fly-to uses short easing; no gratuitous particle bursts

These are not a full WCAG audit claim — the lab is a canvas-heavy WebGL demo — but they keep the control surface operable without a mouse.

Shareable lab state without a server

labState.ts serializes a mid-simulation checkpoint to:

  1. URL?lab= plus a compact base64url payload (preset, region, tick, biome scalars, nodes, recent events)
  2. localStorage — autosave on meaningful progress for resume-after-refresh

Copy lab link in the dashboard encodes the current snapshot. Opening that URL restores the exact node layout and meter values — useful for classroom demos (“start here after the allergen spike”).

Resume banner logic:

  • Offer restore when autosaved state exists and is younger than 7 days
  • Skip if URL already contains ?lab=
  • Dismiss sets sessionStorage so “Start fresh” does not nag in the same session

Source: src/state/labState.ts

URL params as product surface

Beyond lab checkpoints, query params drive preset deep links from health articles:

ParamExampleEffect
presetallergy, candida, lifecycleScenario framing and default env
regionnose, gut, vaginalOpens tissue context
contextlifestageSwaps allergy narrative to life-stage variant
langde, faUI locale

Health posts on omid.dev already embed companions with these URLs. The lab meets readers where the article left off.

Why client-only persistence is enough

Bio-Dynamics has no accounts, no database, no sync service. For an educational sandbox that ships on every commit to playground.omid.dev, that is a feature:

  • Zero backend cost and ops
  • Shareable URLs work forever as static links
  • Privacy-friendly — no health data leaves the browser

The trade-off is payload size limits in URLs and no cross-device sync. Acceptable for 400 capped nodes with compact tuple encoding.

Read the rest of this series

Full documentation index: docs/README.md