2a. Load glossary: Read .claude/skills/translate/glossaries/<lang>.md if it exists. All terms in the glossary are mandatory — use them consistently.
2b. Scan existing translations: Before translating new strings, grep for key domain terms in already-translated strings to identify established conventions. Flag any conflicts with the glossary (e.g., a glossary says "Passwort" but existing translations use "Kennwort").
2c. Resolve unknown terms: If a source string contains a term not covered by the glossary, and the term is ambiguous or has multiple valid translations, ask the user which translation to use (via AskUserQuestion). Add the decision to the glossary file and its Decision Log.
Step 3: Understand the .ts file format
<context><name>AddProfileWindow</name><!-- UI component name --><message><locationfilename="views/profile.py"line="25"/><!-- Source location --><source>Save</source><!-- English text to translate --><translation>Speichern</translation><!-- Translated text --></message></context>
Special case - comment-based strings:
<message><source>messages</source><comment>Please unlock your system password manager</comment><!-- THIS is the text to translate --><translationtype="unfinished"/></message>
When <source> is messages, settings, or app, the actual translatable text is in <comment>.
Buttons: Title Case in English (Save, Cancel, Add Repository)
Labels with colons: Keep the colon (Repository:, Password:)
Menu items: Title Case
Descriptions/tooltips: Sentence case
Technical terms: Keep English for: Borg, BorgBackup, SSH, repository (or use locale-appropriate term)
Placeholders: Preserve {variable} and %s patterns exactly
Step 5: Terminology check
Before writing translations, cross-check all generated translations against the glossary. Verify that no inconsistent terms slipped through (e.g., using "Kennwort" when the glossary specifies "Passwort"). Fix any violations before proceeding.
Step 6: Update the .ts file
Replace <translation type="unfinished"/> with <translation>Translated text</translation>
Translate placeholder text that's clearly an example (e.g., email@example.com)
Translate based on code-internal names instead of actual UI labels (e.g., code says "Misc" but UI shows "Settings / About" — translate as "Settings")
RTL Languages (Arabic):
Text direction is handled by Qt automatically
Ensure no hardcoded LTR punctuation breaks the flow
Test UI layout after translation
Testing Translations
After updating translations:
Compile: make translations-to-qm
Run the app: uv run vorta
Go to Settings tab → Language dropdown
Select the language and restart the app
Verify translations appear correctly in the UI
Workflow Example
# 1. Check translation status
/translate missing
# 2. Review and translate German
/translate review de
# 3. Compile translations
/translate compile
# 4. Test in app
uv run vorta
Glossary of Common Terms
Maintain consistency with these translations:
English
Context
Notes
Repository
Borg repo
Often kept as "Repository" in many languages
Archive
Backup snapshot
Time-based backup point
Backup
Action/noun
The backup operation
Prune
Delete old archives
Technical term
Mount
Make archive browsable
Filesystem operation
Extract
Restore files
Copy files from archive
Passphrase
Password to unlock the borg key
Security credential
Profile
Backup configuration
Group of settings
Schedule
Backup timing
When to run backups
Source
Files to back up
Folders/files to include
Exclude
Files to skip
Patterns to ignore
Per-Language Glossaries
Per-language glossaries live in .claude/skills/translate/glossaries/<lang>.md. These document agreed-upon translations for domain-specific and ambiguous terms. The terms in a glossary are mandatory — they must be used consistently in all translations for that language.
Each glossary contains:
A Terminology table mapping English terms to the agreed translation
A Decision Log recording when and why each term was chosen
Create a new glossary when a language is first reviewed. Update it whenever a new term decision is made.