Skip to content

Move texts to Paraglide JS; Rust reads the same message files - #1

Merged
firsttris merged 1 commit into
mainfrom
claude/sweet-mayer-lebvmf
Oct 3, 2026
Merged

firsttris merged 1 commit into
mainfrom
claude/sweet-mayer-lebvmf

Conversation

@firsttris

Copy link
Copy Markdown
Owner

This moves prefixr's texts to Paraglide JS, as already done in quadeck and haul. They move from src/lib/i18n/{de,en}.ts to messages/{de,en}.json and are compiled into typed message functions. Rust now reads the same two files, so every text exists only once.

Changes

  • Frontend
    • t("gameForm.tabCount", { count }) becomes m.gameForm_tabCount({ count }). This covers all 419 texts, and the placeholders keep their names.
    • Ids chosen at runtime go through pickMsg(msgGroup.x, id): wine tools, Proton options, winetricks verbs, MangoHud presets and colours.
    • The language is still a $state, and Paraglide reads it on every call, so switching re-renders every text without a reload. I checked this in the browser: after a click on "EN", the navigation and content were English straight away.
  • Backend errors stay structured: AppError is sent as { code, ...fields }, and the frontend shows backendErrors_<code> with the fields as placeholders.
  • Rust's own texts now come from messages/*.json, built into the binary (locale::text). That covers:
    • the tray menu and the quit dialog;
    • the dialogs for running as root and for an unreadable config;
    • the headless launch failure dialog;
    • AppError::localized(), so the second copy of every backend error text in error.rs is gone.
  • Dependencies: @inlang/paraglide-js, @inlang/plugin-message-format, vitest, and @types/node, which Paraglide's generated files need for svelte-check. With @types/node in place, the @ts-expect-error in vite.config.js was no longer needed and is removed.
  • README: new section "Texts and languages".

Tests

  • vitest (bun run test, new CI step), in tests/i18n.test.ts:
    • both languages have the same keys and placeholders, and no text is empty;
    • every message is used, by the UI or by Rust;
    • every used key exists.
  • cargo test:
    • every AppError variant has its backendErrors_<code> message, with the variant's fields as placeholders;
    • every key Rust renders exists;
    • AppError::localized and locale::text render correctly.
    • The existing tray tests still pass unchanged, so the texts read from JSON are identical to the old ones.

Checks run:

  • bun install --frozen-lockfile ✅
  • bun run check: 0 errors ✅
  • bun run test ✅
  • cargo test --locked: 129 passed ✅
  • bun run build ✅

🤖 Generated with Claude Code

https://claude.ai/code/session_014W49icb7XZqhYXUBc1UjzL


Generated by Claude Code

As in quadeck and haul: the texts move from src/lib/i18n/{de,en}.ts to
messages/{de,en}.json, compiled by Paraglide to typed message functions.

- Frontend: t("gameForm.tabCount", { count }) becomes
  m.gameForm_tabCount({ count }); ids chosen at runtime (wine tools,
  Proton options, winetricks verbs, MangoHud presets and colours) go
  through pickMsg(msgGroup.x, id). The language stays a $state that
  Paraglide reads on every call, so switching still re-renders every text
  without a reload.
- Backend errors stay structured (AppError → { code, ...fields }); the
  frontend shows backendErrors_<code> with the fields as placeholders.
- Rust's own texts (tray menu, quit dialog, root and config dialogs,
  headless launch failure) and AppError::localized() now read the same
  messages/*.json, built into the binary (locale::text). The second copy
  of every backend error text in error.rs is gone.
- Tests: vitest checks that both languages have the same keys and
  placeholders, none empty, every message used (UI and Rust), every used
  key exists; cargo test checks that every AppError variant has its
  message with its fields as placeholders. CI runs the new test step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W49icb7XZqhYXUBc1UjzL
@firsttris
firsttris merged commit 6c8d1ea into main Oct 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants