feat(ai): Translate picks the target first, so it never burns a wasted call (LC-694) #661
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/LC-694-translate-flow"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Fixes the wasted-call Translate flow and polishes the block/dropdown. Fixes LC-694.
Before: clicking Translate auto-translated to the viewer's own language and ran Ollama immediately. On an already-English message that first call returned the text verbatim (a useless multi-second call), and getting the language you actually wanted meant opening the dropdown and running a second call.
How
routes/translate.rsbranches on the presence oflang: absent -> picker only (translated_html: None); present -> translate. Source-language detection needs its own model call, so picker-first is the correct way to avoid translating a message into the language it is already in.localStorageand pre-selected in the picker (live.js) - applied only to the picker-only select, never auto-translating.<select>and the Translate / Show original controls are styled as proper compact controls. The multi-second call reuses the shareddata-lc-ai-pendingspinner ("Translating..."); the menu trigger drops that spinner since opening the picker is instant.Template stays comparison-free where it matters; the two states are driven by
Option<String>.Scope
routes/translate.rs,views/translate.rs,templates/room/translation_block.html,templates/room/message.html,assets/live.js,assets/main.css(authored CSS, served directly),locales/en+es/room.ftl. No schema change.Deferred (noted in the ticket)
Verification
cargo check/cargo clippy --lib: clean.cargo fmt --check: clean.node --check live.js: clean.🤖 Generated with Claude Code
https://claude.ai/code/session_011M7CNRsGzgzc48KinZUAQ8
Clicking Translate used to immediately translate to the viewer's own language and run the Ollama request. When the message was already in that language the first call produced identical text - a useless multi-second call - and the user then had to open the dropdown and run a SECOND call to get the language they actually wanted. Two calls for one useful result. Translate now opens a language picker instead of auto-translating. The first click renders a picker-only block ("Translate to [language] [Translate]") and runs no LLM call; the AI runs only once the user confirms a target (the Translate button or a language change), so a message is never translated into the language it is already in, and there is exactly one call per chosen target. Results stay cached per (message, target). Source-language detection needs its own model call, so picker-first is the right way to avoid the no-op translation. The viewer's last-chosen target is remembered in localStorage and pre-selected in the picker (live.js), so frequent users do not re-pick every time - applied only to the picker-only select, never auto-translating. Polish: the translation block is now a quiet rounded accent-tinted surface with the accent left border so the translated text reads as distinct from the original; the language select and the Translate / Show original controls are styled as proper compact controls; the multi-second call reuses the shared data-lc-ai-pending spinner ("Translating..."). The menu trigger no longer shows that spinner, since opening the picker is instant. No schema change. i18n en/es both updated (room-msg-translate-to). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7CNRsGzgzc48KinZUAQ8