Files
WhispAssist/docs/i18n-tracking.md
T

143 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# i18n migration tracking
Working doc for the incremental UI-string translation effort. The i18n **mechanism**
is done; this tracks moving the app's remaining hardcoded strings into the translation
files, one batch at a time. Tick boxes as views are converted.
> **Status (2026-07-12): migration complete.** All views and components (Batches A–F) are
> converted; `en.json` holds ~506 keys. Every user-facing English string flows through
> `t()`. What remains is intentionally-untranslated data (see the "Not translated" section)
> — plus the actual work of adding a second language, which is now just translating
> `en.json` into a new `<code>.json`.
- **Engine:** hand-rolled, zero-dependency. `src/lib/i18n/index.svelte.ts`
- **Baseline dictionary:** `src/lib/i18n/en.json` (English is the source-of-truth key set
**and** the fallback for any missing key)
- **Selector:** Settings → Language (`section === "language"`)
- **Scope decision:** display language is separate from the transcription `whisper_language`
setting — don't conflate them.
## What we translate (and what we never do)
**Translate: UI chrome only** — fixed labels, buttons, headings, placeholders,
empty/loading/status states, tooltips, and app-generated default *labels*.
**Never translate: user-authored content.** Meeting titles, tag names, notes, transcript
text, search snippets, dates — these are bound straight from data (`item.title`, tag
values, `s.text`, …) and stay exactly as the user wrote them. If a string comes from the
user or the recording, it does not get a key.
So "convert view X" always means "key its fixed labels", never "touch its content". Most
views are mostly content with a thin shell of labels — MeetingsList, for example, is ~10
fixed labels (search placeholder, empty states, filter/bulk-export labels) wrapped around
a list whose rows are pure user data.
> Caveat — the `"Untitled meeting"` **default title is generated in the Rust backend**, not
> the frontend, so it never reaches `t()`. Localizing app-generated defaults is a separate
> backend decision, out of scope for this frontend effort.
## How to convert a string (the pattern)
1. Add a key to `en.json`. Naming: `<area>.<subarea>.<name>`, dotted, grouped by view —
e.g. `nav.recording`, `settings.transcription.title`, `meetings.empty`.
2. Replace the literal in markup with `{t("key")}` (import `t` from `../i18n/index.svelte`).
3. Dynamic bits use placeholders: `t("meetings.count", { n })` against
`"meetings.count": "{n} meetings"`. Handle plurals with a caller-side ternary for now
(`n === 1 ? t("...one") : t("...many")`) — the engine is intentionally simple.
4. Attributes translate the same way: `title={t("...")}`, `aria-label={t("...")}`,
`placeholder={t("...")}`.
5. `npm run check` must stay at 0 errors/warnings.
> When adding a **new language** (not covered by this doc's batches): copy `en.json` to
> `<code>.json`, translate it, and register it in `DICTS` + `LOCALES` in
> `index.svelte.ts`. That one file is the whole job.
## Done
- [x] i18n engine + `en.json` baseline + Settings language selector (branch
`feature_chore_bug_007`)
- [x] `Settings.svelte` — nav labels (`nav.*`), the Language section
(`settings.language.*`), and the Transcription Language section
(`settings.transcription.*`)
- [x] `src/App.svelte` — app shell chrome (`app.*`): header (tagline, template
picker, record/stop/cancel/add-meeting, recording status, retention, backend,
device-notice), Settings button, consent + vault-unlock dialogs, pane toggles,
splitter labels, and JS strings (discard confirm, SR announcements, vault error).
- [x] `src/lib/views/MeetingsList.svelte` — labels only (`meetings.*`): search/filter/
bulk-export controls, empty/loading/status states, status badges (via
`statusLabel`), resume + delete, delete-confirm, and the pluralized export result.
List rows (titles/tags/dates/snippets) left as user data.
- [x] `src/lib/views/TranscriptNotes.svelte` — transcript + notes chrome (`transcript.*`,
`notes.*`): pane headings/toggles, reprocess controls, notes toolbar (bold/heading/
list/preview), export button tooltips + dialog filter names, empty states, segment
tooltips + note placeholders. Transcript/notes/speaker text left as user data.
- [x] `src/lib/views/SummaryPanel.svelte` — all panel chrome (`summary.*`): section
headings (Recording/Sync/Tags/Summary/Briefs/Action items/Calendar/Participants/
Speakers), buttons, placeholders, empty states, provider labels (via `providerLabel`;
brand names kept literal), action-item + brief + speaker controls. Summary text,
tags, brief content, participant/speaker names left as user data.
- [x] `src/lib/views/Settings.svelte` — **all sections** done (`settings.*`): the modal
shell (dialog title, Close), plus recording, hardware (incl. models/diarization),
storage (+ export/import status), calendar (.pst import/cleanup/events), sync
(targets + WebDAV/OAuth forms), AI (provider + Ollama advanced params), MCP
(transport/scope/token/access log), privacy (egress self-check + vault), about.
OLLAMA_OPTIONS param catalog labels/help left as data (config catalog, like model
ids); example URLs/model-id placeholders left literal.
- [x] Components (Batch F): `ConsentNotice` + `HostedAiBanner` (legal/consent copy,
`consent.*` / `hosted.*`), `ThemeToggle` (`theme.*`), `ImportMeeting` (`import.*`),
`TagChip` (`tagchip.*`), `LevelMeter` (`levelmeter.*`). `Splitter`'s `label` is
caller-supplied and already translated by the parent; no strings of its own.
## Outstanding — suggested batches
_None — all batches complete._ The section below is kept as a record of the plan.
Ordered roughly by user-visibility ÷ effort. Sizes are rough (line count / labeled
attributes) to help portion the work, not exact string counts. A file isn't "done" until
its visible text **and** its `title`/`aria-label`/`placeholder` attributes are keyed.
### ~~Batch A — app shell~~ ✅ done (branch `feature_chore_bug_007`)
Moved to the Done list above.
### ~~Batch B — meetings list~~ ✅ done (branch `feature_chore_bug_007`)
Moved to the Done list above.
### ~~Batch C — transcript & notes~~ ✅ done (branch `feature_chore_bug_007`)
Moved to the Done list above.
### ~~Batch D — summary panel~~ ✅ done (branch `feature_chore_bug_007`)
Moved to the Done list above.
### ~~Batch E — Settings, all sections~~ ✅ done (branch `feature_chore_bug_007`)
All nine sections + the modal shell converted, one commit per section. Moved to the Done
list above.
### ~~Batch F — components~~ ✅ done (branch `feature_chore_bug_007`)
Moved to the Done list above.
## Not translated (intentional)
- **User-authored content** — meeting titles, tag names, notes, transcript text, search
snippets. Bound from data; stays as the user wrote it (see "What we translate" above).
- App-generated defaults created in the backend (e.g. `"Untitled meeting"`) — a separate
backend concern; the frontend `t()` never sees them.
- Backend / Rust error messages surfaced via `WaError` — out of scope for the frontend
`t()`; revisit only if we localize command errors.
- Provider/proper names (Ollama, Anthropic, OpenAI, Obsidian, WebDAV, WhispAssist),
model ids, ISO language codes.
- Console/`tracing` logs.
## Batch log
Record each landed batch here (date / branch / commit) so progress is auditable.
| Date | Batch | Branch / commit | Notes |
|------|-------|-----------------|-------|
| 2026-07-12 | Infra + Settings nav/language/transcription | `feature_chore_bug_007` | engine + en.json + selector |
| 2026-07-12 | Batch A (App shell) + Batch B (MeetingsList) | `feature_chore_bug_007` | +~60 keys; `app.*`, `meetings.*` |
| 2026-07-12 | Batch C (TranscriptNotes) + Batch D (SummaryPanel) | `feature_chore_bug_007` | +~120 keys; `transcript.*`, `notes.*`, `summary.*`; en.json now 198 keys |
| 2026-07-12 | Batch E (Settings, all 9 sections + shell) | `feature_chore_bug_007` | +~270 keys; `settings.*`; en.json now 470 keys; one commit per section |
| 2026-07-12 | Batch F (components) | `feature_chore_bug_007` | +~36 keys; `consent.*`/`hosted.*`/`theme.*`/`import.*`/`tagchip.*`/`levelmeter.*`; en.json now 506 keys — migration complete |