P3-3b: Internationalisation — four languages from the start #41

Open
opened 2026-09-10 17:57:04 +00:00 by Cordy · 0 comments
Owner

Do this immediately after P3-3 (shell) and before P3-4 (setup wizard).

Sequencing matters more than usual here. Retrofitting i18n after three screens of hardcoded English means revisiting every string, every layout, and every test. Doing it first costs almost nothing.

Languages

English, German, French, Italian — English plus the three Swiss official languages. Appropriate for a Swiss company selling to Swiss organisations and public authorities, and it means the product is usable in every canton on day one.

(If you meant a different four — say, dropping Italian for Spanish — change it now, before the catalogues exist.)

Files

  • Create: frontend/i18n/en.json, de.json, fr.json, it.json, frontend/i18n/i18n.ts, internal/app/i18n.go, frontend/i18n/i18n_test.ts (or a Go test that validates the catalogues)

Reuse Cairn's conventions

The Cairn web UI already has a t() helper and key-naming conventions. Match them — same key style, same catalogue shape. The Wails frontend is web technology, so catalogues can be shared or at least kept structurally identical. Two divergent i18n systems in one product is a maintenance tax for no benefit.

The failure mode to guard against

From Cairn's own experience: t() returns the key verbatim when a translation is missing. A button labelled peeringClose in production is a missing key, not a rendering bug — and it ships silently, because nothing errors.

Guard it with a test:

  • Every key present in en.json must exist in all three other catalogues. Fail the build otherwise. This is the single most valuable test in this task.
  • No catalogue may contain keys absent from en.json (catches stale keys after a rename).
  • A lint check that no user-visible string is hardcoded in a component.

Scope — translate the user-facing surface

Setup wizard · tray menu and status lines · main window · conflict UI · skip reasons · settings · notifications · error messages.

Skip reasons and error text matter most. They are what a confused user reads at the worst moment, and they are exactly the strings a developer forgets to externalise because they were written as debug output. The engine's Skip.Reason strings come from Go, not the frontend — decide now whether they are translated server-side in Go, or emitted as structured codes the frontend translates. Codes are the better answer: sync.Skip gains a Code field, and the reason string becomes a fallback for logs and diagnostics.

What is not translated

Log files, the diagnostics bundle, and cairnsync CLI output stay in English. They exist to be pasted into a support conversation, and a German stack trace helps nobody.

Locale detection

Follow the OS locale by default; allow an explicit override in settings; make it policy-settable (P3-1) so an organisation can standardise. Fall back to English for any unsupported locale.

Steps

  • Add the i18n layer and wire it into the Wails frontend.
  • Add Code to sync.Skip and map every existing reason to a code.
  • Write the catalogue-completeness test first — it should fail while de/fr/it.json are empty.
  • Populate English fully; machine-translate the other three as a starting point and flag them for native review before v1.0 (P3-11).
  • Commit: git commit -s -m "feat(i18n): English, German, French and Italian catalogues"

Acceptance criteria

  • All four catalogues have identical key sets, enforced by a failing test if not.
  • No hardcoded user-visible strings remain in the frontend.
  • Switching language changes the whole UI without a restart.
  • Skip reasons and error messages are translated, not just labels.
**Do this immediately after P3-3 (shell) and before P3-4 (setup wizard).** Sequencing matters more than usual here. Retrofitting i18n after three screens of hardcoded English means revisiting every string, every layout, and every test. Doing it first costs almost nothing. ## Languages **English, German, French, Italian** — English plus the three Swiss official languages. Appropriate for a Swiss company selling to Swiss organisations and public authorities, and it means the product is usable in every canton on day one. *(If you meant a different four — say, dropping Italian for Spanish — change it now, before the catalogues exist.)* ## Files - Create: `frontend/i18n/en.json`, `de.json`, `fr.json`, `it.json`, `frontend/i18n/i18n.ts`, `internal/app/i18n.go`, `frontend/i18n/i18n_test.ts` (or a Go test that validates the catalogues) ## Reuse Cairn's conventions The Cairn web UI already has a `t()` helper and key-naming conventions. Match them — same key style, same catalogue shape. The Wails frontend is web technology, so catalogues can be shared or at least kept structurally identical. Two divergent i18n systems in one product is a maintenance tax for no benefit. ## The failure mode to guard against From Cairn's own experience: **`t()` returns the key verbatim when a translation is missing.** A button labelled `peeringClose` in production is a missing key, not a rendering bug — and it ships silently, because nothing errors. Guard it with a test: - [ ] **Every key present in `en.json` must exist in all three other catalogues.** Fail the build otherwise. This is the single most valuable test in this task. - [ ] No catalogue may contain keys absent from `en.json` (catches stale keys after a rename). - [ ] A lint check that no user-visible string is hardcoded in a component. ## Scope — translate the user-facing surface Setup wizard · tray menu and status lines · main window · conflict UI · skip reasons · settings · notifications · error messages. **Skip reasons and error text matter most.** They are what a confused user reads at the worst moment, and they are exactly the strings a developer forgets to externalise because they were written as debug output. The engine's `Skip.Reason` strings come from Go, not the frontend — decide now whether they are translated server-side in Go, or emitted as **structured codes** the frontend translates. Codes are the better answer: `sync.Skip` gains a `Code` field, and the reason string becomes a fallback for logs and diagnostics. ## What is not translated Log files, the diagnostics bundle, and `cairnsync` CLI output stay in English. They exist to be pasted into a support conversation, and a German stack trace helps nobody. ## Locale detection Follow the OS locale by default; allow an explicit override in settings; make it policy-settable (P3-1) so an organisation can standardise. Fall back to English for any unsupported locale. ## Steps - [ ] Add the i18n layer and wire it into the Wails frontend. - [ ] Add `Code` to `sync.Skip` and map every existing reason to a code. - [ ] Write the catalogue-completeness test **first** — it should fail while `de/fr/it.json` are empty. - [ ] Populate English fully; machine-translate the other three as a starting point and flag them for native review before v1.0 (P3-11). - [ ] Commit: `git commit -s -m "feat(i18n): English, German, French and Italian catalogues"` ## Acceptance criteria - All four catalogues have identical key sets, enforced by a failing test if not. - No hardcoded user-visible strings remain in the frontend. - Switching language changes the whole UI without a restart. - Skip reasons and error messages are translated, not just labels.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/cairn-desktop#41
No description provided.