diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..522ac2d --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,141 @@ +# oAI — Development Guide + +## Project Structure + +``` +oAI/ +├── Models/ # Data models +│ ├── Message.swift # Chat message model +│ ├── Conversation.swift # Saved conversation model +│ ├── ModelInfo.swift # AI model metadata +│ └── Settings.swift # App settings enums +│ +├── Views/ # SwiftUI views +│ ├── Main/ # Primary UI components +│ │ ├── ChatView.swift # Main chat interface +│ │ ├── MessageRow.swift # Individual message display +│ │ ├── InputBar.swift # Message input with commands +│ │ ├── HeaderView.swift # Top bar (provider/model/status) +│ │ └── FooterView.swift # Bottom stats bar +│ │ +│ └── Screens/ # Modal/sheet views +│ ├── SettingsView.swift # Settings with tabs +│ ├── ModelSelectorView.swift +│ ├── ConversationListView.swift +│ └── HelpView.swift +│ +├── ViewModels/ # Observable view models +│ └── ChatViewModel.swift # Main chat logic & state +│ +├── Providers/ # AI provider implementations +│ ├── Provider.swift # Protocol definition +│ ├── OpenRouterProvider.swift +│ ├── AnthropicProvider.swift +│ ├── OpenAIProvider.swift +│ └── OllamaProvider.swift +│ +├── Services/ # Business logic & data +│ ├── DatabaseService.swift # SQLite operations (GRDB) +│ ├── SettingsService.swift # Settings persistence +│ ├── ProviderRegistry.swift # AI provider management +│ ├── MCPService.swift # File access (MCP) +│ ├── WebSearchService.swift # DuckDuckGo/Google search +│ ├── GitSyncService.swift # Git synchronization +│ ├── ContextSelectionService.swift # Smart context +│ ├── EmbeddingService.swift # Semantic search +│ ├── EmailService.swift # Email monitoring (IMAP) +│ └── EmailHandlerService.swift # Email AI responder +│ +└── Resources/ + └── oAI.help/ # macOS Help Book +``` + +## Key Technologies + +- **SwiftUI** - Modern declarative UI framework +- **GRDB** - SQLite database wrapper for persistence +- **MarkdownUI** - Markdown rendering with syntax highlighting +- **os.Logger** - Native logging framework +- **Network Framework** - Pure Swift IMAP/SMTP implementation +- **Security Framework** - Keychain and encryption services + +## Database Schema + +**Conversations**: id, name, createdAt, updatedAt +**Messages**: id, conversationId, role, content, tokens, cost, timestamp +**Message Metadata**: message_id, importance_score, user_starred, summary +**Message Embeddings**: message_id, embedding (BLOB), model, dimension +**Conversation Summaries**: id, conversationId, startIndex, endIndex, summary +**Email Logs**: id, sender, subject, status, timestamp + +**Location:** `~/Library/Application Support/oAI/oai_conversations.db` + +## Building + +### Build Scripts + +| Script | Architecture | Output | +|--------|-------------|--------| +| `build.sh` | Apple Silicon (arm64) | Installs directly to `/Applications` | +| `build-dmg.sh` | Apple Silicon (arm64) | `oAI--AppleSilicon.dmg` on Desktop | +| `build-dmg-universal.sh` | Universal (arm64 + x86_64) | `oAI--Universal.dmg` on Desktop | +| `build_nb/sv/da/de/en.sh` | Apple Silicon (arm64) | Build + launch in specific language | + +All scripts: find Developer ID cert, clean-build via `xcodebuild`, re-sign with `codesign --options runtime --timestamp`, verify. Version is read from `MARKETING_VERSION` in `project.pbxproj`. + +### Manual Build Commands + +```bash +# Clean build +xcodebuild clean -scheme oAI + +# Debug build +xcodebuild -scheme oAI -configuration Debug + +# Run tests +xcodebuild test -scheme oAI +``` + +**Output:** `~/Library/Developer/Xcode/DerivedData/oAI-*/Build/Products/Debug/oAI.app` + +In Xcode: `⌘B` build, `⌘R` run, `⌘⇧K` clean, `⌘.` stop. + +### XProtect Note + +XProtect 5331 flags Debug builds (bash + IMAP + file access = RAT signature match). Use `./build.sh` (Developer ID signed) for all local testing. + +## Logs + +``` +~/Library/Logs/oAI.log +``` + +## Performance Notes + +- **Context Selection**: 50-80% token reduction for long conversations +- **Semantic Search**: ~$0.02-0.15/month for heavy users +- **Conversation Export**: Markdown format for human readability +- **Database**: Indexed queries for fast conversation retrieval +- **Streaming**: Efficient memory usage with `AsyncThrowingStream` + +## Contributing + +Contributions are welcome! By submitting a pull request you agree that your contribution will be licensed under the AGPL-3.0. + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +Please ensure: +- Code follows Swift style guidelines +- All tests pass +- Documentation is updated +- Commit messages are descriptive + +## Acknowledgments + +- **MarkdownUI** - Excellent markdown rendering library +- **GRDB** - Robust SQLite wrapper for Swift +- **Anthropic, OpenAI, OpenRouter** - AI API providers diff --git a/Locales/generate_translations.py b/Locales/generate_translations.py new file mode 100644 index 0000000..eacd4fa --- /dev/null +++ b/Locales/generate_translations.py @@ -0,0 +1,886 @@ +#!/usr/bin/env python3 +"""Generate translated XLIFF files for nb, sv, da, de.""" + +import re, os + +# Base path +BASE = "/Users/rune/Code/Swift/oAI/Locales/oAI Localizations" + +# Translations: key -> {lang: translation} +# None means "keep source as-is" +T = { + # --- keep as-is (None = copy source) --- + "%@": None, + "%@ min": None, + "%@ pt": None, + "%@s": None, + "Branch: %@": None, + "Remote: %@": None, + "Bash Execution": None, + "Model Context Protocol": None, + "SSH Key": None, + "OpenAI (text-embedding-3-large)": None, + "OpenAI (text-embedding-3-small)": None, + "OpenRouter (OpenAI large)": None, + "OpenRouter (OpenAI small)": None, + "OpenRouter (Qwen 8B)": None, + "OpenRouter Credits": None, + "OpenAI Balance": None, + "Anthropic Balance": None, + "Ollama (Local)": None, + "Google (Gemini embedding)": None, + "oAI": None, + "tokens": None, + "v%@": None, + "Version %@ (%@)": None, + "↑↓ navigate ↩ open": None, + "⌘N New • ⌘M Model • ⌘S Save": None, + "Built with SwiftUI": None, + "Example: oai-bot-x7k2m9p3@gmail.com": None, + "© 2026 [Rune Olsen](https://blog.rune.pm)": None, + "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)": None, + "Username + Password": None, + "•": None, + "🌐": None, + "🎨": None, + "👁️": None, + "🔧": None, + # The skill example template — keep as English (it's instructional content) + "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases": None, + "Access Token": None, + + # --- translated strings --- + "%@ active — injected into every conversation": { + "nb": "%@ aktiv — injisert i alle samtaler", + "sv": "%@ aktiv — injiceras i alla konversationer", + "da": "%@ aktiv — injiceres i alle samtaler", + "de": "%@ aktiv — in jedes Gespräch eingefügt", + }, + "%@ chars": { + "nb": "%@ tegn", + "sv": "%@ tecken", + "da": "%@ tegn", + "de": "%@ Zeichen", + }, + "(always used)": { + "nb": "(alltid brukt)", + "sv": "(används alltid)", + "da": "(altid brugt)", + "de": "(immer verwendet)", + }, + "(optional)": { + "nb": "(valgfri)", + "sv": "(valfri)", + "da": "(valgfri)", + "de": "(optional)", + }, + "1. Open Paperless-NGX → Settings → API Tokens": { + "nb": "1. Åpne Paperless-NGX → Innstillinger → API-tokens", + "sv": "1. Öppna Paperless-NGX → Inställningar → API-tokens", + "da": "1. Åbn Paperless-NGX → Indstillinger → API-tokens", + "de": "1. Paperless-NGX öffnen → Einstellungen → API-Tokens", + }, + "2. Create or copy your token": { + "nb": "2. Opprett eller kopier tokenet ditt", + "sv": "2. Skapa eller kopiera din token", + "da": "2. Opret eller kopier dit token", + "de": "2. Token erstellen oder kopieren", + }, + "3. Paste it above": { + "nb": "3. Lim det inn ovenfor", + "sv": "3. Klistra in det ovan", + "da": "3. Indsæt det ovenfor", + "de": "3. Oben einfügen", + }, + "A shortcut with command %@ already exists.": { + "nb": "En snarvei med kommandoen %@ finnes allerede.", + "sv": "En genväg med kommandot %@ finns redan.", + "da": "En genvej med kommandoen %@ findes allerede.", + "de": "Eine Verknüpfung mit dem Befehl %@ existiert bereits.", + }, + "API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine.": { + "nb": "API-nøkler og legitimasjon er **ikke** inkludert i sikkerhetskopien. Du må taste dem inn på nytt etter gjenoppretting på en ny maskin.", + "sv": "API-nycklar och inloggningsuppgifter ingår **inte** i säkerhetskopian. Du måste ange dem igen efter återställning på en ny dator.", + "da": "API-nøgler og legitimationsoplysninger er **ikke** inkluderet i sikkerhedskopien. Du skal indtaste dem igen efter gendannelse på en ny maskine.", + "de": "API-Schlüssel und Zugangsdaten sind **nicht** in der Sicherung enthalten. Du musst sie nach der Wiederherstellung auf einem neuen Gerät erneut eingeben.", + }, + "Active": { + "nb": "Aktiv", + "sv": "Aktiv", + "da": "Aktiv", + "de": "Aktiv", + }, + "Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows.": { + "nb": "Aktive ferdigheter legges til systemprompten. Slå dem av og på per ferdighet for å kontrollere hva KI-en vet.", + "sv": "Aktiva färdigheter läggs till i systemprompten. Aktivera/inaktivera dem per färdighet för att kontrollera vad AI:n känner till.", + "da": "Aktive færdigheder tilføjes til systemprompten. Slå dem til og fra per færdighed for at styre, hvad AI'en ved.", + "de": "Aktive Skills werden an den System-Prompt angehängt. Schalte sie einzeln ein oder aus, um zu steuern, was die KI weiß.", + }, + "Agent Skills": { + "nb": "Agent-ferdigheter", + "sv": "Agent-färdigheter", + "da": "Agent-færdigheder", + "de": "Agent-Skills", + }, + "Allow Shell Command?": { + "nb": "Tillat skalkommando?", + "sv": "Tillåt skalkommando?", + "da": "Tillad skalkommando?", + "de": "Shell-Befehl erlauben?", + }, + "Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs.": { + "nb": "Gi KI-en tilgang til å kjøre skalkommandoer på maskinen din. Kommandoer kjøres via /bin/zsh. Aktiver godkjenningsmodus for å se gjennom hver kommando før den kjøres.", + "sv": "Låt AI:n köra skalkommandon på din dator. Kommandon körs via /bin/zsh. Aktivera godkännandeläge för att granska varje kommando innan det körs.", + "da": "Giv AI'en adgang til at køre skalkommandoer på din maskine. Kommandoer udføres via /bin/zsh. Aktiver godkendelsestilstand for at gennemgå hver kommando, inden den køres.", + "de": "Erlaube der KI, Shell-Befehle auf deinem Rechner auszuführen. Befehle werden über /bin/zsh ausgeführt. Aktiviere den Genehmigungsmodus, um jeden Befehl vor der Ausführung zu prüfen.", + }, + "Auto-execute mode: commands run without approval. Use with caution.": { + "nb": "Auto-kjøremodus: kommandoer kjøres uten godkjenning. Bruk med forsiktighet.", + "sv": "Automatisk körning: kommandon körs utan godkännande. Använd med försiktighet.", + "da": "Automatisk kørselstilstand: kommandoer køres uden godkendelse. Brug med forsigtighed.", + "de": "Automatischer Ausführungsmodus: Befehle werden ohne Genehmigung ausgeführt. Mit Vorsicht verwenden.", + }, + "Auto-sync can cause conflicts if running on multiple machines simultaneously.": { + "nb": "Auto-synkronisering kan forårsake konflikter hvis den kjøres på flere maskiner samtidig.", + "sv": "Automatisk synkronisering kan orsaka konflikter om den körs på flera datorer samtidigt.", + "da": "Auto-synkronisering kan forårsage konflikter, hvis den kører på flere maskiner samtidigt.", + "de": "Die automatische Synchronisierung kann zu Konflikten führen, wenn sie auf mehreren Geräten gleichzeitig läuft.", + }, + "Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations.": { + "nb": "Velg automatisk relevante meldinger i stedet for å sende all historikk. Reduserer tokenforbruk for lange samtaler.", + "sv": "Välj automatiskt relevanta meddelanden istället för att skicka all historik. Minskar tokenanvändningen för långa konversationer.", + "da": "Vælg automatisk relevante beskeder i stedet for at sende al historik. Reducerer tokenforbrug for lange samtaler.", + "de": "Relevante Nachrichten automatisch auswählen statt die gesamte Historie zu senden. Reduziert den Token-Verbrauch bei langen Gesprächen.", + }, + "Automatically summarize old portions of long conversations to save tokens and improve context efficiency.": { + "nb": "Sammenfatt automatisk eldre deler av lange samtaler for å spare tokens og forbedre konteksteffektiviteten.", + "sv": "Sammanfatta automatiskt äldre delar av långa konversationer för att spara tokens och förbättra kontexteffektiviteten.", + "da": "Opsummer automatisk ældre dele af lange samtaler for at spare tokens og forbedre konteksteffektiviteten.", + "de": "Ältere Teile langer Gespräche automatisch zusammenfassen, um Tokens zu sparen und die Kontexteffizienz zu verbessern.", + }, + "Back Up Now": { + "nb": "Sikkerhetskopier nå", + "sv": "Säkerhetskopiera nu", + "da": "Sikkerhedskopier nu", + "de": "Jetzt sichern", + }, + "Backup location:": { + "nb": "Sikkerhetskopiplassering:", + "sv": "Säkerhetskopieringsplats:", + "da": "Sikkerhedskopieringsplacering:", + "de": "Sicherungsort:", + }, + "COMMAND": { + "nb": "KOMMANDO", + "sv": "KOMMANDO", + "da": "KOMMANDO", + "de": "BEFEHL", + }, + "Capabilities": { + "nb": "Egenskaper", + "sv": "Funktioner", + "da": "Egenskaber", + "de": "Fähigkeiten", + }, + "Changing these values affects how the AI generates responses. The defaults work well for most use cases.": { + "nb": "Å endre disse verdiene påvirker hvordan KI-en genererer svar. Standardverdiene fungerer bra for de fleste brukstilfeller.", + "sv": "Att ändra dessa värden påverkar hur AI:n genererar svar. Standardvärdena fungerar bra för de flesta användningsfall.", + "da": "Ændring af disse værdier påvirker, hvordan AI'en genererer svar. Standardværdierne fungerer godt til de fleste anvendelser.", + "de": "Das Ändern dieser Werte beeinflusst, wie die KI Antworten generiert. Die Standardwerte funktionieren für die meisten Anwendungsfälle gut.", + }, + "Check your balance at:": { + "nb": "Sjekk saldoen din på:", + "sv": "Kontrollera ditt saldo på:", + "da": "Tjek din saldo på:", + "de": "Guthaben prüfen unter:", + }, + "Check your usage at:": { + "nb": "Sjekk forbruket ditt på:", + "sv": "Kontrollera din användning på:", + "da": "Tjek dit forbrug på:", + "de": "Nutzung prüfen unter:", + }, + "Clear All": { + "nb": "Slett alle", + "sv": "Rensa alla", + "da": "Ryd alle", + "de": "Alle löschen", + }, + "Click 'Add Folder' below or drag folders here from Finder": { + "nb": "Klikk «Legg til mappe» nedenfor eller dra mapper hit fra Finder", + "sv": "Klicka på \"Lägg till mapp\" nedan eller dra mappar hit från Finder", + "da": "Klik på 'Tilføj mappe' nedenfor eller træk mapper hertil fra Finder", + "de": 'Klicke unten auf \u201eOrdner hinzuf\u00fcgen\u201c oder ziehe Ordner aus dem Finder hierher', + }, + "Command": { + "nb": "Kommando", + "sv": "Kommando", + "da": "Kommando", + "de": "Befehl", + }, + "Command History": { + "nb": "Kommandohistorikk", + "sv": "Kommandohistorik", + "da": "Kommandohistorik", + "de": "Befehlshistorie", + }, + "Content (Markdown)": { + "nb": "Innhold (Markdown)", + "sv": "Innehåll (Markdown)", + "da": "Indhold (Markdown)", + "de": "Inhalt (Markdown)", + }, + "Controls which messages are written to ~/Library/Logs/oAI.log": { + "nb": "Styrer hvilke meldinger som skrives til ~/Library/Logs/oAI.log", + "sv": "Styr vilka meddelanden som skrivs till ~/Library/Logs/oAI.log", + "da": "Styrer hvilke beskeder der skrives til ~/Library/Logs/oAI.log", + "de": "Steuert, welche Nachrichten in ~/Library/Logs/oAI.log geschrieben werden", + }, + "Conversations": { + "nb": "Samtaler", + "sv": "Konversationer", + "da": "Samtaler", + "de": "Gespräche", + }, + "Copied!": { + "nb": "Kopiert!", + "sv": "Kopierat!", + "da": "Kopieret!", + "de": "Kopiert!", + }, + "Cost Examples": { + "nb": "Kostnadseksempler", + "sv": "Kostnadsexempel", + "da": "Omkostningseksempler", + "de": "Kostenbeispiele", + }, + "Create a dedicated email account specifically for AI handling. Do NOT use your personal email address.": { + "nb": "Opprett en dedikert e-postkonto spesielt for KI-håndtering. IKKE bruk din personlige e-postadresse.", + "sv": "Skapa ett dedikerat e-postkonto specifikt för AI-hantering. Använd INTE din personliga e-postadress.", + "da": "Opret en dedikeret e-mailkonto specifikt til AI-håndtering. Brug IKKE din personlige e-mailadresse.", + "de": "Erstelle ein dediziertes E-Mail-Konto speziell für die KI-Bearbeitung. Verwende NICHT deine persönliche E-Mail-Adresse.", + }, + "Create a shortcut to save a reusable prompt template accessible from the / command dropdown.": { + "nb": "Opprett en snarvei for å lagre en gjenbrukbar promptmal som er tilgjengelig fra /kommando-rullegardinmenyen.", + "sv": "Skapa en genväg för att spara en återanvändbar promptmall tillgänglig från /-kommandomenyn.", + "da": "Opret en genvej til at gemme en genanvendelig promptskabelon, der er tilgængelig fra /kommando-rullemenuen.", + "de": "Erstelle eine Verknüpfung, um eine wiederverwendbare Prompt-Vorlage zu speichern, die über das /-Befehlsmenü zugänglich ist.", + }, + "Default Prompt": { + "nb": "Standardprompt", + "sv": "Standardprompt", + "da": "Standardprompt", + "de": "Standard-Prompt", + }, + "Delete (%@)": { + "nb": "Slett (%@)", + "sv": "Radera (%@)", + "da": "Slet (%@)", + "de": "Löschen (%@)", + }, + "Description": { + "nb": "Beskrivelse", + "sv": "Beskrivning", + "da": "Beskrivelse", + "de": "Beschreibung", + }, + "Each command will require your approval before running.": { + "nb": "Hver kommando krever din godkjenning før den kjøres.", + "sv": "Varje kommando kräver ditt godkännande innan det körs.", + "da": "Hver kommando kræver din godkendelse, inden den køres.", + "de": "Jeder Befehl erfordert deine Genehmigung, bevor er ausgeführt wird.", + }, + "Edit": { + "nb": "Rediger", + "sv": "Redigera", + "da": "Rediger", + "de": "Bearbeiten", + }, + "Email Activity Log": { + "nb": "E-postaktivitetslogg", + "sv": "E-postaktivitetslogg", + "da": "E-mailaktivitetslog", + "de": "E-Mail-Aktivitätsprotokoll", + }, + "Email Handler System Prompt": { + "nb": "Systemprompt for e-postbehandler", + "sv": "Systemprompt för e-posthanterare", + "da": "Systemprompt til e-mailbehandler", + "de": "System-Prompt für E-Mail-Handler", + }, + "Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults.": { + "nb": "E-postbehandleren bruker KUN sin egen systemprompt, fullstendig isolert fra de viktigste chatinnstillingene dine. En egendefinert prompt nedenfor vil overstyre standardinnstillingene.", + "sv": "E-posthanteraren använder BARA sin egen systemprompt, helt isolerad från dina viktigaste chattinställningar. En anpassad prompt nedan åsidosätter standardinställningarna.", + "da": "E-mailbehandleren bruger KUN sin egen systemprompt, fuldstændig isoleret fra dine vigtigste chatindstillinger. En brugerdefineret prompt nedenfor tilsidesætter standardindstillingerne.", + "de": "Der E-Mail-Handler verwendet NUR seinen eigenen System-Prompt, vollständig isoliert von deinen Chat-Einstellungen. Ein benutzerdefinierter Prompt unten überschreibt die Standardeinstellungen.", + }, + "Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails.": { + "nb": "E-postoppgaver har SKRIVEBESKYTTET tilgang til MCP-mapper. KI-en kan ikke skrive, slette eller endre filer ved behandling av e-post.", + "sv": "E-postuppgifter har LÄSBEHÖRIGHET till MCP-mappar. AI:n kan inte skriva, radera eller ändra filer vid e-postbehandling.", + "da": "E-mailopgaver har KUN LÆSEADGANG til MCP-mapper. AI'en kan ikke skrive, slette eller ændre filer under behandling af e-mails.", + "de": "E-Mail-Aufgaben haben NUR LESEZUGRIFF auf MCP-Ordner. Die KI kann beim Verarbeiten von E-Mails keine Dateien schreiben, löschen oder ändern.", + }, + "Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens.": { + "nb": "Aktiver KI-drevet semantisk søk med %@-innbygginger. Kostnad: ~$0,02–0,15/1M tokens.", + "sv": "Aktivera AI-driven semantisk sökning med %@-embeddings. Kostnad: ~$0,02–0,15/1M tokens.", + "da": "Aktiver AI-drevet semantisk søgning med %@-indlejringer. Omkostning: ~$0,02–0,15/1M tokens.", + "de": "KI-gestützte semantische Suche mit %@-Embeddings aktivieren. Kosten: ~$0,02–0,15/1M Tokens.", + }, + "Enable email handler in Settings to start monitoring emails": { + "nb": "Aktiver e-postbehandler i Innstillinger for å starte overvåking av e-post", + "sv": "Aktivera e-posthanteraren i Inställningar för att börja övervaka e-post", + "da": "Aktiver e-mailbehandler i Indstillinger for at begynde at overvåge e-mails", + "de": "E-Mail-Handler in den Einstellungen aktivieren, um E-Mails zu überwachen", + }, + "Error: %@": { + "nb": "Feil: %@", + "sv": "Fel: %@", + "da": "Fejl: %@", + "de": "Fehler: %@", + }, + "Example structure:": { + "nb": "Eksempelstruktur:", + "sv": "Exempelstruktur:", + "da": "Eksempelstruktur:", + "de": "Beispielstruktur:", + }, + "File Access Permissions": { + "nb": "Filtillatelser", + "sv": "Filbehörigheter", + "da": "Filtilladelser", + "de": "Dateizugriffsberechtigungen", + }, + "Files": { + "nb": "Filer", + "sv": "Filer", + "da": "Filer", + "de": "Dateien", + }, + "Help": { + "nb": "Hjelp", + "sv": "Hjälp", + "da": "Hjælp", + "de": "Hilfe", + }, + "How to get your API token:": { + "nb": "Slik får du API-tokenet ditt:", + "sv": "Så här hämtar du din API-token:", + "da": "Sådan får du dit API-token:", + "de": "So erhältst du deinen API-Token:", + }, + "Initialize Repository": { + "nb": "Initialiser repositorium", + "sv": "Initiera förvar", + "da": "Initialiser repository", + "de": "Repository initialisieren", + }, + "Inject into system prompt for every conversation": { + "nb": "Injiser i systemprompten for hver samtale", + "sv": "Injicera i systemprompten för varje konversation", + "da": "Injicér i systemprompten for hver samtale", + "de": "In den System-Prompt jedes Gesprächs einfügen", + }, + "Large files inflate the system prompt and may hit token limits.": { + "nb": "Store filer blåser opp systemprompten og kan nå token-grenser.", + "sv": "Stora filer blåser upp systemprompten och kan nå token-gränser.", + "da": "Store filer oppblæser systemprompten og kan ramme token-grænser.", + "de": "Große Dateien blähen den System-Prompt auf und können Token-Limits erreichen.", + }, + "Last sync: %@": { + "nb": "Siste synkronisering: %@", + "sv": "Senaste synkronisering: %@", + "da": "Seneste synkronisering: %@", + "de": "Letzte Synchronisierung: %@", + }, + "Leave empty to use the default email handler system prompt.": { + "nb": "La stå tomt for å bruke standard systemprompt for e-postbehandler.", + "sv": "Lämna tomt för att använda e-posthanterarens standardsystemprompt.", + "da": "Lad stå tomt for at bruge standard systemprompt til e-mailbehandler.", + "de": "Leer lassen, um den Standard-System-Prompt des E-Mail-Handlers zu verwenden.", + }, + "Lowercase letters, numbers, and hyphens only. No spaces.": { + "nb": "Kun små bokstaver, tall og bindestreker. Ingen mellomrom.", + "sv": "Endast gemener, siffror och bindestreck. Inga blanksteg.", + "da": "Kun små bogstaver, tal og bindestreger. Ingen mellemrum.", + "de": "Nur Kleinbuchstaben, Zahlen und Bindestriche. Keine Leerzeichen.", + }, + "MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks.": { + "nb": "MCP gir KI-en kontrollert tilgang til å lese og eventuelt skrive filer på datamaskinen din. KI-en kan søke, lese og analysere filer i tillatte mapper for å hjelpe med koding, analyse og andre oppgaver.", + "sv": "MCP ger AI:n kontrollerad åtkomst att läsa och eventuellt skriva filer på din dator. AI:n kan söka, läsa och analysera filer i tillåtna mappar för att hjälpa till med kodning, analys och andra uppgifter.", + "da": "MCP giver AI'en kontrolleret adgang til at læse og eventuelt skrive filer på din computer. AI'en kan søge, læse og analysere filer i tilladte mapper for at hjælpe med kodning, analyse og andre opgaver.", + "de": "MCP gibt der KI kontrollierten Zugriff, um Dateien auf deinem Computer zu lesen und optional zu schreiben. Die KI kann Dateien in erlaubten Ordnern suchen, lesen und analysieren, um bei Programmierung, Analyse und anderen Aufgaben zu helfen.", + }, + "Markdown instruction files injected into the system prompt. Compatible with SKILL.md format.": { + "nb": "Markdown-instruksjonsfiler som injiseres i systemprompten. Kompatibel med SKILL.md-format.", + "sv": "Markdown-instruktionsfiler som injiceras i systemprompten. Kompatibel med SKILL.md-format.", + "da": "Markdown-instruktionsfiler injiceret i systemprompten. Kompatibel med SKILL.md-format.", + "de": "Markdown-Anweisungsdateien, die in den System-Prompt eingefügt werden. Kompatibel mit SKILL.md-Format.", + }, + "Max Tokens: set to 0 to use model default. Higher values allow longer responses.": { + "nb": "Maks tokens: sett til 0 for å bruke modellstandard. Høyere verdier gir lengre svar.", + "sv": "Max tokens: sätt till 0 för att använda modellens standard. Högre värden tillåter längre svar.", + "da": "Maks tokens: sæt til 0 for at bruge modelstandard. Højere værdier tillader længere svar.", + "de": "Maximale Tokens: auf 0 setzen, um den Modell-Standard zu verwenden. Höhere Werte erlauben längere Antworten.", + }, + "Model Info": { + "nb": "Modellinformasjon", + "sv": "Modellinformation", + "da": "Modelinformation", + "de": "Modell-Info", + }, + "Multi-provider AI chat client": { + "nb": "AI-chatklient med støtte for flere tilbydere", + "sv": "AI-chattklient med stöd för flera leverantörer", + "da": "AI-chatklient med understøttelse af flere udbydere", + "de": "KI-Chat-Client mit mehreren Anbietern", + }, + "Name": { + "nb": "Navn", + "sv": "Namn", + "da": "Navn", + "de": "Name", + }, + "Never": { + "nb": "Aldri", + "sv": "Aldrig", + "da": "Aldrig", + "de": "Nie", + }, + "No credit data available": { + "nb": "Ingen kredittdata tilgjengelig", + "sv": "Ingen kreditdata tillgänglig", + "da": "Ingen kreditdata tilgængelig", + "de": "Keine Guthabendaten verfügbar", + }, + "No email activity yet": { + "nb": "Ingen e-postaktivitet ennå", + "sv": "Ingen e-postaktivitet ännu", + "da": "Ingen e-mailaktivitet endnu", + "de": "Noch keine E-Mail-Aktivität", + }, + "No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill.": { + "nb": "Ingen filer vedlagt. Legg til JSON, YAML, CSV eller TXT-filer for å injisere data i systemprompten sammen med denne ferdigheten.", + "sv": "Inga filer bifogade. Lägg till JSON, YAML, CSV eller TXT-filer för att injicera data i systemprompten bredvid denna färdighet.", + "da": "Ingen filer vedhæftet. Tilføj JSON, YAML, CSV eller TXT-filer for at injicere data i systemprompten ved siden af denne færdighed.", + "de": "Keine Dateien angehängt. Füge JSON, YAML, CSV oder TXT-Dateien hinzu, um Daten zusammen mit diesem Skill in den System-Prompt einzufügen.", + }, + "No model selected": { + "nb": "Ingen modell valgt", + "sv": "Ingen modell vald", + "da": "Ingen model valgt", + "de": "Kein Modell ausgewählt", + }, + "No models available": { + "nb": "Ingen modeller tilgjengelig", + "sv": "Inga modeller tillgängliga", + "da": "Ingen modeller tilgængelige", + "de": "Keine Modelle verfügbar", + }, + 'No results for "%@"': { + "nb": "Ingen resultater for «%@»", + "sv": "Inga resultat för \"%@\"", + "da": "Ingen resultater for \"%@\"", + "de": "Keine Ergebnisse f\u00fcr \u201e%@\u201c", + }, + "No shortcuts yet": { + "nb": "Ingen snarveier ennå", + "sv": "Inga genvägar ännu", + "da": "Ingen genveje endnu", + "de": "Noch keine Verknüpfungen", + }, + "No shortcuts yet — click New Shortcut to create one.": { + "nb": "Ingen snarveier ennå — klikk på Ny snarvei for å opprette en.", + "sv": "Inga genvägar ännu — klicka på Ny genväg för att skapa en.", + "da": "Ingen genveje endnu — klik på Ny genvej for at oprette en.", + "de": "Noch keine Verkn\u00fcpfungen \u2014 klicke auf \u201eNeue Verkn\u00fcpfung\u201c, um eine zu erstellen.", + }, + "No skills yet": { + "nb": "Ingen ferdigheter ennå", + "sv": "Inga färdigheter ännu", + "da": "Ingen færdigheder endnu", + "de": "Noch keine Skills", + }, + "No skills yet — click New Skill or Import to get started.": { + "nb": "Ingen ferdigheter ennå — klikk på Ny ferdighet eller Importer for å komme i gang.", + "sv": "Inga färdigheter ännu — klicka på Ny färdighet eller Importera för att komma igång.", + "da": "Ingen færdigheder endnu — klik på Ny færdighed eller Importer for at komme i gang.", + "de": "Noch keine Skills \u2014 klicke auf \u201eNeuer Skill\u201c oder \u201eImportieren\u201c, um loszulegen.", + }, + "Only emails with this text in the subject line will be processed. Example: \"[OAIBOT] What's the weather?\"": { + "nb": "Bare e-poster med denne teksten i emnefeltet behandles. Eksempel: «[OAIBOT] Hva er været?»", + "sv": "Bara e-post med denna text i ämnesraden bearbetas. Exempel: \"[OAIBOT] Vad är vädret?\"", + "da": "Kun e-mails med denne tekst i emnelinjen behandles. Eksempel: \"[OAIBOT] Hvad er vejret?\"", + "de": "Nur E-Mails mit diesem Text in der Betreffzeile werden verarbeitet. Beispiel: \u201e[OAIBOT] Wie ist das Wetter?\u201c", + }, + "Read access (always enabled)": { + "nb": "Lesetilgang (alltid aktivert)", + "sv": "Läsbehörighet (alltid aktiverad)", + "da": "Læseadgang (altid aktiveret)", + "de": "Lesezugriff (immer aktiviert)", + }, + "Restore from File…": { + "nb": "Gjenopprett fra fil…", + "sv": "Återställ från fil…", + "da": "Gendan fra fil…", + "de": "Aus Datei wiederherstellen…", + }, + "Running locally — no credits needed!": { + "nb": "Kjører lokalt — ingen kreditter nødvendig!", + "sv": "Körs lokalt — inga krediter behövs!", + "da": "Kører lokalt — ingen kreditter nødvendige!", + "de": "Läuft lokal — kein Guthaben benötigt!", + }, + "SKILL.md format — write instructions in plain Markdown.": { + "nb": "SKILL.md-format — skriv instruksjoner i vanlig Markdown.", + "sv": "SKILL.md-format — skriv instruktioner i vanlig Markdown.", + "da": "SKILL.md-format — skriv instruktioner i almindelig Markdown.", + "de": "SKILL.md-Format — Anweisungen in einfachem Markdown schreiben.", + }, + "Security Recommendation": { + "nb": "Sikkerhetsanbefaling", + "sv": "Säkerhetsrekommendation", + "da": "Sikkerhedsanbefaling", + "de": "Sicherheitsempfehlung", + }, + "Shell commands have full access to your system. Only approve commands you understand and trust.": { + "nb": "Skalkommandoer har full tilgang til systemet ditt. Godkjenn bare kommandoer du forstår og stoler på.", + "sv": "Skalkommandon har full åtkomst till ditt system. Godkänn bara kommandon du förstår och litar på.", + "da": "Skalkommandoer har fuld adgang til dit system. Godkend kun kommandoer, du forstår og har tillid til.", + "de": "Shell-Befehle haben vollen Zugriff auf dein System. Genehmige nur Befehle, die du verstehst und denen du vertraust.", + }, + "Show text labels below toolbar icons (helpful for new users)": { + "nb": "Vis tekstetiketter under verktøylinjeikonene (nyttig for nye brukere)", + "sv": "Visa textetiketter under verktygsfältets ikoner (hjälpsamt för nya användare)", + "da": "Vis tekstetikvetter under værktøjslinjeikoner (nyttigt for nye brugere)", + "de": "Textbeschriftungen unter Symbolleistensymbolen anzeigen (hilfreich für neue Benutzer)", + }, + "Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt.": { + "nb": "Ferdigheter er Markdown-instruksjonsfiler som lærer KI-en hvordan den skal oppføre seg. Aktive ferdigheter injiseres automatisk i systemprompten.", + "sv": "Färdigheter är Markdown-instruktionsfiler som lär AI:n hur den ska bete sig. Aktiva färdigheter injiceras automatiskt i systemprompten.", + "da": "Færdigheder er Markdown-instruktionsfiler, der lærer AI'en, hvordan den skal opføre sig. Aktive færdigheder injiceres automatisk i systemprompten.", + "de": "Skills sind Markdown-Anweisungsdateien, die der KI beibringen, wie sie sich verhalten soll. Aktive Skills werden automatisch in den System-Prompt eingefügt.", + }, + "Stream responses as they're generated. Disable for single, complete responses.": { + "nb": "Strøm svar etter hvert som de genereres. Deaktiver for enkle, fullstendige svar.", + "sv": "Strömma svar medan de genereras. Inaktivera för fullständiga, enkla svar.", + "da": "Stream svar, efterhånden som de genereres. Deaktiver for enkle, komplette svar.", + "de": "Antworten während der Generierung streamen. Deaktivieren für vollständige Einzelantworten.", + }, + "Sync Now": { + "nb": "Synkroniser nå", + "sv": "Synkronisera nu", + "da": "Synkroniser nu", + "de": "Jetzt synchronisieren", + }, + "Sync conversations and settings across multiple machines using Git.": { + "nb": "Synkroniser samtaler og innstillinger på tvers av flere maskiner ved hjelp av Git.", + "sv": "Synkronisera konversationer och inställningar på flera datorer med Git.", + "da": "Synkroniser samtaler og indstillinger på tværs af flere maskiner ved hjælp af Git.", + "de": "Gespräche und Einstellungen über mehrere Geräte hinweg mit Git synchronisieren.", + }, + "Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative": { + "nb": "Temperatur: 0 = modellstandard · 0,0–0,7 = fokusert · 0,8–2,0 = kreativ", + "sv": "Temperatur: 0 = modellens standard · 0,0–0,7 = fokuserat · 0,8–2,0 = kreativt", + "da": "Temperatur: 0 = modelstandard · 0,0–0,7 = fokuseret · 0,8–2,0 = kreativt", + "de": "Temperatur: 0 = Modell-Standard · 0,0–0,7 = fokussiert · 0,8–2,0 = kreativ", + }, + "Template": { + "nb": "Mal", + "sv": "Mall", + "da": "Skabelon", + "de": "Vorlage", + }, + "Test Connection": { + "nb": "Test tilkobling", + "sv": "Testa anslutning", + "da": "Test forbindelse", + "de": "Verbindung testen", + }, + "Text files are injected into the system prompt alongside the skill.": { + "nb": "Tekstfiler injiseres i systemprompten ved siden av ferdigheten.", + "sv": "Textfiler injiceras i systemprompten bredvid färdigheten.", + "da": "Tekstfiler injiceres i systemprompten ved siden af færdigheden.", + "de": "Textdateien werden zusammen mit dem Skill in den System-Prompt eingefügt.", + }, + "The AI can read and search files in allowed folders": { + "nb": "KI-en kan lese og søke filer i tillatte mapper", + "sv": "AI:n kan läsa och söka i filer i tillåtna mappar", + "da": "AI'en kan læse og søge i filer i tilladte mapper", + "de": "Die KI kann Dateien in erlaubten Ordnern lesen und durchsuchen", + }, + "The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise.": { + "nb": "KI-en leser dette innholdet og bestemmer når det skal brukes. Beskriv **hva** KI-en skal gjøre og **hvordan** — vær spesifikk og konsis.", + "sv": "AI:n läser detta innehåll och bestämmer när det ska tillämpas. Beskriv **vad** AI:n ska göra och **hur** — var specifik och koncis.", + "da": "AI'en læser dette indhold og beslutter, hvornår det skal bruges. Beskriv **hvad** AI'en skal gøre og **hvordan** — vær specifik og præcis.", + "de": "Die KI liest diesen Inhalt und entscheidet, wann sie ihn anwendet. Beschreibe **was** die KI tun soll und **wie** — sei spezifisch und prägnant.", + }, + "The AI wants to run the following command": { + "nb": "KI-en ønsker å kjøre følgende kommando", + "sv": "AI:n vill köra följande kommando", + "da": "AI'en ønsker at køre følgende kommando", + "de": "Die KI möchte den folgenden Befehl ausführen", + }, + "This default prompt is always included to ensure accurate, helpful responses.": { + "nb": "Denne standardprompten er alltid inkludert for å sikre nøyaktige, nyttige svar.", + "sv": "Denna standardprompt ingår alltid för att säkerställa korrekta, användbara svar.", + "da": "Denne standardprompt er altid inkluderet for at sikre præcise, nyttige svar.", + "de": "Dieser Standard-Prompt ist immer enthalten, um genaue und hilfreiche Antworten sicherzustellen.", + }, + "This will permanently delete all email activity logs. This action cannot be undone.": { + "nb": "Dette vil slette alle e-postaktivitetslogger permanent. Denne handlingen kan ikke angres.", + "sv": "Detta raderar alla e-postaktivitetsloggar permanent. Åtgärden kan inte ångras.", + "da": "Dette vil permanent slette alle e-mailaktivitetslogge. Denne handling kan ikke fortrydes.", + "de": "Dadurch werden alle E-Mail-Aktivitätsprotokolle dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.", + }, + "Token Distribution": { + "nb": "Token-fordeling", + "sv": "Token-fördelning", + "da": "Token-fordeling", + "de": "Token-Verteilung", + }, + "Try adjusting your search or filters": { + "nb": "Prøv å justere søket eller filtrene", + "sv": "Försök att justera din sökning eller dina filter", + "da": "Prøv at justere din søgning eller dine filtre", + "de": "Suche oder Filter anpassen", + }, + "Type / in the input to see command suggestions": { + "nb": "Skriv / i inndata for å se kommandoforslag", + "sv": "Skriv / i inmatningsfältet för att se kommandoförslag", + "da": "Skriv / i inputfeltet for at se kommandoforslag", + "de": "/ im Eingabefeld eingeben, um Befehlsvorschläge zu sehen", + }, + "Type a message or / for commands...": { + "nb": "Skriv en melding eller / for kommandoer...", + "sv": "Skriv ett meddelande eller / för kommandon...", + "da": "Skriv en besked eller / for kommandoer...", + "de": "Nachricht eingeben oder / für Befehle...", + }, + "Uncommitted changes: %@": { + "nb": "Ulagrede endringer: %@", + "sv": "Ej sparade ändringar: %@", + "da": "Ikke-committede ændringer: %@", + "de": "Nicht übernommene Änderungen: %@", + }, + "Update Available%@": { + "nb": "Oppdatering tilgjengelig%@", + "sv": "Uppdatering tillgänglig%@", + "da": "Opdatering tilgængelig%@", + "de": "Update verfügbar%@", + }, + "Use **{{input}}** in the template to insert whatever you type after the command.": { + "nb": "Bruk **{{input}}** i malen for å sette inn det du skriver etter kommandoen.", + "sv": "Använd **{{input}}** i mallen för att infoga det du skriver efter kommandot.", + "da": "Brug **{{input}}** i skabelonen til at indsætte det, du skriver efter kommandoen.", + "de": "Verwende **{{input}}** in der Vorlage, um einzufügen, was du nach dem Befehl tippst.", + }, + "Use **{{input}}** to insert whatever you type after the command.": { + "nb": "Bruk **{{input}}** for å sette inn det du skriver etter kommandoen.", + "sv": "Använd **{{input}}** för att infoga det du skriver efter kommandot.", + "da": "Brug **{{input}}** til at indsætte det, du skriver efter kommandoen.", + "de": "Verwende **{{input}}**, um einzufügen, was du nach dem Befehl tippst.", + }, + "Use @filename to attach files to your message": { + "nb": "Bruk @filnavn for å legge ved filer i meldingen", + "sv": "Använd @filnamn för att bifoga filer till ditt meddelande", + "da": "Brug @filnavn for at vedhæfte filer til din besked", + "de": "@dateiname verwenden, um Dateien an die Nachricht anzuhängen", + }, + "View Email Log": { + "nb": "Vis e-postlogg", + "sv": "Visa e-postlogg", + "da": "Vis e-maillog", + "de": "E-Mail-Protokoll anzeigen", + }, + "When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore.": { + "nb": "Når aktivert, hoppes gitignorerte filer over ved listing og søk. Skriveoperasjoner ignorerer alltid .gitignore.", + "sv": "När aktiverat hoppar listning och sökning över gitignorerade filer. Skrivoperationer ignorerar alltid .gitignore.", + "da": "Når aktiveret, springes gitignorerede filer over ved listning og søgning. Skriveoperationer ignorerer altid .gitignore.", + "de": "Wenn aktiviert, werden gitignorierte Dateien beim Auflisten und Suchen übersprungen. Schreiboperationen ignorieren immer .gitignore.", + }, + "Working directory:": { + "nb": "Arbeidsmappe:", + "sv": "Arbetskatalog:", + "da": "Arbejdsmappe:", + "de": "Arbeitsverzeichnis:", + }, + "You can import any SKILL.md file from skill0.io or write your own.": { + "nb": "Du kan importere en hvilken som helst SKILL.md-fil fra skill0.io eller skrive din egen.", + "sv": "Du kan importera vilken SKILL.md-fil som helst från skill0.io eller skriva din egen.", + "da": "Du kan importere en hvilken som helst SKILL.md-fil fra skill0.io eller skrive din egen.", + "de": "Du kannst jede SKILL.md-Datei von skill0.io importieren oder deine eigene schreiben.", + }, + "^[%@ entry](inflect: true)": { + "nb": "^[%@ oppføring](inflect: true)", + "sv": "^[%@ post](inflect: true)", + "da": "^[%@ post](inflect: true)", + "de": "^[%@ Eintrag](inflect: true)", + }, + "active": { + "nb": "aktiv", + "sv": "aktiv", + "da": "aktiv", + "de": "aktiv", + }, + "messages": { + "nb": "meldinger", + "sv": "meddelanden", + "da": "beskeder", + "de": "Nachrichten", + }, + "~750 tokens ≈ 500 words. Online mode allows web search in responses.": { + "nb": "~750 tokens ≈ 500 ord. Nettmodus tillater nettsøk i svar.", + "sv": "~750 tokens ≈ 500 ord. Onlineläge tillåter webbsökning i svar.", + "da": "~750 tokens ≈ 500 ord. Onlinetilstand tillader websøgning i svar.", + "de": "~750 Tokens ≈ 500 Wörter. Online-Modus ermöglicht Websuche in Antworten.", + }, + "• Add public key to your git provider": { + "nb": "• Legg til den offentlige nøkkelen hos git-leverandøren din", + "sv": "• Lägg till den offentliga nyckeln hos din git-leverantör", + "da": "• Tilføj den offentlige nøgle til din git-udbyder", + "de": "• Öffentlichen Schlüssel beim Git-Anbieter hinzufügen", + }, + "• No credentials needed in oAI": { + "nb": "• Ingen legitimasjon nødvendig i oAI", + "sv": "• Inga inloggningsuppgifter behövs i oAI", + "da": "• Ingen legitimationsoplysninger nødvendige i oAI", + "de": "• Keine Zugangsdaten in oAI erforderlich", + }, + "• Uses your system SSH keys (~/.ssh/id_ed25519)": { + "nb": "• Bruker systemets SSH-nøkler (~/.ssh/id_ed25519)", + "sv": "• Använder dina system-SSH-nycklar (~/.ssh/id_ed25519)", + "da": "• Bruger dine system-SSH-nøgler (~/.ssh/id_ed25519)", + "de": "• Verwendet deine System-SSH-Schlüssel (~/.ssh/id_ed25519)", + }, + "ℹ️ SSH Key Authentication": { + "nb": "ℹ️ SSH-nøkkelautentisering", + "sv": "ℹ️ SSH-nyckelautentisering", + "da": "ℹ️ SSH-nøglegodkendelse", + "de": "ℹ️ SSH-Schlüssel-Authentifizierung", + }, + "⚠️ Custom prompt active — only this prompt will be sent to the model.": { + "nb": "⚠️ Egendefinert prompt aktiv — bare denne prompten sendes til modellen.", + "sv": "⚠️ Anpassad prompt aktiv — bara denna prompt skickas till modellen.", + "da": "⚠️ Brugerdefineret prompt aktiv — kun denne prompt sendes til modellen.", + "de": "⚠️ Benutzerdefinierter Prompt aktiv — nur dieser Prompt wird an das Modell gesendet.", + }, + "⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead.": { + "nb": "⚠️ Mange tilbydere (GitHub) støtter ikke lenger passordautentisering. Bruk Access Token i stedet.", + "sv": "⚠️ Många leverantörer (GitHub) stöder inte längre lösenordsautentisering. Använd Access Token istället.", + "da": "⚠️ Mange udbydere (GitHub) understøtter ikke længere adgangskodegodkendelse. Brug Access Token i stedet.", + "de": "⚠️ Viele Anbieter (GitHub) unterstützen keine Passwort-Authentifizierung mehr. Stattdessen Access Token verwenden.", + }, + "⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab.": { + "nb": "⚠️ Ingen innbyggingsleverandører tilgjengelig. Konfigurer en API-nøkkel for OpenAI, OpenRouter eller Google i fanen Generelt.", + "sv": "⚠️ Inga embedding-leverantörer tillgängliga. Konfigurera en API-nyckel för OpenAI, OpenRouter eller Google på fliken Allmänt.", + "da": "⚠️ Ingen indlejringsudbydere tilgængelige. Konfigurer en API-nøgle til OpenAI, OpenRouter eller Google på fanen Generelt.", + "de": "⚠️ Keine Embedding-Anbieter verf\u00fcgbar. Konfiguriere einen API-Schl\u00fcssel f\u00fcr OpenAI, OpenRouter oder Google auf der Registerkarte \u201eAllgemein\u201c.", + }, + "⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages.": { + "nb": "⚠️ Engangsoperasjon — genererer innbygginger for alle meldinger. Estimert kostnad: ~$0,04 for 10 000 meldinger.", + "sv": "⚠️ Engångsoperation — genererar embeddings för alla meddelanden. Uppskattad kostnad: ~$0,04 för 10 000 meddelanden.", + "da": "⚠️ Engangshandling — genererer indlejringer for alle beskeder. Estimeret omkostning: ~$0,04 for 10.000 beskeder.", + "de": "⚠️ Einmaliger Vorgang — generiert Embeddings für alle Nachrichten. Geschätzte Kosten: ~$0,04 für 10.000 Nachrichten.", + }, + "⚠️ These are advanced settings": { + "nb": "⚠️ Dette er avanserte innstillinger", + "sv": "⚠️ Det här är avancerade inställningar", + "da": "⚠️ Dette er avancerede indstillinger", + "de": "⚠️ Das sind erweiterte Einstellungen", + }, + "⚠️ interrupted": { + "nb": "⚠️ avbrutt", + "sv": "⚠️ avbruten", + "da": "⚠️ afbrudt", + "de": "⚠️ unterbrochen", + }, + "💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords.": { + "nb": "💡 For Gmail, bruk et apppassord. Google-konto > Sikkerhet > 2-trinns bekreftelse > Apppassord.", + "sv": "💡 För Gmail, använd ett applösenord. Google-konto > Säkerhet > 2-stegsverifiering > Applösenord.", + "da": "💡 Til Gmail, brug et app-adgangskode. Google-konto > Sikkerhed > 2-trinsbekræftelse > App-adgangskoder.", + "de": "💡 Für Gmail ein App-Passwort verwenden. Google-Konto > Sicherheit > 2-Schritt-Verifizierung > App-Passwörter.", + }, + "💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods.": { + "nb": "💡 Bruk HTTPS URL (f.eks. https://gitlab.pm/user/repo.git) — fungerer med alle autentiseringsmetoder.", + "sv": "💡 Använd HTTPS URL (t.ex. https://gitlab.pm/user/repo.git) — fungerar med alla autentiseringsmetoder.", + "da": "💡 Brug HTTPS URL (f.eks. https://gitlab.pm/user/repo.git) — fungerer med alle godkendelsesmetoder.", + "de": "💡 HTTPS-URL verwenden (z.B. https://gitlab.pm/user/repo.git) — funktioniert mit allen Authentifizierungsmethoden.", + }, +} + + +def xml_escape(s): + """Escape XML special characters.""" + return (s + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace('"', """)) + + +def get_translation(source_text, lang): + """Return the translated text for a given source and language.""" + # Try exact match first + if source_text in T: + val = T[source_text] + if val is None: + return source_text # keep as-is + return val.get(lang, source_text) + # Return source unchanged if not found + return source_text + + +def process_xliff(lang): + src_path = f"{BASE}/{lang}.xcloc/Localized Contents/{lang}.xliff" + dst_path = src_path # overwrite in place + + with open(src_path, "r", encoding="utf-8") as f: + content = f.read() + + # We'll process trans-unit blocks and inject after + def replace_unit(m): + block = m.group(0) + # Extract source text (may span multiple lines) + src_match = re.search(r'(.*?)', block, re.DOTALL) + if not src_match: + return block + # Check if target already exists + if '' in block: + return block + raw_source = src_match.group(1) + # Unescape XML entities to get the actual key + key = (raw_source + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace(""", '"')) + translation = get_translation(key, lang) + escaped_translation = xml_escape(translation) + # Insert after + new_block = block.replace( + f'', + f'\n {escaped_translation}', + 1 + ) + return new_block + + # Match each trans-unit block + new_content = re.sub( + r']*>.*?', + replace_unit, + content, + flags=re.DOTALL + ) + + with open(dst_path, "w", encoding="utf-8") as f: + f.write(new_content) + print(f" ✓ {lang}.xliff written") + + +langs = ["nb", "sv", "da", "de"] +for lang in langs: + print(f"Processing {lang}...") + process_xliff(lang) + +print("\nDone! All four XLIFF files updated.") +print("Next step: In Xcode → Product → Import Localizations → select each .xcloc package.") diff --git a/Locales/oAI Localizations/da.xcloc/Localized Contents/da.xliff b/Locales/oAI Localizations/da.xcloc/Localized Contents/da.xliff new file mode 100644 index 0000000..584c6f0 --- /dev/null +++ b/Locales/oAI Localizations/da.xcloc/Localized Contents/da.xliff @@ -0,0 +1,843 @@ + + + +
+ +
+ + + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + + + + %@ + %@ + + + + %@ active — injected into every conversation + %@ aktiv — injiceres i alle samtaler + A footnote indicating that some skills are active. + + + %@ chars + %@ tegn + A label showing the number of characters in the skill's content. The argument is the number of characters in the content. + + + %@ min + %@ min + + + + %@ pt + %@ pt + A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points. + + + %@s + %@s + + + + (always used) + (altid brugt) + + + + (optional) + (valgfri) + + + + 1. Open Paperless-NGX → Settings → API Tokens + 1. Åbn Paperless-NGX → Indstillinger → API-tokens + A step in the process of getting a Paperless-NGX API token. + + + 2. Create or copy your token + 2. Opret eller kopier dit token + A step in the process of getting a Paperless-NGX API token. + + + 3. Paste it above + 3. Indsæt det ovenfor + A step in the process of getting a Paperless-NGX API token. + + + A shortcut with command %@ already exists. + En genvej med kommandoen %@ findes allerede. + A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut. + + + API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine. + API-nøgler og legitimationsoplysninger er **ikke** inkluderet i sikkerhedskopien. Du skal indtaste dem igen efter gendannelse på en ny maskine. + A warning message displayed in the settings tab of the app. + + + Access Token + Access Token + + + + Active + Aktiv + A toggle + + + Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows. + Aktive færdigheder tilføjes til systemprompten. Slå dem til og fra per færdighed for at styre, hvad AI'en ved. + A description of how to toggle the active status of a skill. + + + Agent Skills + Agent-færdigheder + + + + Allow Shell Command? + Tillad skalkommando? + A title for a modal that asks the user if they want to allow a shell command. + + + Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs. + Giv AI'en adgang til at køre skalkommandoer på din maskine. Kommandoer udføres via /bin/zsh. Aktiver godkendelsestilstand for at gennemgå hver kommando, inden den køres. + + + + Anthropic Balance + Anthropic Balance + A label displayed above the user's balance on the Anthropic platform. + + + Auto-execute mode: commands run without approval. Use with caution. + Automatisk kørselstilstand: kommandoer køres uden godkendelse. Brug med forsigtighed. + + + + Auto-sync can cause conflicts if running on multiple machines simultaneously. + Auto-synkronisering kan forårsage konflikter, hvis den kører på flere maskiner samtidigt. + + + + Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations. + Vælg automatisk relevante beskeder i stedet for at sende al historik. Reducerer tokenforbrug for lange samtaler. + + + + Automatically summarize old portions of long conversations to save tokens and improve context efficiency. + Opsummer automatisk ældre dele af lange samtaler for at spare tokens og forbedre konteksteffektiviteten. + + + + Back Up Now + Sikkerhedskopier nu + A button that backs up the user's data. + + + Backup location: + Sikkerhedskopieringsplacering: + A label displayed under the backup location. + + + Bash Execution + Bash Execution + + + + Branch: %@ + Branch: %@ + + + + Built with SwiftUI + Built with SwiftUI + A label that says that the app is built with SwiftUI. + + + COMMAND + KOMMANDO + A label displayed above the command that the AI wants to run. + + + Capabilities + Egenskaber + A label displayed under the list of capabilities of a model. + + + Changing these values affects how the AI generates responses. The defaults work well for most use cases. + Ændring af disse værdier påvirker, hvordan AI'en genererer svar. Standardværdierne fungerer godt til de fleste anvendelser. + + + + Check your balance at: + Tjek din saldo på: + A label displayed under a link to check the user's balance. + + + Check your usage at: + Tjek dit forbrug på: + A label displayed under a link to OpenAI's usage page. + + + Clear All + Ryd alle + A button to clear all email activity logs. + + + Click 'Add Folder' below or drag folders here from Finder + Klik på 'Tilføj mappe' nedenfor eller træk mapper hertil fra Finder + + + + Command + Kommando + A label displayed above the command field. + + + Command History + Kommandohistorik + A label displayed above the command history. + + + Content (Markdown) + Indhold (Markdown) + A + + + Controls which messages are written to ~/Library/Logs/oAI.log + Styrer hvilke beskeder der skrives til ~/Library/Logs/oAI.log + + + + Conversations + Samtaler + + + + Copied! + Kopieret! + A message displayed when a code block is successfully copied to the clipboard. + + + Cost Examples + Omkostningseksempler + A heading for the cost examples of a model. + + + Create a dedicated email account specifically for AI handling. Do NOT use your personal email address. + Opret en dedikeret e-mailkonto specifikt til AI-håndtering. Brug IKKE din personlige e-mailadresse. + + + + Create a shortcut to save a reusable prompt template accessible from the / command dropdown. + Opret en genvej til at gemme en genanvendelig promptskabelon, der er tilgængelig fra /kommando-rullemenuen. + A description of the functionality of the shortcut editor. + + + Default Prompt + Standardprompt + + + + Delete (%@) + Slet (%@) + + + + Description + Beskrivelse + A label + + + Each command will require your approval before running. + Hver kommando kræver din godkendelse, inden den køres. + + + + Edit + Rediger + A button to edit a skill. + + + Email Activity Log + E-mailaktivitetslog + The title of the email activity log view. + + + Email Handler System Prompt + Systemprompt til e-mailbehandler + + + + Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults. + E-mailbehandleren bruger KUN sin egen systemprompt, fuldstændig isoleret fra dine vigtigste chatindstillinger. En brugerdefineret prompt nedenfor tilsidesætter standardindstillingerne. + + + + Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails. + E-mailopgaver har KUN LÆSEADGANG til MCP-mapper. AI'en kan ikke skrive, slette eller ændre filer under behandling af e-mails. + + + + Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens. + Aktiver AI-drevet semantisk søgning med %@-indlejringer. Omkostning: ~$0,02–0,15/1M tokens. + + + + Enable email handler in Settings to start monitoring emails + Aktiver e-mailbehandler i Indstillinger for at begynde at overvåge e-mails + A message displayed when the email handler is disabled. + + + Error: %@ + Fejl: %@ + A label displaying an error message in the checkout view. + + + Example structure: + Eksempelstruktur: + A + + + Example: oai-bot-x7k2m9p3@gmail.com + Example: oai-bot-x7k2m9p3@gmail.com + + + + File Access Permissions + Filtilladelser + + + + Files + Filer + A label displayed above the list of files attached to a skill. + + + Google (Gemini embedding) + Google (Gemini embedding) + + + + Help + Hjælp + A heading for the help screen + + + How to get your API token: + Sådan får du dit API-token: + A heading for a section that describes how to get your API token. + + + Initialize Repository + Initialiser repository + + + + Inject into system prompt for every conversation + Injicér i systemprompten for hver samtale + + + + Large files inflate the system prompt and may hit token limits. + Store filer oppblæser systemprompten og kan ramme token-grænser. + A warning displayed when a user adds a large file to a skill. + + + Last sync: %@ + Seneste synkronisering: %@ + + + + Leave empty to use the default email handler system prompt. + Lad stå tomt for at bruge standard systemprompt til e-mailbehandler. + + + + Lowercase letters, numbers, and hyphens only. No spaces. + Kun små bogstaver, tal og bindestreger. Ingen mellemrum. + A description of the format of a shortcut's command. + + + MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks. + MCP giver AI'en kontrolleret adgang til at læse og eventuelt skrive filer på din computer. AI'en kan søge, læse og analysere filer i tilladte mapper for at hjælpe med kodning, analyse og andre opgaver. + + + + Markdown instruction files injected into the system prompt. Compatible with SKILL.md format. + Markdown-instruktionsfiler injiceret i systemprompten. Kompatibel med SKILL.md-format. + A description of the format of agent skills. + + + Max Tokens: set to 0 to use model default. Higher values allow longer responses. + Maks tokens: sæt til 0 for at bruge modelstandard. Højere værdier tillader længere svar. + + + + Model Context Protocol + Model Context Protocol + + + + Model Info + Modelinformation + A title for a view that displays information about a model. + + + Multi-provider AI chat client + AI-chatklient med understøttelse af flere udbydere + A description of oAI. + + + Name + Navn + A label + + + Never + Aldrig + A label displayed in the settings view that indicates when the last backup was taken. + + + No credit data available + Ingen kreditdata tilgængelig + A message displayed when there is no credit data available. + + + No email activity yet + Ingen e-mailaktivitet endnu + A message displayed when the user has not yet received any email activity. + + + No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill. + Ingen filer vedhæftet. Tilføj JSON, YAML, CSV eller TXT-filer for at injicere data i systemprompten ved siden af denne færdighed. + A message displayed when a user has not attached any files to a skill. + + + No model selected + Ingen model valgt + A label displayed when no model is selected. + + + No models available + Ingen modeller tilgængelige + + + + No results for "%@" + Ingen resultater for "%@" + A message displayed when there are no search results. The argument is the search term. + + + No shortcuts yet + Ingen genveje endnu + A message displayed when a user has no shortcuts. + + + No shortcuts yet — click New Shortcut to create one. + Ingen genveje endnu — klik på Ny genvej for at oprette en. + A message displayed when a user has no shortcuts. + + + No skills yet + Ingen færdigheder endnu + A message displayed when the user has no skills. + + + No skills yet — click New Skill or Import to get started. + Ingen færdigheder endnu — klik på Ny færdighed eller Importer for at komme i gang. + A message displayed when a user has not created any skills. + + + Ollama (Local) + Ollama (Local) + A label displayed above the credits information for the local Ollie. + + + Only emails with this text in the subject line will be processed. Example: "[OAIBOT] What's the weather?" + Kun e-mails med denne tekst i emnelinjen behandles. Eksempel: "[OAIBOT] Hvad er vejret?" + + + + OpenAI (text-embedding-3-large) + OpenAI (text-embedding-3-large) + + + + OpenAI (text-embedding-3-small) + OpenAI (text-embedding-3-small) + + + + OpenAI Balance + OpenAI Balance + A label displayed above the OpenAI balance section. + + + OpenRouter (OpenAI large) + OpenRouter (OpenAI large) + + + + OpenRouter (OpenAI small) + OpenRouter (OpenAI small) + + + + OpenRouter (Qwen 8B) + OpenRouter (Qwen 8B) + + + + OpenRouter Credits + OpenRouter Credits + A heading for the user's OpenRouter credits. + + + Read access (always enabled) + Læseadgang (altid aktiveret) + + + + Remote: %@ + Remote: %@ + + + + Restore from File… + Gendan fra fil… + A button that allows the user to restore their data from a file. + + + Running locally — no credits needed! + Kører lokalt — ingen kreditter nødvendige! + A message displayed when using an on-device LLM like the one provided by the `.ollama` provider. + + + SKILL.md format — write instructions in plain Markdown. + SKILL.md-format — skriv instruktioner i almindelig Markdown. + + + + SSH Key + SSH Key + + + + Security Recommendation + Sikkerhedsanbefaling + + + + Shell commands have full access to your system. Only approve commands you understand and trust. + Skalkommandoer har fuld adgang til dit system. Godkend kun kommandoer, du forstår og har tillid til. + A warning banner displayed in the Bash Approval Sheet. + + + Show text labels below toolbar icons (helpful for new users) + Vis tekstetikvetter under værktøjslinjeikoner (nyttigt for nye brugere) + A description of the feature that shows text labels below toolbar icons. + + + Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt. + Færdigheder er Markdown-instruktionsfiler, der lærer AI'en, hvordan den skal opføre sig. Aktive færdigheder injiceres automatisk i systemprompten. + A description of how skills are used by the AI. + + + Stream responses as they're generated. Disable for single, complete responses. + Stream svar, efterhånden som de genereres. Deaktiver for enkle, komplette svar. + + + + Sync Now + Synkroniser nu + + + + Sync conversations and settings across multiple machines using Git. + Synkroniser samtaler og indstillinger på tværs af flere maskiner ved hjælp af Git. + + + + Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative + Temperatur: 0 = modelstandard · 0,0–0,7 = fokuseret · 0,8–2,0 = kreativt + + + + Template + Skabelon + A label displayed above the text editor for the shortcut's template. + + + Test Connection + Test forbindelse + + + + Text files are injected into the system prompt alongside the skill. + Tekstfiler injiceres i systemprompten ved siden af færdigheden. + A description of how text files are injected into the system prompt. + + + The AI can read and search files in allowed folders + AI'en kan læse og søge i filer i tilladte mapper + + + + The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise. + AI'en læser dette indhold og beslutter, hvornår det skal bruges. Beskriv **hvad** AI'en skal gøre og **hvordan** — vær specifik og præcis. + + + + The AI wants to run the following command + AI'en ønsker at køre følgende kommando + A description of the command that the AI wants to run. + + + This default prompt is always included to ensure accurate, helpful responses. + Denne standardprompt er altid inkluderet for at sikre præcise, nyttige svar. + + + + This will permanently delete all email activity logs. This action cannot be undone. + Dette vil permanent slette alle e-mailaktivitetslogge. Denne handling kan ikke fortrydes. + A message displayed in an alert when the user is about to clear all email logs. + + + Token Distribution + Token-fordeling + A label displayed under the token distribution bar. + + + Try adjusting your search or filters + Prøv at justere din søgning eller dine filtre + A description of the error that occurs when no models match the user's search. + + + Type / in the input to see command suggestions + Skriv / i inputfeltet for at se kommandoforslag + A description of how to use the keyboard shortcut. + + + Type a message or / for commands... + Skriv en besked eller / for kommandoer... + A placeholder text displayed in the input area when + + + Uncommitted changes: %@ + Ikke-committede ændringer: %@ + + + + Update Available%@ + Opdatering tilgængelig%@ + A button that opens a website with information about a new version of oAI. The argument is the version number of the new version. + + + Use **{{input}}** in the template to insert whatever you type after the command. + Brug **{{input}}** i skabelonen til at indsætte det, du skriver efter kommandoen. + A tooltip for the "lightbulb" icon in the shortcuts view + + + Use **{{input}}** to insert whatever you type after the command. + Brug **{{input}}** til at indsætte det, du skriver efter kommandoen. + A description of how to use the shortcut template. + + + Use @filename to attach files to your message + Brug @filnavn for at vedhæfte filer til din besked + A description of how to attach files to a message. + + + Username + Password + Username + Password + + + + Version %1$@ (%2$@) + Version %1$@ (%2$@) + A version number in the format "Version 1.0 (1)" + + + View Email Log + Vis e-maillog + + + + When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore. + Når aktiveret, springes gitignorerede filer over ved listning og søgning. Skriveoperationer ignorerer altid .gitignore. + + + + Working directory: + Arbejdsmappe: + A label displayed under the working directory of a pending shell command. + + + You can import any SKILL.md file from skill0.io or write your own. + Du kan importere en hvilken som helst SKILL.md-fil fra skill0.io eller skrive din egen. + A description of how to use skills. + + + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + A link to the GNU Affero General Public License. + + + ^[%@ entry](inflect: true) + ^[%@ post](inflect: true) + A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier. + + + active + aktiv + A label for an active skill. + + + messages + beskeder + + + + oAI + oAI + The name of the app. + + + tokens + tokens + + + + v%@ + v%@ + A label showing the current version of oAI. + + + ~750 tokens ≈ 500 words. Online mode allows web search in responses. + ~750 tokens ≈ 500 ord. Onlinetilstand tillader websøgning i svar. + + + + © 2026 [Rune Olsen](https://blog.rune.pm) + © 2026 [Rune Olsen](https://blog.rune.pm) + A copyright notice with the copyright holder's name. + + + • + + A bullet point. + + + • Add public key to your git provider + • Tilføj den offentlige nøgle til din git-udbyder + + + + • No credentials needed in oAI + • Ingen legitimationsoplysninger nødvendige i oAI + + + + • Uses your system SSH keys (~/.ssh/id_ed25519) + • Bruger dine system-SSH-nøgler (~/.ssh/id_ed25519) + + + + ℹ️ SSH Key Authentication + ℹ️ SSH-nøglegodkendelse + + + + ↑↓ navigate ↩ open + ↑↓ navigate ↩ open + + + + ⌘N New • ⌘M Model • ⌘S Save + ⌘N New • ⌘M Model • ⌘S Save + A hint that appears on macOS when using keyboard shortcuts. + + + ⚠️ Custom prompt active — only this prompt will be sent to the model. + ⚠️ Brugerdefineret prompt aktiv — kun denne prompt sendes til modellen. + + + + ⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead. + ⚠️ Mange udbydere (GitHub) understøtter ikke længere adgangskodegodkendelse. Brug Access Token i stedet. + + + + ⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab. + ⚠️ Ingen indlejringsudbydere tilgængelige. Konfigurer en API-nøgle til OpenAI, OpenRouter eller Google på fanen Generelt. + + + + ⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages. + ⚠️ Engangshandling — genererer indlejringer for alle beskeder. Estimeret omkostning: ~$0,04 for 10.000 beskeder. + + + + ⚠️ These are advanced settings + ⚠️ Dette er avancerede indstillinger + + + + ⚠️ interrupted + ⚠️ afbrudt + A warning label that appears next to a message that was interrupted. + + + 🌐 + 🌐 + A label for a model that can be used to generate images. + + + 🎨 + 🎨 + A button that generates an image. + + + 👁️ + 👁️ + A checkmark emoji. + + + 💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords. + 💡 Til Gmail, brug et app-adgangskode. Google-konto > Sikkerhed > 2-trinsbekræftelse > App-adgangskoder. + + + + 💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods. + 💡 Brug HTTPS URL (f.eks. https://gitlab.pm/user/repo.git) — fungerer med alle godkendelsesmetoder. + + + + 🔧 + 🔧 + A checkmark emoji. + + +
+ +
+ +
+ + + oAI + oAI + Bundle name + + +
+
diff --git a/Locales/oAI Localizations/da.xcloc/Source Contents/oAI/Localizable.xcstrings b/Locales/oAI Localizations/da.xcloc/Source Contents/oAI/Localizable.xcstrings new file mode 100644 index 0000000..bfdb18c --- /dev/null +++ b/Locales/oAI Localizations/da.xcloc/Source Contents/oAI/Localizable.xcstrings @@ -0,0 +1,590 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "(always used)" : { + + }, + "(optional)" : { + + }, + "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" : { + "comment" : "A link to the GNU Affero General Public License.", + "isCommentAutoGenerated" : true + }, + "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" : { + + }, + "%@" : { + + }, + "%@ active — injected into every conversation" : { + "comment" : "A footnote indicating that some skills are active.", + "isCommentAutoGenerated" : true + }, + "%@ chars" : { + "comment" : "A label showing the number of characters in the skill's content. The argument is the number of characters in the content.", + "isCommentAutoGenerated" : true + }, + "%@ min" : { + + }, + "%@ pt" : { + "comment" : "A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points.", + "isCommentAutoGenerated" : true + }, + "%@s" : { + + }, + "•" : { + "comment" : "A bullet point.", + "isCommentAutoGenerated" : true + }, + "• Add public key to your git provider" : { + + }, + "• No credentials needed in oAI" : { + + }, + "• Uses your system SSH keys (~/.ssh/id_ed25519)" : { + + }, + "^[%@ entry](inflect: true)" : { + "comment" : "A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier.", + "isCommentAutoGenerated" : true + }, + "© 2026 [Rune Olsen](https://blog.rune.pm)" : { + "comment" : "A copyright notice with the copyright holder's name.", + "isCommentAutoGenerated" : true + }, + "↑↓ navigate ↩ open" : { + + }, + "~750 tokens ≈ 500 words. Online mode allows web search in responses." : { + + }, + "⌘N New • ⌘M Model • ⌘S Save" : { + "comment" : "A hint that appears on macOS when using keyboard shortcuts.", + "isCommentAutoGenerated" : true + }, + "⚠️ Custom prompt active — only this prompt will be sent to the model." : { + + }, + "⚠️ interrupted" : { + "comment" : "A warning label that appears next to a message that was interrupted.", + "isCommentAutoGenerated" : true + }, + "⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead." : { + + }, + "⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab." : { + + }, + "⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages." : { + + }, + "⚠️ These are advanced settings" : { + + }, + "🌐" : { + "comment" : "A label for a model that can be used to generate images.", + "isCommentAutoGenerated" : true + }, + "🎨" : { + "comment" : "A button that generates an image.", + "isCommentAutoGenerated" : true + }, + "👁️" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords." : { + + }, + "💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods." : { + + }, + "🔧" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "1. Open Paperless-NGX → Settings → API Tokens" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "2. Create or copy your token" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "3. Paste it above" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "A shortcut with command %@ already exists." : { + "comment" : "A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut.", + "isCommentAutoGenerated" : true + }, + "Access Token" : { + + }, + "active" : { + "comment" : "A label for an active skill.", + "isCommentAutoGenerated" : true + }, + "Active" : { + "comment" : "A toggle", + "isCommentAutoGenerated" : true + }, + "Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows." : { + "comment" : "A description of how to toggle the active status of a skill.", + "isCommentAutoGenerated" : true + }, + "Agent Skills" : { + + }, + "Allow Shell Command?" : { + "comment" : "A title for a modal that asks the user if they want to allow a shell command.", + "isCommentAutoGenerated" : true + }, + "Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs." : { + + }, + "Anthropic Balance" : { + "comment" : "A label displayed above the user's balance on the Anthropic platform.", + "isCommentAutoGenerated" : true + }, + "API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine." : { + "comment" : "A warning message displayed in the settings tab of the app.", + "isCommentAutoGenerated" : true + }, + "Auto-execute mode: commands run without approval. Use with caution." : { + + }, + "Auto-sync can cause conflicts if running on multiple machines simultaneously." : { + + }, + "Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations." : { + + }, + "Automatically summarize old portions of long conversations to save tokens and improve context efficiency." : { + + }, + "Back Up Now" : { + "comment" : "A button that backs up the user's data.", + "isCommentAutoGenerated" : true + }, + "Backup location:" : { + "comment" : "A label displayed under the backup location.", + "isCommentAutoGenerated" : true + }, + "Bash Execution" : { + + }, + "Branch: %@" : { + + }, + "Built with SwiftUI" : { + "comment" : "A label that says that the app is built with SwiftUI.", + "isCommentAutoGenerated" : true + }, + "Capabilities" : { + "comment" : "A label displayed under the list of capabilities of a model.", + "isCommentAutoGenerated" : true + }, + "Changing these values affects how the AI generates responses. The defaults work well for most use cases." : { + + }, + "Check your balance at:" : { + "comment" : "A label displayed under a link to check the user's balance.", + "isCommentAutoGenerated" : true + }, + "Check your usage at:" : { + "comment" : "A label displayed under a link to OpenAI's usage page.", + "isCommentAutoGenerated" : true + }, + "Clear All" : { + "comment" : "A button to clear all email activity logs.", + "isCommentAutoGenerated" : true + }, + "Click 'Add Folder' below or drag folders here from Finder" : { + + }, + "Command" : { + "comment" : "A label displayed above the command field.", + "isCommentAutoGenerated" : true + }, + "COMMAND" : { + "comment" : "A label displayed above the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "Command History" : { + "comment" : "A label displayed above the command history.", + "isCommentAutoGenerated" : true + }, + "Content (Markdown)" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Controls which messages are written to ~/Library/Logs/oAI.log" : { + + }, + "Conversations" : { + + }, + "Copied!" : { + "comment" : "A message displayed when a code block is successfully copied to the clipboard.", + "isCommentAutoGenerated" : true + }, + "Cost Examples" : { + "comment" : "A heading for the cost examples of a model.", + "isCommentAutoGenerated" : true + }, + "Create a dedicated email account specifically for AI handling. Do NOT use your personal email address." : { + + }, + "Create a shortcut to save a reusable prompt template accessible from the / command dropdown." : { + "comment" : "A description of the functionality of the shortcut editor.", + "isCommentAutoGenerated" : true + }, + "Default Prompt" : { + + }, + "Delete (%@)" : { + + }, + "Description" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Each command will require your approval before running." : { + + }, + "Edit" : { + "comment" : "A button to edit a skill.", + "isCommentAutoGenerated" : true + }, + "Email Activity Log" : { + "comment" : "The title of the email activity log view.", + "isCommentAutoGenerated" : true + }, + "Email Handler System Prompt" : { + + }, + "Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults." : { + + }, + "Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails." : { + + }, + "Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens." : { + + }, + "Enable email handler in Settings to start monitoring emails" : { + "comment" : "A message displayed when the email handler is disabled.", + "isCommentAutoGenerated" : true + }, + "Error: %@" : { + "comment" : "A label displaying an error message in the checkout view.", + "isCommentAutoGenerated" : true + }, + "Example structure:" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Example: oai-bot-x7k2m9p3@gmail.com" : { + + }, + "File Access Permissions" : { + + }, + "Files" : { + "comment" : "A label displayed above the list of files attached to a skill.", + "isCommentAutoGenerated" : true + }, + "Google (Gemini embedding)" : { + + }, + "Help" : { + "comment" : "A heading for the help screen", + "isCommentAutoGenerated" : true + }, + "How to get your API token:" : { + "comment" : "A heading for a section that describes how to get your API token.", + "isCommentAutoGenerated" : true + }, + "ℹ️ SSH Key Authentication" : { + + }, + "Initialize Repository" : { + + }, + "Inject into system prompt for every conversation" : { + + }, + "Large files inflate the system prompt and may hit token limits." : { + "comment" : "A warning displayed when a user adds a large file to a skill.", + "isCommentAutoGenerated" : true + }, + "Last sync: %@" : { + + }, + "Leave empty to use the default email handler system prompt." : { + + }, + "Lowercase letters, numbers, and hyphens only. No spaces." : { + "comment" : "A description of the format of a shortcut's command.", + "isCommentAutoGenerated" : true + }, + "Markdown instruction files injected into the system prompt. Compatible with SKILL.md format." : { + "comment" : "A description of the format of agent skills.", + "isCommentAutoGenerated" : true + }, + "Max Tokens: set to 0 to use model default. Higher values allow longer responses." : { + + }, + "MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks." : { + + }, + "messages" : { + + }, + "Model Context Protocol" : { + + }, + "Model Info" : { + "comment" : "A title for a view that displays information about a model.", + "isCommentAutoGenerated" : true + }, + "Multi-provider AI chat client" : { + "comment" : "A description of oAI.", + "isCommentAutoGenerated" : true + }, + "Name" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Never" : { + "comment" : "A label displayed in the settings view that indicates when the last backup was taken.", + "isCommentAutoGenerated" : true + }, + "No credit data available" : { + "comment" : "A message displayed when there is no credit data available.", + "isCommentAutoGenerated" : true + }, + "No email activity yet" : { + "comment" : "A message displayed when the user has not yet received any email activity.", + "isCommentAutoGenerated" : true + }, + "No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill." : { + "comment" : "A message displayed when a user has not attached any files to a skill.", + "isCommentAutoGenerated" : true + }, + "No model selected" : { + "comment" : "A label displayed when no model is selected.", + "isCommentAutoGenerated" : true + }, + "No models available" : { + + }, + "No results for \"%@\"" : { + "comment" : "A message displayed when there are no search results. The argument is the search term.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet" : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet — click New Shortcut to create one." : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No skills yet" : { + "comment" : "A message displayed when the user has no skills.", + "isCommentAutoGenerated" : true + }, + "No skills yet — click New Skill or Import to get started." : { + "comment" : "A message displayed when a user has not created any skills.", + "isCommentAutoGenerated" : true + }, + "oAI" : { + "comment" : "The name of the app.", + "isCommentAutoGenerated" : true + }, + "Ollama (Local)" : { + "comment" : "A label displayed above the credits information for the local Ollie.", + "isCommentAutoGenerated" : true + }, + "Only emails with this text in the subject line will be processed. Example: \"[OAIBOT] What's the weather?\"" : { + + }, + "OpenAI (text-embedding-3-large)" : { + + }, + "OpenAI (text-embedding-3-small)" : { + + }, + "OpenAI Balance" : { + "comment" : "A label displayed above the OpenAI balance section.", + "isCommentAutoGenerated" : true + }, + "OpenRouter (OpenAI large)" : { + + }, + "OpenRouter (OpenAI small)" : { + + }, + "OpenRouter (Qwen 8B)" : { + + }, + "OpenRouter Credits" : { + "comment" : "A heading for the user's OpenRouter credits.", + "isCommentAutoGenerated" : true + }, + "Read access (always enabled)" : { + + }, + "Remote: %@" : { + + }, + "Restore from File…" : { + "comment" : "A button that allows the user to restore their data from a file.", + "isCommentAutoGenerated" : true + }, + "Running locally — no credits needed!" : { + "comment" : "A message displayed when using an on-device LLM like the one provided by the `.ollama` provider.", + "isCommentAutoGenerated" : true + }, + "Security Recommendation" : { + + }, + "Shell commands have full access to your system. Only approve commands you understand and trust." : { + "comment" : "A warning banner displayed in the Bash Approval Sheet.", + "isCommentAutoGenerated" : true + }, + "Show text labels below toolbar icons (helpful for new users)" : { + "comment" : "A description of the feature that shows text labels below toolbar icons.", + "isCommentAutoGenerated" : true + }, + "SKILL.md format — write instructions in plain Markdown." : { + + }, + "Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt." : { + "comment" : "A description of how skills are used by the AI.", + "isCommentAutoGenerated" : true + }, + "SSH Key" : { + + }, + "Stream responses as they're generated. Disable for single, complete responses." : { + + }, + "Sync conversations and settings across multiple machines using Git." : { + + }, + "Sync Now" : { + + }, + "Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative" : { + + }, + "Template" : { + "comment" : "A label displayed above the text editor for the shortcut's template.", + "isCommentAutoGenerated" : true + }, + "Test Connection" : { + + }, + "Text files are injected into the system prompt alongside the skill." : { + "comment" : "A description of how text files are injected into the system prompt.", + "isCommentAutoGenerated" : true + }, + "The AI can read and search files in allowed folders" : { + + }, + "The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise." : { + + }, + "The AI wants to run the following command" : { + "comment" : "A description of the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "This default prompt is always included to ensure accurate, helpful responses." : { + + }, + "This will permanently delete all email activity logs. This action cannot be undone." : { + "comment" : "A message displayed in an alert when the user is about to clear all email logs.", + "isCommentAutoGenerated" : true + }, + "Token Distribution" : { + "comment" : "A label displayed under the token distribution bar.", + "isCommentAutoGenerated" : true + }, + "tokens" : { + + }, + "Try adjusting your search or filters" : { + "comment" : "A description of the error that occurs when no models match the user's search.", + "isCommentAutoGenerated" : true + }, + "Type / in the input to see command suggestions" : { + "comment" : "A description of how to use the keyboard shortcut.", + "isCommentAutoGenerated" : true + }, + "Type a message or / for commands..." : { + "comment" : "A placeholder text displayed in the input area when", + "isCommentAutoGenerated" : true + }, + "Uncommitted changes: %@" : { + + }, + "Update Available%@" : { + "comment" : "A button that opens a website with information about a new version of oAI. The argument is the version number of the new version.", + "isCommentAutoGenerated" : true + }, + "Use @filename to attach files to your message" : { + "comment" : "A description of how to attach files to a message.", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** in the template to insert whatever you type after the command." : { + "comment" : "A tooltip for the \"lightbulb\" icon in the shortcuts view", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** to insert whatever you type after the command." : { + "comment" : "A description of how to use the shortcut template.", + "isCommentAutoGenerated" : true + }, + "Username + Password" : { + + }, + "v%@" : { + "comment" : "A label showing the current version of oAI.", + "isCommentAutoGenerated" : true + }, + "Version %@ (%@)" : { + "comment" : "A version number in the format \"Version 1.0 (1)\"", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Version %1$@ (%2$@)" + } + } + } + }, + "View Email Log" : { + + }, + "When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore." : { + + }, + "Working directory:" : { + "comment" : "A label displayed under the working directory of a pending shell command.", + "isCommentAutoGenerated" : true + }, + "You can import any SKILL.md file from skill0.io or write your own." : { + "comment" : "A description of how to use skills.", + "isCommentAutoGenerated" : true + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/da.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings b/Locales/oAI Localizations/da.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings new file mode 100644 index 0000000..5448135 --- /dev/null +++ b/Locales/oAI Localizations/da.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings @@ -0,0 +1,18 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleName" : { + "comment" : "Bundle name", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "oAI" + } + } + } + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/da.xcloc/contents.json b/Locales/oAI Localizations/da.xcloc/contents.json new file mode 100644 index 0000000..6903b74 --- /dev/null +++ b/Locales/oAI Localizations/da.xcloc/contents.json @@ -0,0 +1,12 @@ +{ + "developmentRegion" : "en", + "project" : "oAI.xcodeproj", + "targetLocale" : "da", + "toolInfo" : { + "toolBuildNumber" : "17C52", + "toolID" : "com.apple.dt.xcode", + "toolName" : "Xcode", + "toolVersion" : "26.2" + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/de.xcloc/Localized Contents/de.xliff b/Locales/oAI Localizations/de.xcloc/Localized Contents/de.xliff new file mode 100644 index 0000000..2e58d6e --- /dev/null +++ b/Locales/oAI Localizations/de.xcloc/Localized Contents/de.xliff @@ -0,0 +1,843 @@ + + + +
+ +
+ + + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + + + + %@ + %@ + + + + %@ active — injected into every conversation + %@ aktiv — in jedes Gespräch eingefügt + A footnote indicating that some skills are active. + + + %@ chars + %@ Zeichen + A label showing the number of characters in the skill's content. The argument is the number of characters in the content. + + + %@ min + %@ min + + + + %@ pt + %@ pt + A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points. + + + %@s + %@s + + + + (always used) + (immer verwendet) + + + + (optional) + (optional) + + + + 1. Open Paperless-NGX → Settings → API Tokens + 1. Paperless-NGX öffnen → Einstellungen → API-Tokens + A step in the process of getting a Paperless-NGX API token. + + + 2. Create or copy your token + 2. Token erstellen oder kopieren + A step in the process of getting a Paperless-NGX API token. + + + 3. Paste it above + 3. Oben einfügen + A step in the process of getting a Paperless-NGX API token. + + + A shortcut with command %@ already exists. + Eine Verknüpfung mit dem Befehl %@ existiert bereits. + A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut. + + + API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine. + API-Schlüssel und Zugangsdaten sind **nicht** in der Sicherung enthalten. Du musst sie nach der Wiederherstellung auf einem neuen Gerät erneut eingeben. + A warning message displayed in the settings tab of the app. + + + Access Token + Access Token + + + + Active + Aktiv + A toggle + + + Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows. + Aktive Skills werden an den System-Prompt angehängt. Schalte sie einzeln ein oder aus, um zu steuern, was die KI weiß. + A description of how to toggle the active status of a skill. + + + Agent Skills + Agent-Skills + + + + Allow Shell Command? + Shell-Befehl erlauben? + A title for a modal that asks the user if they want to allow a shell command. + + + Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs. + Erlaube der KI, Shell-Befehle auf deinem Rechner auszuführen. Befehle werden über /bin/zsh ausgeführt. Aktiviere den Genehmigungsmodus, um jeden Befehl vor der Ausführung zu prüfen. + + + + Anthropic Balance + Anthropic Balance + A label displayed above the user's balance on the Anthropic platform. + + + Auto-execute mode: commands run without approval. Use with caution. + Automatischer Ausführungsmodus: Befehle werden ohne Genehmigung ausgeführt. Mit Vorsicht verwenden. + + + + Auto-sync can cause conflicts if running on multiple machines simultaneously. + Die automatische Synchronisierung kann zu Konflikten führen, wenn sie auf mehreren Geräten gleichzeitig läuft. + + + + Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations. + Relevante Nachrichten automatisch auswählen statt die gesamte Historie zu senden. Reduziert den Token-Verbrauch bei langen Gesprächen. + + + + Automatically summarize old portions of long conversations to save tokens and improve context efficiency. + Ältere Teile langer Gespräche automatisch zusammenfassen, um Tokens zu sparen und die Kontexteffizienz zu verbessern. + + + + Back Up Now + Jetzt sichern + A button that backs up the user's data. + + + Backup location: + Sicherungsort: + A label displayed under the backup location. + + + Bash Execution + Bash Execution + + + + Branch: %@ + Branch: %@ + + + + Built with SwiftUI + Built with SwiftUI + A label that says that the app is built with SwiftUI. + + + COMMAND + BEFEHL + A label displayed above the command that the AI wants to run. + + + Capabilities + Fähigkeiten + A label displayed under the list of capabilities of a model. + + + Changing these values affects how the AI generates responses. The defaults work well for most use cases. + Das Ändern dieser Werte beeinflusst, wie die KI Antworten generiert. Die Standardwerte funktionieren für die meisten Anwendungsfälle gut. + + + + Check your balance at: + Guthaben prüfen unter: + A label displayed under a link to check the user's balance. + + + Check your usage at: + Nutzung prüfen unter: + A label displayed under a link to OpenAI's usage page. + + + Clear All + Alle löschen + A button to clear all email activity logs. + + + Click 'Add Folder' below or drag folders here from Finder + Klicke unten auf „Ordner hinzufügen“ oder ziehe Ordner aus dem Finder hierher + + + + Command + Befehl + A label displayed above the command field. + + + Command History + Befehlshistorie + A label displayed above the command history. + + + Content (Markdown) + Inhalt (Markdown) + A + + + Controls which messages are written to ~/Library/Logs/oAI.log + Steuert, welche Nachrichten in ~/Library/Logs/oAI.log geschrieben werden + + + + Conversations + Gespräche + + + + Copied! + Kopiert! + A message displayed when a code block is successfully copied to the clipboard. + + + Cost Examples + Kostenbeispiele + A heading for the cost examples of a model. + + + Create a dedicated email account specifically for AI handling. Do NOT use your personal email address. + Erstelle ein dediziertes E-Mail-Konto speziell für die KI-Bearbeitung. Verwende NICHT deine persönliche E-Mail-Adresse. + + + + Create a shortcut to save a reusable prompt template accessible from the / command dropdown. + Erstelle eine Verknüpfung, um eine wiederverwendbare Prompt-Vorlage zu speichern, die über das /-Befehlsmenü zugänglich ist. + A description of the functionality of the shortcut editor. + + + Default Prompt + Standard-Prompt + + + + Delete (%@) + Löschen (%@) + + + + Description + Beschreibung + A label + + + Each command will require your approval before running. + Jeder Befehl erfordert deine Genehmigung, bevor er ausgeführt wird. + + + + Edit + Bearbeiten + A button to edit a skill. + + + Email Activity Log + E-Mail-Aktivitätsprotokoll + The title of the email activity log view. + + + Email Handler System Prompt + System-Prompt für E-Mail-Handler + + + + Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults. + Der E-Mail-Handler verwendet NUR seinen eigenen System-Prompt, vollständig isoliert von deinen Chat-Einstellungen. Ein benutzerdefinierter Prompt unten überschreibt die Standardeinstellungen. + + + + Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails. + E-Mail-Aufgaben haben NUR LESEZUGRIFF auf MCP-Ordner. Die KI kann beim Verarbeiten von E-Mails keine Dateien schreiben, löschen oder ändern. + + + + Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens. + KI-gestützte semantische Suche mit %@-Embeddings aktivieren. Kosten: ~$0,02–0,15/1M Tokens. + + + + Enable email handler in Settings to start monitoring emails + E-Mail-Handler in den Einstellungen aktivieren, um E-Mails zu überwachen + A message displayed when the email handler is disabled. + + + Error: %@ + Fehler: %@ + A label displaying an error message in the checkout view. + + + Example structure: + Beispielstruktur: + A + + + Example: oai-bot-x7k2m9p3@gmail.com + Example: oai-bot-x7k2m9p3@gmail.com + + + + File Access Permissions + Dateizugriffsberechtigungen + + + + Files + Dateien + A label displayed above the list of files attached to a skill. + + + Google (Gemini embedding) + Google (Gemini embedding) + + + + Help + Hilfe + A heading for the help screen + + + How to get your API token: + So erhältst du deinen API-Token: + A heading for a section that describes how to get your API token. + + + Initialize Repository + Repository initialisieren + + + + Inject into system prompt for every conversation + In den System-Prompt jedes Gesprächs einfügen + + + + Large files inflate the system prompt and may hit token limits. + Große Dateien blähen den System-Prompt auf und können Token-Limits erreichen. + A warning displayed when a user adds a large file to a skill. + + + Last sync: %@ + Letzte Synchronisierung: %@ + + + + Leave empty to use the default email handler system prompt. + Leer lassen, um den Standard-System-Prompt des E-Mail-Handlers zu verwenden. + + + + Lowercase letters, numbers, and hyphens only. No spaces. + Nur Kleinbuchstaben, Zahlen und Bindestriche. Keine Leerzeichen. + A description of the format of a shortcut's command. + + + MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks. + MCP gibt der KI kontrollierten Zugriff, um Dateien auf deinem Computer zu lesen und optional zu schreiben. Die KI kann Dateien in erlaubten Ordnern suchen, lesen und analysieren, um bei Programmierung, Analyse und anderen Aufgaben zu helfen. + + + + Markdown instruction files injected into the system prompt. Compatible with SKILL.md format. + Markdown-Anweisungsdateien, die in den System-Prompt eingefügt werden. Kompatibel mit SKILL.md-Format. + A description of the format of agent skills. + + + Max Tokens: set to 0 to use model default. Higher values allow longer responses. + Maximale Tokens: auf 0 setzen, um den Modell-Standard zu verwenden. Höhere Werte erlauben längere Antworten. + + + + Model Context Protocol + Model Context Protocol + + + + Model Info + Modell-Info + A title for a view that displays information about a model. + + + Multi-provider AI chat client + KI-Chat-Client mit mehreren Anbietern + A description of oAI. + + + Name + Name + A label + + + Never + Nie + A label displayed in the settings view that indicates when the last backup was taken. + + + No credit data available + Keine Guthabendaten verfügbar + A message displayed when there is no credit data available. + + + No email activity yet + Noch keine E-Mail-Aktivität + A message displayed when the user has not yet received any email activity. + + + No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill. + Keine Dateien angehängt. Füge JSON, YAML, CSV oder TXT-Dateien hinzu, um Daten zusammen mit diesem Skill in den System-Prompt einzufügen. + A message displayed when a user has not attached any files to a skill. + + + No model selected + Kein Modell ausgewählt + A label displayed when no model is selected. + + + No models available + Keine Modelle verfügbar + + + + No results for "%@" + Keine Ergebnisse für „%@“ + A message displayed when there are no search results. The argument is the search term. + + + No shortcuts yet + Noch keine Verknüpfungen + A message displayed when a user has no shortcuts. + + + No shortcuts yet — click New Shortcut to create one. + Noch keine Verknüpfungen — klicke auf „Neue Verknüpfung“, um eine zu erstellen. + A message displayed when a user has no shortcuts. + + + No skills yet + Noch keine Skills + A message displayed when the user has no skills. + + + No skills yet — click New Skill or Import to get started. + Noch keine Skills — klicke auf „Neuer Skill“ oder „Importieren“, um loszulegen. + A message displayed when a user has not created any skills. + + + Ollama (Local) + Ollama (Local) + A label displayed above the credits information for the local Ollie. + + + Only emails with this text in the subject line will be processed. Example: "[OAIBOT] What's the weather?" + Nur E-Mails mit diesem Text in der Betreffzeile werden verarbeitet. Beispiel: „[OAIBOT] Wie ist das Wetter?“ + + + + OpenAI (text-embedding-3-large) + OpenAI (text-embedding-3-large) + + + + OpenAI (text-embedding-3-small) + OpenAI (text-embedding-3-small) + + + + OpenAI Balance + OpenAI Balance + A label displayed above the OpenAI balance section. + + + OpenRouter (OpenAI large) + OpenRouter (OpenAI large) + + + + OpenRouter (OpenAI small) + OpenRouter (OpenAI small) + + + + OpenRouter (Qwen 8B) + OpenRouter (Qwen 8B) + + + + OpenRouter Credits + OpenRouter Credits + A heading for the user's OpenRouter credits. + + + Read access (always enabled) + Lesezugriff (immer aktiviert) + + + + Remote: %@ + Remote: %@ + + + + Restore from File… + Aus Datei wiederherstellen… + A button that allows the user to restore their data from a file. + + + Running locally — no credits needed! + Läuft lokal — kein Guthaben benötigt! + A message displayed when using an on-device LLM like the one provided by the `.ollama` provider. + + + SKILL.md format — write instructions in plain Markdown. + SKILL.md-Format — Anweisungen in einfachem Markdown schreiben. + + + + SSH Key + SSH Key + + + + Security Recommendation + Sicherheitsempfehlung + + + + Shell commands have full access to your system. Only approve commands you understand and trust. + Shell-Befehle haben vollen Zugriff auf dein System. Genehmige nur Befehle, die du verstehst und denen du vertraust. + A warning banner displayed in the Bash Approval Sheet. + + + Show text labels below toolbar icons (helpful for new users) + Textbeschriftungen unter Symbolleistensymbolen anzeigen (hilfreich für neue Benutzer) + A description of the feature that shows text labels below toolbar icons. + + + Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt. + Skills sind Markdown-Anweisungsdateien, die der KI beibringen, wie sie sich verhalten soll. Aktive Skills werden automatisch in den System-Prompt eingefügt. + A description of how skills are used by the AI. + + + Stream responses as they're generated. Disable for single, complete responses. + Antworten während der Generierung streamen. Deaktivieren für vollständige Einzelantworten. + + + + Sync Now + Jetzt synchronisieren + + + + Sync conversations and settings across multiple machines using Git. + Gespräche und Einstellungen über mehrere Geräte hinweg mit Git synchronisieren. + + + + Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative + Temperatur: 0 = Modell-Standard · 0,0–0,7 = fokussiert · 0,8–2,0 = kreativ + + + + Template + Vorlage + A label displayed above the text editor for the shortcut's template. + + + Test Connection + Verbindung testen + + + + Text files are injected into the system prompt alongside the skill. + Textdateien werden zusammen mit dem Skill in den System-Prompt eingefügt. + A description of how text files are injected into the system prompt. + + + The AI can read and search files in allowed folders + Die KI kann Dateien in erlaubten Ordnern lesen und durchsuchen + + + + The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise. + Die KI liest diesen Inhalt und entscheidet, wann sie ihn anwendet. Beschreibe **was** die KI tun soll und **wie** — sei spezifisch und prägnant. + + + + The AI wants to run the following command + Die KI möchte den folgenden Befehl ausführen + A description of the command that the AI wants to run. + + + This default prompt is always included to ensure accurate, helpful responses. + Dieser Standard-Prompt ist immer enthalten, um genaue und hilfreiche Antworten sicherzustellen. + + + + This will permanently delete all email activity logs. This action cannot be undone. + Dadurch werden alle E-Mail-Aktivitätsprotokolle dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden. + A message displayed in an alert when the user is about to clear all email logs. + + + Token Distribution + Token-Verteilung + A label displayed under the token distribution bar. + + + Try adjusting your search or filters + Suche oder Filter anpassen + A description of the error that occurs when no models match the user's search. + + + Type / in the input to see command suggestions + / im Eingabefeld eingeben, um Befehlsvorschläge zu sehen + A description of how to use the keyboard shortcut. + + + Type a message or / for commands... + Nachricht eingeben oder / für Befehle... + A placeholder text displayed in the input area when + + + Uncommitted changes: %@ + Nicht übernommene Änderungen: %@ + + + + Update Available%@ + Update verfügbar%@ + A button that opens a website with information about a new version of oAI. The argument is the version number of the new version. + + + Use **{{input}}** in the template to insert whatever you type after the command. + Verwende **{{input}}** in der Vorlage, um einzufügen, was du nach dem Befehl tippst. + A tooltip for the "lightbulb" icon in the shortcuts view + + + Use **{{input}}** to insert whatever you type after the command. + Verwende **{{input}}**, um einzufügen, was du nach dem Befehl tippst. + A description of how to use the shortcut template. + + + Use @filename to attach files to your message + @dateiname verwenden, um Dateien an die Nachricht anzuhängen + A description of how to attach files to a message. + + + Username + Password + Username + Password + + + + Version %1$@ (%2$@) + Version %1$@ (%2$@) + A version number in the format "Version 1.0 (1)" + + + View Email Log + E-Mail-Protokoll anzeigen + + + + When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore. + Wenn aktiviert, werden gitignorierte Dateien beim Auflisten und Suchen übersprungen. Schreiboperationen ignorieren immer .gitignore. + + + + Working directory: + Arbeitsverzeichnis: + A label displayed under the working directory of a pending shell command. + + + You can import any SKILL.md file from skill0.io or write your own. + Du kannst jede SKILL.md-Datei von skill0.io importieren oder deine eigene schreiben. + A description of how to use skills. + + + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + A link to the GNU Affero General Public License. + + + ^[%@ entry](inflect: true) + ^[%@ Eintrag](inflect: true) + A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier. + + + active + aktiv + A label for an active skill. + + + messages + Nachrichten + + + + oAI + oAI + The name of the app. + + + tokens + tokens + + + + v%@ + v%@ + A label showing the current version of oAI. + + + ~750 tokens ≈ 500 words. Online mode allows web search in responses. + ~750 Tokens ≈ 500 Wörter. Online-Modus ermöglicht Websuche in Antworten. + + + + © 2026 [Rune Olsen](https://blog.rune.pm) + © 2026 [Rune Olsen](https://blog.rune.pm) + A copyright notice with the copyright holder's name. + + + • + + A bullet point. + + + • Add public key to your git provider + • Öffentlichen Schlüssel beim Git-Anbieter hinzufügen + + + + • No credentials needed in oAI + • Keine Zugangsdaten in oAI erforderlich + + + + • Uses your system SSH keys (~/.ssh/id_ed25519) + • Verwendet deine System-SSH-Schlüssel (~/.ssh/id_ed25519) + + + + ℹ️ SSH Key Authentication + ℹ️ SSH-Schlüssel-Authentifizierung + + + + ↑↓ navigate ↩ open + ↑↓ navigate ↩ open + + + + ⌘N New • ⌘M Model • ⌘S Save + ⌘N New • ⌘M Model • ⌘S Save + A hint that appears on macOS when using keyboard shortcuts. + + + ⚠️ Custom prompt active — only this prompt will be sent to the model. + ⚠️ Benutzerdefinierter Prompt aktiv — nur dieser Prompt wird an das Modell gesendet. + + + + ⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead. + ⚠️ Viele Anbieter (GitHub) unterstützen keine Passwort-Authentifizierung mehr. Stattdessen Access Token verwenden. + + + + ⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab. + ⚠️ Keine Embedding-Anbieter verfügbar. Konfiguriere einen API-Schlüssel für OpenAI, OpenRouter oder Google auf der Registerkarte „Allgemein“. + + + + ⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages. + ⚠️ Einmaliger Vorgang — generiert Embeddings für alle Nachrichten. Geschätzte Kosten: ~$0,04 für 10.000 Nachrichten. + + + + ⚠️ These are advanced settings + ⚠️ Das sind erweiterte Einstellungen + + + + ⚠️ interrupted + ⚠️ unterbrochen + A warning label that appears next to a message that was interrupted. + + + 🌐 + 🌐 + A label for a model that can be used to generate images. + + + 🎨 + 🎨 + A button that generates an image. + + + 👁️ + 👁️ + A checkmark emoji. + + + 💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords. + 💡 Für Gmail ein App-Passwort verwenden. Google-Konto > Sicherheit > 2-Schritt-Verifizierung > App-Passwörter. + + + + 💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods. + 💡 HTTPS-URL verwenden (z.B. https://gitlab.pm/user/repo.git) — funktioniert mit allen Authentifizierungsmethoden. + + + + 🔧 + 🔧 + A checkmark emoji. + + +
+ +
+ +
+ + + oAI + oAI + Bundle name + + +
+
diff --git a/Locales/oAI Localizations/de.xcloc/Source Contents/oAI/Localizable.xcstrings b/Locales/oAI Localizations/de.xcloc/Source Contents/oAI/Localizable.xcstrings new file mode 100644 index 0000000..bfdb18c --- /dev/null +++ b/Locales/oAI Localizations/de.xcloc/Source Contents/oAI/Localizable.xcstrings @@ -0,0 +1,590 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "(always used)" : { + + }, + "(optional)" : { + + }, + "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" : { + "comment" : "A link to the GNU Affero General Public License.", + "isCommentAutoGenerated" : true + }, + "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" : { + + }, + "%@" : { + + }, + "%@ active — injected into every conversation" : { + "comment" : "A footnote indicating that some skills are active.", + "isCommentAutoGenerated" : true + }, + "%@ chars" : { + "comment" : "A label showing the number of characters in the skill's content. The argument is the number of characters in the content.", + "isCommentAutoGenerated" : true + }, + "%@ min" : { + + }, + "%@ pt" : { + "comment" : "A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points.", + "isCommentAutoGenerated" : true + }, + "%@s" : { + + }, + "•" : { + "comment" : "A bullet point.", + "isCommentAutoGenerated" : true + }, + "• Add public key to your git provider" : { + + }, + "• No credentials needed in oAI" : { + + }, + "• Uses your system SSH keys (~/.ssh/id_ed25519)" : { + + }, + "^[%@ entry](inflect: true)" : { + "comment" : "A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier.", + "isCommentAutoGenerated" : true + }, + "© 2026 [Rune Olsen](https://blog.rune.pm)" : { + "comment" : "A copyright notice with the copyright holder's name.", + "isCommentAutoGenerated" : true + }, + "↑↓ navigate ↩ open" : { + + }, + "~750 tokens ≈ 500 words. Online mode allows web search in responses." : { + + }, + "⌘N New • ⌘M Model • ⌘S Save" : { + "comment" : "A hint that appears on macOS when using keyboard shortcuts.", + "isCommentAutoGenerated" : true + }, + "⚠️ Custom prompt active — only this prompt will be sent to the model." : { + + }, + "⚠️ interrupted" : { + "comment" : "A warning label that appears next to a message that was interrupted.", + "isCommentAutoGenerated" : true + }, + "⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead." : { + + }, + "⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab." : { + + }, + "⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages." : { + + }, + "⚠️ These are advanced settings" : { + + }, + "🌐" : { + "comment" : "A label for a model that can be used to generate images.", + "isCommentAutoGenerated" : true + }, + "🎨" : { + "comment" : "A button that generates an image.", + "isCommentAutoGenerated" : true + }, + "👁️" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords." : { + + }, + "💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods." : { + + }, + "🔧" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "1. Open Paperless-NGX → Settings → API Tokens" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "2. Create or copy your token" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "3. Paste it above" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "A shortcut with command %@ already exists." : { + "comment" : "A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut.", + "isCommentAutoGenerated" : true + }, + "Access Token" : { + + }, + "active" : { + "comment" : "A label for an active skill.", + "isCommentAutoGenerated" : true + }, + "Active" : { + "comment" : "A toggle", + "isCommentAutoGenerated" : true + }, + "Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows." : { + "comment" : "A description of how to toggle the active status of a skill.", + "isCommentAutoGenerated" : true + }, + "Agent Skills" : { + + }, + "Allow Shell Command?" : { + "comment" : "A title for a modal that asks the user if they want to allow a shell command.", + "isCommentAutoGenerated" : true + }, + "Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs." : { + + }, + "Anthropic Balance" : { + "comment" : "A label displayed above the user's balance on the Anthropic platform.", + "isCommentAutoGenerated" : true + }, + "API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine." : { + "comment" : "A warning message displayed in the settings tab of the app.", + "isCommentAutoGenerated" : true + }, + "Auto-execute mode: commands run without approval. Use with caution." : { + + }, + "Auto-sync can cause conflicts if running on multiple machines simultaneously." : { + + }, + "Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations." : { + + }, + "Automatically summarize old portions of long conversations to save tokens and improve context efficiency." : { + + }, + "Back Up Now" : { + "comment" : "A button that backs up the user's data.", + "isCommentAutoGenerated" : true + }, + "Backup location:" : { + "comment" : "A label displayed under the backup location.", + "isCommentAutoGenerated" : true + }, + "Bash Execution" : { + + }, + "Branch: %@" : { + + }, + "Built with SwiftUI" : { + "comment" : "A label that says that the app is built with SwiftUI.", + "isCommentAutoGenerated" : true + }, + "Capabilities" : { + "comment" : "A label displayed under the list of capabilities of a model.", + "isCommentAutoGenerated" : true + }, + "Changing these values affects how the AI generates responses. The defaults work well for most use cases." : { + + }, + "Check your balance at:" : { + "comment" : "A label displayed under a link to check the user's balance.", + "isCommentAutoGenerated" : true + }, + "Check your usage at:" : { + "comment" : "A label displayed under a link to OpenAI's usage page.", + "isCommentAutoGenerated" : true + }, + "Clear All" : { + "comment" : "A button to clear all email activity logs.", + "isCommentAutoGenerated" : true + }, + "Click 'Add Folder' below or drag folders here from Finder" : { + + }, + "Command" : { + "comment" : "A label displayed above the command field.", + "isCommentAutoGenerated" : true + }, + "COMMAND" : { + "comment" : "A label displayed above the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "Command History" : { + "comment" : "A label displayed above the command history.", + "isCommentAutoGenerated" : true + }, + "Content (Markdown)" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Controls which messages are written to ~/Library/Logs/oAI.log" : { + + }, + "Conversations" : { + + }, + "Copied!" : { + "comment" : "A message displayed when a code block is successfully copied to the clipboard.", + "isCommentAutoGenerated" : true + }, + "Cost Examples" : { + "comment" : "A heading for the cost examples of a model.", + "isCommentAutoGenerated" : true + }, + "Create a dedicated email account specifically for AI handling. Do NOT use your personal email address." : { + + }, + "Create a shortcut to save a reusable prompt template accessible from the / command dropdown." : { + "comment" : "A description of the functionality of the shortcut editor.", + "isCommentAutoGenerated" : true + }, + "Default Prompt" : { + + }, + "Delete (%@)" : { + + }, + "Description" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Each command will require your approval before running." : { + + }, + "Edit" : { + "comment" : "A button to edit a skill.", + "isCommentAutoGenerated" : true + }, + "Email Activity Log" : { + "comment" : "The title of the email activity log view.", + "isCommentAutoGenerated" : true + }, + "Email Handler System Prompt" : { + + }, + "Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults." : { + + }, + "Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails." : { + + }, + "Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens." : { + + }, + "Enable email handler in Settings to start monitoring emails" : { + "comment" : "A message displayed when the email handler is disabled.", + "isCommentAutoGenerated" : true + }, + "Error: %@" : { + "comment" : "A label displaying an error message in the checkout view.", + "isCommentAutoGenerated" : true + }, + "Example structure:" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Example: oai-bot-x7k2m9p3@gmail.com" : { + + }, + "File Access Permissions" : { + + }, + "Files" : { + "comment" : "A label displayed above the list of files attached to a skill.", + "isCommentAutoGenerated" : true + }, + "Google (Gemini embedding)" : { + + }, + "Help" : { + "comment" : "A heading for the help screen", + "isCommentAutoGenerated" : true + }, + "How to get your API token:" : { + "comment" : "A heading for a section that describes how to get your API token.", + "isCommentAutoGenerated" : true + }, + "ℹ️ SSH Key Authentication" : { + + }, + "Initialize Repository" : { + + }, + "Inject into system prompt for every conversation" : { + + }, + "Large files inflate the system prompt and may hit token limits." : { + "comment" : "A warning displayed when a user adds a large file to a skill.", + "isCommentAutoGenerated" : true + }, + "Last sync: %@" : { + + }, + "Leave empty to use the default email handler system prompt." : { + + }, + "Lowercase letters, numbers, and hyphens only. No spaces." : { + "comment" : "A description of the format of a shortcut's command.", + "isCommentAutoGenerated" : true + }, + "Markdown instruction files injected into the system prompt. Compatible with SKILL.md format." : { + "comment" : "A description of the format of agent skills.", + "isCommentAutoGenerated" : true + }, + "Max Tokens: set to 0 to use model default. Higher values allow longer responses." : { + + }, + "MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks." : { + + }, + "messages" : { + + }, + "Model Context Protocol" : { + + }, + "Model Info" : { + "comment" : "A title for a view that displays information about a model.", + "isCommentAutoGenerated" : true + }, + "Multi-provider AI chat client" : { + "comment" : "A description of oAI.", + "isCommentAutoGenerated" : true + }, + "Name" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Never" : { + "comment" : "A label displayed in the settings view that indicates when the last backup was taken.", + "isCommentAutoGenerated" : true + }, + "No credit data available" : { + "comment" : "A message displayed when there is no credit data available.", + "isCommentAutoGenerated" : true + }, + "No email activity yet" : { + "comment" : "A message displayed when the user has not yet received any email activity.", + "isCommentAutoGenerated" : true + }, + "No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill." : { + "comment" : "A message displayed when a user has not attached any files to a skill.", + "isCommentAutoGenerated" : true + }, + "No model selected" : { + "comment" : "A label displayed when no model is selected.", + "isCommentAutoGenerated" : true + }, + "No models available" : { + + }, + "No results for \"%@\"" : { + "comment" : "A message displayed when there are no search results. The argument is the search term.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet" : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet — click New Shortcut to create one." : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No skills yet" : { + "comment" : "A message displayed when the user has no skills.", + "isCommentAutoGenerated" : true + }, + "No skills yet — click New Skill or Import to get started." : { + "comment" : "A message displayed when a user has not created any skills.", + "isCommentAutoGenerated" : true + }, + "oAI" : { + "comment" : "The name of the app.", + "isCommentAutoGenerated" : true + }, + "Ollama (Local)" : { + "comment" : "A label displayed above the credits information for the local Ollie.", + "isCommentAutoGenerated" : true + }, + "Only emails with this text in the subject line will be processed. Example: \"[OAIBOT] What's the weather?\"" : { + + }, + "OpenAI (text-embedding-3-large)" : { + + }, + "OpenAI (text-embedding-3-small)" : { + + }, + "OpenAI Balance" : { + "comment" : "A label displayed above the OpenAI balance section.", + "isCommentAutoGenerated" : true + }, + "OpenRouter (OpenAI large)" : { + + }, + "OpenRouter (OpenAI small)" : { + + }, + "OpenRouter (Qwen 8B)" : { + + }, + "OpenRouter Credits" : { + "comment" : "A heading for the user's OpenRouter credits.", + "isCommentAutoGenerated" : true + }, + "Read access (always enabled)" : { + + }, + "Remote: %@" : { + + }, + "Restore from File…" : { + "comment" : "A button that allows the user to restore their data from a file.", + "isCommentAutoGenerated" : true + }, + "Running locally — no credits needed!" : { + "comment" : "A message displayed when using an on-device LLM like the one provided by the `.ollama` provider.", + "isCommentAutoGenerated" : true + }, + "Security Recommendation" : { + + }, + "Shell commands have full access to your system. Only approve commands you understand and trust." : { + "comment" : "A warning banner displayed in the Bash Approval Sheet.", + "isCommentAutoGenerated" : true + }, + "Show text labels below toolbar icons (helpful for new users)" : { + "comment" : "A description of the feature that shows text labels below toolbar icons.", + "isCommentAutoGenerated" : true + }, + "SKILL.md format — write instructions in plain Markdown." : { + + }, + "Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt." : { + "comment" : "A description of how skills are used by the AI.", + "isCommentAutoGenerated" : true + }, + "SSH Key" : { + + }, + "Stream responses as they're generated. Disable for single, complete responses." : { + + }, + "Sync conversations and settings across multiple machines using Git." : { + + }, + "Sync Now" : { + + }, + "Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative" : { + + }, + "Template" : { + "comment" : "A label displayed above the text editor for the shortcut's template.", + "isCommentAutoGenerated" : true + }, + "Test Connection" : { + + }, + "Text files are injected into the system prompt alongside the skill." : { + "comment" : "A description of how text files are injected into the system prompt.", + "isCommentAutoGenerated" : true + }, + "The AI can read and search files in allowed folders" : { + + }, + "The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise." : { + + }, + "The AI wants to run the following command" : { + "comment" : "A description of the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "This default prompt is always included to ensure accurate, helpful responses." : { + + }, + "This will permanently delete all email activity logs. This action cannot be undone." : { + "comment" : "A message displayed in an alert when the user is about to clear all email logs.", + "isCommentAutoGenerated" : true + }, + "Token Distribution" : { + "comment" : "A label displayed under the token distribution bar.", + "isCommentAutoGenerated" : true + }, + "tokens" : { + + }, + "Try adjusting your search or filters" : { + "comment" : "A description of the error that occurs when no models match the user's search.", + "isCommentAutoGenerated" : true + }, + "Type / in the input to see command suggestions" : { + "comment" : "A description of how to use the keyboard shortcut.", + "isCommentAutoGenerated" : true + }, + "Type a message or / for commands..." : { + "comment" : "A placeholder text displayed in the input area when", + "isCommentAutoGenerated" : true + }, + "Uncommitted changes: %@" : { + + }, + "Update Available%@" : { + "comment" : "A button that opens a website with information about a new version of oAI. The argument is the version number of the new version.", + "isCommentAutoGenerated" : true + }, + "Use @filename to attach files to your message" : { + "comment" : "A description of how to attach files to a message.", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** in the template to insert whatever you type after the command." : { + "comment" : "A tooltip for the \"lightbulb\" icon in the shortcuts view", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** to insert whatever you type after the command." : { + "comment" : "A description of how to use the shortcut template.", + "isCommentAutoGenerated" : true + }, + "Username + Password" : { + + }, + "v%@" : { + "comment" : "A label showing the current version of oAI.", + "isCommentAutoGenerated" : true + }, + "Version %@ (%@)" : { + "comment" : "A version number in the format \"Version 1.0 (1)\"", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Version %1$@ (%2$@)" + } + } + } + }, + "View Email Log" : { + + }, + "When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore." : { + + }, + "Working directory:" : { + "comment" : "A label displayed under the working directory of a pending shell command.", + "isCommentAutoGenerated" : true + }, + "You can import any SKILL.md file from skill0.io or write your own." : { + "comment" : "A description of how to use skills.", + "isCommentAutoGenerated" : true + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/de.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings b/Locales/oAI Localizations/de.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings new file mode 100644 index 0000000..5448135 --- /dev/null +++ b/Locales/oAI Localizations/de.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings @@ -0,0 +1,18 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleName" : { + "comment" : "Bundle name", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "oAI" + } + } + } + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/de.xcloc/contents.json b/Locales/oAI Localizations/de.xcloc/contents.json new file mode 100644 index 0000000..c95d449 --- /dev/null +++ b/Locales/oAI Localizations/de.xcloc/contents.json @@ -0,0 +1,12 @@ +{ + "developmentRegion" : "en", + "project" : "oAI.xcodeproj", + "targetLocale" : "de", + "toolInfo" : { + "toolBuildNumber" : "17C52", + "toolID" : "com.apple.dt.xcode", + "toolName" : "Xcode", + "toolVersion" : "26.2" + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/en.xcloc/Localized Contents/en.xliff b/Locales/oAI Localizations/en.xcloc/Localized Contents/en.xliff new file mode 100644 index 0000000..c662f4d --- /dev/null +++ b/Locales/oAI Localizations/en.xcloc/Localized Contents/en.xliff @@ -0,0 +1,843 @@ + + + +
+ +
+ + + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + + + + %@ + %@ + + + + %@ active — injected into every conversation + %@ active — injected into every conversation + A footnote indicating that some skills are active. + + + %@ chars + %@ chars + A label showing the number of characters in the skill's content. The argument is the number of characters in the content. + + + %@ min + %@ min + + + + %@ pt + %@ pt + A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points. + + + %@s + %@s + + + + (always used) + (always used) + + + + (optional) + (optional) + + + + 1. Open Paperless-NGX → Settings → API Tokens + 1. Open Paperless-NGX → Settings → API Tokens + A step in the process of getting a Paperless-NGX API token. + + + 2. Create or copy your token + 2. Create or copy your token + A step in the process of getting a Paperless-NGX API token. + + + 3. Paste it above + 3. Paste it above + A step in the process of getting a Paperless-NGX API token. + + + A shortcut with command %@ already exists. + A shortcut with command %@ already exists. + A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut. + + + API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine. + API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine. + A warning message displayed in the settings tab of the app. + + + Access Token + Access Token + + + + Active + Active + A toggle + + + Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows. + Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows. + A description of how to toggle the active status of a skill. + + + Agent Skills + Agent Skills + + + + Allow Shell Command? + Allow Shell Command? + A title for a modal that asks the user if they want to allow a shell command. + + + Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs. + Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs. + + + + Anthropic Balance + Anthropic Balance + A label displayed above the user's balance on the Anthropic platform. + + + Auto-execute mode: commands run without approval. Use with caution. + Auto-execute mode: commands run without approval. Use with caution. + + + + Auto-sync can cause conflicts if running on multiple machines simultaneously. + Auto-sync can cause conflicts if running on multiple machines simultaneously. + + + + Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations. + Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations. + + + + Automatically summarize old portions of long conversations to save tokens and improve context efficiency. + Automatically summarize old portions of long conversations to save tokens and improve context efficiency. + + + + Back Up Now + Back Up Now + A button that backs up the user's data. + + + Backup location: + Backup location: + A label displayed under the backup location. + + + Bash Execution + Bash Execution + + + + Branch: %@ + Branch: %@ + + + + Built with SwiftUI + Built with SwiftUI + A label that says that the app is built with SwiftUI. + + + COMMAND + COMMAND + A label displayed above the command that the AI wants to run. + + + Capabilities + Capabilities + A label displayed under the list of capabilities of a model. + + + Changing these values affects how the AI generates responses. The defaults work well for most use cases. + Changing these values affects how the AI generates responses. The defaults work well for most use cases. + + + + Check your balance at: + Check your balance at: + A label displayed under a link to check the user's balance. + + + Check your usage at: + Check your usage at: + A label displayed under a link to OpenAI's usage page. + + + Clear All + Clear All + A button to clear all email activity logs. + + + Click 'Add Folder' below or drag folders here from Finder + Click 'Add Folder' below or drag folders here from Finder + + + + Command + Command + A label displayed above the command field. + + + Command History + Command History + A label displayed above the command history. + + + Content (Markdown) + Content (Markdown) + A + + + Controls which messages are written to ~/Library/Logs/oAI.log + Controls which messages are written to ~/Library/Logs/oAI.log + + + + Conversations + Conversations + + + + Copied! + Copied! + A message displayed when a code block is successfully copied to the clipboard. + + + Cost Examples + Cost Examples + A heading for the cost examples of a model. + + + Create a dedicated email account specifically for AI handling. Do NOT use your personal email address. + Create a dedicated email account specifically for AI handling. Do NOT use your personal email address. + + + + Create a shortcut to save a reusable prompt template accessible from the / command dropdown. + Create a shortcut to save a reusable prompt template accessible from the / command dropdown. + A description of the functionality of the shortcut editor. + + + Default Prompt + Default Prompt + + + + Delete (%@) + Delete (%@) + + + + Description + Description + A label + + + Each command will require your approval before running. + Each command will require your approval before running. + + + + Edit + Edit + A button to edit a skill. + + + Email Activity Log + Email Activity Log + The title of the email activity log view. + + + Email Handler System Prompt + Email Handler System Prompt + + + + Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults. + Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults. + + + + Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails. + Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails. + + + + Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens. + Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens. + + + + Enable email handler in Settings to start monitoring emails + Enable email handler in Settings to start monitoring emails + A message displayed when the email handler is disabled. + + + Error: %@ + Error: %@ + A label displaying an error message in the checkout view. + + + Example structure: + Example structure: + A + + + Example: oai-bot-x7k2m9p3@gmail.com + Example: oai-bot-x7k2m9p3@gmail.com + + + + File Access Permissions + File Access Permissions + + + + Files + Files + A label displayed above the list of files attached to a skill. + + + Google (Gemini embedding) + Google (Gemini embedding) + + + + Help + Help + A heading for the help screen + + + How to get your API token: + How to get your API token: + A heading for a section that describes how to get your API token. + + + Initialize Repository + Initialize Repository + + + + Inject into system prompt for every conversation + Inject into system prompt for every conversation + + + + Large files inflate the system prompt and may hit token limits. + Large files inflate the system prompt and may hit token limits. + A warning displayed when a user adds a large file to a skill. + + + Last sync: %@ + Last sync: %@ + + + + Leave empty to use the default email handler system prompt. + Leave empty to use the default email handler system prompt. + + + + Lowercase letters, numbers, and hyphens only. No spaces. + Lowercase letters, numbers, and hyphens only. No spaces. + A description of the format of a shortcut's command. + + + MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks. + MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks. + + + + Markdown instruction files injected into the system prompt. Compatible with SKILL.md format. + Markdown instruction files injected into the system prompt. Compatible with SKILL.md format. + A description of the format of agent skills. + + + Max Tokens: set to 0 to use model default. Higher values allow longer responses. + Max Tokens: set to 0 to use model default. Higher values allow longer responses. + + + + Model Context Protocol + Model Context Protocol + + + + Model Info + Model Info + A title for a view that displays information about a model. + + + Multi-provider AI chat client + Multi-provider AI chat client + A description of oAI. + + + Name + Name + A label + + + Never + Never + A label displayed in the settings view that indicates when the last backup was taken. + + + No credit data available + No credit data available + A message displayed when there is no credit data available. + + + No email activity yet + No email activity yet + A message displayed when the user has not yet received any email activity. + + + No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill. + No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill. + A message displayed when a user has not attached any files to a skill. + + + No model selected + No model selected + A label displayed when no model is selected. + + + No models available + No models available + + + + No results for "%@" + No results for "%@" + A message displayed when there are no search results. The argument is the search term. + + + No shortcuts yet + No shortcuts yet + A message displayed when a user has no shortcuts. + + + No shortcuts yet — click New Shortcut to create one. + No shortcuts yet — click New Shortcut to create one. + A message displayed when a user has no shortcuts. + + + No skills yet + No skills yet + A message displayed when the user has no skills. + + + No skills yet — click New Skill or Import to get started. + No skills yet — click New Skill or Import to get started. + A message displayed when a user has not created any skills. + + + Ollama (Local) + Ollama (Local) + A label displayed above the credits information for the local Ollie. + + + Only emails with this text in the subject line will be processed. Example: "[OAIBOT] What's the weather?" + Only emails with this text in the subject line will be processed. Example: "[OAIBOT] What's the weather?" + + + + OpenAI (text-embedding-3-large) + OpenAI (text-embedding-3-large) + + + + OpenAI (text-embedding-3-small) + OpenAI (text-embedding-3-small) + + + + OpenAI Balance + OpenAI Balance + A label displayed above the OpenAI balance section. + + + OpenRouter (OpenAI large) + OpenRouter (OpenAI large) + + + + OpenRouter (OpenAI small) + OpenRouter (OpenAI small) + + + + OpenRouter (Qwen 8B) + OpenRouter (Qwen 8B) + + + + OpenRouter Credits + OpenRouter Credits + A heading for the user's OpenRouter credits. + + + Read access (always enabled) + Read access (always enabled) + + + + Remote: %@ + Remote: %@ + + + + Restore from File… + Restore from File… + A button that allows the user to restore their data from a file. + + + Running locally — no credits needed! + Running locally — no credits needed! + A message displayed when using an on-device LLM like the one provided by the `.ollama` provider. + + + SKILL.md format — write instructions in plain Markdown. + SKILL.md format — write instructions in plain Markdown. + + + + SSH Key + SSH Key + + + + Security Recommendation + Security Recommendation + + + + Shell commands have full access to your system. Only approve commands you understand and trust. + Shell commands have full access to your system. Only approve commands you understand and trust. + A warning banner displayed in the Bash Approval Sheet. + + + Show text labels below toolbar icons (helpful for new users) + Show text labels below toolbar icons (helpful for new users) + A description of the feature that shows text labels below toolbar icons. + + + Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt. + Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt. + A description of how skills are used by the AI. + + + Stream responses as they're generated. Disable for single, complete responses. + Stream responses as they're generated. Disable for single, complete responses. + + + + Sync Now + Sync Now + + + + Sync conversations and settings across multiple machines using Git. + Sync conversations and settings across multiple machines using Git. + + + + Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative + Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative + + + + Template + Template + A label displayed above the text editor for the shortcut's template. + + + Test Connection + Test Connection + + + + Text files are injected into the system prompt alongside the skill. + Text files are injected into the system prompt alongside the skill. + A description of how text files are injected into the system prompt. + + + The AI can read and search files in allowed folders + The AI can read and search files in allowed folders + + + + The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise. + The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise. + + + + The AI wants to run the following command + The AI wants to run the following command + A description of the command that the AI wants to run. + + + This default prompt is always included to ensure accurate, helpful responses. + This default prompt is always included to ensure accurate, helpful responses. + + + + This will permanently delete all email activity logs. This action cannot be undone. + This will permanently delete all email activity logs. This action cannot be undone. + A message displayed in an alert when the user is about to clear all email logs. + + + Token Distribution + Token Distribution + A label displayed under the token distribution bar. + + + Try adjusting your search or filters + Try adjusting your search or filters + A description of the error that occurs when no models match the user's search. + + + Type / in the input to see command suggestions + Type / in the input to see command suggestions + A description of how to use the keyboard shortcut. + + + Type a message or / for commands... + Type a message or / for commands... + A placeholder text displayed in the input area when + + + Uncommitted changes: %@ + Uncommitted changes: %@ + + + + Update Available%@ + Update Available%@ + A button that opens a website with information about a new version of oAI. The argument is the version number of the new version. + + + Use **{{input}}** in the template to insert whatever you type after the command. + Use **{{input}}** in the template to insert whatever you type after the command. + A tooltip for the "lightbulb" icon in the shortcuts view + + + Use **{{input}}** to insert whatever you type after the command. + Use **{{input}}** to insert whatever you type after the command. + A description of how to use the shortcut template. + + + Use @filename to attach files to your message + Use @filename to attach files to your message + A description of how to attach files to a message. + + + Username + Password + Username + Password + + + + Version %1$@ (%2$@) + Version %1$@ (%2$@) + A version number in the format "Version 1.0 (1)" + + + View Email Log + View Email Log + + + + When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore. + When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore. + + + + Working directory: + Working directory: + A label displayed under the working directory of a pending shell command. + + + You can import any SKILL.md file from skill0.io or write your own. + You can import any SKILL.md file from skill0.io or write your own. + A description of how to use skills. + + + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + A link to the GNU Affero General Public License. + + + ^[%@ entry](inflect: true) + ^[%@ entry](inflect: true) + A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier. + + + active + active + A label for an active skill. + + + messages + messages + + + + oAI + oAI + The name of the app. + + + tokens + tokens + + + + v%@ + v%@ + A label showing the current version of oAI. + + + ~750 tokens ≈ 500 words. Online mode allows web search in responses. + ~750 tokens ≈ 500 words. Online mode allows web search in responses. + + + + © 2026 [Rune Olsen](https://blog.rune.pm) + © 2026 [Rune Olsen](https://blog.rune.pm) + A copyright notice with the copyright holder's name. + + + • + + A bullet point. + + + • Add public key to your git provider + • Add public key to your git provider + + + + • No credentials needed in oAI + • No credentials needed in oAI + + + + • Uses your system SSH keys (~/.ssh/id_ed25519) + • Uses your system SSH keys (~/.ssh/id_ed25519) + + + + ℹ️ SSH Key Authentication + ℹ️ SSH Key Authentication + + + + ↑↓ navigate ↩ open + ↑↓ navigate ↩ open + + + + ⌘N New • ⌘M Model • ⌘S Save + ⌘N New • ⌘M Model • ⌘S Save + A hint that appears on macOS when using keyboard shortcuts. + + + ⚠️ Custom prompt active — only this prompt will be sent to the model. + ⚠️ Custom prompt active — only this prompt will be sent to the model. + + + + ⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead. + ⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead. + + + + ⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab. + ⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab. + + + + ⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages. + ⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages. + + + + ⚠️ These are advanced settings + ⚠️ These are advanced settings + + + + ⚠️ interrupted + ⚠️ interrupted + A warning label that appears next to a message that was interrupted. + + + 🌐 + 🌐 + A label for a model that can be used to generate images. + + + 🎨 + 🎨 + A button that generates an image. + + + 👁️ + 👁️ + A checkmark emoji. + + + 💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords. + 💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords. + + + + 💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods. + 💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods. + + + + 🔧 + 🔧 + A checkmark emoji. + + +
+ +
+ +
+ + + oAI + oAI + Bundle name + + +
+
diff --git a/Locales/oAI Localizations/en.xcloc/Source Contents/oAI/Localizable.xcstrings b/Locales/oAI Localizations/en.xcloc/Source Contents/oAI/Localizable.xcstrings new file mode 100644 index 0000000..bfdb18c --- /dev/null +++ b/Locales/oAI Localizations/en.xcloc/Source Contents/oAI/Localizable.xcstrings @@ -0,0 +1,590 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "(always used)" : { + + }, + "(optional)" : { + + }, + "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" : { + "comment" : "A link to the GNU Affero General Public License.", + "isCommentAutoGenerated" : true + }, + "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" : { + + }, + "%@" : { + + }, + "%@ active — injected into every conversation" : { + "comment" : "A footnote indicating that some skills are active.", + "isCommentAutoGenerated" : true + }, + "%@ chars" : { + "comment" : "A label showing the number of characters in the skill's content. The argument is the number of characters in the content.", + "isCommentAutoGenerated" : true + }, + "%@ min" : { + + }, + "%@ pt" : { + "comment" : "A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points.", + "isCommentAutoGenerated" : true + }, + "%@s" : { + + }, + "•" : { + "comment" : "A bullet point.", + "isCommentAutoGenerated" : true + }, + "• Add public key to your git provider" : { + + }, + "• No credentials needed in oAI" : { + + }, + "• Uses your system SSH keys (~/.ssh/id_ed25519)" : { + + }, + "^[%@ entry](inflect: true)" : { + "comment" : "A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier.", + "isCommentAutoGenerated" : true + }, + "© 2026 [Rune Olsen](https://blog.rune.pm)" : { + "comment" : "A copyright notice with the copyright holder's name.", + "isCommentAutoGenerated" : true + }, + "↑↓ navigate ↩ open" : { + + }, + "~750 tokens ≈ 500 words. Online mode allows web search in responses." : { + + }, + "⌘N New • ⌘M Model • ⌘S Save" : { + "comment" : "A hint that appears on macOS when using keyboard shortcuts.", + "isCommentAutoGenerated" : true + }, + "⚠️ Custom prompt active — only this prompt will be sent to the model." : { + + }, + "⚠️ interrupted" : { + "comment" : "A warning label that appears next to a message that was interrupted.", + "isCommentAutoGenerated" : true + }, + "⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead." : { + + }, + "⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab." : { + + }, + "⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages." : { + + }, + "⚠️ These are advanced settings" : { + + }, + "🌐" : { + "comment" : "A label for a model that can be used to generate images.", + "isCommentAutoGenerated" : true + }, + "🎨" : { + "comment" : "A button that generates an image.", + "isCommentAutoGenerated" : true + }, + "👁️" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords." : { + + }, + "💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods." : { + + }, + "🔧" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "1. Open Paperless-NGX → Settings → API Tokens" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "2. Create or copy your token" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "3. Paste it above" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "A shortcut with command %@ already exists." : { + "comment" : "A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut.", + "isCommentAutoGenerated" : true + }, + "Access Token" : { + + }, + "active" : { + "comment" : "A label for an active skill.", + "isCommentAutoGenerated" : true + }, + "Active" : { + "comment" : "A toggle", + "isCommentAutoGenerated" : true + }, + "Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows." : { + "comment" : "A description of how to toggle the active status of a skill.", + "isCommentAutoGenerated" : true + }, + "Agent Skills" : { + + }, + "Allow Shell Command?" : { + "comment" : "A title for a modal that asks the user if they want to allow a shell command.", + "isCommentAutoGenerated" : true + }, + "Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs." : { + + }, + "Anthropic Balance" : { + "comment" : "A label displayed above the user's balance on the Anthropic platform.", + "isCommentAutoGenerated" : true + }, + "API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine." : { + "comment" : "A warning message displayed in the settings tab of the app.", + "isCommentAutoGenerated" : true + }, + "Auto-execute mode: commands run without approval. Use with caution." : { + + }, + "Auto-sync can cause conflicts if running on multiple machines simultaneously." : { + + }, + "Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations." : { + + }, + "Automatically summarize old portions of long conversations to save tokens and improve context efficiency." : { + + }, + "Back Up Now" : { + "comment" : "A button that backs up the user's data.", + "isCommentAutoGenerated" : true + }, + "Backup location:" : { + "comment" : "A label displayed under the backup location.", + "isCommentAutoGenerated" : true + }, + "Bash Execution" : { + + }, + "Branch: %@" : { + + }, + "Built with SwiftUI" : { + "comment" : "A label that says that the app is built with SwiftUI.", + "isCommentAutoGenerated" : true + }, + "Capabilities" : { + "comment" : "A label displayed under the list of capabilities of a model.", + "isCommentAutoGenerated" : true + }, + "Changing these values affects how the AI generates responses. The defaults work well for most use cases." : { + + }, + "Check your balance at:" : { + "comment" : "A label displayed under a link to check the user's balance.", + "isCommentAutoGenerated" : true + }, + "Check your usage at:" : { + "comment" : "A label displayed under a link to OpenAI's usage page.", + "isCommentAutoGenerated" : true + }, + "Clear All" : { + "comment" : "A button to clear all email activity logs.", + "isCommentAutoGenerated" : true + }, + "Click 'Add Folder' below or drag folders here from Finder" : { + + }, + "Command" : { + "comment" : "A label displayed above the command field.", + "isCommentAutoGenerated" : true + }, + "COMMAND" : { + "comment" : "A label displayed above the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "Command History" : { + "comment" : "A label displayed above the command history.", + "isCommentAutoGenerated" : true + }, + "Content (Markdown)" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Controls which messages are written to ~/Library/Logs/oAI.log" : { + + }, + "Conversations" : { + + }, + "Copied!" : { + "comment" : "A message displayed when a code block is successfully copied to the clipboard.", + "isCommentAutoGenerated" : true + }, + "Cost Examples" : { + "comment" : "A heading for the cost examples of a model.", + "isCommentAutoGenerated" : true + }, + "Create a dedicated email account specifically for AI handling. Do NOT use your personal email address." : { + + }, + "Create a shortcut to save a reusable prompt template accessible from the / command dropdown." : { + "comment" : "A description of the functionality of the shortcut editor.", + "isCommentAutoGenerated" : true + }, + "Default Prompt" : { + + }, + "Delete (%@)" : { + + }, + "Description" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Each command will require your approval before running." : { + + }, + "Edit" : { + "comment" : "A button to edit a skill.", + "isCommentAutoGenerated" : true + }, + "Email Activity Log" : { + "comment" : "The title of the email activity log view.", + "isCommentAutoGenerated" : true + }, + "Email Handler System Prompt" : { + + }, + "Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults." : { + + }, + "Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails." : { + + }, + "Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens." : { + + }, + "Enable email handler in Settings to start monitoring emails" : { + "comment" : "A message displayed when the email handler is disabled.", + "isCommentAutoGenerated" : true + }, + "Error: %@" : { + "comment" : "A label displaying an error message in the checkout view.", + "isCommentAutoGenerated" : true + }, + "Example structure:" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Example: oai-bot-x7k2m9p3@gmail.com" : { + + }, + "File Access Permissions" : { + + }, + "Files" : { + "comment" : "A label displayed above the list of files attached to a skill.", + "isCommentAutoGenerated" : true + }, + "Google (Gemini embedding)" : { + + }, + "Help" : { + "comment" : "A heading for the help screen", + "isCommentAutoGenerated" : true + }, + "How to get your API token:" : { + "comment" : "A heading for a section that describes how to get your API token.", + "isCommentAutoGenerated" : true + }, + "ℹ️ SSH Key Authentication" : { + + }, + "Initialize Repository" : { + + }, + "Inject into system prompt for every conversation" : { + + }, + "Large files inflate the system prompt and may hit token limits." : { + "comment" : "A warning displayed when a user adds a large file to a skill.", + "isCommentAutoGenerated" : true + }, + "Last sync: %@" : { + + }, + "Leave empty to use the default email handler system prompt." : { + + }, + "Lowercase letters, numbers, and hyphens only. No spaces." : { + "comment" : "A description of the format of a shortcut's command.", + "isCommentAutoGenerated" : true + }, + "Markdown instruction files injected into the system prompt. Compatible with SKILL.md format." : { + "comment" : "A description of the format of agent skills.", + "isCommentAutoGenerated" : true + }, + "Max Tokens: set to 0 to use model default. Higher values allow longer responses." : { + + }, + "MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks." : { + + }, + "messages" : { + + }, + "Model Context Protocol" : { + + }, + "Model Info" : { + "comment" : "A title for a view that displays information about a model.", + "isCommentAutoGenerated" : true + }, + "Multi-provider AI chat client" : { + "comment" : "A description of oAI.", + "isCommentAutoGenerated" : true + }, + "Name" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Never" : { + "comment" : "A label displayed in the settings view that indicates when the last backup was taken.", + "isCommentAutoGenerated" : true + }, + "No credit data available" : { + "comment" : "A message displayed when there is no credit data available.", + "isCommentAutoGenerated" : true + }, + "No email activity yet" : { + "comment" : "A message displayed when the user has not yet received any email activity.", + "isCommentAutoGenerated" : true + }, + "No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill." : { + "comment" : "A message displayed when a user has not attached any files to a skill.", + "isCommentAutoGenerated" : true + }, + "No model selected" : { + "comment" : "A label displayed when no model is selected.", + "isCommentAutoGenerated" : true + }, + "No models available" : { + + }, + "No results for \"%@\"" : { + "comment" : "A message displayed when there are no search results. The argument is the search term.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet" : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet — click New Shortcut to create one." : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No skills yet" : { + "comment" : "A message displayed when the user has no skills.", + "isCommentAutoGenerated" : true + }, + "No skills yet — click New Skill or Import to get started." : { + "comment" : "A message displayed when a user has not created any skills.", + "isCommentAutoGenerated" : true + }, + "oAI" : { + "comment" : "The name of the app.", + "isCommentAutoGenerated" : true + }, + "Ollama (Local)" : { + "comment" : "A label displayed above the credits information for the local Ollie.", + "isCommentAutoGenerated" : true + }, + "Only emails with this text in the subject line will be processed. Example: \"[OAIBOT] What's the weather?\"" : { + + }, + "OpenAI (text-embedding-3-large)" : { + + }, + "OpenAI (text-embedding-3-small)" : { + + }, + "OpenAI Balance" : { + "comment" : "A label displayed above the OpenAI balance section.", + "isCommentAutoGenerated" : true + }, + "OpenRouter (OpenAI large)" : { + + }, + "OpenRouter (OpenAI small)" : { + + }, + "OpenRouter (Qwen 8B)" : { + + }, + "OpenRouter Credits" : { + "comment" : "A heading for the user's OpenRouter credits.", + "isCommentAutoGenerated" : true + }, + "Read access (always enabled)" : { + + }, + "Remote: %@" : { + + }, + "Restore from File…" : { + "comment" : "A button that allows the user to restore their data from a file.", + "isCommentAutoGenerated" : true + }, + "Running locally — no credits needed!" : { + "comment" : "A message displayed when using an on-device LLM like the one provided by the `.ollama` provider.", + "isCommentAutoGenerated" : true + }, + "Security Recommendation" : { + + }, + "Shell commands have full access to your system. Only approve commands you understand and trust." : { + "comment" : "A warning banner displayed in the Bash Approval Sheet.", + "isCommentAutoGenerated" : true + }, + "Show text labels below toolbar icons (helpful for new users)" : { + "comment" : "A description of the feature that shows text labels below toolbar icons.", + "isCommentAutoGenerated" : true + }, + "SKILL.md format — write instructions in plain Markdown." : { + + }, + "Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt." : { + "comment" : "A description of how skills are used by the AI.", + "isCommentAutoGenerated" : true + }, + "SSH Key" : { + + }, + "Stream responses as they're generated. Disable for single, complete responses." : { + + }, + "Sync conversations and settings across multiple machines using Git." : { + + }, + "Sync Now" : { + + }, + "Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative" : { + + }, + "Template" : { + "comment" : "A label displayed above the text editor for the shortcut's template.", + "isCommentAutoGenerated" : true + }, + "Test Connection" : { + + }, + "Text files are injected into the system prompt alongside the skill." : { + "comment" : "A description of how text files are injected into the system prompt.", + "isCommentAutoGenerated" : true + }, + "The AI can read and search files in allowed folders" : { + + }, + "The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise." : { + + }, + "The AI wants to run the following command" : { + "comment" : "A description of the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "This default prompt is always included to ensure accurate, helpful responses." : { + + }, + "This will permanently delete all email activity logs. This action cannot be undone." : { + "comment" : "A message displayed in an alert when the user is about to clear all email logs.", + "isCommentAutoGenerated" : true + }, + "Token Distribution" : { + "comment" : "A label displayed under the token distribution bar.", + "isCommentAutoGenerated" : true + }, + "tokens" : { + + }, + "Try adjusting your search or filters" : { + "comment" : "A description of the error that occurs when no models match the user's search.", + "isCommentAutoGenerated" : true + }, + "Type / in the input to see command suggestions" : { + "comment" : "A description of how to use the keyboard shortcut.", + "isCommentAutoGenerated" : true + }, + "Type a message or / for commands..." : { + "comment" : "A placeholder text displayed in the input area when", + "isCommentAutoGenerated" : true + }, + "Uncommitted changes: %@" : { + + }, + "Update Available%@" : { + "comment" : "A button that opens a website with information about a new version of oAI. The argument is the version number of the new version.", + "isCommentAutoGenerated" : true + }, + "Use @filename to attach files to your message" : { + "comment" : "A description of how to attach files to a message.", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** in the template to insert whatever you type after the command." : { + "comment" : "A tooltip for the \"lightbulb\" icon in the shortcuts view", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** to insert whatever you type after the command." : { + "comment" : "A description of how to use the shortcut template.", + "isCommentAutoGenerated" : true + }, + "Username + Password" : { + + }, + "v%@" : { + "comment" : "A label showing the current version of oAI.", + "isCommentAutoGenerated" : true + }, + "Version %@ (%@)" : { + "comment" : "A version number in the format \"Version 1.0 (1)\"", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Version %1$@ (%2$@)" + } + } + } + }, + "View Email Log" : { + + }, + "When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore." : { + + }, + "Working directory:" : { + "comment" : "A label displayed under the working directory of a pending shell command.", + "isCommentAutoGenerated" : true + }, + "You can import any SKILL.md file from skill0.io or write your own." : { + "comment" : "A description of how to use skills.", + "isCommentAutoGenerated" : true + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/en.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings b/Locales/oAI Localizations/en.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings new file mode 100644 index 0000000..5448135 --- /dev/null +++ b/Locales/oAI Localizations/en.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings @@ -0,0 +1,18 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleName" : { + "comment" : "Bundle name", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "oAI" + } + } + } + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/en.xcloc/contents.json b/Locales/oAI Localizations/en.xcloc/contents.json new file mode 100644 index 0000000..f1c3213 --- /dev/null +++ b/Locales/oAI Localizations/en.xcloc/contents.json @@ -0,0 +1,12 @@ +{ + "developmentRegion" : "en", + "project" : "oAI.xcodeproj", + "targetLocale" : "en", + "toolInfo" : { + "toolBuildNumber" : "17C52", + "toolID" : "com.apple.dt.xcode", + "toolName" : "Xcode", + "toolVersion" : "26.2" + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/nb.xcloc/Localized Contents/nb.xliff b/Locales/oAI Localizations/nb.xcloc/Localized Contents/nb.xliff new file mode 100644 index 0000000..41e3640 --- /dev/null +++ b/Locales/oAI Localizations/nb.xcloc/Localized Contents/nb.xliff @@ -0,0 +1,843 @@ + + + +
+ +
+ + + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + + + + %@ + %@ + + + + %@ active — injected into every conversation + %@ aktiv — injisert i alle samtaler + A footnote indicating that some skills are active. + + + %@ chars + %@ tegn + A label showing the number of characters in the skill's content. The argument is the number of characters in the content. + + + %@ min + %@ min + + + + %@ pt + %@ pt + A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points. + + + %@s + %@s + + + + (always used) + (alltid brukt) + + + + (optional) + (valgfri) + + + + 1. Open Paperless-NGX → Settings → API Tokens + 1. Åpne Paperless-NGX → Innstillinger → API-tokens + A step in the process of getting a Paperless-NGX API token. + + + 2. Create or copy your token + 2. Opprett eller kopier tokenet ditt + A step in the process of getting a Paperless-NGX API token. + + + 3. Paste it above + 3. Lim det inn ovenfor + A step in the process of getting a Paperless-NGX API token. + + + A shortcut with command %@ already exists. + En snarvei med kommandoen %@ finnes allerede. + A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut. + + + API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine. + API-nøkler og legitimasjon er **ikke** inkludert i sikkerhetskopien. Du må taste dem inn på nytt etter gjenoppretting på en ny maskin. + A warning message displayed in the settings tab of the app. + + + Access Token + Access Token + + + + Active + Aktiv + A toggle + + + Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows. + Aktive ferdigheter legges til systemprompten. Slå dem av og på per ferdighet for å kontrollere hva KI-en vet. + A description of how to toggle the active status of a skill. + + + Agent Skills + Agent-ferdigheter + + + + Allow Shell Command? + Tillat skalkommando? + A title for a modal that asks the user if they want to allow a shell command. + + + Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs. + Gi KI-en tilgang til å kjøre skalkommandoer på maskinen din. Kommandoer kjøres via /bin/zsh. Aktiver godkjenningsmodus for å se gjennom hver kommando før den kjøres. + + + + Anthropic Balance + Anthropic Balance + A label displayed above the user's balance on the Anthropic platform. + + + Auto-execute mode: commands run without approval. Use with caution. + Auto-kjøremodus: kommandoer kjøres uten godkjenning. Bruk med forsiktighet. + + + + Auto-sync can cause conflicts if running on multiple machines simultaneously. + Auto-synkronisering kan forårsake konflikter hvis den kjøres på flere maskiner samtidig. + + + + Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations. + Velg automatisk relevante meldinger i stedet for å sende all historikk. Reduserer tokenforbruk for lange samtaler. + + + + Automatically summarize old portions of long conversations to save tokens and improve context efficiency. + Sammenfatt automatisk eldre deler av lange samtaler for å spare tokens og forbedre konteksteffektiviteten. + + + + Back Up Now + Sikkerhetskopier nå + A button that backs up the user's data. + + + Backup location: + Sikkerhetskopiplassering: + A label displayed under the backup location. + + + Bash Execution + Bash Execution + + + + Branch: %@ + Branch: %@ + + + + Built with SwiftUI + Built with SwiftUI + A label that says that the app is built with SwiftUI. + + + COMMAND + KOMMANDO + A label displayed above the command that the AI wants to run. + + + Capabilities + Egenskaper + A label displayed under the list of capabilities of a model. + + + Changing these values affects how the AI generates responses. The defaults work well for most use cases. + Å endre disse verdiene påvirker hvordan KI-en genererer svar. Standardverdiene fungerer bra for de fleste brukstilfeller. + + + + Check your balance at: + Sjekk saldoen din på: + A label displayed under a link to check the user's balance. + + + Check your usage at: + Sjekk forbruket ditt på: + A label displayed under a link to OpenAI's usage page. + + + Clear All + Slett alle + A button to clear all email activity logs. + + + Click 'Add Folder' below or drag folders here from Finder + Klikk «Legg til mappe» nedenfor eller dra mapper hit fra Finder + + + + Command + Kommando + A label displayed above the command field. + + + Command History + Kommandohistorikk + A label displayed above the command history. + + + Content (Markdown) + Innhold (Markdown) + A + + + Controls which messages are written to ~/Library/Logs/oAI.log + Styrer hvilke meldinger som skrives til ~/Library/Logs/oAI.log + + + + Conversations + Samtaler + + + + Copied! + Kopiert! + A message displayed when a code block is successfully copied to the clipboard. + + + Cost Examples + Kostnadseksempler + A heading for the cost examples of a model. + + + Create a dedicated email account specifically for AI handling. Do NOT use your personal email address. + Opprett en dedikert e-postkonto spesielt for KI-håndtering. IKKE bruk din personlige e-postadresse. + + + + Create a shortcut to save a reusable prompt template accessible from the / command dropdown. + Opprett en snarvei for å lagre en gjenbrukbar promptmal som er tilgjengelig fra /kommando-rullegardinmenyen. + A description of the functionality of the shortcut editor. + + + Default Prompt + Standardprompt + + + + Delete (%@) + Slett (%@) + + + + Description + Beskrivelse + A label + + + Each command will require your approval before running. + Hver kommando krever din godkjenning før den kjøres. + + + + Edit + Rediger + A button to edit a skill. + + + Email Activity Log + E-postaktivitetslogg + The title of the email activity log view. + + + Email Handler System Prompt + Systemprompt for e-postbehandler + + + + Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults. + E-postbehandleren bruker KUN sin egen systemprompt, fullstendig isolert fra de viktigste chatinnstillingene dine. En egendefinert prompt nedenfor vil overstyre standardinnstillingene. + + + + Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails. + E-postoppgaver har SKRIVEBESKYTTET tilgang til MCP-mapper. KI-en kan ikke skrive, slette eller endre filer ved behandling av e-post. + + + + Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens. + Aktiver KI-drevet semantisk søk med %@-innbygginger. Kostnad: ~$0,02–0,15/1M tokens. + + + + Enable email handler in Settings to start monitoring emails + Aktiver e-postbehandler i Innstillinger for å starte overvåking av e-post + A message displayed when the email handler is disabled. + + + Error: %@ + Feil: %@ + A label displaying an error message in the checkout view. + + + Example structure: + Eksempelstruktur: + A + + + Example: oai-bot-x7k2m9p3@gmail.com + Example: oai-bot-x7k2m9p3@gmail.com + + + + File Access Permissions + Filtillatelser + + + + Files + Filer + A label displayed above the list of files attached to a skill. + + + Google (Gemini embedding) + Google (Gemini embedding) + + + + Help + Hjelp + A heading for the help screen + + + How to get your API token: + Slik får du API-tokenet ditt: + A heading for a section that describes how to get your API token. + + + Initialize Repository + Initialiser repositorium + + + + Inject into system prompt for every conversation + Injiser i systemprompten for hver samtale + + + + Large files inflate the system prompt and may hit token limits. + Store filer blåser opp systemprompten og kan nå token-grenser. + A warning displayed when a user adds a large file to a skill. + + + Last sync: %@ + Siste synkronisering: %@ + + + + Leave empty to use the default email handler system prompt. + La stå tomt for å bruke standard systemprompt for e-postbehandler. + + + + Lowercase letters, numbers, and hyphens only. No spaces. + Kun små bokstaver, tall og bindestreker. Ingen mellomrom. + A description of the format of a shortcut's command. + + + MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks. + MCP gir KI-en kontrollert tilgang til å lese og eventuelt skrive filer på datamaskinen din. KI-en kan søke, lese og analysere filer i tillatte mapper for å hjelpe med koding, analyse og andre oppgaver. + + + + Markdown instruction files injected into the system prompt. Compatible with SKILL.md format. + Markdown-instruksjonsfiler som injiseres i systemprompten. Kompatibel med SKILL.md-format. + A description of the format of agent skills. + + + Max Tokens: set to 0 to use model default. Higher values allow longer responses. + Maks tokens: sett til 0 for å bruke modellstandard. Høyere verdier gir lengre svar. + + + + Model Context Protocol + Model Context Protocol + + + + Model Info + Modellinformasjon + A title for a view that displays information about a model. + + + Multi-provider AI chat client + AI-chatklient med støtte for flere tilbydere + A description of oAI. + + + Name + Navn + A label + + + Never + Aldri + A label displayed in the settings view that indicates when the last backup was taken. + + + No credit data available + Ingen kredittdata tilgjengelig + A message displayed when there is no credit data available. + + + No email activity yet + Ingen e-postaktivitet ennå + A message displayed when the user has not yet received any email activity. + + + No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill. + Ingen filer vedlagt. Legg til JSON, YAML, CSV eller TXT-filer for å injisere data i systemprompten sammen med denne ferdigheten. + A message displayed when a user has not attached any files to a skill. + + + No model selected + Ingen modell valgt + A label displayed when no model is selected. + + + No models available + Ingen modeller tilgjengelig + + + + No results for "%@" + Ingen resultater for «%@» + A message displayed when there are no search results. The argument is the search term. + + + No shortcuts yet + Ingen snarveier ennå + A message displayed when a user has no shortcuts. + + + No shortcuts yet — click New Shortcut to create one. + Ingen snarveier ennå — klikk på Ny snarvei for å opprette en. + A message displayed when a user has no shortcuts. + + + No skills yet + Ingen ferdigheter ennå + A message displayed when the user has no skills. + + + No skills yet — click New Skill or Import to get started. + Ingen ferdigheter ennå — klikk på Ny ferdighet eller Importer for å komme i gang. + A message displayed when a user has not created any skills. + + + Ollama (Local) + Ollama (Local) + A label displayed above the credits information for the local Ollie. + + + Only emails with this text in the subject line will be processed. Example: "[OAIBOT] What's the weather?" + Bare e-poster med denne teksten i emnefeltet behandles. Eksempel: «[OAIBOT] Hva er været?» + + + + OpenAI (text-embedding-3-large) + OpenAI (text-embedding-3-large) + + + + OpenAI (text-embedding-3-small) + OpenAI (text-embedding-3-small) + + + + OpenAI Balance + OpenAI Balance + A label displayed above the OpenAI balance section. + + + OpenRouter (OpenAI large) + OpenRouter (OpenAI large) + + + + OpenRouter (OpenAI small) + OpenRouter (OpenAI small) + + + + OpenRouter (Qwen 8B) + OpenRouter (Qwen 8B) + + + + OpenRouter Credits + OpenRouter Credits + A heading for the user's OpenRouter credits. + + + Read access (always enabled) + Lesetilgang (alltid aktivert) + + + + Remote: %@ + Remote: %@ + + + + Restore from File… + Gjenopprett fra fil… + A button that allows the user to restore their data from a file. + + + Running locally — no credits needed! + Kjører lokalt — ingen kreditter nødvendig! + A message displayed when using an on-device LLM like the one provided by the `.ollama` provider. + + + SKILL.md format — write instructions in plain Markdown. + SKILL.md-format — skriv instruksjoner i vanlig Markdown. + + + + SSH Key + SSH Key + + + + Security Recommendation + Sikkerhetsanbefaling + + + + Shell commands have full access to your system. Only approve commands you understand and trust. + Skalkommandoer har full tilgang til systemet ditt. Godkjenn bare kommandoer du forstår og stoler på. + A warning banner displayed in the Bash Approval Sheet. + + + Show text labels below toolbar icons (helpful for new users) + Vis tekstetiketter under verktøylinjeikonene (nyttig for nye brukere) + A description of the feature that shows text labels below toolbar icons. + + + Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt. + Ferdigheter er Markdown-instruksjonsfiler som lærer KI-en hvordan den skal oppføre seg. Aktive ferdigheter injiseres automatisk i systemprompten. + A description of how skills are used by the AI. + + + Stream responses as they're generated. Disable for single, complete responses. + Strøm svar etter hvert som de genereres. Deaktiver for enkle, fullstendige svar. + + + + Sync Now + Synkroniser nå + + + + Sync conversations and settings across multiple machines using Git. + Synkroniser samtaler og innstillinger på tvers av flere maskiner ved hjelp av Git. + + + + Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative + Temperatur: 0 = modellstandard · 0,0–0,7 = fokusert · 0,8–2,0 = kreativ + + + + Template + Mal + A label displayed above the text editor for the shortcut's template. + + + Test Connection + Test tilkobling + + + + Text files are injected into the system prompt alongside the skill. + Tekstfiler injiseres i systemprompten ved siden av ferdigheten. + A description of how text files are injected into the system prompt. + + + The AI can read and search files in allowed folders + KI-en kan lese og søke filer i tillatte mapper + + + + The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise. + KI-en leser dette innholdet og bestemmer når det skal brukes. Beskriv **hva** KI-en skal gjøre og **hvordan** — vær spesifikk og konsis. + + + + The AI wants to run the following command + KI-en ønsker å kjøre følgende kommando + A description of the command that the AI wants to run. + + + This default prompt is always included to ensure accurate, helpful responses. + Denne standardprompten er alltid inkludert for å sikre nøyaktige, nyttige svar. + + + + This will permanently delete all email activity logs. This action cannot be undone. + Dette vil slette alle e-postaktivitetslogger permanent. Denne handlingen kan ikke angres. + A message displayed in an alert when the user is about to clear all email logs. + + + Token Distribution + Token-fordeling + A label displayed under the token distribution bar. + + + Try adjusting your search or filters + Prøv å justere søket eller filtrene + A description of the error that occurs when no models match the user's search. + + + Type / in the input to see command suggestions + Skriv / i inndata for å se kommandoforslag + A description of how to use the keyboard shortcut. + + + Type a message or / for commands... + Skriv en melding eller / for kommandoer... + A placeholder text displayed in the input area when + + + Uncommitted changes: %@ + Ulagrede endringer: %@ + + + + Update Available%@ + Oppdatering tilgjengelig%@ + A button that opens a website with information about a new version of oAI. The argument is the version number of the new version. + + + Use **{{input}}** in the template to insert whatever you type after the command. + Bruk **{{input}}** i malen for å sette inn det du skriver etter kommandoen. + A tooltip for the "lightbulb" icon in the shortcuts view + + + Use **{{input}}** to insert whatever you type after the command. + Bruk **{{input}}** for å sette inn det du skriver etter kommandoen. + A description of how to use the shortcut template. + + + Use @filename to attach files to your message + Bruk @filnavn for å legge ved filer i meldingen + A description of how to attach files to a message. + + + Username + Password + Username + Password + + + + Version %1$@ (%2$@) + Version %1$@ (%2$@) + A version number in the format "Version 1.0 (1)" + + + View Email Log + Vis e-postlogg + + + + When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore. + Når aktivert, hoppes gitignorerte filer over ved listing og søk. Skriveoperasjoner ignorerer alltid .gitignore. + + + + Working directory: + Arbeidsmappe: + A label displayed under the working directory of a pending shell command. + + + You can import any SKILL.md file from skill0.io or write your own. + Du kan importere en hvilken som helst SKILL.md-fil fra skill0.io eller skrive din egen. + A description of how to use skills. + + + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + A link to the GNU Affero General Public License. + + + ^[%@ entry](inflect: true) + ^[%@ oppføring](inflect: true) + A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier. + + + active + aktiv + A label for an active skill. + + + messages + meldinger + + + + oAI + oAI + The name of the app. + + + tokens + tokens + + + + v%@ + v%@ + A label showing the current version of oAI. + + + ~750 tokens ≈ 500 words. Online mode allows web search in responses. + ~750 tokens ≈ 500 ord. Nettmodus tillater nettsøk i svar. + + + + © 2026 [Rune Olsen](https://blog.rune.pm) + © 2026 [Rune Olsen](https://blog.rune.pm) + A copyright notice with the copyright holder's name. + + + • + + A bullet point. + + + • Add public key to your git provider + • Legg til den offentlige nøkkelen hos git-leverandøren din + + + + • No credentials needed in oAI + • Ingen legitimasjon nødvendig i oAI + + + + • Uses your system SSH keys (~/.ssh/id_ed25519) + • Bruker systemets SSH-nøkler (~/.ssh/id_ed25519) + + + + ℹ️ SSH Key Authentication + ℹ️ SSH-nøkkelautentisering + + + + ↑↓ navigate ↩ open + ↑↓ navigate ↩ open + + + + ⌘N New • ⌘M Model • ⌘S Save + ⌘N New • ⌘M Model • ⌘S Save + A hint that appears on macOS when using keyboard shortcuts. + + + ⚠️ Custom prompt active — only this prompt will be sent to the model. + ⚠️ Egendefinert prompt aktiv — bare denne prompten sendes til modellen. + + + + ⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead. + ⚠️ Mange tilbydere (GitHub) støtter ikke lenger passordautentisering. Bruk Access Token i stedet. + + + + ⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab. + ⚠️ Ingen innbyggingsleverandører tilgjengelig. Konfigurer en API-nøkkel for OpenAI, OpenRouter eller Google i fanen Generelt. + + + + ⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages. + ⚠️ Engangsoperasjon — genererer innbygginger for alle meldinger. Estimert kostnad: ~$0,04 for 10 000 meldinger. + + + + ⚠️ These are advanced settings + ⚠️ Dette er avanserte innstillinger + + + + ⚠️ interrupted + ⚠️ avbrutt + A warning label that appears next to a message that was interrupted. + + + 🌐 + 🌐 + A label for a model that can be used to generate images. + + + 🎨 + 🎨 + A button that generates an image. + + + 👁️ + 👁️ + A checkmark emoji. + + + 💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords. + 💡 For Gmail, bruk et apppassord. Google-konto > Sikkerhet > 2-trinns bekreftelse > Apppassord. + + + + 💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods. + 💡 Bruk HTTPS URL (f.eks. https://gitlab.pm/user/repo.git) — fungerer med alle autentiseringsmetoder. + + + + 🔧 + 🔧 + A checkmark emoji. + + +
+ +
+ +
+ + + oAI + oAI + Bundle name + + +
+
diff --git a/Locales/oAI Localizations/nb.xcloc/Source Contents/oAI/Localizable.xcstrings b/Locales/oAI Localizations/nb.xcloc/Source Contents/oAI/Localizable.xcstrings new file mode 100644 index 0000000..bfdb18c --- /dev/null +++ b/Locales/oAI Localizations/nb.xcloc/Source Contents/oAI/Localizable.xcstrings @@ -0,0 +1,590 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "(always used)" : { + + }, + "(optional)" : { + + }, + "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" : { + "comment" : "A link to the GNU Affero General Public License.", + "isCommentAutoGenerated" : true + }, + "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" : { + + }, + "%@" : { + + }, + "%@ active — injected into every conversation" : { + "comment" : "A footnote indicating that some skills are active.", + "isCommentAutoGenerated" : true + }, + "%@ chars" : { + "comment" : "A label showing the number of characters in the skill's content. The argument is the number of characters in the content.", + "isCommentAutoGenerated" : true + }, + "%@ min" : { + + }, + "%@ pt" : { + "comment" : "A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points.", + "isCommentAutoGenerated" : true + }, + "%@s" : { + + }, + "•" : { + "comment" : "A bullet point.", + "isCommentAutoGenerated" : true + }, + "• Add public key to your git provider" : { + + }, + "• No credentials needed in oAI" : { + + }, + "• Uses your system SSH keys (~/.ssh/id_ed25519)" : { + + }, + "^[%@ entry](inflect: true)" : { + "comment" : "A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier.", + "isCommentAutoGenerated" : true + }, + "© 2026 [Rune Olsen](https://blog.rune.pm)" : { + "comment" : "A copyright notice with the copyright holder's name.", + "isCommentAutoGenerated" : true + }, + "↑↓ navigate ↩ open" : { + + }, + "~750 tokens ≈ 500 words. Online mode allows web search in responses." : { + + }, + "⌘N New • ⌘M Model • ⌘S Save" : { + "comment" : "A hint that appears on macOS when using keyboard shortcuts.", + "isCommentAutoGenerated" : true + }, + "⚠️ Custom prompt active — only this prompt will be sent to the model." : { + + }, + "⚠️ interrupted" : { + "comment" : "A warning label that appears next to a message that was interrupted.", + "isCommentAutoGenerated" : true + }, + "⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead." : { + + }, + "⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab." : { + + }, + "⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages." : { + + }, + "⚠️ These are advanced settings" : { + + }, + "🌐" : { + "comment" : "A label for a model that can be used to generate images.", + "isCommentAutoGenerated" : true + }, + "🎨" : { + "comment" : "A button that generates an image.", + "isCommentAutoGenerated" : true + }, + "👁️" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords." : { + + }, + "💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods." : { + + }, + "🔧" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "1. Open Paperless-NGX → Settings → API Tokens" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "2. Create or copy your token" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "3. Paste it above" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "A shortcut with command %@ already exists." : { + "comment" : "A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut.", + "isCommentAutoGenerated" : true + }, + "Access Token" : { + + }, + "active" : { + "comment" : "A label for an active skill.", + "isCommentAutoGenerated" : true + }, + "Active" : { + "comment" : "A toggle", + "isCommentAutoGenerated" : true + }, + "Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows." : { + "comment" : "A description of how to toggle the active status of a skill.", + "isCommentAutoGenerated" : true + }, + "Agent Skills" : { + + }, + "Allow Shell Command?" : { + "comment" : "A title for a modal that asks the user if they want to allow a shell command.", + "isCommentAutoGenerated" : true + }, + "Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs." : { + + }, + "Anthropic Balance" : { + "comment" : "A label displayed above the user's balance on the Anthropic platform.", + "isCommentAutoGenerated" : true + }, + "API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine." : { + "comment" : "A warning message displayed in the settings tab of the app.", + "isCommentAutoGenerated" : true + }, + "Auto-execute mode: commands run without approval. Use with caution." : { + + }, + "Auto-sync can cause conflicts if running on multiple machines simultaneously." : { + + }, + "Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations." : { + + }, + "Automatically summarize old portions of long conversations to save tokens and improve context efficiency." : { + + }, + "Back Up Now" : { + "comment" : "A button that backs up the user's data.", + "isCommentAutoGenerated" : true + }, + "Backup location:" : { + "comment" : "A label displayed under the backup location.", + "isCommentAutoGenerated" : true + }, + "Bash Execution" : { + + }, + "Branch: %@" : { + + }, + "Built with SwiftUI" : { + "comment" : "A label that says that the app is built with SwiftUI.", + "isCommentAutoGenerated" : true + }, + "Capabilities" : { + "comment" : "A label displayed under the list of capabilities of a model.", + "isCommentAutoGenerated" : true + }, + "Changing these values affects how the AI generates responses. The defaults work well for most use cases." : { + + }, + "Check your balance at:" : { + "comment" : "A label displayed under a link to check the user's balance.", + "isCommentAutoGenerated" : true + }, + "Check your usage at:" : { + "comment" : "A label displayed under a link to OpenAI's usage page.", + "isCommentAutoGenerated" : true + }, + "Clear All" : { + "comment" : "A button to clear all email activity logs.", + "isCommentAutoGenerated" : true + }, + "Click 'Add Folder' below or drag folders here from Finder" : { + + }, + "Command" : { + "comment" : "A label displayed above the command field.", + "isCommentAutoGenerated" : true + }, + "COMMAND" : { + "comment" : "A label displayed above the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "Command History" : { + "comment" : "A label displayed above the command history.", + "isCommentAutoGenerated" : true + }, + "Content (Markdown)" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Controls which messages are written to ~/Library/Logs/oAI.log" : { + + }, + "Conversations" : { + + }, + "Copied!" : { + "comment" : "A message displayed when a code block is successfully copied to the clipboard.", + "isCommentAutoGenerated" : true + }, + "Cost Examples" : { + "comment" : "A heading for the cost examples of a model.", + "isCommentAutoGenerated" : true + }, + "Create a dedicated email account specifically for AI handling. Do NOT use your personal email address." : { + + }, + "Create a shortcut to save a reusable prompt template accessible from the / command dropdown." : { + "comment" : "A description of the functionality of the shortcut editor.", + "isCommentAutoGenerated" : true + }, + "Default Prompt" : { + + }, + "Delete (%@)" : { + + }, + "Description" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Each command will require your approval before running." : { + + }, + "Edit" : { + "comment" : "A button to edit a skill.", + "isCommentAutoGenerated" : true + }, + "Email Activity Log" : { + "comment" : "The title of the email activity log view.", + "isCommentAutoGenerated" : true + }, + "Email Handler System Prompt" : { + + }, + "Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults." : { + + }, + "Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails." : { + + }, + "Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens." : { + + }, + "Enable email handler in Settings to start monitoring emails" : { + "comment" : "A message displayed when the email handler is disabled.", + "isCommentAutoGenerated" : true + }, + "Error: %@" : { + "comment" : "A label displaying an error message in the checkout view.", + "isCommentAutoGenerated" : true + }, + "Example structure:" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Example: oai-bot-x7k2m9p3@gmail.com" : { + + }, + "File Access Permissions" : { + + }, + "Files" : { + "comment" : "A label displayed above the list of files attached to a skill.", + "isCommentAutoGenerated" : true + }, + "Google (Gemini embedding)" : { + + }, + "Help" : { + "comment" : "A heading for the help screen", + "isCommentAutoGenerated" : true + }, + "How to get your API token:" : { + "comment" : "A heading for a section that describes how to get your API token.", + "isCommentAutoGenerated" : true + }, + "ℹ️ SSH Key Authentication" : { + + }, + "Initialize Repository" : { + + }, + "Inject into system prompt for every conversation" : { + + }, + "Large files inflate the system prompt and may hit token limits." : { + "comment" : "A warning displayed when a user adds a large file to a skill.", + "isCommentAutoGenerated" : true + }, + "Last sync: %@" : { + + }, + "Leave empty to use the default email handler system prompt." : { + + }, + "Lowercase letters, numbers, and hyphens only. No spaces." : { + "comment" : "A description of the format of a shortcut's command.", + "isCommentAutoGenerated" : true + }, + "Markdown instruction files injected into the system prompt. Compatible with SKILL.md format." : { + "comment" : "A description of the format of agent skills.", + "isCommentAutoGenerated" : true + }, + "Max Tokens: set to 0 to use model default. Higher values allow longer responses." : { + + }, + "MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks." : { + + }, + "messages" : { + + }, + "Model Context Protocol" : { + + }, + "Model Info" : { + "comment" : "A title for a view that displays information about a model.", + "isCommentAutoGenerated" : true + }, + "Multi-provider AI chat client" : { + "comment" : "A description of oAI.", + "isCommentAutoGenerated" : true + }, + "Name" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Never" : { + "comment" : "A label displayed in the settings view that indicates when the last backup was taken.", + "isCommentAutoGenerated" : true + }, + "No credit data available" : { + "comment" : "A message displayed when there is no credit data available.", + "isCommentAutoGenerated" : true + }, + "No email activity yet" : { + "comment" : "A message displayed when the user has not yet received any email activity.", + "isCommentAutoGenerated" : true + }, + "No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill." : { + "comment" : "A message displayed when a user has not attached any files to a skill.", + "isCommentAutoGenerated" : true + }, + "No model selected" : { + "comment" : "A label displayed when no model is selected.", + "isCommentAutoGenerated" : true + }, + "No models available" : { + + }, + "No results for \"%@\"" : { + "comment" : "A message displayed when there are no search results. The argument is the search term.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet" : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet — click New Shortcut to create one." : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No skills yet" : { + "comment" : "A message displayed when the user has no skills.", + "isCommentAutoGenerated" : true + }, + "No skills yet — click New Skill or Import to get started." : { + "comment" : "A message displayed when a user has not created any skills.", + "isCommentAutoGenerated" : true + }, + "oAI" : { + "comment" : "The name of the app.", + "isCommentAutoGenerated" : true + }, + "Ollama (Local)" : { + "comment" : "A label displayed above the credits information for the local Ollie.", + "isCommentAutoGenerated" : true + }, + "Only emails with this text in the subject line will be processed. Example: \"[OAIBOT] What's the weather?\"" : { + + }, + "OpenAI (text-embedding-3-large)" : { + + }, + "OpenAI (text-embedding-3-small)" : { + + }, + "OpenAI Balance" : { + "comment" : "A label displayed above the OpenAI balance section.", + "isCommentAutoGenerated" : true + }, + "OpenRouter (OpenAI large)" : { + + }, + "OpenRouter (OpenAI small)" : { + + }, + "OpenRouter (Qwen 8B)" : { + + }, + "OpenRouter Credits" : { + "comment" : "A heading for the user's OpenRouter credits.", + "isCommentAutoGenerated" : true + }, + "Read access (always enabled)" : { + + }, + "Remote: %@" : { + + }, + "Restore from File…" : { + "comment" : "A button that allows the user to restore their data from a file.", + "isCommentAutoGenerated" : true + }, + "Running locally — no credits needed!" : { + "comment" : "A message displayed when using an on-device LLM like the one provided by the `.ollama` provider.", + "isCommentAutoGenerated" : true + }, + "Security Recommendation" : { + + }, + "Shell commands have full access to your system. Only approve commands you understand and trust." : { + "comment" : "A warning banner displayed in the Bash Approval Sheet.", + "isCommentAutoGenerated" : true + }, + "Show text labels below toolbar icons (helpful for new users)" : { + "comment" : "A description of the feature that shows text labels below toolbar icons.", + "isCommentAutoGenerated" : true + }, + "SKILL.md format — write instructions in plain Markdown." : { + + }, + "Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt." : { + "comment" : "A description of how skills are used by the AI.", + "isCommentAutoGenerated" : true + }, + "SSH Key" : { + + }, + "Stream responses as they're generated. Disable for single, complete responses." : { + + }, + "Sync conversations and settings across multiple machines using Git." : { + + }, + "Sync Now" : { + + }, + "Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative" : { + + }, + "Template" : { + "comment" : "A label displayed above the text editor for the shortcut's template.", + "isCommentAutoGenerated" : true + }, + "Test Connection" : { + + }, + "Text files are injected into the system prompt alongside the skill." : { + "comment" : "A description of how text files are injected into the system prompt.", + "isCommentAutoGenerated" : true + }, + "The AI can read and search files in allowed folders" : { + + }, + "The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise." : { + + }, + "The AI wants to run the following command" : { + "comment" : "A description of the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "This default prompt is always included to ensure accurate, helpful responses." : { + + }, + "This will permanently delete all email activity logs. This action cannot be undone." : { + "comment" : "A message displayed in an alert when the user is about to clear all email logs.", + "isCommentAutoGenerated" : true + }, + "Token Distribution" : { + "comment" : "A label displayed under the token distribution bar.", + "isCommentAutoGenerated" : true + }, + "tokens" : { + + }, + "Try adjusting your search or filters" : { + "comment" : "A description of the error that occurs when no models match the user's search.", + "isCommentAutoGenerated" : true + }, + "Type / in the input to see command suggestions" : { + "comment" : "A description of how to use the keyboard shortcut.", + "isCommentAutoGenerated" : true + }, + "Type a message or / for commands..." : { + "comment" : "A placeholder text displayed in the input area when", + "isCommentAutoGenerated" : true + }, + "Uncommitted changes: %@" : { + + }, + "Update Available%@" : { + "comment" : "A button that opens a website with information about a new version of oAI. The argument is the version number of the new version.", + "isCommentAutoGenerated" : true + }, + "Use @filename to attach files to your message" : { + "comment" : "A description of how to attach files to a message.", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** in the template to insert whatever you type after the command." : { + "comment" : "A tooltip for the \"lightbulb\" icon in the shortcuts view", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** to insert whatever you type after the command." : { + "comment" : "A description of how to use the shortcut template.", + "isCommentAutoGenerated" : true + }, + "Username + Password" : { + + }, + "v%@" : { + "comment" : "A label showing the current version of oAI.", + "isCommentAutoGenerated" : true + }, + "Version %@ (%@)" : { + "comment" : "A version number in the format \"Version 1.0 (1)\"", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Version %1$@ (%2$@)" + } + } + } + }, + "View Email Log" : { + + }, + "When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore." : { + + }, + "Working directory:" : { + "comment" : "A label displayed under the working directory of a pending shell command.", + "isCommentAutoGenerated" : true + }, + "You can import any SKILL.md file from skill0.io or write your own." : { + "comment" : "A description of how to use skills.", + "isCommentAutoGenerated" : true + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/nb.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings b/Locales/oAI Localizations/nb.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings new file mode 100644 index 0000000..5448135 --- /dev/null +++ b/Locales/oAI Localizations/nb.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings @@ -0,0 +1,18 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleName" : { + "comment" : "Bundle name", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "oAI" + } + } + } + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/nb.xcloc/contents.json b/Locales/oAI Localizations/nb.xcloc/contents.json new file mode 100644 index 0000000..8b299c0 --- /dev/null +++ b/Locales/oAI Localizations/nb.xcloc/contents.json @@ -0,0 +1,12 @@ +{ + "developmentRegion" : "en", + "project" : "oAI.xcodeproj", + "targetLocale" : "nb", + "toolInfo" : { + "toolBuildNumber" : "17C52", + "toolID" : "com.apple.dt.xcode", + "toolName" : "Xcode", + "toolVersion" : "26.2" + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/sv.xcloc/Localized Contents/sv.xliff b/Locales/oAI Localizations/sv.xcloc/Localized Contents/sv.xliff new file mode 100644 index 0000000..c928ffb --- /dev/null +++ b/Locales/oAI Localizations/sv.xcloc/Localized Contents/sv.xliff @@ -0,0 +1,843 @@ + + + +
+ +
+ + + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + # When reviewing code, always: +- Check for security vulnerabilities +- Verify error handling +- Suggest tests for edge cases + + + + %@ + %@ + + + + %@ active — injected into every conversation + %@ aktiv — injiceras i alla konversationer + A footnote indicating that some skills are active. + + + %@ chars + %@ tecken + A label showing the number of characters in the skill's content. The argument is the number of characters in the content. + + + %@ min + %@ min + + + + %@ pt + %@ pt + A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points. + + + %@s + %@s + + + + (always used) + (används alltid) + + + + (optional) + (valfri) + + + + 1. Open Paperless-NGX → Settings → API Tokens + 1. Öppna Paperless-NGX → Inställningar → API-tokens + A step in the process of getting a Paperless-NGX API token. + + + 2. Create or copy your token + 2. Skapa eller kopiera din token + A step in the process of getting a Paperless-NGX API token. + + + 3. Paste it above + 3. Klistra in det ovan + A step in the process of getting a Paperless-NGX API token. + + + A shortcut with command %@ already exists. + En genväg med kommandot %@ finns redan. + A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut. + + + API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine. + API-nycklar och inloggningsuppgifter ingår **inte** i säkerhetskopian. Du måste ange dem igen efter återställning på en ny dator. + A warning message displayed in the settings tab of the app. + + + Access Token + Access Token + + + + Active + Aktiv + A toggle + + + Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows. + Aktiva färdigheter läggs till i systemprompten. Aktivera/inaktivera dem per färdighet för att kontrollera vad AI:n känner till. + A description of how to toggle the active status of a skill. + + + Agent Skills + Agent-färdigheter + + + + Allow Shell Command? + Tillåt skalkommando? + A title for a modal that asks the user if they want to allow a shell command. + + + Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs. + Låt AI:n köra skalkommandon på din dator. Kommandon körs via /bin/zsh. Aktivera godkännandeläge för att granska varje kommando innan det körs. + + + + Anthropic Balance + Anthropic Balance + A label displayed above the user's balance on the Anthropic platform. + + + Auto-execute mode: commands run without approval. Use with caution. + Automatisk körning: kommandon körs utan godkännande. Använd med försiktighet. + + + + Auto-sync can cause conflicts if running on multiple machines simultaneously. + Automatisk synkronisering kan orsaka konflikter om den körs på flera datorer samtidigt. + + + + Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations. + Välj automatiskt relevanta meddelanden istället för att skicka all historik. Minskar tokenanvändningen för långa konversationer. + + + + Automatically summarize old portions of long conversations to save tokens and improve context efficiency. + Sammanfatta automatiskt äldre delar av långa konversationer för att spara tokens och förbättra kontexteffektiviteten. + + + + Back Up Now + Säkerhetskopiera nu + A button that backs up the user's data. + + + Backup location: + Säkerhetskopieringsplats: + A label displayed under the backup location. + + + Bash Execution + Bash Execution + + + + Branch: %@ + Branch: %@ + + + + Built with SwiftUI + Built with SwiftUI + A label that says that the app is built with SwiftUI. + + + COMMAND + KOMMANDO + A label displayed above the command that the AI wants to run. + + + Capabilities + Funktioner + A label displayed under the list of capabilities of a model. + + + Changing these values affects how the AI generates responses. The defaults work well for most use cases. + Att ändra dessa värden påverkar hur AI:n genererar svar. Standardvärdena fungerar bra för de flesta användningsfall. + + + + Check your balance at: + Kontrollera ditt saldo på: + A label displayed under a link to check the user's balance. + + + Check your usage at: + Kontrollera din användning på: + A label displayed under a link to OpenAI's usage page. + + + Clear All + Rensa alla + A button to clear all email activity logs. + + + Click 'Add Folder' below or drag folders here from Finder + Klicka på "Lägg till mapp" nedan eller dra mappar hit från Finder + + + + Command + Kommando + A label displayed above the command field. + + + Command History + Kommandohistorik + A label displayed above the command history. + + + Content (Markdown) + Innehåll (Markdown) + A + + + Controls which messages are written to ~/Library/Logs/oAI.log + Styr vilka meddelanden som skrivs till ~/Library/Logs/oAI.log + + + + Conversations + Konversationer + + + + Copied! + Kopierat! + A message displayed when a code block is successfully copied to the clipboard. + + + Cost Examples + Kostnadsexempel + A heading for the cost examples of a model. + + + Create a dedicated email account specifically for AI handling. Do NOT use your personal email address. + Skapa ett dedikerat e-postkonto specifikt för AI-hantering. Använd INTE din personliga e-postadress. + + + + Create a shortcut to save a reusable prompt template accessible from the / command dropdown. + Skapa en genväg för att spara en återanvändbar promptmall tillgänglig från /-kommandomenyn. + A description of the functionality of the shortcut editor. + + + Default Prompt + Standardprompt + + + + Delete (%@) + Radera (%@) + + + + Description + Beskrivning + A label + + + Each command will require your approval before running. + Varje kommando kräver ditt godkännande innan det körs. + + + + Edit + Redigera + A button to edit a skill. + + + Email Activity Log + E-postaktivitetslogg + The title of the email activity log view. + + + Email Handler System Prompt + Systemprompt för e-posthanterare + + + + Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults. + E-posthanteraren använder BARA sin egen systemprompt, helt isolerad från dina viktigaste chattinställningar. En anpassad prompt nedan åsidosätter standardinställningarna. + + + + Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails. + E-postuppgifter har LÄSBEHÖRIGHET till MCP-mappar. AI:n kan inte skriva, radera eller ändra filer vid e-postbehandling. + + + + Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens. + Aktivera AI-driven semantisk sökning med %@-embeddings. Kostnad: ~$0,02–0,15/1M tokens. + + + + Enable email handler in Settings to start monitoring emails + Aktivera e-posthanteraren i Inställningar för att börja övervaka e-post + A message displayed when the email handler is disabled. + + + Error: %@ + Fel: %@ + A label displaying an error message in the checkout view. + + + Example structure: + Exempelstruktur: + A + + + Example: oai-bot-x7k2m9p3@gmail.com + Example: oai-bot-x7k2m9p3@gmail.com + + + + File Access Permissions + Filbehörigheter + + + + Files + Filer + A label displayed above the list of files attached to a skill. + + + Google (Gemini embedding) + Google (Gemini embedding) + + + + Help + Hjälp + A heading for the help screen + + + How to get your API token: + Så här hämtar du din API-token: + A heading for a section that describes how to get your API token. + + + Initialize Repository + Initiera förvar + + + + Inject into system prompt for every conversation + Injicera i systemprompten för varje konversation + + + + Large files inflate the system prompt and may hit token limits. + Stora filer blåser upp systemprompten och kan nå token-gränser. + A warning displayed when a user adds a large file to a skill. + + + Last sync: %@ + Senaste synkronisering: %@ + + + + Leave empty to use the default email handler system prompt. + Lämna tomt för att använda e-posthanterarens standardsystemprompt. + + + + Lowercase letters, numbers, and hyphens only. No spaces. + Endast gemener, siffror och bindestreck. Inga blanksteg. + A description of the format of a shortcut's command. + + + MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks. + MCP ger AI:n kontrollerad åtkomst att läsa och eventuellt skriva filer på din dator. AI:n kan söka, läsa och analysera filer i tillåtna mappar för att hjälpa till med kodning, analys och andra uppgifter. + + + + Markdown instruction files injected into the system prompt. Compatible with SKILL.md format. + Markdown-instruktionsfiler som injiceras i systemprompten. Kompatibel med SKILL.md-format. + A description of the format of agent skills. + + + Max Tokens: set to 0 to use model default. Higher values allow longer responses. + Max tokens: sätt till 0 för att använda modellens standard. Högre värden tillåter längre svar. + + + + Model Context Protocol + Model Context Protocol + + + + Model Info + Modellinformation + A title for a view that displays information about a model. + + + Multi-provider AI chat client + AI-chattklient med stöd för flera leverantörer + A description of oAI. + + + Name + Namn + A label + + + Never + Aldrig + A label displayed in the settings view that indicates when the last backup was taken. + + + No credit data available + Ingen kreditdata tillgänglig + A message displayed when there is no credit data available. + + + No email activity yet + Ingen e-postaktivitet ännu + A message displayed when the user has not yet received any email activity. + + + No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill. + Inga filer bifogade. Lägg till JSON, YAML, CSV eller TXT-filer för att injicera data i systemprompten bredvid denna färdighet. + A message displayed when a user has not attached any files to a skill. + + + No model selected + Ingen modell vald + A label displayed when no model is selected. + + + No models available + Inga modeller tillgängliga + + + + No results for "%@" + Inga resultat för "%@" + A message displayed when there are no search results. The argument is the search term. + + + No shortcuts yet + Inga genvägar ännu + A message displayed when a user has no shortcuts. + + + No shortcuts yet — click New Shortcut to create one. + Inga genvägar ännu — klicka på Ny genväg för att skapa en. + A message displayed when a user has no shortcuts. + + + No skills yet + Inga färdigheter ännu + A message displayed when the user has no skills. + + + No skills yet — click New Skill or Import to get started. + Inga färdigheter ännu — klicka på Ny färdighet eller Importera för att komma igång. + A message displayed when a user has not created any skills. + + + Ollama (Local) + Ollama (Local) + A label displayed above the credits information for the local Ollie. + + + Only emails with this text in the subject line will be processed. Example: "[OAIBOT] What's the weather?" + Bara e-post med denna text i ämnesraden bearbetas. Exempel: "[OAIBOT] Vad är vädret?" + + + + OpenAI (text-embedding-3-large) + OpenAI (text-embedding-3-large) + + + + OpenAI (text-embedding-3-small) + OpenAI (text-embedding-3-small) + + + + OpenAI Balance + OpenAI Balance + A label displayed above the OpenAI balance section. + + + OpenRouter (OpenAI large) + OpenRouter (OpenAI large) + + + + OpenRouter (OpenAI small) + OpenRouter (OpenAI small) + + + + OpenRouter (Qwen 8B) + OpenRouter (Qwen 8B) + + + + OpenRouter Credits + OpenRouter Credits + A heading for the user's OpenRouter credits. + + + Read access (always enabled) + Läsbehörighet (alltid aktiverad) + + + + Remote: %@ + Remote: %@ + + + + Restore from File… + Återställ från fil… + A button that allows the user to restore their data from a file. + + + Running locally — no credits needed! + Körs lokalt — inga krediter behövs! + A message displayed when using an on-device LLM like the one provided by the `.ollama` provider. + + + SKILL.md format — write instructions in plain Markdown. + SKILL.md-format — skriv instruktioner i vanlig Markdown. + + + + SSH Key + SSH Key + + + + Security Recommendation + Säkerhetsrekommendation + + + + Shell commands have full access to your system. Only approve commands you understand and trust. + Skalkommandon har full åtkomst till ditt system. Godkänn bara kommandon du förstår och litar på. + A warning banner displayed in the Bash Approval Sheet. + + + Show text labels below toolbar icons (helpful for new users) + Visa textetiketter under verktygsfältets ikoner (hjälpsamt för nya användare) + A description of the feature that shows text labels below toolbar icons. + + + Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt. + Färdigheter är Markdown-instruktionsfiler som lär AI:n hur den ska bete sig. Aktiva färdigheter injiceras automatiskt i systemprompten. + A description of how skills are used by the AI. + + + Stream responses as they're generated. Disable for single, complete responses. + Strömma svar medan de genereras. Inaktivera för fullständiga, enkla svar. + + + + Sync Now + Synkronisera nu + + + + Sync conversations and settings across multiple machines using Git. + Synkronisera konversationer och inställningar på flera datorer med Git. + + + + Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative + Temperatur: 0 = modellens standard · 0,0–0,7 = fokuserat · 0,8–2,0 = kreativt + + + + Template + Mall + A label displayed above the text editor for the shortcut's template. + + + Test Connection + Testa anslutning + + + + Text files are injected into the system prompt alongside the skill. + Textfiler injiceras i systemprompten bredvid färdigheten. + A description of how text files are injected into the system prompt. + + + The AI can read and search files in allowed folders + AI:n kan läsa och söka i filer i tillåtna mappar + + + + The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise. + AI:n läser detta innehåll och bestämmer när det ska tillämpas. Beskriv **vad** AI:n ska göra och **hur** — var specifik och koncis. + + + + The AI wants to run the following command + AI:n vill köra följande kommando + A description of the command that the AI wants to run. + + + This default prompt is always included to ensure accurate, helpful responses. + Denna standardprompt ingår alltid för att säkerställa korrekta, användbara svar. + + + + This will permanently delete all email activity logs. This action cannot be undone. + Detta raderar alla e-postaktivitetsloggar permanent. Åtgärden kan inte ångras. + A message displayed in an alert when the user is about to clear all email logs. + + + Token Distribution + Token-fördelning + A label displayed under the token distribution bar. + + + Try adjusting your search or filters + Försök att justera din sökning eller dina filter + A description of the error that occurs when no models match the user's search. + + + Type / in the input to see command suggestions + Skriv / i inmatningsfältet för att se kommandoförslag + A description of how to use the keyboard shortcut. + + + Type a message or / for commands... + Skriv ett meddelande eller / för kommandon... + A placeholder text displayed in the input area when + + + Uncommitted changes: %@ + Ej sparade ändringar: %@ + + + + Update Available%@ + Uppdatering tillgänglig%@ + A button that opens a website with information about a new version of oAI. The argument is the version number of the new version. + + + Use **{{input}}** in the template to insert whatever you type after the command. + Använd **{{input}}** i mallen för att infoga det du skriver efter kommandot. + A tooltip for the "lightbulb" icon in the shortcuts view + + + Use **{{input}}** to insert whatever you type after the command. + Använd **{{input}}** för att infoga det du skriver efter kommandot. + A description of how to use the shortcut template. + + + Use @filename to attach files to your message + Använd @filnamn för att bifoga filer till ditt meddelande + A description of how to attach files to a message. + + + Username + Password + Username + Password + + + + Version %1$@ (%2$@) + Version %1$@ (%2$@) + A version number in the format "Version 1.0 (1)" + + + View Email Log + Visa e-postlogg + + + + When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore. + När aktiverat hoppar listning och sökning över gitignorerade filer. Skrivoperationer ignorerar alltid .gitignore. + + + + Working directory: + Arbetskatalog: + A label displayed under the working directory of a pending shell command. + + + You can import any SKILL.md file from skill0.io or write your own. + Du kan importera vilken SKILL.md-fil som helst från skill0.io eller skriva din egen. + A description of how to use skills. + + + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html) + A link to the GNU Affero General Public License. + + + ^[%@ entry](inflect: true) + ^[%@ post](inflect: true) + A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier. + + + active + aktiv + A label for an active skill. + + + messages + meddelanden + + + + oAI + oAI + The name of the app. + + + tokens + tokens + + + + v%@ + v%@ + A label showing the current version of oAI. + + + ~750 tokens ≈ 500 words. Online mode allows web search in responses. + ~750 tokens ≈ 500 ord. Onlineläge tillåter webbsökning i svar. + + + + © 2026 [Rune Olsen](https://blog.rune.pm) + © 2026 [Rune Olsen](https://blog.rune.pm) + A copyright notice with the copyright holder's name. + + + • + + A bullet point. + + + • Add public key to your git provider + • Lägg till den offentliga nyckeln hos din git-leverantör + + + + • No credentials needed in oAI + • Inga inloggningsuppgifter behövs i oAI + + + + • Uses your system SSH keys (~/.ssh/id_ed25519) + • Använder dina system-SSH-nycklar (~/.ssh/id_ed25519) + + + + ℹ️ SSH Key Authentication + ℹ️ SSH-nyckelautentisering + + + + ↑↓ navigate ↩ open + ↑↓ navigate ↩ open + + + + ⌘N New • ⌘M Model • ⌘S Save + ⌘N New • ⌘M Model • ⌘S Save + A hint that appears on macOS when using keyboard shortcuts. + + + ⚠️ Custom prompt active — only this prompt will be sent to the model. + ⚠️ Anpassad prompt aktiv — bara denna prompt skickas till modellen. + + + + ⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead. + ⚠️ Många leverantörer (GitHub) stöder inte längre lösenordsautentisering. Använd Access Token istället. + + + + ⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab. + ⚠️ Inga embedding-leverantörer tillgängliga. Konfigurera en API-nyckel för OpenAI, OpenRouter eller Google på fliken Allmänt. + + + + ⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages. + ⚠️ Engångsoperation — genererar embeddings för alla meddelanden. Uppskattad kostnad: ~$0,04 för 10 000 meddelanden. + + + + ⚠️ These are advanced settings + ⚠️ Det här är avancerade inställningar + + + + ⚠️ interrupted + ⚠️ avbruten + A warning label that appears next to a message that was interrupted. + + + 🌐 + 🌐 + A label for a model that can be used to generate images. + + + 🎨 + 🎨 + A button that generates an image. + + + 👁️ + 👁️ + A checkmark emoji. + + + 💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords. + 💡 För Gmail, använd ett applösenord. Google-konto > Säkerhet > 2-stegsverifiering > Applösenord. + + + + 💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods. + 💡 Använd HTTPS URL (t.ex. https://gitlab.pm/user/repo.git) — fungerar med alla autentiseringsmetoder. + + + + 🔧 + 🔧 + A checkmark emoji. + + +
+ +
+ +
+ + + oAI + oAI + Bundle name + + +
+
diff --git a/Locales/oAI Localizations/sv.xcloc/Source Contents/oAI/Localizable.xcstrings b/Locales/oAI Localizations/sv.xcloc/Source Contents/oAI/Localizable.xcstrings new file mode 100644 index 0000000..bfdb18c --- /dev/null +++ b/Locales/oAI Localizations/sv.xcloc/Source Contents/oAI/Localizable.xcstrings @@ -0,0 +1,590 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "(always used)" : { + + }, + "(optional)" : { + + }, + "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" : { + "comment" : "A link to the GNU Affero General Public License.", + "isCommentAutoGenerated" : true + }, + "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" : { + + }, + "%@" : { + + }, + "%@ active — injected into every conversation" : { + "comment" : "A footnote indicating that some skills are active.", + "isCommentAutoGenerated" : true + }, + "%@ chars" : { + "comment" : "A label showing the number of characters in the skill's content. The argument is the number of characters in the content.", + "isCommentAutoGenerated" : true + }, + "%@ min" : { + + }, + "%@ pt" : { + "comment" : "A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points.", + "isCommentAutoGenerated" : true + }, + "%@s" : { + + }, + "•" : { + "comment" : "A bullet point.", + "isCommentAutoGenerated" : true + }, + "• Add public key to your git provider" : { + + }, + "• No credentials needed in oAI" : { + + }, + "• Uses your system SSH keys (~/.ssh/id_ed25519)" : { + + }, + "^[%@ entry](inflect: true)" : { + "comment" : "A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier.", + "isCommentAutoGenerated" : true + }, + "© 2026 [Rune Olsen](https://blog.rune.pm)" : { + "comment" : "A copyright notice with the copyright holder's name.", + "isCommentAutoGenerated" : true + }, + "↑↓ navigate ↩ open" : { + + }, + "~750 tokens ≈ 500 words. Online mode allows web search in responses." : { + + }, + "⌘N New • ⌘M Model • ⌘S Save" : { + "comment" : "A hint that appears on macOS when using keyboard shortcuts.", + "isCommentAutoGenerated" : true + }, + "⚠️ Custom prompt active — only this prompt will be sent to the model." : { + + }, + "⚠️ interrupted" : { + "comment" : "A warning label that appears next to a message that was interrupted.", + "isCommentAutoGenerated" : true + }, + "⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead." : { + + }, + "⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab." : { + + }, + "⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages." : { + + }, + "⚠️ These are advanced settings" : { + + }, + "🌐" : { + "comment" : "A label for a model that can be used to generate images.", + "isCommentAutoGenerated" : true + }, + "🎨" : { + "comment" : "A button that generates an image.", + "isCommentAutoGenerated" : true + }, + "👁️" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords." : { + + }, + "💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods." : { + + }, + "🔧" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true + }, + "1. Open Paperless-NGX → Settings → API Tokens" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "2. Create or copy your token" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "3. Paste it above" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true + }, + "A shortcut with command %@ already exists." : { + "comment" : "A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut.", + "isCommentAutoGenerated" : true + }, + "Access Token" : { + + }, + "active" : { + "comment" : "A label for an active skill.", + "isCommentAutoGenerated" : true + }, + "Active" : { + "comment" : "A toggle", + "isCommentAutoGenerated" : true + }, + "Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows." : { + "comment" : "A description of how to toggle the active status of a skill.", + "isCommentAutoGenerated" : true + }, + "Agent Skills" : { + + }, + "Allow Shell Command?" : { + "comment" : "A title for a modal that asks the user if they want to allow a shell command.", + "isCommentAutoGenerated" : true + }, + "Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs." : { + + }, + "Anthropic Balance" : { + "comment" : "A label displayed above the user's balance on the Anthropic platform.", + "isCommentAutoGenerated" : true + }, + "API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine." : { + "comment" : "A warning message displayed in the settings tab of the app.", + "isCommentAutoGenerated" : true + }, + "Auto-execute mode: commands run without approval. Use with caution." : { + + }, + "Auto-sync can cause conflicts if running on multiple machines simultaneously." : { + + }, + "Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations." : { + + }, + "Automatically summarize old portions of long conversations to save tokens and improve context efficiency." : { + + }, + "Back Up Now" : { + "comment" : "A button that backs up the user's data.", + "isCommentAutoGenerated" : true + }, + "Backup location:" : { + "comment" : "A label displayed under the backup location.", + "isCommentAutoGenerated" : true + }, + "Bash Execution" : { + + }, + "Branch: %@" : { + + }, + "Built with SwiftUI" : { + "comment" : "A label that says that the app is built with SwiftUI.", + "isCommentAutoGenerated" : true + }, + "Capabilities" : { + "comment" : "A label displayed under the list of capabilities of a model.", + "isCommentAutoGenerated" : true + }, + "Changing these values affects how the AI generates responses. The defaults work well for most use cases." : { + + }, + "Check your balance at:" : { + "comment" : "A label displayed under a link to check the user's balance.", + "isCommentAutoGenerated" : true + }, + "Check your usage at:" : { + "comment" : "A label displayed under a link to OpenAI's usage page.", + "isCommentAutoGenerated" : true + }, + "Clear All" : { + "comment" : "A button to clear all email activity logs.", + "isCommentAutoGenerated" : true + }, + "Click 'Add Folder' below or drag folders here from Finder" : { + + }, + "Command" : { + "comment" : "A label displayed above the command field.", + "isCommentAutoGenerated" : true + }, + "COMMAND" : { + "comment" : "A label displayed above the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "Command History" : { + "comment" : "A label displayed above the command history.", + "isCommentAutoGenerated" : true + }, + "Content (Markdown)" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Controls which messages are written to ~/Library/Logs/oAI.log" : { + + }, + "Conversations" : { + + }, + "Copied!" : { + "comment" : "A message displayed when a code block is successfully copied to the clipboard.", + "isCommentAutoGenerated" : true + }, + "Cost Examples" : { + "comment" : "A heading for the cost examples of a model.", + "isCommentAutoGenerated" : true + }, + "Create a dedicated email account specifically for AI handling. Do NOT use your personal email address." : { + + }, + "Create a shortcut to save a reusable prompt template accessible from the / command dropdown." : { + "comment" : "A description of the functionality of the shortcut editor.", + "isCommentAutoGenerated" : true + }, + "Default Prompt" : { + + }, + "Delete (%@)" : { + + }, + "Description" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Each command will require your approval before running." : { + + }, + "Edit" : { + "comment" : "A button to edit a skill.", + "isCommentAutoGenerated" : true + }, + "Email Activity Log" : { + "comment" : "The title of the email activity log view.", + "isCommentAutoGenerated" : true + }, + "Email Handler System Prompt" : { + + }, + "Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults." : { + + }, + "Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails." : { + + }, + "Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens." : { + + }, + "Enable email handler in Settings to start monitoring emails" : { + "comment" : "A message displayed when the email handler is disabled.", + "isCommentAutoGenerated" : true + }, + "Error: %@" : { + "comment" : "A label displaying an error message in the checkout view.", + "isCommentAutoGenerated" : true + }, + "Example structure:" : { + "comment" : "A", + "isCommentAutoGenerated" : true + }, + "Example: oai-bot-x7k2m9p3@gmail.com" : { + + }, + "File Access Permissions" : { + + }, + "Files" : { + "comment" : "A label displayed above the list of files attached to a skill.", + "isCommentAutoGenerated" : true + }, + "Google (Gemini embedding)" : { + + }, + "Help" : { + "comment" : "A heading for the help screen", + "isCommentAutoGenerated" : true + }, + "How to get your API token:" : { + "comment" : "A heading for a section that describes how to get your API token.", + "isCommentAutoGenerated" : true + }, + "ℹ️ SSH Key Authentication" : { + + }, + "Initialize Repository" : { + + }, + "Inject into system prompt for every conversation" : { + + }, + "Large files inflate the system prompt and may hit token limits." : { + "comment" : "A warning displayed when a user adds a large file to a skill.", + "isCommentAutoGenerated" : true + }, + "Last sync: %@" : { + + }, + "Leave empty to use the default email handler system prompt." : { + + }, + "Lowercase letters, numbers, and hyphens only. No spaces." : { + "comment" : "A description of the format of a shortcut's command.", + "isCommentAutoGenerated" : true + }, + "Markdown instruction files injected into the system prompt. Compatible with SKILL.md format." : { + "comment" : "A description of the format of agent skills.", + "isCommentAutoGenerated" : true + }, + "Max Tokens: set to 0 to use model default. Higher values allow longer responses." : { + + }, + "MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks." : { + + }, + "messages" : { + + }, + "Model Context Protocol" : { + + }, + "Model Info" : { + "comment" : "A title for a view that displays information about a model.", + "isCommentAutoGenerated" : true + }, + "Multi-provider AI chat client" : { + "comment" : "A description of oAI.", + "isCommentAutoGenerated" : true + }, + "Name" : { + "comment" : "A label", + "isCommentAutoGenerated" : true + }, + "Never" : { + "comment" : "A label displayed in the settings view that indicates when the last backup was taken.", + "isCommentAutoGenerated" : true + }, + "No credit data available" : { + "comment" : "A message displayed when there is no credit data available.", + "isCommentAutoGenerated" : true + }, + "No email activity yet" : { + "comment" : "A message displayed when the user has not yet received any email activity.", + "isCommentAutoGenerated" : true + }, + "No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill." : { + "comment" : "A message displayed when a user has not attached any files to a skill.", + "isCommentAutoGenerated" : true + }, + "No model selected" : { + "comment" : "A label displayed when no model is selected.", + "isCommentAutoGenerated" : true + }, + "No models available" : { + + }, + "No results for \"%@\"" : { + "comment" : "A message displayed when there are no search results. The argument is the search term.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet" : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No shortcuts yet — click New Shortcut to create one." : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true + }, + "No skills yet" : { + "comment" : "A message displayed when the user has no skills.", + "isCommentAutoGenerated" : true + }, + "No skills yet — click New Skill or Import to get started." : { + "comment" : "A message displayed when a user has not created any skills.", + "isCommentAutoGenerated" : true + }, + "oAI" : { + "comment" : "The name of the app.", + "isCommentAutoGenerated" : true + }, + "Ollama (Local)" : { + "comment" : "A label displayed above the credits information for the local Ollie.", + "isCommentAutoGenerated" : true + }, + "Only emails with this text in the subject line will be processed. Example: \"[OAIBOT] What's the weather?\"" : { + + }, + "OpenAI (text-embedding-3-large)" : { + + }, + "OpenAI (text-embedding-3-small)" : { + + }, + "OpenAI Balance" : { + "comment" : "A label displayed above the OpenAI balance section.", + "isCommentAutoGenerated" : true + }, + "OpenRouter (OpenAI large)" : { + + }, + "OpenRouter (OpenAI small)" : { + + }, + "OpenRouter (Qwen 8B)" : { + + }, + "OpenRouter Credits" : { + "comment" : "A heading for the user's OpenRouter credits.", + "isCommentAutoGenerated" : true + }, + "Read access (always enabled)" : { + + }, + "Remote: %@" : { + + }, + "Restore from File…" : { + "comment" : "A button that allows the user to restore their data from a file.", + "isCommentAutoGenerated" : true + }, + "Running locally — no credits needed!" : { + "comment" : "A message displayed when using an on-device LLM like the one provided by the `.ollama` provider.", + "isCommentAutoGenerated" : true + }, + "Security Recommendation" : { + + }, + "Shell commands have full access to your system. Only approve commands you understand and trust." : { + "comment" : "A warning banner displayed in the Bash Approval Sheet.", + "isCommentAutoGenerated" : true + }, + "Show text labels below toolbar icons (helpful for new users)" : { + "comment" : "A description of the feature that shows text labels below toolbar icons.", + "isCommentAutoGenerated" : true + }, + "SKILL.md format — write instructions in plain Markdown." : { + + }, + "Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt." : { + "comment" : "A description of how skills are used by the AI.", + "isCommentAutoGenerated" : true + }, + "SSH Key" : { + + }, + "Stream responses as they're generated. Disable for single, complete responses." : { + + }, + "Sync conversations and settings across multiple machines using Git." : { + + }, + "Sync Now" : { + + }, + "Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative" : { + + }, + "Template" : { + "comment" : "A label displayed above the text editor for the shortcut's template.", + "isCommentAutoGenerated" : true + }, + "Test Connection" : { + + }, + "Text files are injected into the system prompt alongside the skill." : { + "comment" : "A description of how text files are injected into the system prompt.", + "isCommentAutoGenerated" : true + }, + "The AI can read and search files in allowed folders" : { + + }, + "The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise." : { + + }, + "The AI wants to run the following command" : { + "comment" : "A description of the command that the AI wants to run.", + "isCommentAutoGenerated" : true + }, + "This default prompt is always included to ensure accurate, helpful responses." : { + + }, + "This will permanently delete all email activity logs. This action cannot be undone." : { + "comment" : "A message displayed in an alert when the user is about to clear all email logs.", + "isCommentAutoGenerated" : true + }, + "Token Distribution" : { + "comment" : "A label displayed under the token distribution bar.", + "isCommentAutoGenerated" : true + }, + "tokens" : { + + }, + "Try adjusting your search or filters" : { + "comment" : "A description of the error that occurs when no models match the user's search.", + "isCommentAutoGenerated" : true + }, + "Type / in the input to see command suggestions" : { + "comment" : "A description of how to use the keyboard shortcut.", + "isCommentAutoGenerated" : true + }, + "Type a message or / for commands..." : { + "comment" : "A placeholder text displayed in the input area when", + "isCommentAutoGenerated" : true + }, + "Uncommitted changes: %@" : { + + }, + "Update Available%@" : { + "comment" : "A button that opens a website with information about a new version of oAI. The argument is the version number of the new version.", + "isCommentAutoGenerated" : true + }, + "Use @filename to attach files to your message" : { + "comment" : "A description of how to attach files to a message.", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** in the template to insert whatever you type after the command." : { + "comment" : "A tooltip for the \"lightbulb\" icon in the shortcuts view", + "isCommentAutoGenerated" : true + }, + "Use **{{input}}** to insert whatever you type after the command." : { + "comment" : "A description of how to use the shortcut template.", + "isCommentAutoGenerated" : true + }, + "Username + Password" : { + + }, + "v%@" : { + "comment" : "A label showing the current version of oAI.", + "isCommentAutoGenerated" : true + }, + "Version %@ (%@)" : { + "comment" : "A version number in the format \"Version 1.0 (1)\"", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Version %1$@ (%2$@)" + } + } + } + }, + "View Email Log" : { + + }, + "When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore." : { + + }, + "Working directory:" : { + "comment" : "A label displayed under the working directory of a pending shell command.", + "isCommentAutoGenerated" : true + }, + "You can import any SKILL.md file from skill0.io or write your own." : { + "comment" : "A description of how to use skills.", + "isCommentAutoGenerated" : true + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/sv.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings b/Locales/oAI Localizations/sv.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings new file mode 100644 index 0000000..5448135 --- /dev/null +++ b/Locales/oAI Localizations/sv.xcloc/Source Contents/oAI/oAI-InfoPlist.xcstrings @@ -0,0 +1,18 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleName" : { + "comment" : "Bundle name", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "oAI" + } + } + } + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/Locales/oAI Localizations/sv.xcloc/contents.json b/Locales/oAI Localizations/sv.xcloc/contents.json new file mode 100644 index 0000000..ce9686a --- /dev/null +++ b/Locales/oAI Localizations/sv.xcloc/contents.json @@ -0,0 +1,12 @@ +{ + "developmentRegion" : "en", + "project" : "oAI.xcodeproj", + "targetLocale" : "sv", + "toolInfo" : { + "toolBuildNumber" : "17C52", + "toolID" : "com.apple.dt.xcode", + "toolName" : "Xcode", + "toolVersion" : "26.2" + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/README.md b/README.md index c02e106..3089427 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ A powerful native macOS AI chat application with support for multiple providers, - **Streaming Responses** - Real-time token streaming for faster interactions - **Conversation Management** - Save, load, export, and search conversations - **File Attachments** - Support for text files, images, and PDFs -- **Image Generation** - Create images with supported models (DALL-E, Flux, etc.) +- **Image Generation** - Create images with supported models (DALL-E, Flux, etc.) — renders inline in chat +- **Reasoning / Thinking Tokens** - Stream live reasoning from thinking-capable models (DeepSeek R1, Claude 3.7+, o1/o3, Qwen); configurable effort level (High/Medium/Low/Minimal); collapsible block auto-expands while thinking and collapses when the answer arrives - **Online Mode** - DuckDuckGo and Google web search integration - **Session Statistics** - Track token usage, costs, and response times - **Command History** - Navigate previous commands with searchable modal (⌘H) @@ -48,6 +49,16 @@ Seamless conversation backup and sync across devices: ![Model Selector](Screenshots/3.png) +### ⚡ Shortcuts & Agent Skills +- **Shortcuts** - Personal slash commands that expand to prompt templates; optional `{{input}}` placeholder for inline input +- **Agent Skills (SKILL.md)** - Markdown instruction files injected into the system prompt; compatible with skill0.io, skillsmp.com, and other SKILL.md marketplaces; import as `.md` or `.zip` bundle with attached data files + +### 🖥️ Power-User Features +- **Bash Execution** - AI can run shell commands via `/bin/zsh` (opt-in, with per-command approval prompt) +- **iCloud Backup** - One-click settings backup to iCloud Drive; restore on any Mac; API keys excluded for security +- **Paperless-NGX Integration** *(Beta)* - Search, read, and interact with documents in a self-hosted Paperless instance +- **Tool Call Inspection** - Click any 🔧 tool message to expand input/output JSON for all tool calls + ### 📧 Email Handler (AI Email Assistant) Automated email responses powered by AI: - **IMAP Polling** - Monitor inbox for emails with specific subject identifiers @@ -65,6 +76,8 @@ Automated email responses powered by AI: - Footer stats display (messages, tokens, cost, sync status) - Header status indicators (MCP, Online mode, Git sync) - Responsive message layout with copy buttons +- **Model Selector (⌘M)** - Filter by capability (Vision / Tools / Online / Image Gen / Thinking 🧠), sort by price or context window, search by name or description, per-row ⓘ info button +- **Localization** - UI fully translated into Norwegian Bokmål, Swedish, Danish, and German; follows macOS language preference automatically ![Advanced Features](Screenshots/4.png) @@ -127,6 +140,7 @@ Add your API keys in Settings (⌘,) → General tab: - **Online Mode** - Enable web search integration - **Max Tokens** - Set maximum response length - **Temperature** - Control response randomness (0.0 - 2.0) +- **Reasoning** - Enable thinking tokens for supported models; set effort level (High/Medium/Low/Minimal); optionally hide reasoning content from chat #### Advanced Tab - **Smart Context Selection** - Reduce token usage automatically @@ -254,99 +268,6 @@ AI-powered email auto-responder: - **Tracking**: Email log with success/error status - **Security**: AES-256-GCM encrypted credentials -## Development - -### Project Structure -``` -oAI/ -├── Models/ # Data models -│ ├── Message.swift # Chat message model -│ ├── Conversation.swift # Saved conversation model -│ ├── ModelInfo.swift # AI model metadata -│ └── Settings.swift # App settings enums -│ -├── Views/ # SwiftUI views -│ ├── Main/ # Primary UI components -│ │ ├── ChatView.swift # Main chat interface -│ │ ├── MessageRow.swift # Individual message display -│ │ ├── InputBar.swift # Message input with commands -│ │ ├── HeaderView.swift # Top bar (provider/model/status) -│ │ └── FooterView.swift # Bottom stats bar -│ │ -│ └── Screens/ # Modal/sheet views -│ ├── SettingsView.swift # Settings with tabs -│ ├── ModelSelectorView.swift -│ ├── ConversationListView.swift -│ └── HelpView.swift -│ -├── ViewModels/ # Observable view models -│ └── ChatViewModel.swift # Main chat logic & state -│ -├── Providers/ # AI provider implementations -│ ├── Provider.swift # Protocol definition -│ ├── OpenRouterProvider.swift -│ ├── AnthropicProvider.swift -│ ├── OpenAIProvider.swift -│ └── OllamaProvider.swift -│ -├── Services/ # Business logic & data -│ ├── DatabaseService.swift # SQLite operations (GRDB) -│ ├── SettingsService.swift # Settings persistence -│ ├── ProviderRegistry.swift # AI provider management -│ ├── MCPService.swift # File access (MCP) -│ ├── WebSearchService.swift # DuckDuckGo/Google search -│ ├── GitSyncService.swift # Git synchronization -│ ├── ContextSelectionService.swift # Smart context -│ ├── EmbeddingService.swift # Semantic search -│ ├── EmailService.swift # Email monitoring (IMAP) -│ └── EmailHandlerService.swift # Email AI responder -│ -└── Resources/ - └── oAI.help/ # macOS Help Book -``` - -### Key Technologies - -- **SwiftUI** - Modern declarative UI framework -- **GRDB** - SQLite database wrapper for persistence -- **MarkdownUI** - Markdown rendering with syntax highlighting -- **os.Logger** - Native logging framework -- **Network Framework** - Pure Swift IMAP/SMTP implementation -- **Security Framework** - Keychain and encryption services - -### Database Schema - -**Conversations**: id, name, createdAt, updatedAt -**Messages**: id, conversationId, role, content, tokens, cost, timestamp -**Message Metadata**: message_id, importance_score, user_starred, summary -**Message Embeddings**: message_id, embedding (BLOB), model, dimension -**Conversation Summaries**: id, conversationId, startIndex, endIndex, summary -**Email Logs**: id, sender, subject, status, timestamp - -### Building & Debugging - -**Build Commands:** -```bash -# Clean build -xcodebuild clean -scheme oAI - -# Build -xcodebuild -scheme oAI -configuration Debug - -# Run tests -xcodebuild test -scheme oAI -``` - -**Logs Location:** -``` -~/Library/Logs/oAI.log -``` - -**Database Location:** -``` -~/Library/Application Support/oAI/oai_conversations.db -``` - ## Troubleshooting ### Common Issues @@ -378,21 +299,17 @@ xcodebuild test -scheme oAI - Check Settings → Advanced → Semantic Search - Verify embedding provider is selected -## Performance Notes - -- **Context Selection**: 50-80% token reduction for long conversations -- **Semantic Search**: ~$0.02-0.15/month for heavy users -- **Conversation Export**: Markdown format for human readability -- **Database**: Indexed queries for fast conversation retrieval -- **Streaming**: Efficient memory usage with AsyncThrowingStream - ## Roadmap - [x] Vector index for faster semantic search (sqlite-vss) +- [x] Reasoning / thinking tokens (streamed live, collapsible) +- [x] Localization (Norwegian Bokmål, Swedish, Danish, German) +- [x] iCloud Backup (settings export/restore) +- [x] Bash execution with per-command approval +- [ ] SOUL.md / USER.md — living identity documents injected into system prompt +- [ ] Parallel research agents (read-only, concurrent) - [ ] Local embeddings (sentence-transformers, $0 cost) -- [ ] Conversation clustering and recommendations - [ ] Multi-modal conversation export (PDF, HTML) -- [ ] Plugin system for custom tools - [ ] iOS companion app with CloudKit sync ## License @@ -403,34 +320,21 @@ This means you are free to use, study, modify, and distribute oAI, but any modif See [LICENSE](LICENSE) for the full license text, or visit [gnu.org/licenses/agpl-3.0](https://www.gnu.org/licenses/agpl-3.0.html). +## Development + +See [DEVELOPMENT.md](DEVELOPMENT.md) for project structure, build scripts, database schema, and contribution guidelines. + ## Author **Rune Olsen** -- Website: [https://blog.rune.pm](https://blog.rune.pm) +- Website: https://mac.oai.pm +- Blog: [https://blog.rune.pm](https://blog.rune.pm) - Gitlab.pm: [@rune](https://gitlab.pm/rune) ## Contributing -Contributions are welcome! By submitting a pull request you agree that your contribution will be licensed under the AGPL-3.0. - -1. Fork the repository -2. Create a feature branch (`git checkout -b feature/amazing-feature`) -3. Commit your changes (`git commit -m 'Add amazing feature'`) -4. Push to the branch (`git push origin feature/amazing-feature`) -5. Open a Pull Request - -Please ensure: -- Code follows Swift style guidelines -- All tests pass -- Documentation is updated -- Commit messages are descriptive - -## Acknowledgments - -- **MarkdownUI** - Excellent markdown rendering library -- **GRDB** - Robust SQLite wrapper for Swift -- **Anthropic, OpenAI, OpenRouter** - AI API providers +Contributions are welcome! See [DEVELOPMENT.md](DEVELOPMENT.md) for build instructions and project structure. --- @@ -438,4 +342,3 @@ Please ensure: **🐛 Found a bug?** [Open an issue](https://gitlab.pm/rune/oai-swift/issues/new) - diff --git a/oAI.xcodeproj/project.pbxproj b/oAI.xcodeproj/project.pbxproj index 898331f..bcfd403 100644 --- a/oAI.xcodeproj/project.pbxproj +++ b/oAI.xcodeproj/project.pbxproj @@ -100,6 +100,10 @@ knownRegions = ( en, Base, + nb, + da, + de, + sv, ); mainGroup = A550A6592F3B72EA00136F2B; minimizedProjectReferenceProxies = 1; @@ -288,7 +292,7 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,7"; @@ -332,7 +336,7 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,7"; diff --git a/oAI.xcodeproj/xcshareddata/xcschemes/oAI.xcscheme b/oAI.xcodeproj/xcshareddata/xcschemes/oAI.xcscheme new file mode 100644 index 0000000..1ace073 --- /dev/null +++ b/oAI.xcodeproj/xcshareddata/xcschemes/oAI.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/oAI/Localizable.xcstrings b/oAI/Localizable.xcstrings new file mode 100644 index 0000000..b7fd5c3 --- /dev/null +++ b/oAI/Localizable.xcstrings @@ -0,0 +1,4750 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "(always used)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "(altid brugt)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "(immer verwendet)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "(alltid brukt)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "(används alltid)" + } + } + } + }, + "(optional)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "(valgfri)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "(optional)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "(valgfri)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "(valfri)" + } + } + } + }, + "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" : { + "comment" : "A link to the GNU Affero General Public License.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "[GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html)" + } + } + } + }, + "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases" + } + } + } + }, + "%@" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + } + } + }, + "%@ active — injected into every conversation" : { + "comment" : "A footnote indicating that some skills are active.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ aktiv — injiceres i alle samtaler" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ aktiv — in jedes Gespräch eingefügt" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ aktiv — injisert i alle samtaler" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ aktiv — injiceras i alla konversationer" + } + } + } + }, + "%@ chars" : { + "comment" : "A label showing the number of characters in the skill's content. The argument is the number of characters in the content.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ tegn" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Zeichen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ tegn" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ tecken" + } + } + } + }, + "%@ min" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ min" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ min" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ min" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ min" + } + } + } + }, + "%@ pt" : { + "comment" : "A label showing the current size of the GUI text. The value is a slider that lets the user adjust the size, with a label showing the current size in points.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ pt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ pt" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ pt" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ pt" + } + } + } + }, + "%@s" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@s" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@s" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@s" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@s" + } + } + } + }, + "•" : { + "comment" : "A bullet point.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "•" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "•" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "•" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "•" + } + } + } + }, + "• Add public key to your git provider" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Tilføj den offentlige nøgle til din git-udbyder" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Öffentlichen Schlüssel beim Git-Anbieter hinzufügen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Legg til den offentlige nøkkelen hos git-leverandøren din" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Lägg till den offentliga nyckeln hos din git-leverantör" + } + } + } + }, + "• No credentials needed in oAI" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Ingen legitimationsoplysninger nødvendige i oAI" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Keine Zugangsdaten in oAI erforderlich" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Ingen legitimasjon nødvendig i oAI" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Inga inloggningsuppgifter behövs i oAI" + } + } + } + }, + "• Uses your system SSH keys (~/.ssh/id_ed25519)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Bruger dine system-SSH-nøgler (~/.ssh/id_ed25519)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Verwendet deine System-SSH-Schlüssel (~/.ssh/id_ed25519)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Bruker systemets SSH-nøkler (~/.ssh/id_ed25519)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "• Använder dina system-SSH-nycklar (~/.ssh/id_ed25519)" + } + } + } + }, + "^[%@ entry](inflect: true)" : { + "comment" : "A link that shows the number of email logs. The number is pluralized based on the value of the `inflect: true` modifier.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "^[%@ post](inflect: true)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "^[%@ Eintrag](inflect: true)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "^[%@ oppføring](inflect: true)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "^[%@ post](inflect: true)" + } + } + } + }, + "© 2026 [Rune Olsen](https://blog.rune.pm)" : { + "comment" : "A copyright notice with the copyright holder's name.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "© 2026 [Rune Olsen](https://blog.rune.pm)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "© 2026 [Rune Olsen](https://blog.rune.pm)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "© 2026 [Rune Olsen](https://blog.rune.pm)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "© 2026 [Rune Olsen](https://blog.rune.pm)" + } + } + } + }, + "↑↓ navigate ↩ open" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "↑↓ navigate ↩ open" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "↑↓ navigate ↩ open" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "↑↓ navigate ↩ open" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "↑↓ navigate ↩ open" + } + } + } + }, + "~750 tokens ≈ 500 words. Online mode allows web search in responses." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "~750 tokens ≈ 500 ord. Onlinetilstand tillader websøgning i svar." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "~750 Tokens ≈ 500 Wörter. Online-Modus ermöglicht Websuche in Antworten." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "~750 tokens ≈ 500 ord. Nettmodus tillater nettsøk i svar." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "~750 tokens ≈ 500 ord. Onlineläge tillåter webbsökning i svar." + } + } + } + }, + "⌘N New • ⌘M Model • ⌘S Save" : { + "comment" : "A hint that appears on macOS when using keyboard shortcuts.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "⌘N New • ⌘M Model • ⌘S Save" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "⌘N New • ⌘M Model • ⌘S Save" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "⌘N New • ⌘M Model • ⌘S Save" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "⌘N New • ⌘M Model • ⌘S Save" + } + } + } + }, + "⚠️ Custom prompt active — only this prompt will be sent to the model." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Brugerdefineret prompt aktiv — kun denne prompt sendes til modellen." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Benutzerdefinierter Prompt aktiv — nur dieser Prompt wird an das Modell gesendet." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Egendefinert prompt aktiv — bare denne prompten sendes til modellen." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Anpassad prompt aktiv — bara denna prompt skickas till modellen." + } + } + } + }, + "⚠️ interrupted" : { + "comment" : "A warning label that appears next to a message that was interrupted.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ afbrudt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ unterbrochen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ avbrutt" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ avbruten" + } + } + } + }, + "⚠️ Many providers (GitHub) no longer support password authentication. Use Access Token instead." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Mange udbydere (GitHub) understøtter ikke længere adgangskodegodkendelse. Brug Access Token i stedet." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Viele Anbieter (GitHub) unterstützen keine Passwort-Authentifizierung mehr. Stattdessen Access Token verwenden." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Mange tilbydere (GitHub) støtter ikke lenger passordautentisering. Bruk Access Token i stedet." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Många leverantörer (GitHub) stöder inte längre lösenordsautentisering. Använd Access Token istället." + } + } + } + }, + "⚠️ No embedding providers available. Configure an API key for OpenAI, OpenRouter, or Google in the General tab." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Ingen indlejringsudbydere tilgængelige. Konfigurer en API-nøgle til OpenAI, OpenRouter eller Google på fanen Generelt." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Keine Embedding-Anbieter verfügbar. Konfiguriere einen API-Schlüssel für OpenAI, OpenRouter oder Google auf der Registerkarte „Allgemein“." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Ingen innbyggingsleverandører tilgjengelig. Konfigurer en API-nøkkel for OpenAI, OpenRouter eller Google i fanen Generelt." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Inga embedding-leverantörer tillgängliga. Konfigurera en API-nyckel för OpenAI, OpenRouter eller Google på fliken Allmänt." + } + } + } + }, + "⚠️ One-time operation — generates embeddings for all messages. Estimated cost: ~$0.04 for 10,000 messages." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Engangshandling — genererer indlejringer for alle beskeder. Estimeret omkostning: ~$0,04 for 10.000 beskeder." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Einmaliger Vorgang — generiert Embeddings für alle Nachrichten. Geschätzte Kosten: ~$0,04 für 10.000 Nachrichten." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Engangsoperasjon — genererer innbygginger for alle meldinger. Estimert kostnad: ~$0,04 for 10 000 meldinger." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Engångsoperation — genererar embeddings för alla meddelanden. Uppskattad kostnad: ~$0,04 för 10 000 meddelanden." + } + } + } + }, + "⚠️ These are advanced settings" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Dette er avancerede indstillinger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Das sind erweiterte Einstellungen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Dette er avanserte innstillinger" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Det här är avancerade inställningar" + } + } + } + }, + "🌐" : { + "comment" : "A label for a model that can be used to generate images.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "🌐" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "🌐" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "🌐" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "🌐" + } + } + } + }, + "🎨" : { + "comment" : "A button that generates an image.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "🎨" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "🎨" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "🎨" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "🎨" + } + } + } + }, + "👁️" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "👁️" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "👁️" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "👁️" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "👁️" + } + } + } + }, + "💡 For Gmail, use an App Password. Google Account > Security > 2-Step Verification > App passwords." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 Til Gmail, brug et app-adgangskode. Google-konto > Sikkerhed > 2-trinsbekræftelse > App-adgangskoder." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 Für Gmail ein App-Passwort verwenden. Google-Konto > Sicherheit > 2-Schritt-Verifizierung > App-Passwörter." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 For Gmail, bruk et apppassord. Google-konto > Sikkerhet > 2-trinns bekreftelse > Apppassord." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 För Gmail, använd ett applösenord. Google-konto > Säkerhet > 2-stegsverifiering > Applösenord." + } + } + } + }, + "💡 Use HTTPS URL (e.g., https://gitlab.pm/user/repo.git) — works with all auth methods." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 Brug HTTPS URL (f.eks. https://gitlab.pm/user/repo.git) — fungerer med alle godkendelsesmetoder." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 HTTPS-URL verwenden (z.B. https://gitlab.pm/user/repo.git) — funktioniert mit allen Authentifizierungsmethoden." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 Bruk HTTPS URL (f.eks. https://gitlab.pm/user/repo.git) — fungerer med alle autentiseringsmetoder." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "💡 Använd HTTPS URL (t.ex. https://gitlab.pm/user/repo.git) — fungerar med alla autentiseringsmetoder." + } + } + } + }, + "🔧" : { + "comment" : "A checkmark emoji.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔧" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔧" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔧" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔧" + } + } + } + }, + "1. Open Paperless-NGX → Settings → API Tokens" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "1. Åbn Paperless-NGX → Indstillinger → API-tokens" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "1. Paperless-NGX öffnen → Einstellungen → API-Tokens" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "1. Åpne Paperless-NGX → Innstillinger → API-tokens" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "1. Öppna Paperless-NGX → Inställningar → API-tokens" + } + } + } + }, + "2. Create or copy your token" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "2. Opret eller kopier dit token" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "2. Token erstellen oder kopieren" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "2. Opprett eller kopier tokenet ditt" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "2. Skapa eller kopiera din token" + } + } + } + }, + "3. Paste it above" : { + "comment" : "A step in the process of getting a Paperless-NGX API token.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "3. Indsæt det ovenfor" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "3. Oben einfügen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "3. Lim det inn ovenfor" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "3. Klistra in det ovan" + } + } + } + }, + "A shortcut with command %@ already exists." : { + "comment" : "A warning message that appears when a user tries to import a shortcut that already exists in their settings. The argument is the command of the conflicting shortcut.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "En genvej med kommandoen %@ findes allerede." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eine Verknüpfung mit dem Befehl %@ existiert bereits." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "En snarvei med kommandoen %@ finnes allerede." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "En genväg med kommandot %@ finns redan." + } + } + } + }, + "Access Token" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Access Token" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Access Token" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Access Token" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Access Token" + } + } + } + }, + "active" : { + "comment" : "A label for an active skill.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "aktiv" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "aktiv" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "aktiv" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "aktiv" + } + } + } + }, + "Active" : { + "comment" : "A toggle", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiv" + } + } + } + }, + "Active skills are appended to the system prompt. Toggle them per-skill to control what the AI knows." : { + "comment" : "A description of how to toggle the active status of a skill.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktive færdigheder tilføjes til systemprompten. Slå dem til og fra per færdighed for at styre, hvad AI'en ved." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktive Skills werden an den System-Prompt angehängt. Schalte sie einzeln ein oder aus, um zu steuern, was die KI weiß." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktive ferdigheter legges til systemprompten. Slå dem av og på per ferdighet for å kontrollere hva KI-en vet." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiva färdigheter läggs till i systemprompten. Aktivera/inaktivera dem per färdighet för att kontrollera vad AI:n känner till." + } + } + } + }, + "Agent Skills" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agent-færdigheder" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agent-Skills" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agent-ferdigheter" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agent-färdigheter" + } + } + } + }, + "Allow Shell Command?" : { + "comment" : "A title for a modal that asks the user if they want to allow a shell command.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillad skalkommando?" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shell-Befehl erlauben?" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillat skalkommando?" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillåt skalkommando?" + } + } + } + }, + "Allow the AI to run shell commands on your machine. Commands are executed via /bin/zsh. Enable approval mode to review each command before it runs." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giv AI'en adgang til at køre skalkommandoer på din maskine. Kommandoer udføres via /bin/zsh. Aktiver godkendelsestilstand for at gennemgå hver kommando, inden den køres." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erlaube der KI, Shell-Befehle auf deinem Rechner auszuführen. Befehle werden über /bin/zsh ausgeführt. Aktiviere den Genehmigungsmodus, um jeden Befehl vor der Ausführung zu prüfen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gi KI-en tilgang til å kjøre skalkommandoer på maskinen din. Kommandoer kjøres via /bin/zsh. Aktiver godkjenningsmodus for å se gjennom hver kommando før den kjøres." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Låt AI:n köra skalkommandon på din dator. Kommandon körs via /bin/zsh. Aktivera godkännandeläge för att granska varje kommando innan det körs." + } + } + } + }, + "Anthropic Balance" : { + "comment" : "A label displayed above the user's balance on the Anthropic platform.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anthropic Balance" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anthropic Balance" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anthropic Balance" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anthropic Balance" + } + } + } + }, + "API keys and credentials are **not** included in the backup. You will need to re-enter them after restoring on a new machine." : { + "comment" : "A warning message displayed in the settings tab of the app.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "API-nøgler og legitimationsoplysninger er **ikke** inkluderet i sikkerhedskopien. Du skal indtaste dem igen efter gendannelse på en ny maskine." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "API-Schlüssel und Zugangsdaten sind **nicht** in der Sicherung enthalten. Du musst sie nach der Wiederherstellung auf einem neuen Gerät erneut eingeben." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "API-nøkler og legitimasjon er **ikke** inkludert i sikkerhetskopien. Du må taste dem inn på nytt etter gjenoppretting på en ny maskin." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "API-nycklar och inloggningsuppgifter ingår **inte** i säkerhetskopian. Du måste ange dem igen efter återställning på en ny dator." + } + } + } + }, + "Auto-execute mode: commands run without approval. Use with caution." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk kørselstilstand: kommandoer køres uden godkendelse. Brug med forsigtighed." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatischer Ausführungsmodus: Befehle werden ohne Genehmigung ausgeführt. Mit Vorsicht verwenden." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto-kjøremodus: kommandoer kjøres uten godkjenning. Bruk med forsiktighet." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk körning: kommandon körs utan godkännande. Använd med försiktighet." + } + } + } + }, + "Auto-sync can cause conflicts if running on multiple machines simultaneously." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto-synkronisering kan forårsage konflikter, hvis den kører på flere maskiner samtidigt." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die automatische Synchronisierung kann zu Konflikten führen, wenn sie auf mehreren Geräten gleichzeitig läuft." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto-synkronisering kan forårsake konflikter hvis den kjøres på flere maskiner samtidig." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk synkronisering kan orsaka konflikter om den körs på flera datorer samtidigt." + } + } + } + }, + "Automatically select relevant messages instead of sending all history. Reduces token usage for long conversations." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vælg automatisk relevante beskeder i stedet for at sende al historik. Reducerer tokenforbrug for lange samtaler." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relevante Nachrichten automatisch auswählen statt die gesamte Historie zu senden. Reduziert den Token-Verbrauch bei langen Gesprächen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg automatisk relevante meldinger i stedet for å sende all historikk. Reduserer tokenforbruk for lange samtaler." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Välj automatiskt relevanta meddelanden istället för att skicka all historik. Minskar tokenanvändningen för långa konversationer." + } + } + } + }, + "Automatically summarize old portions of long conversations to save tokens and improve context efficiency." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opsummer automatisk ældre dele af lange samtaler for at spare tokens og forbedre konteksteffektiviteten." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ältere Teile langer Gespräche automatisch zusammenfassen, um Tokens zu sparen und die Kontexteffizienz zu verbessern." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sammenfatt automatisk eldre deler av lange samtaler for å spare tokens og forbedre konteksteffektiviteten." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sammanfatta automatiskt äldre delar av långa konversationer för att spara tokens och förbättra kontexteffektiviteten." + } + } + } + }, + "Back Up Now" : { + "comment" : "A button that backs up the user's data.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sikkerhedskopier nu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jetzt sichern" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sikkerhetskopier nå" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Säkerhetskopiera nu" + } + } + } + }, + "Backup location:" : { + "comment" : "A label displayed under the backup location.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sikkerhedskopieringsplacering:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sicherungsort:" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sikkerhetskopiplassering:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Säkerhetskopieringsplats:" + } + } + } + }, + "Bash Execution" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bash Execution" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bash Execution" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bash Execution" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bash Execution" + } + } + } + }, + "Branch: %@" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Branch: %@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Branch: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Branch: %@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Branch: %@" + } + } + } + }, + "Built with SwiftUI" : { + "comment" : "A label that says that the app is built with SwiftUI.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Built with SwiftUI" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Built with SwiftUI" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Built with SwiftUI" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Built with SwiftUI" + } + } + } + }, + "Capabilities" : { + "comment" : "A label displayed under the list of capabilities of a model.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Egenskaber" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fähigkeiten" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Egenskaper" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Funktioner" + } + } + } + }, + "Changing these values affects how the AI generates responses. The defaults work well for most use cases." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ændring af disse værdier påvirker, hvordan AI'en genererer svar. Standardværdierne fungerer godt til de fleste anvendelser." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Ändern dieser Werte beeinflusst, wie die KI Antworten generiert. Die Standardwerte funktionieren für die meisten Anwendungsfälle gut." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Å endre disse verdiene påvirker hvordan KI-en genererer svar. Standardverdiene fungerer bra for de fleste brukstilfeller." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Att ändra dessa värden påverkar hur AI:n genererar svar. Standardvärdena fungerar bra för de flesta användningsfall." + } + } + } + }, + "Check your balance at:" : { + "comment" : "A label displayed under a link to check the user's balance.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tjek din saldo på:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guthaben prüfen unter:" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sjekk saldoen din på:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrollera ditt saldo på:" + } + } + } + }, + "Check your usage at:" : { + "comment" : "A label displayed under a link to OpenAI's usage page.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tjek dit forbrug på:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nutzung prüfen unter:" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sjekk forbruket ditt på:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrollera din användning på:" + } + } + } + }, + "Clear All" : { + "comment" : "A button to clear all email activity logs.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ryd alle" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle löschen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slett alle" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rensa alla" + } + } + } + }, + "Click 'Add Folder' below or drag folders here from Finder" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klik på 'Tilføj mappe' nedenfor eller træk mapper hertil fra Finder" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klicke unten auf „Ordner hinzufügen“ oder ziehe Ordner aus dem Finder hierher" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klikk «Legg til mappe» nedenfor eller dra mapper hit fra Finder" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klicka på \"Lägg till mapp\" nedan eller dra mappar hit från Finder" + } + } + } + }, + "Command" : { + "comment" : "A label displayed above the command field.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommando" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehl" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommando" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommando" + } + } + } + }, + "COMMAND" : { + "comment" : "A label displayed above the command that the AI wants to run.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "KOMMANDO" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "BEFEHL" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "KOMMANDO" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "KOMMANDO" + } + } + } + }, + "Command History" : { + "comment" : "A label displayed above the command history.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandohistorik" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Befehlshistorie" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandohistorikk" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommandohistorik" + } + } + } + }, + "Content (Markdown)" : { + "comment" : "A", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indhold (Markdown)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inhalt (Markdown)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innhold (Markdown)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innehåll (Markdown)" + } + } + } + }, + "Controls which messages are written to ~/Library/Logs/oAI.log" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Styrer hvilke beskeder der skrives til ~/Library/Logs/oAI.log" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Steuert, welche Nachrichten in ~/Library/Logs/oAI.log geschrieben werden" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Styrer hvilke meldinger som skrives til ~/Library/Logs/oAI.log" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Styr vilka meddelanden som skrivs till ~/Library/Logs/oAI.log" + } + } + } + }, + "Conversations" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Samtaler" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gespräche" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Samtaler" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konversationer" + } + } + } + }, + "Copied!" : { + "comment" : "A message displayed when a code block is successfully copied to the clipboard.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopieret!" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopiert!" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopiert!" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopierat!" + } + } + } + }, + "Cost Examples" : { + "comment" : "A heading for the cost examples of a model.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omkostningseksempler" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kostenbeispiele" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kostnadseksempler" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kostnadsexempel" + } + } + } + }, + "Create a dedicated email account specifically for AI handling. Do NOT use your personal email address." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opret en dedikeret e-mailkonto specifikt til AI-håndtering. Brug IKKE din personlige e-mailadresse." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erstelle ein dediziertes E-Mail-Konto speziell für die KI-Bearbeitung. Verwende NICHT deine persönliche E-Mail-Adresse." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opprett en dedikert e-postkonto spesielt for KI-håndtering. IKKE bruk din personlige e-postadresse." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapa ett dedikerat e-postkonto specifikt för AI-hantering. Använd INTE din personliga e-postadress." + } + } + } + }, + "Create a shortcut to save a reusable prompt template accessible from the / command dropdown." : { + "comment" : "A description of the functionality of the shortcut editor.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opret en genvej til at gemme en genanvendelig promptskabelon, der er tilgængelig fra /kommando-rullemenuen." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erstelle eine Verknüpfung, um eine wiederverwendbare Prompt-Vorlage zu speichern, die über das /-Befehlsmenü zugänglich ist." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opprett en snarvei for å lagre en gjenbrukbar promptmal som er tilgjengelig fra /kommando-rullegardinmenyen." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapa en genväg för att spara en återanvändbar promptmall tillgänglig från /-kommandomenyn." + } + } + } + }, + "Default Prompt" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standardprompt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standard-Prompt" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standardprompt" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standardprompt" + } + } + } + }, + "Delete (%@)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slet (%@)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen (%@)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slett (%@)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Radera (%@)" + } + } + } + }, + "Description" : { + "comment" : "A label", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beskrivelse" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beschreibung" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beskrivelse" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beskrivning" + } + } + } + }, + "Each command will require your approval before running." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hver kommando kræver din godkendelse, inden den køres." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jeder Befehl erfordert deine Genehmigung, bevor er ausgeführt wird." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hver kommando krever din godkjenning før den kjøres." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Varje kommando kräver ditt godkännande innan det körs." + } + } + } + }, + "Edit" : { + "comment" : "A button to edit a skill.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rediger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bearbeiten" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rediger" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Redigera" + } + } + } + }, + "Email Activity Log" : { + "comment" : "The title of the email activity log view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mailaktivitetslog" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-Mail-Aktivitätsprotokoll" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-postaktivitetslogg" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-postaktivitetslogg" + } + } + } + }, + "Email Handler System Prompt" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systemprompt til e-mailbehandler" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "System-Prompt für E-Mail-Handler" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systemprompt for e-postbehandler" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systemprompt för e-posthanterare" + } + } + } + }, + "Email handler uses ONLY its own system prompt, completely isolated from your main chat settings. A custom prompt below will override the defaults." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mailbehandleren bruger KUN sin egen systemprompt, fuldstændig isoleret fra dine vigtigste chatindstillinger. En brugerdefineret prompt nedenfor tilsidesætter standardindstillingerne." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der E-Mail-Handler verwendet NUR seinen eigenen System-Prompt, vollständig isoliert von deinen Chat-Einstellungen. Ein benutzerdefinierter Prompt unten überschreibt die Standardeinstellungen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-postbehandleren bruker KUN sin egen systemprompt, fullstendig isolert fra de viktigste chatinnstillingene dine. En egendefinert prompt nedenfor vil overstyre standardinnstillingene." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-posthanteraren använder BARA sin egen systemprompt, helt isolerad från dina viktigaste chattinställningar. En anpassad prompt nedan åsidosätter standardinställningarna." + } + } + } + }, + "Email tasks have READ-ONLY access to MCP folders. The AI cannot write, delete, or modify files when processing emails." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mailopgaver har KUN LÆSEADGANG til MCP-mapper. AI'en kan ikke skrive, slette eller ændre filer under behandling af e-mails." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-Mail-Aufgaben haben NUR LESEZUGRIFF auf MCP-Ordner. Die KI kann beim Verarbeiten von E-Mails keine Dateien schreiben, löschen oder ändern." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-postoppgaver har SKRIVEBESKYTTET tilgang til MCP-mapper. KI-en kan ikke skrive, slette eller endre filer ved behandling av e-post." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-postuppgifter har LÄSBEHÖRIGHET till MCP-mappar. AI:n kan inte skriva, radera eller ändra filer vid e-postbehandling." + } + } + } + }, + "Enable AI-powered semantic search using %@ embeddings. Cost: ~$0.02–0.15/1M tokens." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver AI-drevet semantisk søgning med %@-indlejringer. Omkostning: ~$0,02–0,15/1M tokens." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "KI-gestützte semantische Suche mit %@-Embeddings aktivieren. Kosten: ~$0,02–0,15/1M Tokens." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver KI-drevet semantisk søk med %@-innbygginger. Kostnad: ~$0,02–0,15/1M tokens." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivera AI-driven semantisk sökning med %@-embeddings. Kostnad: ~$0,02–0,15/1M tokens." + } + } + } + }, + "Enable email handler in Settings to start monitoring emails" : { + "comment" : "A message displayed when the email handler is disabled.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver e-mailbehandler i Indstillinger for at begynde at overvåge e-mails" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-Mail-Handler in den Einstellungen aktivieren, um E-Mails zu überwachen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiver e-postbehandler i Innstillinger for å starte overvåking av e-post" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivera e-posthanteraren i Inställningar för att börja övervaka e-post" + } + } + } + }, + "Error: %@" : { + "comment" : "A label displaying an error message in the checkout view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fejl: %@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehler: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feil: %@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fel: %@" + } + } + } + }, + "Example structure:" : { + "comment" : "A", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eksempelstruktur:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beispielstruktur:" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eksempelstruktur:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exempelstruktur:" + } + } + } + }, + "Example: oai-bot-x7k2m9p3@gmail.com" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + } + } + } + }, + "File Access Permissions" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtilladelser" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dateizugriffsberechtigungen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtillatelser" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filbehörigheter" + } + } + } + }, + "Files" : { + "comment" : "A label displayed above the list of files attached to a skill.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filer" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dateien" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filer" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filer" + } + } + } + }, + "Google (Gemini embedding)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Google (Gemini embedding)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Google (Gemini embedding)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Google (Gemini embedding)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Google (Gemini embedding)" + } + } + } + }, + "Help" : { + "comment" : "A heading for the help screen", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hjælp" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hilfe" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hjelp" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hjälp" + } + } + } + }, + "How to get your API token:" : { + "comment" : "A heading for a section that describes how to get your API token.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sådan får du dit API-token:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "So erhältst du deinen API-Token:" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slik får du API-tokenet ditt:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Så här hämtar du din API-token:" + } + } + } + }, + "ℹ️ SSH Key Authentication" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "ℹ️ SSH-nøglegodkendelse" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "ℹ️ SSH-Schlüssel-Authentifizierung" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "ℹ️ SSH-nøkkelautentisering" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "ℹ️ SSH-nyckelautentisering" + } + } + } + }, + "Initialize Repository" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Initialiser repository" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repository initialisieren" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Initialiser repositorium" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Initiera förvar" + } + } + } + }, + "Inject into system prompt for every conversation" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Injicér i systemprompten for hver samtale" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "In den System-Prompt jedes Gesprächs einfügen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Injiser i systemprompten for hver samtale" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Injicera i systemprompten för varje konversation" + } + } + } + }, + "Large files inflate the system prompt and may hit token limits." : { + "comment" : "A warning displayed when a user adds a large file to a skill.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Store filer oppblæser systemprompten og kan ramme token-grænser." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Große Dateien blähen den System-Prompt auf und können Token-Limits erreichen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Store filer blåser opp systemprompten og kan nå token-grenser." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stora filer blåser upp systemprompten och kan nå token-gränser." + } + } + } + }, + "Last sync: %@" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seneste synkronisering: %@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Letzte Synchronisierung: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siste synkronisering: %@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Senaste synkronisering: %@" + } + } + } + }, + "Leave empty to use the default email handler system prompt." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lad stå tomt for at bruge standard systemprompt til e-mailbehandler." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leer lassen, um den Standard-System-Prompt des E-Mail-Handlers zu verwenden." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "La stå tomt for å bruke standard systemprompt for e-postbehandler." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lämna tomt för att använda e-posthanterarens standardsystemprompt." + } + } + } + }, + "Lowercase letters, numbers, and hyphens only. No spaces." : { + "comment" : "A description of the format of a shortcut's command.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kun små bogstaver, tal og bindestreger. Ingen mellemrum." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nur Kleinbuchstaben, Zahlen und Bindestriche. Keine Leerzeichen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kun små bokstaver, tall og bindestreker. Ingen mellomrom." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endast gemener, siffror och bindestreck. Inga blanksteg." + } + } + } + }, + "Markdown instruction files injected into the system prompt. Compatible with SKILL.md format." : { + "comment" : "A description of the format of agent skills.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Markdown-instruktionsfiler injiceret i systemprompten. Kompatibel med SKILL.md-format." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Markdown-Anweisungsdateien, die in den System-Prompt eingefügt werden. Kompatibel mit SKILL.md-Format." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Markdown-instruksjonsfiler som injiseres i systemprompten. Kompatibel med SKILL.md-format." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Markdown-instruktionsfiler som injiceras i systemprompten. Kompatibel med SKILL.md-format." + } + } + } + }, + "Max Tokens: set to 0 to use model default. Higher values allow longer responses." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maks tokens: sæt til 0 for at bruge modelstandard. Højere værdier tillader længere svar." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maximale Tokens: auf 0 setzen, um den Modell-Standard zu verwenden. Höhere Werte erlauben längere Antworten." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maks tokens: sett til 0 for å bruke modellstandard. Høyere verdier gir lengre svar." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Max tokens: sätt till 0 för att använda modellens standard. Högre värden tillåter längre svar." + } + } + } + }, + "MCP gives the AI controlled access to read and optionally write files on your computer. The AI can search, read, and analyze files in allowed folders to help with coding, analysis, and other tasks." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "MCP giver AI'en kontrolleret adgang til at læse og eventuelt skrive filer på din computer. AI'en kan søge, læse og analysere filer i tilladte mapper for at hjælpe med kodning, analyse og andre opgaver." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "MCP gibt der KI kontrollierten Zugriff, um Dateien auf deinem Computer zu lesen und optional zu schreiben. Die KI kann Dateien in erlaubten Ordnern suchen, lesen und analysieren, um bei Programmierung, Analyse und anderen Aufgaben zu helfen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "MCP gir KI-en kontrollert tilgang til å lese og eventuelt skrive filer på datamaskinen din. KI-en kan søke, lese og analysere filer i tillatte mapper for å hjelpe med koding, analyse og andre oppgaver." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "MCP ger AI:n kontrollerad åtkomst att läsa och eventuellt skriva filer på din dator. AI:n kan söka, läsa och analysera filer i tillåtna mappar för att hjälpa till med kodning, analys och andra uppgifter." + } + } + } + }, + "messages" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "beskeder" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nachrichten" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "meldinger" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "meddelanden" + } + } + } + }, + "Model Context Protocol" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Model Context Protocol" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Model Context Protocol" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Model Context Protocol" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Model Context Protocol" + } + } + } + }, + "Model Info" : { + "comment" : "A title for a view that displays information about a model.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modelinformation" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modell-Info" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modellinformasjon" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modellinformation" + } + } + } + }, + "Multi-provider AI chat client" : { + "comment" : "A description of oAI.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI-chatklient med understøttelse af flere udbydere" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "KI-Chat-Client mit mehreren Anbietern" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI-chatklient med støtte for flere tilbydere" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI-chattklient med stöd för flera leverantörer" + } + } + } + }, + "Name" : { + "comment" : "A label", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Navn" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Navn" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Namn" + } + } + } + }, + "Never" : { + "comment" : "A label displayed in the settings view that indicates when the last backup was taken.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aldrig" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aldri" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aldrig" + } + } + } + }, + "No credit data available" : { + "comment" : "A message displayed when there is no credit data available.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen kreditdata tilgængelig" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Guthabendaten verfügbar" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen kredittdata tilgjengelig" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen kreditdata tillgänglig" + } + } + } + }, + "No email activity yet" : { + "comment" : "A message displayed when the user has not yet received any email activity.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen e-mailaktivitet endnu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch keine E-Mail-Aktivität" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen e-postaktivitet ennå" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen e-postaktivitet ännu" + } + } + } + }, + "No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill." : { + "comment" : "A message displayed when a user has not attached any files to a skill.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen filer vedhæftet. Tilføj JSON, YAML, CSV eller TXT-filer for at injicere data i systemprompten ved siden af denne færdighed." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Dateien angehängt. Füge JSON, YAML, CSV oder TXT-Dateien hinzu, um Daten zusammen mit diesem Skill in den System-Prompt einzufügen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen filer vedlagt. Legg til JSON, YAML, CSV eller TXT-filer for å injisere data i systemprompten sammen med denne ferdigheten." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga filer bifogade. Lägg till JSON, YAML, CSV eller TXT-filer för att injicera data i systemprompten bredvid denna färdighet." + } + } + } + }, + "No model selected" : { + "comment" : "A label displayed when no model is selected.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen model valgt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein Modell ausgewählt" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen modell valgt" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen modell vald" + } + } + } + }, + "No models available" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen modeller tilgængelige" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Modelle verfügbar" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen modeller tilgjengelig" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga modeller tillgängliga" + } + } + } + }, + "No results for \"%@\"" : { + "comment" : "A message displayed when there are no search results. The argument is the search term.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen resultater for \"%@\"" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Ergebnisse für „%@“" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen resultater for «%@»" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga resultat för \"%@\"" + } + } + } + }, + "No shortcuts yet" : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen genveje endnu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch keine Verknüpfungen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen snarveier ennå" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga genvägar ännu" + } + } + } + }, + "No shortcuts yet — click New Shortcut to create one." : { + "comment" : "A message displayed when a user has no shortcuts.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen genveje endnu — klik på Ny genvej for at oprette en." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch keine Verknüpfungen — klicke auf „Neue Verknüpfung“, um eine zu erstellen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen snarveier ennå — klikk på Ny snarvei for å opprette en." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga genvägar ännu — klicka på Ny genväg för att skapa en." + } + } + } + }, + "No skills yet" : { + "comment" : "A message displayed when the user has no skills.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen færdigheder endnu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch keine Skills" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen ferdigheter ennå" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga färdigheter ännu" + } + } + } + }, + "No skills yet — click New Skill or Import to get started." : { + "comment" : "A message displayed when a user has not created any skills.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen færdigheder endnu — klik på Ny færdighed eller Importer for at komme i gang." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch keine Skills — klicke auf „Neuer Skill“ oder „Importieren“, um loszulegen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen ferdigheter ennå — klikk på Ny ferdighet eller Importer for å komme i gang." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga färdigheter ännu — klicka på Ny färdighet eller Importera för att komma igång." + } + } + } + }, + "oAI" : { + "comment" : "The name of the app.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + } + } + }, + "Ollama (Local)" : { + "comment" : "A label displayed above the credits information for the local Ollie.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ollama (Local)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ollama (Local)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ollama (Local)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ollama (Local)" + } + } + } + }, + "Only emails with this text in the subject line will be processed. Example: \"[OAIBOT] What's the weather?\"" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kun e-mails med denne tekst i emnelinjen behandles. Eksempel: \"[OAIBOT] Hvad er vejret?\"" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nur E-Mails mit diesem Text in der Betreffzeile werden verarbeitet. Beispiel: „[OAIBOT] Wie ist das Wetter?“" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bare e-poster med denne teksten i emnefeltet behandles. Eksempel: «[OAIBOT] Hva er været?»" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bara e-post med denna text i ämnesraden bearbetas. Exempel: \"[OAIBOT] Vad är vädret?\"" + } + } + } + }, + "OpenAI (text-embedding-3-large)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-large)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-large)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-large)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-large)" + } + } + } + }, + "OpenAI (text-embedding-3-small)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-small)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-small)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-small)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI (text-embedding-3-small)" + } + } + } + }, + "OpenAI Balance" : { + "comment" : "A label displayed above the OpenAI balance section.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI Balance" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI Balance" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI Balance" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenAI Balance" + } + } + } + }, + "OpenRouter (OpenAI large)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI large)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI large)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI large)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI large)" + } + } + } + }, + "OpenRouter (OpenAI small)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI small)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI small)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI small)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (OpenAI small)" + } + } + } + }, + "OpenRouter (Qwen 8B)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (Qwen 8B)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (Qwen 8B)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (Qwen 8B)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter (Qwen 8B)" + } + } + } + }, + "OpenRouter Credits" : { + "comment" : "A heading for the user's OpenRouter credits.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter Credits" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter Credits" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter Credits" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "OpenRouter Credits" + } + } + } + }, + "Read access (always enabled)" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Læseadgang (altid aktiveret)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lesezugriff (immer aktiviert)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lesetilgang (alltid aktivert)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Läsbehörighet (alltid aktiverad)" + } + } + } + }, + "Remote: %@" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote: %@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote: %@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remote: %@" + } + } + } + }, + "Restore from File…" : { + "comment" : "A button that allows the user to restore their data from a file.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gendan fra fil…" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus Datei wiederherstellen…" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjenopprett fra fil…" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Återställ från fil…" + } + } + } + }, + "Running locally — no credits needed!" : { + "comment" : "A message displayed when using an on-device LLM like the one provided by the `.ollama` provider.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kører lokalt — ingen kreditter nødvendige!" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Läuft lokal — kein Guthaben benötigt!" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kjører lokalt — ingen kreditter nødvendig!" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Körs lokalt — inga krediter behövs!" + } + } + } + }, + "Security Recommendation" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sikkerhedsanbefaling" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sicherheitsempfehlung" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sikkerhetsanbefaling" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Säkerhetsrekommendation" + } + } + } + }, + "Shell commands have full access to your system. Only approve commands you understand and trust." : { + "comment" : "A warning banner displayed in the Bash Approval Sheet.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skalkommandoer har fuld adgang til dit system. Godkend kun kommandoer, du forstår og har tillid til." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shell-Befehle haben vollen Zugriff auf dein System. Genehmige nur Befehle, die du verstehst und denen du vertraust." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skalkommandoer har full tilgang til systemet ditt. Godkjenn bare kommandoer du forstår og stoler på." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skalkommandon har full åtkomst till ditt system. Godkänn bara kommandon du förstår och litar på." + } + } + } + }, + "Show text labels below toolbar icons (helpful for new users)" : { + "comment" : "A description of the feature that shows text labels below toolbar icons.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis tekstetikvetter under værktøjslinjeikoner (nyttigt for nye brugere)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Textbeschriftungen unter Symbolleistensymbolen anzeigen (hilfreich für neue Benutzer)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis tekstetiketter under verktøylinjeikonene (nyttig for nye brukere)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visa textetiketter under verktygsfältets ikoner (hjälpsamt för nya användare)" + } + } + } + }, + "SKILL.md format — write instructions in plain Markdown." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "SKILL.md-format — skriv instruktioner i almindelig Markdown." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SKILL.md-Format — Anweisungen in einfachem Markdown schreiben." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SKILL.md-format — skriv instruksjoner i vanlig Markdown." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "SKILL.md-format — skriv instruktioner i vanlig Markdown." + } + } + } + }, + "Skills are markdown instruction files that teach the AI how to behave. Active skills are automatically injected into the system prompt." : { + "comment" : "A description of how skills are used by the AI.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Færdigheder er Markdown-instruktionsfiler, der lærer AI'en, hvordan den skal opføre sig. Aktive færdigheder injiceres automatisk i systemprompten." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skills sind Markdown-Anweisungsdateien, die der KI beibringen, wie sie sich verhalten soll. Aktive Skills werden automatisch in den System-Prompt eingefügt." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ferdigheter er Markdown-instruksjonsfiler som lærer KI-en hvordan den skal oppføre seg. Aktive ferdigheter injiseres automatisk i systemprompten." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Färdigheter är Markdown-instruktionsfiler som lär AI:n hur den ska bete sig. Aktiva färdigheter injiceras automatiskt i systemprompten." + } + } + } + }, + "SSH Key" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSH Key" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSH Key" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSH Key" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSH Key" + } + } + } + }, + "Stream responses as they're generated. Disable for single, complete responses." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stream svar, efterhånden som de genereres. Deaktiver for enkle, komplette svar." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Antworten während der Generierung streamen. Deaktivieren für vollständige Einzelantworten." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Strøm svar etter hvert som de genereres. Deaktiver for enkle, fullstendige svar." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Strömma svar medan de genereras. Inaktivera för fullständiga, enkla svar." + } + } + } + }, + "Sync conversations and settings across multiple machines using Git." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synkroniser samtaler og indstillinger på tværs af flere maskiner ved hjælp af Git." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gespräche und Einstellungen über mehrere Geräte hinweg mit Git synchronisieren." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synkroniser samtaler og innstillinger på tvers av flere maskiner ved hjelp av Git." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synkronisera konversationer och inställningar på flera datorer med Git." + } + } + } + }, + "Sync Now" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synkroniser nu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jetzt synchronisieren" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synkroniser nå" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synkronisera nu" + } + } + } + }, + "Temperature: 0 = model default · 0.0–0.7 = focused · 0.8–2.0 = creative" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatur: 0 = modelstandard · 0,0–0,7 = fokuseret · 0,8–2,0 = kreativt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatur: 0 = Modell-Standard · 0,0–0,7 = fokussiert · 0,8–2,0 = kreativ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatur: 0 = modellstandard · 0,0–0,7 = fokusert · 0,8–2,0 = kreativ" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatur: 0 = modellens standard · 0,0–0,7 = fokuserat · 0,8–2,0 = kreativt" + } + } + } + }, + "Template" : { + "comment" : "A label displayed above the text editor for the shortcut's template.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skabelon" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorlage" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mal" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mall" + } + } + } + }, + "Test Connection" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test forbindelse" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung testen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test tilkobling" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Testa anslutning" + } + } + } + }, + "Text files are injected into the system prompt alongside the skill." : { + "comment" : "A description of how text files are injected into the system prompt.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekstfiler injiceres i systemprompten ved siden af færdigheden." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Textdateien werden zusammen mit dem Skill in den System-Prompt eingefügt." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tekstfiler injiseres i systemprompten ved siden av ferdigheten." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Textfiler injiceras i systemprompten bredvid färdigheten." + } + } + } + }, + "The AI can read and search files in allowed folders" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI'en kan læse og søge i filer i tilladte mapper" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die KI kann Dateien in erlaubten Ordnern lesen und durchsuchen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "KI-en kan lese og søke filer i tillatte mapper" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI:n kan läsa och söka i filer i tillåtna mappar" + } + } + } + }, + "The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI'en læser dette indhold og beslutter, hvornår det skal bruges. Beskriv **hvad** AI'en skal gøre og **hvordan** — vær specifik og præcis." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die KI liest diesen Inhalt und entscheidet, wann sie ihn anwendet. Beschreibe **was** die KI tun soll und **wie** — sei spezifisch und prägnant." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "KI-en leser dette innholdet og bestemmer når det skal brukes. Beskriv **hva** KI-en skal gjøre og **hvordan** — vær spesifikk og konsis." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI:n läser detta innehåll och bestämmer när det ska tillämpas. Beskriv **vad** AI:n ska göra och **hur** — var specifik och koncis." + } + } + } + }, + "The AI wants to run the following command" : { + "comment" : "A description of the command that the AI wants to run.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI'en ønsker at køre følgende kommando" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die KI möchte den folgenden Befehl ausführen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "KI-en ønsker å kjøre følgende kommando" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "AI:n vill köra följande kommando" + } + } + } + }, + "This default prompt is always included to ensure accurate, helpful responses." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denne standardprompt er altid inkluderet for at sikre præcise, nyttige svar." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dieser Standard-Prompt ist immer enthalten, um genaue und hilfreiche Antworten sicherzustellen." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denne standardprompten er alltid inkludert for å sikre nøyaktige, nyttige svar." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denna standardprompt ingår alltid för att säkerställa korrekta, användbara svar." + } + } + } + }, + "This will permanently delete all email activity logs. This action cannot be undone." : { + "comment" : "A message displayed in an alert when the user is about to clear all email logs.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dette vil permanent slette alle e-mailaktivitetslogge. Denne handling kan ikke fortrydes." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dadurch werden alle E-Mail-Aktivitätsprotokolle dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dette vil slette alle e-postaktivitetslogger permanent. Denne handlingen kan ikke angres." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detta raderar alla e-postaktivitetsloggar permanent. Åtgärden kan inte ångras." + } + } + } + }, + "Token Distribution" : { + "comment" : "A label displayed under the token distribution bar.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Token-fordeling" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Token-Verteilung" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Token-fordeling" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Token-fördelning" + } + } + } + }, + "tokens" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokens" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokens" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokens" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokens" + } + } + } + }, + "Try adjusting your search or filters" : { + "comment" : "A description of the error that occurs when no models match the user's search.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prøv at justere din søgning eller dine filtre" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suche oder Filter anpassen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prøv å justere søket eller filtrene" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Försök att justera din sökning eller dina filter" + } + } + } + }, + "Type / in the input to see command suggestions" : { + "comment" : "A description of how to use the keyboard shortcut.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv / i inputfeltet for at se kommandoforslag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "/ im Eingabefeld eingeben, um Befehlsvorschläge zu sehen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv / i inndata for å se kommandoforslag" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv / i inmatningsfältet för att se kommandoförslag" + } + } + } + }, + "Type a message or / for commands..." : { + "comment" : "A placeholder text displayed in the input area when", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv en besked eller / for kommandoer..." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nachricht eingeben oder / für Befehle..." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv en melding eller / for kommandoer..." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skriv ett meddelande eller / för kommandon..." + } + } + } + }, + "Uncommitted changes: %@" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ikke-committede ændringer: %@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nicht übernommene Änderungen: %@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ulagrede endringer: %@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ej sparade ändringar: %@" + } + } + } + }, + "Update Available%@" : { + "comment" : "A button that opens a website with information about a new version of oAI. The argument is the version number of the new version.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdatering tilgængelig%@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Update verfügbar%@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oppdatering tilgjengelig%@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uppdatering tillgänglig%@" + } + } + } + }, + "Use @filename to attach files to your message" : { + "comment" : "A description of how to attach files to a message.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brug @filnavn for at vedhæfte filer til din besked" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "@dateiname verwenden, um Dateien an die Nachricht anzuhängen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bruk @filnavn for å legge ved filer i meldingen" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Använd @filnamn för att bifoga filer till ditt meddelande" + } + } + } + }, + "Use **{{input}}** in the template to insert whatever you type after the command." : { + "comment" : "A tooltip for the \"lightbulb\" icon in the shortcuts view", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brug **{{input}}** i skabelonen til at indsætte det, du skriver efter kommandoen." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwende **{{input}}** in der Vorlage, um einzufügen, was du nach dem Befehl tippst." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bruk **{{input}}** i malen for å sette inn det du skriver etter kommandoen." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Använd **{{input}}** i mallen för att infoga det du skriver efter kommandot." + } + } + } + }, + "Use **{{input}}** to insert whatever you type after the command." : { + "comment" : "A description of how to use the shortcut template.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brug **{{input}}** til at indsætte det, du skriver efter kommandoen." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwende **{{input}}**, um einzufügen, was du nach dem Befehl tippst." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bruk **{{input}}** for å sette inn det du skriver etter kommandoen." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Använd **{{input}}** för att infoga det du skriver efter kommandot." + } + } + } + }, + "Username + Password" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username + Password" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username + Password" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username + Password" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Username + Password" + } + } + } + }, + "v%@" : { + "comment" : "A label showing the current version of oAI.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "v%@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "v%@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "v%@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "v%@" + } + } + } + }, + "Version %@ (%@)" : { + "comment" : "A version number in the format \"Version 1.0 (1)\"", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version %1$@ (%2$@)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version %1$@ (%2$@)" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Version %1$@ (%2$@)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version %1$@ (%2$@)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version %1$@ (%2$@)" + } + } + } + }, + "View Email Log" : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis e-maillog" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-Mail-Protokoll anzeigen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vis e-postlogg" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visa e-postlogg" + } + } + } + }, + "When enabled, listing and searching skip gitignored files. Write operations always ignore .gitignore." : { + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Når aktiveret, springes gitignorerede filer over ved listning og søgning. Skriveoperationer ignorerer altid .gitignore." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wenn aktiviert, werden gitignorierte Dateien beim Auflisten und Suchen übersprungen. Schreiboperationen ignorieren immer .gitignore." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Når aktivert, hoppes gitignorerte filer over ved listing og søk. Skriveoperasjoner ignorerer alltid .gitignore." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "När aktiverat hoppar listning och sökning över gitignorerade filer. Skrivoperationer ignorerar alltid .gitignore." + } + } + } + }, + "Working directory:" : { + "comment" : "A label displayed under the working directory of a pending shell command.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arbejdsmappe:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arbeitsverzeichnis:" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arbeidsmappe:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arbetskatalog:" + } + } + } + }, + "You can import any SKILL.md file from skill0.io or write your own." : { + "comment" : "A description of how to use skills.", + "isCommentAutoGenerated" : true, + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du kan importere en hvilken som helst SKILL.md-fil fra skill0.io eller skrive din egen." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du kannst jede SKILL.md-Datei von skill0.io importieren oder deine eigene schreiben." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du kan importere en hvilken som helst SKILL.md-fil fra skill0.io eller skrive din egen." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du kan importera vilken SKILL.md-fil som helst från skill0.io eller skriva din egen." + } + } + } + } + }, + "version" : "1.1" +} \ No newline at end of file diff --git a/oAI/Models/Message.swift b/oAI/Models/Message.swift index 042d2fd..56c2e27 100644 --- a/oAI/Models/Message.swift +++ b/oAI/Models/Message.swift @@ -63,6 +63,9 @@ struct Message: Identifiable, Codable, Equatable { // Tool call details (not persisted — in-memory only for expandable display) var toolCalls: [ToolCallDetail]? = nil + // Reasoning/thinking content (not persisted — in-memory only) + var thinkingContent: String? = nil + init( id: UUID = UUID(), role: MessageRole, diff --git a/oAI/Models/ModelInfo.swift b/oAI/Models/ModelInfo.swift index 3bdfd11..7d0f049 100644 --- a/oAI/Models/ModelInfo.swift +++ b/oAI/Models/ModelInfo.swift @@ -45,6 +45,7 @@ struct ModelInfo: Identifiable, Codable, Hashable { let tools: Bool // Function calling let online: Bool // Web search var imageGeneration: Bool = false // Image output + var thinking: Bool = false // Reasoning/thinking tokens } struct Architecture: Codable, Hashable { diff --git a/oAI/Providers/AIProvider.swift b/oAI/Providers/AIProvider.swift index 6be43ca..da6d418 100644 --- a/oAI/Providers/AIProvider.swift +++ b/oAI/Providers/AIProvider.swift @@ -61,6 +61,11 @@ struct ProviderCapabilities: Codable { // MARK: - Chat Request +struct ReasoningConfig: Sendable { + let effort: String // "high", "medium", "low", "minimal" + let exclude: Bool // true = use reasoning internally, hide from response +} + struct ChatRequest { let messages: [Message] let model: String @@ -72,6 +77,7 @@ struct ChatRequest { let tools: [Tool]? let onlineMode: Bool let imageGeneration: Bool + let reasoning: ReasoningConfig? init( messages: [Message], @@ -83,7 +89,8 @@ struct ChatRequest { systemPrompt: String? = nil, tools: [Tool]? = nil, onlineMode: Bool = false, - imageGeneration: Bool = false + imageGeneration: Bool = false, + reasoning: ReasoningConfig? = nil ) { self.messages = messages self.model = model @@ -95,6 +102,7 @@ struct ChatRequest { self.tools = tools self.onlineMode = onlineMode self.imageGeneration = imageGeneration + self.reasoning = reasoning } } @@ -174,6 +182,14 @@ struct StreamChunk { let content: String? let role: String? let images: [Data]? + let thinking: String? // reasoning/thinking tokens (nil if excluded or not supported) + + init(content: String?, role: String?, images: [Data]? = nil, thinking: String? = nil) { + self.content = content + self.role = role + self.images = images + self.thinking = thinking + } } var deltaContent: String? { diff --git a/oAI/Providers/OpenRouterModels.swift b/oAI/Providers/OpenRouterModels.swift index 81a8e22..7ad8504 100644 --- a/oAI/Providers/OpenRouterModels.swift +++ b/oAI/Providers/OpenRouterModels.swift @@ -27,6 +27,16 @@ import Foundation // MARK: - API Request +struct ReasoningAPIConfig: Codable { + let effort: String + let exclude: Bool? + + enum CodingKeys: String, CodingKey { + case effort + case exclude + } +} + struct OpenRouterChatRequest: Codable { let model: String let messages: [APIMessage] @@ -37,6 +47,7 @@ struct OpenRouterChatRequest: Codable { let tools: [Tool]? let toolChoice: String? let modalities: [String]? + let reasoning: ReasoningAPIConfig? struct APIMessage: Codable { let role: String @@ -126,6 +137,7 @@ struct OpenRouterChatRequest: Codable { case tools case toolChoice = "tool_choice" case modalities + case reasoning } } @@ -205,7 +217,46 @@ struct OpenRouterStreamChunk: Codable { struct Delta: Codable { let role: String? let content: String? + let reasoning: String? + // images[] from top-level delta field (custom OpenRouter format) let images: [OpenRouterChatResponse.ImageOutput]? + // images extracted from content[] array (standard OpenAI content-block format) + let contentBlockImages: [OpenRouterChatResponse.ImageOutput] + + private struct ContentBlock: Codable { + let type: String + let text: String? + let imageUrl: OpenRouterChatResponse.ImageOutput.ImageURL? + enum CodingKeys: String, CodingKey { + case type, text + case imageUrl = "image_url" + } + } + + enum CodingKeys: String, CodingKey { + case role, content, images, reasoning + } + + init(from decoder: Decoder) throws { + let c = try decoder.container(keyedBy: CodingKeys.self) + role = try c.decodeIfPresent(String.self, forKey: .role) + images = try c.decodeIfPresent([OpenRouterChatResponse.ImageOutput].self, forKey: .images) + reasoning = try c.decodeIfPresent(String.self, forKey: .reasoning) + // content can be a plain String OR an array of content blocks + if let text = try? c.decodeIfPresent(String.self, forKey: .content) { + content = text + contentBlockImages = [] + } else if let blocks = try? c.decodeIfPresent([ContentBlock].self, forKey: .content) { + content = blocks.compactMap { $0.text }.joined().nonEmptyOrNil + contentBlockImages = blocks.compactMap { block in + guard block.type == "image_url", let url = block.imageUrl else { return nil } + return OpenRouterChatResponse.ImageOutput(imageUrl: url) + } + } else { + content = nil + contentBlockImages = [] + } + } } enum CodingKeys: String, CodingKey { diff --git a/oAI/Providers/OpenRouterProvider.swift b/oAI/Providers/OpenRouterProvider.swift index 294ea8f..a02906b 100644 --- a/oAI/Providers/OpenRouterProvider.swift +++ b/oAI/Providers/OpenRouterProvider.swift @@ -110,7 +110,8 @@ class OpenRouterProvider: AIProvider { return output.contains("image") } return false - }() + }(), + thinking: modelData.supportedParameters?.contains("reasoning") ?? false ), architecture: modelData.architecture.map { arch in ModelInfo.Architecture( @@ -368,6 +369,10 @@ class OpenRouterProvider: AIProvider { effectiveModel = request.model } + let reasoningConfig: ReasoningAPIConfig? = request.reasoning.map { + ReasoningAPIConfig(effort: $0.effort, exclude: $0.exclude ? true : nil) + } + return OpenRouterChatRequest( model: effectiveModel, messages: apiMessages, @@ -377,7 +382,8 @@ class OpenRouterProvider: AIProvider { topP: request.topP, tools: request.tools, toolChoice: request.tools != nil ? "auto" : nil, - modalities: request.imageGeneration ? ["text", "image"] : nil + modalities: request.imageGeneration ? ["text", "image"] : nil, + reasoning: reasoningConfig ) } @@ -416,7 +422,11 @@ class OpenRouterProvider: AIProvider { throw ProviderError.invalidResponse } - let images = choice.delta.images.flatMap { decodeImageOutputs($0) } + // Merge images from both sources: top-level `images` field and content-block images + let topLevelImages = choice.delta.images.flatMap { decodeImageOutputs($0) } ?? [] + let blockImages = decodeImageOutputs(choice.delta.contentBlockImages) ?? [] + let allImages = topLevelImages + blockImages + let images: [Data]? = allImages.isEmpty ? nil : allImages return StreamChunk( id: apiChunk.id, @@ -424,7 +434,8 @@ class OpenRouterProvider: AIProvider { delta: StreamChunk.Delta( content: choice.delta.content, role: choice.delta.role, - images: images + images: images, + thinking: choice.delta.reasoning ), finishReason: choice.finishReason, usage: apiChunk.usage.map { usage in diff --git a/oAI/Resources/oAI.help/Contents/Resources/en.lproj/index.html b/oAI/Resources/oAI.help/Contents/Resources/en.lproj/index.html index 751acec..31f5815 100644 --- a/oAI/Resources/oAI.help/Contents/Resources/en.lproj/index.html +++ b/oAI/Resources/oAI.help/Contents/Resources/en.lproj/index.html @@ -35,6 +35,9 @@
  • Email Handler (AI Assistant)
  • Shortcuts (Prompt Templates)
  • Agent Skills (SKILL.md)
  • +
  • Bash Execution
  • +
  • iCloud Backup
  • +
  • Reasoning / Thinking Tokens
  • Keyboard Shortcuts
  • Settings
  • System Prompts
  • @@ -45,7 +48,7 @@

    Getting Started

    -

    oAI is a powerful AI chat assistant that connects to multiple AI providers including OpenAI, Anthropic, OpenRouter, and local models via Ollama.

    +

    oAI is a powerful AI chat assistant that connects to multiple AI providers including OpenAI, Anthropic, OpenRouter, and local models via Ollama. The app is available in English, Norwegian Bokmål, Swedish, Danish, and German — it follows your macOS language preference automatically.

    Quick Start

    @@ -101,10 +104,32 @@
  • Click the model name in the header
  • +

    Searching & Filtering

    +

    Type in the search bar to filter by model name, ID, or description. Quick-filter buttons narrow results by capability:

    +
      +
    • 👁️ Vision — models that can read images
    • +
    • 🔧 Tools — models that can call tools (MCP, web search, bash)
    • +
    • 🌐 Online — models with built-in web search
    • +
    • 🎨 Image Gen — models that generate images
    • +
    • 🧠 Thinking — models that support reasoning / thinking tokens
    • +
    + +

    Sorting

    +

    Click the ↑↓ Sort button to sort the list by:

    +
      +
    • Default — provider order
    • +
    • Price: Low to High — cheapest per million tokens first
    • +
    • Price: High to Low — most capable/expensive first
    • +
    • Context: High to Low — largest context window first
    • +
    +

    Model Information

    -

    View details about any model:

    +

    Click the icon on any model row to open a full details sheet — context length, pricing, capabilities, and description — without selecting that model. You can also type:

    /info -

    Shows information about the currently selected model including context length, pricing, and capabilities.

    +

    Shows information about the currently selected model.

    + +

    Keyboard Navigation

    +

    Use / to move through the list, Return to select the highlighted model.

    Default Model

    Your selected model is automatically saved and will be restored when you restart the app.

    @@ -133,6 +158,9 @@

    If you're not satisfied with a response:

    /retry

    Resends your last message to generate a new response.

    + +

    Tool Call Inspection

    +

    When the AI uses tools (file access, web search, bash commands), a 🔧 Calling: … system message appears in the chat. Click it to expand an inline view showing each tool's input arguments and result as pretty-printed JSON. A spinner indicates a pending tool call; a green checkmark shows when it completes.

    @@ -408,10 +436,14 @@

    Setting Up MCP

    1. Enable MCP: /mcp on
    2. -
    3. Add a folder: /mcp add ~/Projects/myapp
    4. +
    5. Add a folder: /mcp add ~/Projects/myapp, click + Add Folder… in Settings → MCP, or drag a folder from Finder onto the allowed folders list
    6. Ask the AI questions about your files
    +
    + 💡 Drag & Drop: You can drag one or more folders directly from Finder onto the allowed folders area in Settings → MCP. The list highlights when you drag over it, and multiple folders can be dropped at once. +
    +

    What the AI Can Do

    Read permissions (always enabled when MCP is on):

      @@ -456,6 +488,20 @@ /save my-project-chat

      Saves all current messages under the specified name.

      +

      From the File menu you also have:

      +
        +
      • Save Chat (⌘S) — Re-saves under the current name, or prompts for a name if the conversation hasn't been saved yet.
      • +
      • Save Chat As… — Always prompts for a new name and creates a fresh copy, switching the session to that copy. Useful for branching a conversation.
      • +
      + +

      Renaming Conversations

      +

      In the Conversations list (⌘L):

      +
        +
      • Click the ✏️ pencil icon next to any conversation to rename it inline.
      • +
      • Swipe left on a conversation row to reveal Rename, Export, and Delete actions.
      • +
      +

      If the renamed conversation is currently open, the session name updates immediately so ⌘S saves under the new name.

      +

      Loading Conversations

      /load

      Opens a list of saved conversations. Select one to load.

      @@ -1155,6 +1201,165 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok + +
      +

      Bash Execution

      +

      When enabled, the AI can run shell commands directly on your Mac via /bin/zsh. This lets it read output, run scripts, compile code, check system state, and more — all without leaving the chat.

      + +
      + ⚠️ Security: Bash execution gives the AI the ability to run arbitrary commands on your machine. Only enable it when you need it, and use the approval prompt to review each command before it runs. +
      + +

      Enabling Bash Execution

      +
        +
      1. Press ⌘, to open Settings
      2. +
      3. Go to the MCP tab
      4. +
      5. Scroll to the Bash Execution section
      6. +
      7. Toggle Enable Bash Execution on
      8. +
      + +
      + Note: Bash execution is disabled by default and must be explicitly turned on. It is independent of MCP — you do not need MCP folders configured to use it. +
      + +

      Approval Prompt

      +

      When Require Approval is enabled (the default), a sheet appears before each command showing:

      +
        +
      • The full command to be run
      • +
      • The working directory
      • +
      • A security warning
      • +
      +

      You can then choose:

      +
        +
      • Allow Once — run this command and ask again next time
      • +
      • Allow for Session — skip the approval prompt for the rest of this chat
      • +
      • Deny — cancel the command; the AI is told it was rejected
      • +
      + +
      + 💡 Session Scope: "Allow for Session" resets when you start a new chat, switch models, or load a saved conversation. It is never permanent. +
      + +

      Configuration Options

      +
      +
      Working Directory
      +
      The directory commands run in (default: ~). Set to your project folder to avoid needing full paths.
      + +
      Timeout
      +
      Maximum seconds a command can run before being killed (default: 30 s). Increase for long-running builds.
      + +
      Require Approval
      +
      Show the approval sheet before each command (default: on). Disable only if you fully trust the AI's judgment for the current session.
      +
      + +

      Output Limits

      +

      To keep the context manageable, output is capped:

      +
        +
      • stdout: first 20,000 characters
      • +
      • stderr: first 5,000 characters
      • +
      +

      If output is truncated, the AI sees a note indicating how much was omitted.

      + +

      Example Usage

      +
      +

      You: "Run the test suite and tell me what's failing."

      +

      AI: (bash approval sheet appears with cd ~/myproject && swift test)

      +

      After Allow: "3 tests failed: testLogin, testSignup, testLogout. The error in testLogin is…"

      +
      +
      + + +
      +

      iCloud Backup

      +

      Back up and restore all your oAI settings with one click. Backups are saved to iCloud Drive so they're available on any Mac where you're signed in.

      + +
      + What is included: All settings and preferences — providers, model defaults, MCP configuration, appearance, advanced options, shortcuts, skills, and more.

      + What is excluded: API keys, passwords, and other credentials. These are intentionally left out for security and must be re-entered after restoring on a new machine. +
      + +

      Backing Up

      +
        +
      1. Press ⌘, to open Settings
      2. +
      3. Go to the Backup tab
      4. +
      5. Click Back Up Now
      6. +
      +

      The backup file is saved to ~/iCloud Drive/oAI/oai_backup.json. If iCloud Drive is not available, it falls back to your Downloads folder. The date of the last backup is shown in the tab.

      + +

      Restoring

      +
        +
      1. Open Settings → Backup tab
      2. +
      3. Click Restore from File…
      4. +
      5. Select your oai_backup.json file
      6. +
      7. Settings are applied immediately — no restart required
      8. +
      9. Re-enter your API keys in Settings → General
      10. +
      + +
      + 💡 New Mac Setup: Back up on your old Mac, sign in to iCloud on your new Mac, open oAI, restore from the backup file — and all your settings are restored in seconds. You only need to re-enter API keys. +
      + +

      Backup Format

      +

      Backups are plain JSON files (versioned for forward compatibility). You can inspect them in any text editor. The format is designed to be safe to share — no credentials are ever included.

      +
      + + +
      +

      Reasoning / Thinking Tokens

      +

      Some AI models can "think out loud" before giving their final answer — reasoning through the problem step by step. oAI streams this thinking content live and displays it in a collapsible block above the response.

      + +

      Supported Models

      +

      Reasoning is available on models that support extended thinking, including:

      +
        +
      • DeepSeek R1 and DeepSeek R1 variants (via OpenRouter)
      • +
      • Qwen thinking models (via OpenRouter)
      • +
      • Claude 3.7+ with extended thinking (via Anthropic or OpenRouter)
      • +
      • OpenAI o1, o3, and similar reasoning models
      • +
      • Grok reasoning models (via OpenRouter)
      • +
      +

      Look for the 🧠 badge in the model selector to identify thinking-capable models. Use the 🧠 Thinking quick-filter to show only reasoning models.

      + +

      Enabling Reasoning

      +
        +
      1. Press ⌘, to open Settings
      2. +
      3. Go to the General tab → Features section
      4. +
      5. Toggle Reasoning on
      6. +
      +

      Reasoning only activates when the selected model supports extended thinking (🧠 badge). The setting has no effect on standard models.

      + +

      Effort Level

      +

      The effort level controls how many tokens the model spends on reasoning (as a share of its total token budget):

      +
      +
      High (~80%)
      +
      Deep, methodical reasoning. Best for math, logic, multi-step planning, and complex coding tasks. More expensive.
      + +
      Medium (~50%)
      +
      Balanced reasoning. Good for most tasks that benefit from thinking (default).
      + +
      Low (~20%)
      +
      Light reasoning pass. Suitable for moderately complex questions without a large token budget.
      + +
      Minimal (~10%)
      +
      Minimal internal thinking — just a quick check before answering. Fastest and cheapest.
      +
      + +

      Hiding Reasoning Content

      +

      Enable Hide reasoning content in Settings → General → Features to keep reasoning internal. The model still thinks at the selected effort level, but the thinking block is not shown — only the final answer appears in chat.

      + +

      In-Chat Experience

      +

      When reasoning is enabled and a thinking-capable model responds:

      +
        +
      1. A 🧠 Thinking… block appears above the response and auto-expands
      2. +
      3. Reasoning content streams in live as the model thinks
      4. +
      5. When the final answer starts arriving, the thinking block collapses automatically
      6. +
      7. Click the block header at any time to manually expand or collapse it
      8. +
      + +
      + 💡 Tip: Medium effort is a great default. Switch to High for math problems, deep code analysis, or anything requiring careful step-by-step reasoning. +
      +
      +

      Keyboard Shortcuts

      @@ -1183,6 +1388,9 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok
      ⌘K
      Clear Chat
      +
      ⌘S
      +
      Save Chat (re-saves if already named, prompts for name otherwise)
      +
      ⇧⌘S
      Show Statistics
      @@ -1211,18 +1419,25 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

      Settings

      Customize oAI to your preferences. Press ⌘, to open Settings.

      -

      Providers Tab

      +

      General Tab

        -
      • Add and manage API keys for different providers
      • -
      • Switch between providers
      • -
      • Set default models
      • +
      • Add and manage API keys for all providers (OpenAI, Anthropic, OpenRouter, Google, Ollama)
      • +
      • Switch between providers and set the default model
      • +
      • Configure streaming, memory, online mode, and max tokens
      • +
      • Features: +
          +
        • Reasoning — enable thinking tokens, set effort level (High / Medium / Low / Minimal), optionally hide reasoning content from chat (see Reasoning / Thinking Tokens)
        • +
        +

      MCP Tab

        -
      • Manage folder access permissions
      • -
      • Enable/disable write operations
      • +
      • Enable/disable MCP file access
      • +
      • Add folders via the + Add Folder… button, or drag from Finder
      • +
      • Enable/disable write, delete, move, and bash execution permissions
      • Configure gitignore respect
      • +
      • Bash Execution — enable AI shell access, set working directory, timeout, and approval behaviour (see Bash Execution)

      Sync Tab

      @@ -1294,6 +1509,16 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok ⚠️ Security Note: All email credentials are encrypted with AES-256-GCM. For Gmail, use app-specific passwords (not your main password). Keep your subject identifier private to prevent abuse. +

      Paperless Tab Beta

      +

      Connect oAI to a self-hosted Paperless-NGX instance so the AI can search and read your document archive.

      +
        +
      • URL — Base URL of your Paperless instance (e.g. https://paperless.yourdomain.com)
      • +
      • API Token — Found in Paperless → Settings → API Tokens
      • +
      • Test Connection — Verify connectivity and display document count
      • +
      +

      When enabled, the AI can use paperless_search, paperless_get_document, paperless_list_tags, and other tools to interact with your document library.

      +

      Beta: Paperless integration is under active development. Some features may be incomplete or behave unexpectedly.

      +

      Shortcuts Tab

      Create and manage personal prompt template commands. See Shortcuts section for full details.

      @@ -1312,9 +1537,17 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok
    • Set maximum tokens (response length limit)
    • Adjust temperature (creativity vs focus)
    • Configure system prompts (see below)
    • -
    • Smart Context Selection - Intelligently select relevant messages to reduce token usage
    • -
    • Semantic Search - Enable AI-powered conversation search using embeddings
    • -
    • Progressive Summarization - Automatically summarize old portions of long conversations
    • +
    • Smart Context Selection — Intelligently select relevant messages to reduce token usage
    • +
    • Semantic Search — Enable AI-powered conversation search using embeddings
    • +
    • Progressive Summarization — Automatically summarize old portions of long conversations
    • +
    + +

    Backup Tab

    +

    One-click backup and restore of all settings to iCloud Drive. See iCloud Backup for full details.

    +
      +
    • Back Up Now — exports settings to ~/iCloud Drive/oAI/oai_backup.json
    • +
    • Restore from File… — imports settings from a backup file
    • +
    • API keys and credentials are excluded from backups and must be re-entered after restore
    diff --git a/oAI/Services/AnytypeMCPService.swift b/oAI/Services/AnytypeMCPService.swift index 0e6a29b..54c2f84 100644 --- a/oAI/Services/AnytypeMCPService.swift +++ b/oAI/Services/AnytypeMCPService.swift @@ -410,7 +410,7 @@ class AnytypeMCPService { updated[idx] = line let newMarkdown = updated.joined(separator: "\n") - let patchResult = try await request( + _ = try await request( endpoint: "/v1/spaces/\(spaceId)/objects/\(objectId)", method: "PATCH", body: ["markdown": newMarkdown] diff --git a/oAI/Services/IMAPClient.swift b/oAI/Services/IMAPClient.swift index 2c3b480..65b0c42 100644 --- a/oAI/Services/IMAPClient.swift +++ b/oAI/Services/IMAPClient.swift @@ -59,7 +59,7 @@ class IMAPClient { let hostName = self.host return try await withCheckedThrowingContinuation { continuation in - final class ResumeOnce { + final class ResumeOnce: @unchecked Sendable { var resumed = false let lock = NSLock() } diff --git a/oAI/Services/SMTPClient.swift b/oAI/Services/SMTPClient.swift index a3aeac1..fad4c97 100644 --- a/oAI/Services/SMTPClient.swift +++ b/oAI/Services/SMTPClient.swift @@ -54,7 +54,7 @@ class SMTPClient { let hostName = self.host try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - final class ResumeOnce { + final class ResumeOnce: @unchecked Sendable { var resumed = false let lock = NSLock() } @@ -92,7 +92,7 @@ class SMTPClient { let hostName = self.host try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - final class ResumeOnce { + final class ResumeOnce: @unchecked Sendable { var resumed = false let lock = NSLock() } @@ -251,7 +251,7 @@ class SMTPClient { self.connection = NWConnection(host: NWEndpoint.Host(host), port: NWEndpoint.Port(integerLiteral: port), using: params) try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - final class ResumeOnce { + final class ResumeOnce: @unchecked Sendable { var resumed = false let lock = NSLock() } diff --git a/oAI/Services/SettingsService.swift b/oAI/Services/SettingsService.swift index d702319..b8918bd 100644 --- a/oAI/Services/SettingsService.swift +++ b/oAI/Services/SettingsService.swift @@ -222,6 +222,34 @@ class SettingsService { } } + // MARK: - Reasoning Settings + + var reasoningEnabled: Bool { + get { cache["reasoningEnabled"] == "true" } + set { + cache["reasoningEnabled"] = String(newValue) + DatabaseService.shared.setSetting(key: "reasoningEnabled", value: String(newValue)) + } + } + + /// "high", "medium", "low", "minimal" — default "medium" + var reasoningEffort: String { + get { cache["reasoningEffort"] ?? "medium" } + set { + cache["reasoningEffort"] = newValue + DatabaseService.shared.setSetting(key: "reasoningEffort", value: newValue) + } + } + + /// When true, model reasons internally but thinking content is excluded from response + var reasoningExclude: Bool { + get { cache["reasoningExclude"] == "true" } + set { + cache["reasoningExclude"] = String(newValue) + DatabaseService.shared.setSetting(key: "reasoningExclude", value: String(newValue)) + } + } + // MARK: - Text Size Settings /// GUI text size (headers, labels, buttons) — default 13 diff --git a/oAI/Utilities/Extensions/String+Extensions.swift b/oAI/Utilities/Extensions/String+Extensions.swift index 69aed93..d4f2acc 100644 --- a/oAI/Utilities/Extensions/String+Extensions.swift +++ b/oAI/Utilities/Extensions/String+Extensions.swift @@ -93,6 +93,13 @@ extension String { return max(1, count / 4) } + // MARK: - Nil coalescing helpers + + /// Returns nil if the string is empty, otherwise self. + var nonEmptyOrNil: String? { + isEmpty ? nil : self + } + // MARK: - Truncation func truncated(to length: Int, trailing: String = "...") -> String { diff --git a/oAI/ViewModels/ChatViewModel.swift b/oAI/ViewModels/ChatViewModel.swift index 35474ce..9fc86fe 100644 --- a/oAI/ViewModels/ChatViewModel.swift +++ b/oAI/ViewModels/ChatViewModel.swift @@ -640,7 +640,7 @@ Don't narrate future actions ("Let me...") - just use the tools. case "/memory": if let arg = args.first?.lowercased() { memoryEnabled = arg == "on" - showSystemMessage("Memory \(memoryEnabled ? "enabled" : "disabled")") + showSystemMessage(memoryEnabled ? "Memory enabled" : "Memory disabled") } else { showSystemMessage("Usage: /memory on|off") } @@ -648,7 +648,7 @@ Don't narrate future actions ("Let me...") - just use the tools. case "/online": if let arg = args.first?.lowercased() { onlineMode = arg == "on" - showSystemMessage("Online mode \(onlineMode ? "enabled" : "disabled")") + showSystemMessage(onlineMode ? "Online mode enabled" : "Online mode disabled") } else { showSystemMessage("Usage: /online on|off") } @@ -881,6 +881,13 @@ Don't narrate future actions ("Let me...") - just use the tools. finalSystemPrompt = summariesText + "\n\n---\n\n" + effectiveSystemPrompt } + let reasoningConfig: ReasoningConfig? = { + guard settings.reasoningEnabled, + selectedModel?.capabilities.thinking == true, + !isImageGen else { return nil } + return ReasoningConfig(effort: settings.reasoningEffort, exclude: settings.reasoningExclude) + }() + let chatRequest = ChatRequest( messages: contextWindow.messages, model: modelId, @@ -891,7 +898,8 @@ Don't narrate future actions ("Let me...") - just use the tools. systemPrompt: finalSystemPrompt, tools: nil, onlineMode: onlineMode, - imageGeneration: isImageGen + imageGeneration: isImageGen, + reasoning: reasoningConfig ) if isImageGen { @@ -934,6 +942,8 @@ Don't narrate future actions ("Let me...") - just use the tools. } else { // Regular text: stream response var fullContent = "" + var fullThinking = "" + var collectedImages: [Data] = [] var totalTokens: ChatResponse.Usage? = nil var wasCancelled = false @@ -943,6 +953,13 @@ Don't narrate future actions ("Let me...") - just use the tools. break } + if let thinking = chunk.delta.thinking { + fullThinking += thinking + if let index = messages.firstIndex(where: { $0.id == messageId }) { + messages[index].thinkingContent = fullThinking + } + } + if let content = chunk.deltaContent { fullContent += content if let index = messages.firstIndex(where: { $0.id == messageId }) { @@ -950,6 +967,10 @@ Don't narrate future actions ("Let me...") - just use the tools. } } + if let images = chunk.delta.images { + collectedImages.append(contentsOf: images) + } + if let usage = chunk.usage { totalTokens = usage } @@ -968,6 +989,9 @@ Don't narrate future actions ("Let me...") - just use the tools. messages[index].isStreaming = false messages[index].responseTime = responseTime messages[index].wasInterrupted = wasCancelled + if !collectedImages.isEmpty { + messages[index].generatedImages = collectedImages + } if let usage = totalTokens { messages[index].tokens = usage.completionTokens @@ -1151,7 +1175,7 @@ Don't narrate future actions ("Let me...") - just use the tools. mcpEnabled = true settings.mcpEnabled = true mcpStatus = "MCP" - showSystemMessage("MCP enabled (\(mcp.allowedFolders.count) folder\(mcp.allowedFolders.count == 1 ? "" : "s") registered)") + showSystemMessage("MCP enabled (^[\(mcp.allowedFolders.count) folder](inflect: true) registered)") case "off": mcpEnabled = false @@ -1165,7 +1189,7 @@ Don't narrate future actions ("Let me...") - just use the tools. if let error = mcp.addFolder(path) { showSystemMessage("MCP: \(error)") } else { - showSystemMessage("MCP: Added folder — \(mcp.allowedFolders.count) folder\(mcp.allowedFolders.count == 1 ? "" : "s") registered") + showSystemMessage("MCP: Added folder — ^[\(mcp.allowedFolders.count) folder](inflect: true) registered") } } else { showSystemMessage("Usage: /mcp add ") @@ -1230,7 +1254,7 @@ Don't narrate future actions ("Let me...") - just use the tools. if settings.mcpCanCreateDirectories { perms.append("mkdir") } if settings.mcpCanMoveFiles { perms.append("move/copy") } let permStr = perms.isEmpty ? "read-only" : "read + \(perms.joined(separator: ", "))" - showSystemMessage("MCP: \(enabled), \(folders) folder\(folders == 1 ? "" : "s"), \(permStr), gitignore: \(settings.mcpRespectGitignore ? "on" : "off")") + showSystemMessage("MCP: \(enabled), ^[\(folders) folder](inflect: true), \(permStr), gitignore: \(settings.mcpRespectGitignore ? "on" : "off")") default: showSystemMessage("MCP subcommands: on, off, status, add, remove, list, write") @@ -1525,10 +1549,10 @@ Don't narrate future actions ("Let me...") - just use the tools. } @discardableResult - private func showSystemMessage(_ text: String) -> UUID { + private func showSystemMessage(_ text: String.LocalizationValue) -> UUID { let message = Message( role: .system, - content: text, + content: String(localized: text), tokens: nil, cost: nil, timestamp: Date(), diff --git a/oAI/Views/Main/ContentView.swift b/oAI/Views/Main/ContentView.swift index c664615..2de9db0 100644 --- a/oAI/Views/Main/ContentView.swift +++ b/oAI/Views/Main/ContentView.swift @@ -200,7 +200,7 @@ struct ContentView: View { // Helper view for toolbar labels struct ToolbarLabel: View { - let title: String + let title: LocalizedStringKey let systemImage: String let showLabels: Bool let scale: Image.Scale diff --git a/oAI/Views/Main/FooterView.swift b/oAI/Views/Main/FooterView.swift index 03a6192..090f200 100644 --- a/oAI/Views/Main/FooterView.swift +++ b/oAI/Views/Main/FooterView.swift @@ -47,19 +47,19 @@ struct FooterView: View { HStack(spacing: 16) { FooterItem( icon: "message", - label: "Messages", + label: "Messages:", value: "\(stats.messageCount)" ) FooterItem( icon: "chart.bar.xaxis", - label: "Tokens", + label: "Tokens:", value: "\(stats.totalTokens) (\(stats.totalInputTokens) in, \(stats.totalOutputTokens) out)" ) FooterItem( icon: "dollarsign.circle", - label: "Cost", + label: "Cost:", value: stats.totalCostDisplay ) @@ -134,7 +134,7 @@ struct SaveIndicator: View { return .secondary } - private var tooltip: String { + private var tooltip: LocalizedStringKey { if isModified { return "Click to re-save \"\(conversationName ?? "")\"" } if isSaved { return "Saved — no changes" } return "Not saved — use /save " @@ -162,7 +162,7 @@ struct SaveIndicator: View { struct FooterItem: View { let icon: String - let label: String + let label: LocalizedStringKey let value: String private let guiSize = SettingsService.shared.guiTextSize @@ -172,7 +172,7 @@ struct FooterItem: View { .font(.system(size: guiSize - 2)) .foregroundColor(.oaiSecondary) - Text(label + ":") + Text(label) .font(.system(size: guiSize - 2)) .foregroundColor(.oaiSecondary) @@ -187,9 +187,24 @@ struct SyncStatusFooter: View { private let gitSync = GitSyncService.shared private let settings = SettingsService.shared private let guiSize = SettingsService.shared.guiTextSize - @State private var syncText = "Not Synced" + + private enum SyncState { case off, notInitialized, ready, syncing, error, synced(Date) } + @State private var syncState: SyncState = .off @State private var syncColor: Color = .secondary + private var syncText: LocalizedStringKey { + switch syncState { + case .off: return "Sync: Off" + case .notInitialized: return "Sync: Not Initialized" + case .ready: return "Sync: Ready" + case .syncing: return "Syncing..." + case .error: return "Sync Error" + case .synced(let date): + let rel = RelativeDateTimeFormatter().localizedString(for: date, relativeTo: .now) + return "Last Sync: \(rel)" + } + } + var body: some View { HStack(spacing: 6) { Image(systemName: "arrow.triangle.2.circlepath") @@ -200,61 +215,27 @@ struct SyncStatusFooter: View { .font(.system(size: guiSize - 2, weight: .medium)) .foregroundColor(syncColor) } - .onAppear { - updateSyncStatus() - } - .onChange(of: gitSync.syncStatus.lastSyncTime) { - updateSyncStatus() - } - .onChange(of: gitSync.syncStatus.isCloned) { - updateSyncStatus() - } - .onChange(of: gitSync.lastSyncError) { - updateSyncStatus() - } - .onChange(of: gitSync.isSyncing) { - updateSyncStatus() - } - .onChange(of: settings.syncConfigured) { - updateSyncStatus() - } + .onAppear { updateSyncStatus() } + .onChange(of: gitSync.syncStatus.lastSyncTime) { updateSyncStatus() } + .onChange(of: gitSync.syncStatus.isCloned) { updateSyncStatus() } + .onChange(of: gitSync.lastSyncError) { updateSyncStatus() } + .onChange(of: gitSync.isSyncing) { updateSyncStatus() } + .onChange(of: settings.syncConfigured) { updateSyncStatus() } } private func updateSyncStatus() { if gitSync.lastSyncError != nil { - syncText = "Sync Error" - syncColor = .red + syncState = .error; syncColor = .red } else if gitSync.isSyncing { - syncText = "Syncing..." - syncColor = .orange + syncState = .syncing; syncColor = .orange } else if let lastSync = gitSync.syncStatus.lastSyncTime { - syncText = "Last Sync: \(timeAgo(lastSync))" - syncColor = .green + syncState = .synced(lastSync); syncColor = .green } else if gitSync.syncStatus.isCloned { - syncText = "Sync: Ready" - syncColor = .secondary + syncState = .ready; syncColor = .secondary } else if settings.syncConfigured { - syncText = "Sync: Not Initialized" - syncColor = .orange + syncState = .notInitialized; syncColor = .orange } else { - syncText = "Sync: Off" - syncColor = .secondary - } - } - - private func timeAgo(_ date: Date) -> String { - let seconds = Int(Date().timeIntervalSince(date)) - if seconds < 60 { - return "just now" - } else if seconds < 3600 { - let minutes = seconds / 60 - return "\(minutes)m ago" - } else if seconds < 86400 { - let hours = seconds / 3600 - return "\(hours)h ago" - } else { - let days = seconds / 86400 - return "\(days)d ago" + syncState = .off; syncColor = .secondary } } } diff --git a/oAI/Views/Main/HeaderView.swift b/oAI/Views/Main/HeaderView.swift index 6e1baa7..c118784 100644 --- a/oAI/Views/Main/HeaderView.swift +++ b/oAI/Views/Main/HeaderView.swift @@ -190,7 +190,7 @@ struct StatItem: View { struct StatusPill: View { let icon: String - let label: String + let label: LocalizedStringKey let color: Color var body: some View { @@ -210,9 +210,31 @@ struct StatusPill: View { struct SyncStatusPill: View { private let gitSync = GitSyncService.shared + + private enum SyncPillState { case notConfigured, syncing, error(String), synced(Date?) } + @State private var syncState: SyncPillState = .notConfigured @State private var syncColor: Color = .secondary - @State private var syncLabel: String = "Sync" - @State private var tooltipText: String = "" + + private var syncLabel: LocalizedStringKey { + switch syncState { + case .notConfigured: return "Sync" + case .syncing: return "Syncing" + case .error: return "Error" + case .synced: return "Synced" + } + } + + private var tooltipText: LocalizedStringKey { + switch syncState { + case .notConfigured: return "Sync not configured" + case .syncing: return "Syncing..." + case .error(let msg): return "Sync failed: \(msg)" + case .synced(let date): + guard let date else { return "Synced" } + let rel = RelativeDateTimeFormatter().localizedString(for: date, relativeTo: .now) + return "Last synced: \(rel)" + } + } var body: some View { HStack(spacing: 3) { @@ -227,58 +249,21 @@ struct SyncStatusPill: View { .padding(.vertical, 2) .background(syncColor.opacity(0.1), in: Capsule()) .help(tooltipText) - .onAppear { - updateState() - } - .onChange(of: gitSync.syncStatus) { - updateState() - } - .onChange(of: gitSync.isSyncing) { - updateState() - } - .onChange(of: gitSync.lastSyncError) { - updateState() - } + .onAppear { updateState() } + .onChange(of: gitSync.syncStatus) { updateState() } + .onChange(of: gitSync.isSyncing) { updateState() } + .onChange(of: gitSync.lastSyncError) { updateState() } } private func updateState() { - // Determine sync state if let error = gitSync.lastSyncError { - syncColor = .red - syncLabel = "Error" - tooltipText = "Sync failed: \(error)" + syncState = .error(error); syncColor = .red } else if gitSync.isSyncing { - syncColor = .orange - syncLabel = "Syncing" - tooltipText = "Syncing..." + syncState = .syncing; syncColor = .orange } else if gitSync.syncStatus.isCloned { - syncColor = .green - syncLabel = "Synced" - if let lastSync = gitSync.syncStatus.lastSyncTime { - tooltipText = "Last synced: \(timeAgo(lastSync))" - } else { - tooltipText = "Synced" - } + syncState = .synced(gitSync.syncStatus.lastSyncTime); syncColor = .green } else { - syncColor = .secondary - syncLabel = "Sync" - tooltipText = "Sync not configured" - } - } - - private func timeAgo(_ date: Date) -> String { - let seconds = Int(Date().timeIntervalSince(date)) - if seconds < 60 { - return "just now" - } else if seconds < 3600 { - let minutes = seconds / 60 - return "\(minutes)m ago" - } else if seconds < 86400 { - let hours = seconds / 3600 - return "\(hours)h ago" - } else { - let days = seconds / 86400 - return "\(days)d ago" + syncState = .notConfigured; syncColor = .secondary } } } diff --git a/oAI/Views/Main/InputBar.swift b/oAI/Views/Main/InputBar.swift index 3d15dc3..067b21a 100644 --- a/oAI/Views/Main/InputBar.swift +++ b/oAI/Views/Main/InputBar.swift @@ -271,7 +271,7 @@ struct CommandSuggestionsView: View { let selectedIndex: Int let onSelect: (String) -> Void - static let builtInCommands: [(command: String, description: String)] = [ + static let builtInCommands: [(command: String, description: LocalizedStringKey)] = [ ("/help", "Show help and available commands"), ("/history", "View command history"), ("/model", "Select AI model"), @@ -302,19 +302,19 @@ struct CommandSuggestionsView: View { ("/mcp write off", "Disable MCP write permissions"), ] - static func allCommands() -> [(command: String, description: String)] { + static func allCommands() -> [(command: String, description: LocalizedStringKey)] { let shortcuts = SettingsService.shared.userShortcuts.map { s in - (s.command, "⚡ \(s.description)") + (s.command, LocalizedStringKey("⚡ \(s.description)")) } return builtInCommands + shortcuts } - static func filteredCommands(for searchText: String) -> [(command: String, description: String)] { + static func filteredCommands(for searchText: String) -> [(command: String, description: LocalizedStringKey)] { let search = searchText.lowercased() return allCommands().filter { $0.command.contains(search) || search == "/" } } - private var suggestions: [(command: String, description: String)] { + private var suggestions: [(command: String, description: LocalizedStringKey)] { Self.filteredCommands(for: searchText) } diff --git a/oAI/Views/Main/MessageRow.swift b/oAI/Views/Main/MessageRow.swift index 979da58..16f554c 100644 --- a/oAI/Views/Main/MessageRow.swift +++ b/oAI/Views/Main/MessageRow.swift @@ -34,6 +34,7 @@ struct MessageRow: View { private let settings = SettingsService.shared @State private var isExpanded = false + @State private var isThinkingExpanded = true // auto-expand while streaming, collapse after #if os(macOS) @State private var isHovering = false @@ -107,6 +108,27 @@ struct MessageRow: View { .foregroundColor(.oaiSecondary) } + // Thinking / reasoning block (collapsible) + if let thinking = message.thinkingContent, !thinking.isEmpty { + thinkingBlock(thinking) + .onChange(of: message.content) { _, newContent in + // Auto-collapse when response content starts arriving + if !newContent.isEmpty && isThinkingExpanded && !message.isStreaming { + withAnimation(.easeInOut(duration: 0.2)) { + isThinkingExpanded = false + } + } + } + .onChange(of: message.isStreaming) { _, streaming in + // Collapse when streaming finishes + if !streaming && !message.content.isEmpty { + withAnimation(.easeInOut(duration: 0.3)) { + isThinkingExpanded = false + } + } + } + } + // Content if !message.content.isEmpty { messageContent @@ -186,6 +208,64 @@ struct MessageRow: View { // Close standardMessageLayout - the above closing braces close it // The body: some View now handles the split between compact and standard + // MARK: - Thinking Block + + @ViewBuilder + private func thinkingBlock(_ thinking: String) -> some View { + VStack(alignment: .leading, spacing: 0) { + // Header button + Button(action: { + withAnimation(.easeInOut(duration: 0.18)) { isThinkingExpanded.toggle() } + }) { + HStack(spacing: 6) { + if message.isStreaming && message.content.isEmpty { + ProgressView() + .scaleEffect(0.5) + .frame(width: 12, height: 12) + Text("Thinking…") + .font(.system(size: 11)) + .foregroundStyle(.secondary) + } else { + Image(systemName: "brain") + .font(.system(size: 11)) + .foregroundStyle(.secondary) + Text("Reasoning") + .font(.system(size: 11)) + .foregroundStyle(.secondary) + } + Spacer() + Image(systemName: isThinkingExpanded ? "chevron.up" : "chevron.down") + .font(.system(size: 9, weight: .medium)) + .foregroundStyle(.secondary.opacity(0.5)) + } + .padding(.horizontal, 10) + .padding(.vertical, 6) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + + if isThinkingExpanded { + Divider() + .padding(.horizontal, 6) + ScrollView { + Text(thinking) + .font(.system(size: 12)) + .foregroundStyle(.secondary) + .textSelection(.enabled) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(10) + } + .frame(maxHeight: 220) + } + } + .background(Color.secondary.opacity(0.07)) + .cornerRadius(6) + .overlay( + RoundedRectangle(cornerRadius: 6) + .strokeBorder(Color.secondary.opacity(0.15), lineWidth: 1) + ) + } + // MARK: - Compact System Message @ViewBuilder diff --git a/oAI/Views/Screens/AgentSkillsView.swift b/oAI/Views/Screens/AgentSkillsView.swift index 03797c3..cdfcc05 100644 --- a/oAI/Views/Screens/AgentSkillsView.swift +++ b/oAI/Views/Screens/AgentSkillsView.swift @@ -38,7 +38,7 @@ struct AgentSkillsView: View { @Bindable private var settings = SettingsService.shared @State private var editContext: SkillEditContext? = nil - @State private var statusMessage: String? = nil + @State private var statusMessage: LocalizedStringKey? = nil private var activeCount: Int { settings.agentSkills.filter { $0.isActive }.count } @@ -166,7 +166,7 @@ struct AgentSkillsView: View { if importMarkdown(url) { imported += 1 } } } - if imported > 0 { show("Imported \(imported) skill\(imported == 1 ? "" : "s")") } + if imported > 0 { show("^[\(imported) skill](inflect: true) imported") } } @discardableResult @@ -256,7 +256,7 @@ struct AgentSkillsView: View { } exported += 1 } - show("Exported \(exported) skill\(exported == 1 ? "" : "s") to Downloads") + show("^[\(exported) skill](inflect: true) exported to Downloads") } private func exportOne(_ skill: AgentSkill) { @@ -335,7 +335,7 @@ struct AgentSkillsView: View { return "" } - private func show(_ text: String) { + private func show(_ text: LocalizedStringKey) { statusMessage = text Task { try? await Task.sleep(for: .seconds(3)); statusMessage = nil } } @@ -380,7 +380,7 @@ private struct AgentSkillRow: View { // File count badge if fileCount > 0 { - Label("\(fileCount) file\(fileCount == 1 ? "" : "s")", systemImage: "doc") + Label("^[\(fileCount) file](inflect: true)", systemImage: "doc") .font(.caption2).foregroundStyle(.secondary) .padding(.horizontal, 6).padding(.vertical, 3) .background(.blue.opacity(0.1), in: Capsule()) @@ -411,7 +411,7 @@ struct AgentSkillsTabContent: View { @Bindable private var settings = SettingsService.shared @State private var editContext: SkillEditContext? = nil - @State private var statusMessage: String? = nil + @State private var statusMessage: LocalizedStringKey? = nil private var activeCount: Int { settings.agentSkills.filter { $0.isActive }.count } @@ -431,7 +431,7 @@ struct AgentSkillsTabContent: View { .background(.purple.opacity(0.07), in: RoundedRectangle(cornerRadius: 8)) if activeCount > 0 { - Label("\(activeCount) skill\(activeCount == 1 ? "" : "s") active — appended to every system prompt", systemImage: "checkmark.circle.fill") + Label("^[\(activeCount) skill](inflect: true) active — appended to every system prompt", systemImage: "checkmark.circle.fill") .font(.caption).foregroundStyle(.green) } @@ -516,7 +516,7 @@ struct AgentSkillsTabContent: View { if importMarkdown(url) { imported += 1 } } } - if imported > 0 { show("Imported \(imported) skill\(imported == 1 ? "" : "s")") } + if imported > 0 { show("^[\(imported) skill](inflect: true) imported") } } @discardableResult @@ -602,7 +602,7 @@ struct AgentSkillsTabContent: View { } exported += 1 } - show("Exported \(exported) skill\(exported == 1 ? "" : "s") to Downloads") + show("^[\(exported) skill](inflect: true) exported to Downloads") } private func exportOne(_ skill: AgentSkill) { @@ -671,7 +671,7 @@ struct AgentSkillsTabContent: View { return "" } - private func show(_ text: String) { + private func show(_ text: LocalizedStringKey) { statusMessage = text Task { try? await Task.sleep(for: .seconds(3)); statusMessage = nil } } diff --git a/oAI/Views/Screens/CreditsView.swift b/oAI/Views/Screens/CreditsView.swift index 2ceb49e..b60bdd4 100644 --- a/oAI/Views/Screens/CreditsView.swift +++ b/oAI/Views/Screens/CreditsView.swift @@ -155,7 +155,7 @@ struct CreditsView: View { } struct CreditRow: View { - let label: String + let label: LocalizedStringKey let value: String var highlight: Bool = false diff --git a/oAI/Views/Screens/EmailLogView.swift b/oAI/Views/Screens/EmailLogView.swift index aece4b3..da93730 100644 --- a/oAI/Views/Screens/EmailLogView.swift +++ b/oAI/Views/Screens/EmailLogView.swift @@ -190,7 +190,7 @@ struct EmailLogView: View { private var bottomActions: some View { HStack { - Text("\(logs.count) \(logs.count == 1 ? "entry" : "entries")") + Text("^[\(logs.count) entry](inflect: true)") .font(.system(size: 13)) .foregroundColor(.secondary) @@ -327,7 +327,7 @@ struct EmailLogRow: View { // MARK: - Stat Item struct EmailStatItem: View { - let title: String + let title: LocalizedStringKey let value: String let color: Color diff --git a/oAI/Views/Screens/HelpView.swift b/oAI/Views/Screens/HelpView.swift index e0b8304..c5b8231 100644 --- a/oAI/Views/Screens/HelpView.swift +++ b/oAI/Views/Screens/HelpView.swift @@ -38,7 +38,7 @@ struct CommandDetail: Identifiable { struct CommandCategory: Identifiable { let id = UUID() - let name: String + let name: LocalizedStringKey let icon: String let commands: [CommandDetail] } @@ -358,7 +358,7 @@ struct HelpView: View { .padding(.vertical, 4) .background(.quaternary, in: RoundedRectangle(cornerRadius: 5)) Spacer() - Text(shortcut.description) + Text(LocalizedStringKey(shortcut.description)) .font(.callout) .foregroundStyle(.secondary) } @@ -463,7 +463,7 @@ private struct CommandRow: View { .font(.system(size: 13, weight: .medium, design: .monospaced)) .foregroundStyle(.primary) - Text(command.brief) + Text(LocalizedStringKey(command.brief)) .font(.callout) .foregroundStyle(.secondary) .lineLimit(1) @@ -493,14 +493,14 @@ private struct CommandRow: View { // Expanded detail if isExpanded { VStack(alignment: .leading, spacing: 10) { - Text(command.detail) + Text(LocalizedStringKey(command.detail)) .font(.callout) .foregroundStyle(.primary) .fixedSize(horizontal: false, vertical: true) if !command.examples.isEmpty { VStack(alignment: .leading, spacing: 6) { - Text(command.examples.count == 1 ? "Example" : "Examples") + Text(command.examples.count == 1 ? "Example" as LocalizedStringKey : "Examples") .font(.caption) .foregroundStyle(.secondary) .fontWeight(.medium) diff --git a/oAI/Views/Screens/ModelInfoView.swift b/oAI/Views/Screens/ModelInfoView.swift index 21502e5..fc342bd 100644 --- a/oAI/Views/Screens/ModelInfoView.swift +++ b/oAI/Views/Screens/ModelInfoView.swift @@ -132,6 +132,7 @@ struct ModelInfoView: View { capabilityBadge(icon: "wrench.fill", label: "Tools", active: model.capabilities.tools) capabilityBadge(icon: "globe", label: "Online", active: model.capabilities.online) capabilityBadge(icon: "photo.fill", label: "Image Gen", active: model.capabilities.imageGeneration) + capabilityBadge(icon: "brain", label: "Thinking", active: model.capabilities.thinking) } // Architecture (if available) @@ -172,14 +173,14 @@ struct ModelInfoView: View { // MARK: - Layout Helpers - private func sectionHeader(_ title: String) -> some View { + private func sectionHeader(_ title: LocalizedStringKey) -> some View { Text(title) .font(.system(size: 13, weight: .semibold)) .foregroundStyle(.secondary) .textCase(.uppercase) } - private func infoRow(_ label: String, _ value: String) -> some View { + private func infoRow(_ label: LocalizedStringKey, _ value: String) -> some View { HStack { Text(label) .font(.body) @@ -193,7 +194,7 @@ struct ModelInfoView: View { } @ViewBuilder - private func costExample(label: String, inputTokens: Int) -> some View { + private func costExample(label: LocalizedStringKey, inputTokens: Int) -> some View { let cost = (Double(inputTokens) * model.pricing.prompt / 1_000_000) + (Double(inputTokens) * model.pricing.completion / 1_000_000) VStack(spacing: 2) { @@ -211,7 +212,7 @@ struct ModelInfoView: View { } @ViewBuilder - private func capabilityBadge(icon: String, label: String, active: Bool) -> some View { + private func capabilityBadge(icon: String, label: LocalizedStringKey, active: Bool) -> some View { VStack(spacing: 4) { Image(systemName: icon) .font(.title3) diff --git a/oAI/Views/Screens/ModelSelectorView.swift b/oAI/Views/Screens/ModelSelectorView.swift index 41b0d3b..c3a321d 100644 --- a/oAI/Views/Screens/ModelSelectorView.swift +++ b/oAI/Views/Screens/ModelSelectorView.swift @@ -36,20 +36,37 @@ struct ModelSelectorView: View { @State private var filterTools = false @State private var filterOnline = false @State private var filterImageGen = false + @State private var filterThinking = false @State private var keyboardIndex: Int = -1 + @State private var sortOrder: ModelSortOrder = .default + @State private var selectedInfoModel: ModelInfo? = nil private var filteredModels: [ModelInfo] { - models.filter { model in + let q = searchText.lowercased() + let filtered = models.filter { model in let matchesSearch = searchText.isEmpty || - model.name.lowercased().contains(searchText.lowercased()) || - model.id.lowercased().contains(searchText.lowercased()) + model.name.lowercased().contains(q) || + model.id.lowercased().contains(q) || + model.description?.lowercased().contains(q) == true let matchesVision = !filterVision || model.capabilities.vision let matchesTools = !filterTools || model.capabilities.tools let matchesOnline = !filterOnline || model.capabilities.online let matchesImageGen = !filterImageGen || model.capabilities.imageGeneration + let matchesThinking = !filterThinking || model.capabilities.thinking - return matchesSearch && matchesVision && matchesTools && matchesOnline && matchesImageGen + return matchesSearch && matchesVision && matchesTools && matchesOnline && matchesImageGen && matchesThinking + } + + switch sortOrder { + case .default: + return filtered + case .priceLowHigh: + return filtered.sorted { $0.pricing.prompt < $1.pricing.prompt } + case .priceHighLow: + return filtered.sorted { $0.pricing.prompt > $1.pricing.prompt } + case .contextHighLow: + return filtered.sorted { $0.contextLength > $1.contextLength } } } @@ -61,16 +78,47 @@ struct ModelSelectorView: View { .textFieldStyle(.roundedBorder) .padding() .onChange(of: searchText) { - // Reset keyboard index when search changes keyboardIndex = -1 } - // Filters + // Filters + Sort HStack(spacing: 12) { FilterToggle(isOn: $filterVision, icon: "\u{1F441}\u{FE0F}", label: "Vision") FilterToggle(isOn: $filterTools, icon: "\u{1F527}", label: "Tools") FilterToggle(isOn: $filterOnline, icon: "\u{1F310}", label: "Online") FilterToggle(isOn: $filterImageGen, icon: "\u{1F3A8}", label: "Image Gen") + FilterToggle(isOn: $filterThinking, icon: "\u{1F9E0}", label: "Thinking") + + Spacer() + + // Sort menu + Menu { + ForEach(ModelSortOrder.allCases, id: \.rawValue) { order in + Button { + sortOrder = order + keyboardIndex = -1 + } label: { + if sortOrder == order { + Label(order.label, systemImage: "checkmark") + } else { + Text(order.label) + } + } + } + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.up.arrow.down") + Text("Sort") + } + .font(.caption) + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background(sortOrder != .default ? Color.blue.opacity(0.2) : Color.gray.opacity(0.1)) + .foregroundColor(sortOrder != .default ? .blue : .secondary) + .cornerRadius(6) + } + .menuStyle(.borderlessButton) + .fixedSize() } .padding(.horizontal) .padding(.bottom, 12) @@ -91,13 +139,11 @@ struct ModelSelectorView: View { ModelRowView( model: model, isSelected: model.id == selectedModel?.id, - isKeyboardHighlighted: index == keyboardIndex + isKeyboardHighlighted: index == keyboardIndex, + onSelect: { onSelect(model) }, + onInfo: { selectedInfoModel = model } ) .id(model.id) - .contentShape(Rectangle()) - .onTapGesture { - onSelect(model) - } } .listStyle(.plain) .onChange(of: keyboardIndex) { _, newIndex in @@ -143,20 +189,42 @@ struct ModelSelectorView: View { } } .onAppear { - // Initialize keyboard index to current selection if let selected = selectedModel, let index = filteredModels.firstIndex(where: { $0.id == selected.id }) { keyboardIndex = index } } + .sheet(item: $selectedInfoModel) { model in + ModelInfoView(model: model) + } } } } +// MARK: - Sort Order + +enum ModelSortOrder: String, CaseIterable { + case `default` + case priceLowHigh + case priceHighLow + case contextHighLow + + var label: LocalizedStringKey { + switch self { + case .default: return "Default" + case .priceLowHigh: return "Price: Low to High" + case .priceHighLow: return "Price: High to Low" + case .contextHighLow: return "Context: High to Low" + } + } +} + +// MARK: - Filter Toggle + struct FilterToggle: View { @Binding var isOn: Bool let icon: String - let label: String + let label: LocalizedStringKey var body: some View { Button(action: { isOn.toggle() }) { @@ -175,51 +243,74 @@ struct FilterToggle: View { } } +// MARK: - Model Row + struct ModelRowView: View { let model: ModelInfo let isSelected: Bool var isKeyboardHighlighted: Bool = false + let onSelect: () -> Void + let onInfo: () -> Void var body: some View { - VStack(alignment: .leading, spacing: 6) { - HStack { - Text(model.name) - .font(.headline) - .foregroundColor(isSelected ? .blue : .primary) + HStack(alignment: .top, spacing: 8) { + // Selectable main content + VStack(alignment: .leading, spacing: 6) { + HStack { + Text(model.name) + .font(.headline) + .foregroundColor(isSelected ? .blue : .primary) - Spacer() + if isSelected { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.blue) + } + } - // Capabilities + Text(model.id) + .font(.caption) + .foregroundColor(.secondary) + + if let description = model.description { + Text(description) + .font(.caption) + .foregroundColor(.secondary) + .lineLimit(2) + } + + HStack(spacing: 12) { + Label(model.contextLengthDisplay, systemImage: "text.alignleft") + Label(model.promptPriceDisplay + "/1M", systemImage: "dollarsign.circle") + } + .font(.caption2) + .foregroundColor(.secondary) + } + .frame(maxWidth: .infinity, alignment: .leading) + .contentShape(Rectangle()) + .onTapGesture { onSelect() } + + // Right side: capabilities + info button + VStack(alignment: .trailing, spacing: 6) { + // Capability icons HStack(spacing: 4) { if model.capabilities.vision { Text("\u{1F441}\u{FE0F}").font(.caption) } if model.capabilities.tools { Text("\u{1F527}").font(.caption) } if model.capabilities.online { Text("\u{1F310}").font(.caption) } if model.capabilities.imageGeneration { Text("\u{1F3A8}").font(.caption) } + if model.capabilities.thinking { Text("\u{1F9E0}").font(.caption) } } - if isSelected { - Image(systemName: "checkmark.circle.fill") - .foregroundColor(.blue) + // Info button + Button(action: onInfo) { + Image(systemName: "info.circle") + .font(.caption) + .foregroundColor(.secondary) + .frame(width: 20, height: 20) } + .buttonStyle(.plain) + .help("Show model info") } - - Text(model.id) - .font(.caption) - .foregroundColor(.secondary) - - if let description = model.description { - Text(description) - .font(.caption) - .foregroundColor(.secondary) - .lineLimit(2) - } - - HStack(spacing: 12) { - Label(model.contextLengthDisplay, systemImage: "text.alignleft") - Label(model.promptPriceDisplay + "/1M", systemImage: "dollarsign.circle") - } - .font(.caption2) - .foregroundColor(.secondary) + .padding(.top, 2) } .padding(.vertical, 6) .padding(.horizontal, isKeyboardHighlighted ? 4 : 0) diff --git a/oAI/Views/Screens/SettingsView.swift b/oAI/Views/Screens/SettingsView.swift index f196a81..8587285 100644 --- a/oAI/Views/Screens/SettingsView.swift +++ b/oAI/Views/Screens/SettingsView.swift @@ -142,7 +142,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak tabButton(7, icon: "brain", label: "Skills") tabButton(4, icon: "arrow.triangle.2.circlepath", label: "Sync") tabButton(5, icon: "envelope", label: "Email") - tabButton(8, icon: "doc.text", label: "Paperless") + tabButton(8, icon: "doc.text", label: "Paperless", beta: true) tabButton(9, icon: "icloud.and.arrow.up", label: "Backup") } .padding(.horizontal, 16) @@ -283,10 +283,42 @@ It's better to admit "I need more information" or "I cannot do that" than to fak .toggleStyle(.switch) } rowDivider() - row("MCP (File Access)") { - Toggle("", isOn: $settingsService.mcpEnabled) + row("Reasoning (Thinking)") { + Toggle("", isOn: $settingsService.reasoningEnabled) .toggleStyle(.switch) } + if settingsService.reasoningEnabled { + rowDivider() + row("Reasoning Effort") { + Picker("", selection: $settingsService.reasoningEffort) { + Text("High (~80%)").tag("high") + Text("Medium (~50%)").tag("medium") + Text("Low (~20%)").tag("low") + Text("Minimal (~10%)").tag("minimal") + } + .labelsHidden() + .fixedSize() + } + VStack(alignment: .leading, spacing: 2) { + Text(reasoningEffortDescription) + .font(.caption) + .foregroundStyle(.secondary) + } + .padding(.horizontal, 12) + .padding(.bottom, 4) + rowDivider() + row("Hide Reasoning in Response") { + Toggle("", isOn: $settingsService.reasoningExclude) + .toggleStyle(.switch) + } + VStack(alignment: .leading, spacing: 2) { + Text("Model thinks internally but reasoning is not shown in chat") + .font(.caption) + .foregroundStyle(.secondary) + } + .padding(.horizontal, 12) + .padding(.bottom, 4) + } } } @@ -2050,13 +2082,25 @@ It's better to admit "I need more information" or "I cannot do that" than to fak // MARK: - Tab Navigation - private func tabButton(_ tag: Int, icon: String, label: String) -> some View { + private func tabButton(_ tag: Int, icon: String, label: LocalizedStringKey, beta: Bool = false) -> some View { Button(action: { selectedTab = tag }) { VStack(spacing: 3) { - Image(systemName: icon) - .font(.system(size: 22)) - .frame(height: 28) - .foregroundStyle(selectedTab == tag ? .blue : .secondary) + ZStack(alignment: .topTrailing) { + Image(systemName: icon) + .font(.system(size: 22)) + .frame(height: 28) + .foregroundStyle(selectedTab == tag ? .blue : .secondary) + if beta { + Text("β") + .font(.system(size: 8, weight: .bold)) + .foregroundStyle(.white) + .padding(.horizontal, 3) + .padding(.vertical, 1) + .background(Color.orange) + .clipShape(Capsule()) + .offset(x: 6, y: -2) + } + } Text(label) .font(.system(size: 11)) .foregroundStyle(selectedTab == tag ? .blue : .secondary) @@ -2070,7 +2114,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak .buttonStyle(.plain) } - private func tabTitle(_ tag: Int) -> String { + private func tabTitle(_ tag: Int) -> LocalizedStringKey { switch tag { case 0: return "General" case 1: return "MCP" @@ -2086,13 +2130,23 @@ It's better to admit "I need more information" or "I cannot do that" than to fak } } + // MARK: - Reasoning Helpers + + private var reasoningEffortDescription: LocalizedStringKey { + switch settingsService.reasoningEffort { + case "high": return "Uses ~80% of max tokens for reasoning — best for hard problems" + case "medium": return "Uses ~50% of max tokens for reasoning — balanced default" + case "low": return "Uses ~20% of max tokens for reasoning — faster, cheaper" + case "minimal": return "Uses ~10% of max tokens for reasoning — lightest thinking" + default: return "Uses ~50% of max tokens for reasoning — balanced default" + } + } + // MARK: - Layout Helpers - private func row(_ label: String, @ViewBuilder content: () -> Content) -> some View { + private func row(_ label: LocalizedStringKey, @ViewBuilder content: () -> Content) -> some View { HStack(alignment: .center, spacing: 12) { - if !label.isEmpty { - Text(label).font(.system(size: 14)) - } + Text(label).font(.system(size: 14)) Spacer() content() } @@ -2100,7 +2154,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak .padding(.vertical, 10) } - private func sectionHeader(_ title: String) -> some View { + private func sectionHeader(_ title: LocalizedStringKey) -> some View { Text(title) .font(.system(size: 12, weight: .semibold)) .foregroundStyle(.secondary) @@ -2205,7 +2259,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak return .green } - private var syncStatusText: String { + private var syncStatusText: LocalizedStringKey { guard settingsService.syncEnabled else { return "Disabled" } guard settingsService.syncConfigured else { return "Not configured" } guard gitSync.syncStatus.isCloned else { return "Not cloned" } @@ -2318,19 +2372,9 @@ It's better to admit "I need more information" or "I cannot do that" than to fak } private func timeAgo(_ date: Date) -> String { - let seconds = Int(Date().timeIntervalSince(date)) - if seconds < 60 { - return "just now" - } else if seconds < 3600 { - let minutes = seconds / 60 - return "\(minutes) minute\(minutes == 1 ? "" : "s") ago" - } else if seconds < 86400 { - let hours = seconds / 3600 - return "\(hours) hour\(hours == 1 ? "" : "s") ago" - } else { - let days = seconds / 86400 - return "\(days) day\(days == 1 ? "" : "s") ago" - } + let formatter = RelativeDateTimeFormatter() + formatter.unitsStyle = .full + return formatter.localizedString(for: date, relativeTo: .now) } } diff --git a/oAI/Views/Screens/StatsView.swift b/oAI/Views/Screens/StatsView.swift index f4c7241..e3ad090 100644 --- a/oAI/Views/Screens/StatsView.swift +++ b/oAI/Views/Screens/StatsView.swift @@ -120,7 +120,7 @@ struct StatsView: View { } struct StatRow: View { - let label: String + let label: LocalizedStringKey let value: String var body: some View { @@ -137,7 +137,7 @@ struct StatRow: View { struct CapabilityBadge: View { let icon: String - let label: String + let label: LocalizedStringKey var body: some View { HStack(spacing: 2) { diff --git a/oAI/oAI-InfoPlist.xcstrings b/oAI/oAI-InfoPlist.xcstrings new file mode 100644 index 0000000..62030c1 --- /dev/null +++ b/oAI/oAI-InfoPlist.xcstrings @@ -0,0 +1,42 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleName" : { + "comment" : "Bundle name", + "extractionState" : "extracted_with_value", + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "oAI" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "oAI" + } + } + } + } + }, + "version" : "1.1" +} \ No newline at end of file