You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AI-assisted translation that respects how this project translates its business terms — not generic machine translation. A project designates a leading language; the app learns how specific terms are consistently translated into each target language and applies that knowledge when suggesting translations.
This ticket is design-first: agree on the concept below (or amend it), then split implementation into follow-up issues.
Proposed concept
1. Leading language
New project-config field: translations.leadingLanguage (must be one of the configured languages)
2. Glossary as a first-class, committable artifact
babelshift.glossary.json next to the project config — plain JSON so it's reviewable in PRs, mergeable, and shared with the team
Entry shape: source term (in leading language) → per-language approved translation(s), plus optional note ("brand name — never translate") and a locked flag
Three ways entries get in:
Manual: glossary editor UI in the app
Mined: an analysis pass over the existing catalog finds recurring terms in leading-language values and checks whether their translations are consistent across keys; consistent pairs become suggested entries the user confirms (never auto-accepted). Inconsistent ones surface as a consistency report — valuable on its own.
Imported: from an existing file the team already maintains (e.g. a GLOSSARY.md) — import is a mapping step where the user marks which terms are translation-relevant, since such files usually define terms rather than translate them
3. Suggestion pipeline
For "translate key X into language Y":
Context assembled by the Rust side: the source value, matched glossary entries for Y, and a handful of similar existing translations (same key namespace / similar source strings) as few-shot examples — this is what makes it learn the project's voice, tone, and formality (Du/Sie matters in German)
Provider-agnostic LLM layer via the Vercel AI SDK on the TypeScript side — no hardcoded provider. Two tiers:
Generic OpenAI-compatible endpoint (@ai-sdk/openai-compatible) with configurable base URL — covers Ollama & LM Studio (local models, nothing leaves the machine — relevant for teams with sensitive strings), OpenRouter, Mistral, Groq, DeepSeek, …
Division of labor: Rust assembles the context (glossary matching, few-shot retrieval, placeholder validation — all provider-agnostic) and exposes it via a command; the frontend makes the model call through the AI SDK. Webview CORS is bypassed by injecting @tauri-apps/plugin-http's fetch into the provider config
Bring-your-own-key stored in the OS keychain (never in project files), fetched into memory only at call time; model id is a free-form string per provider; batch mode for "fill everything missing in FR"
Config scope: provider + model live in the project config as a committed, team-shared default, overridable per user in local app settings (teammates may use different providers or local models). Keys are per-user (keychain) regardless
Suggestions are drafts: shown in the details panel with a distinct state (e.g. amber), never written to disk until the user accepts. Accepting is a normal value commit; explicit accept/reject also feeds term-usage stats
4. Learning loop (the honest version)
No model training — "learning" is retrieval: glossary + similar-translation few-shots improve as the catalog and glossary grow. When a user edits an AI suggestion before accepting, diff the changed terms and offer to add them to the glossary. A consistency checker (pure Rust, no AI) continuously flags values that contradict glossary entries.
Open questions
Glossary matching: exact + case-insensitive first; stemming/lemmatization per language later?
Placeholder safety: suggestions must preserve {{interpolations}}/ICU syntax — validate before showing
Where do suggestion drafts live — frontend-only state, or persisted so they survive restarts?
Batch cost control: token estimate + confirmation before large runs?
Suggested split after concept sign-off
Leading language config + glossary file format + manual editor
Consistency checker + glossary mining report
AI SDK provider layer + single-key suggestions with glossary/few-shot context
Goal
AI-assisted translation that respects how this project translates its business terms — not generic machine translation. A project designates a leading language; the app learns how specific terms are consistently translated into each target language and applies that knowledge when suggesting translations.
This ticket is design-first: agree on the concept below (or amend it), then split implementation into follow-up issues.
Proposed concept
1. Leading language
translations.leadingLanguage(must be one of the configured languages)2. Glossary as a first-class, committable artifact
babelshift.glossary.jsonnext to the project config — plain JSON so it's reviewable in PRs, mergeable, and shared with the teamlockedflag3. Suggestion pipeline
For "translate key X into language Y":
@ai-sdk/anthropic/@ai-sdk/openai/@ai-sdk/google)@ai-sdk/openai-compatible) with configurable base URL — covers Ollama & LM Studio (local models, nothing leaves the machine — relevant for teams with sensitive strings), OpenRouter, Mistral, Groq, DeepSeek, …@tauri-apps/plugin-http'sfetchinto the provider config4. Learning loop (the honest version)
No model training — "learning" is retrieval: glossary + similar-translation few-shots improve as the catalog and glossary grow. When a user edits an AI suggestion before accepting, diff the changed terms and offer to add them to the glossary. A consistency checker (pure Rust, no AI) continuously flags values that contradict glossary entries.
Open questions
{{interpolations}}/ICU syntax — validate before showingSuggested split after concept sign-off