// English dictionary for vue-i18n. // // Structure is grouped by feature area (common, chat, session, ...). // Prefer nested objects over flat keys so related strings stay // together and the namespace serves as self-documentation. // No `as const` — the module augmentation in src/types/vue-i18n.d.ts // reads `typeof en` to feed `DefineLocaleMessage`, and readonly literal // types would conflict with vue-i18n's writable message interface. // ⚠️ Angle-bracket text (e.g. ``, ``) in a plain string // message trips vue-i18n's XSS heuristic and logs // `[intlify] Detected HTML in '…' message` on every mount. If the // copy must contain `<...>`, use the **function form** so the // message compiler is skipped entirely: // // subheading: ({ named }: { named: (key: string) => unknown }) => // `${named("count")} available · invokes as /`, // // See `argsPlaceholder` and `pluginManageSkills.subheading` for // worked examples. Mirror the same form in every other lang file // when the English side uses a function. const enMessages = { common: { downloadZip: "ZIP", downloadFailed: "Download failed", save: "Save", cancel: "Cancel", loading: "Loading...", close: "Close", dismiss: "Dismiss", add: "Add", remove: "Remove", yes: "Yes", no: "No", saving: "Saving...", saved: "Saved", noResultsYet: "No results yet", noImageYet: "No image yet", sendChat: "Start a new chat", }, sessionTabBar: { newSession: "New session", // vue-i18n pluralization: `t(key, count)` picks singular / plural // based on the number. `{count}` is interpolated. activeSessions: "{count} active session (agent running) | {count} active sessions (agent running)", unreadReplies: "{count} unread reply | {count} unread replies", unreadDot: "New reply", origin: { scheduler: "Started by scheduler", skill: "Started by skill", bridge: "Started by bridge", }, }, chatInput: { placeholder: "Message Claude…", send: "Send", stop: "Stop", runningPlaceholder: "Running… press Enter to queue for later", removeBuffered: "Remove queued message", attachFile: "Attach file", fileTooLarge: "File too large ({sizeMB} MB). Maximum is 30 MB.", unsupportedFileType: "File type not supported. Accepted: images, PDF, DOCX, XLSX, PPTX, text files.", attachImageFailed: "Failed to attach image: {error}", stopFailed: "Failed to stop the run: {error}", dropHint: "Drop file to attach", tooManyFiles: "You can attach up to {max} files at once.", removeAttachment: "Remove {name}", attachmentFallbackName: "attachment", voice: { start: "Start voice input", stop: "Stop voice input", }, }, cspViolation: { notice: "⚠ A view tried to load {host}, but the content security policy blocked it ({directive}). To allow it, add the host to config/csp.json — only if you trust it.", dismiss: "Dismiss", }, sessionHistoryPanel: { filters: { all: "All", unread: "Unread", bookmarked: "Bookmarked", longRunning: "Long-running (24h+)", human: "Human", scheduler: "Scheduler", skill: "Skill", bridge: "Bridge", }, failedToRefresh: "⚠ Failed to refresh: {error}", showingLastKnown: " — showing last known list.", noSessions: "No sessions yet.", noMatching: "No matching sessions.", running: "Running", noMessages: "(no messages)", openRowAria: "Open session: {preview}", rowMenuAria: "Session actions", bookmark: "Bookmark", unbookmark: "Remove bookmark", delete: "Delete", deleteConfirm: "Delete this session?\n\n{preview}\n\nThis cannot be undone.", }, notificationBell: { notifications: "Notifications", activeSection: "Active", historySection: "History", noActive: "No active notifications", noHistory: "No recent activity", clearAll: "Clear", dismiss: "Dismiss", cancel: "Cancel", showMore: "Show more ({count})", showLess: "Show less", openTarget: "Open", expandDetails: "Expand details", }, pluginDiagnostics: { title: "Plugin configuration issue", hostBody: 'Plugin "{plugin}" tried to register the {label} key "{key}" but it is reserved by the host. The plugin\'s entry has been dropped.', intraBody: 'Plugins "{first}" and "{second}" both register {dimension} "{key}". "{first}" claimed it first, so "{second}"\'s registration is ignored.', }, shadowedEnv: { title: "Shell env is overriding .env", body: "Set in both your shell and .env: {keys}. The shell value wins, so .env is ignored. If you edited .env, update or unset the shell value and restart.", }, optionalDeps: { // Generic `title` kept for backward compatibility with persisted // history entries published before the reason-aware split below; // new code paths use `titleNotFound` / `titleNotResponding`. title: "Optional dependency unavailable", titleNotFound: "{command} not installed", titleNotResponding: "{command} not running", notFound: "{command} not found — related features are disabled. Install {command} and restart MulmoClaude to enable them.", notResponding: "{command} is installed but not running — related features are disabled. Start {command} and restart MulmoClaude to enable them.", }, billingMigration: { title: "Invoicing moved to on-demand setup", body: "The bundled clients, worklog, invoice, and profile collections were removed from your dashboard, but your data is safe and untouched. Ask to set up client & time tracking, then invoicing, to recreate them — your existing records will reappear.", }, backendOffline: { title: "Can't reach the backend", body: "The MulmoClaude server may not be running. Check the dev server, then retry.", retry: "Retry", }, pluginErrorBoundary: { title: "Plugin {pkg} crashed", subtitle: "The plugin failed to render. The error has been logged to the console.", showDetails: "Show details", hideDetails: "Hide details", retry: "Retry", }, remoteHostOffline: { title: "Remote host disconnected", body: "Your phone can't send to this device until you reconnect.", reconnect: "Reconnect", }, remoteHost: { title: "Remote host", online: "Remote host online", offline: "Remote host offline", uid: "uid {uid}", signIn: "Sign in with Google", connecting: "Connecting…", disconnect: "Disconnect", disconnecting: "Disconnecting…", noToken: "Google sign-in returned no idToken", connectFailed: "Connect failed", disconnectFailed: "Disconnect failed", signInFailed: "Google sign-in failed", statusFailed: "Failed to load status", description: "Remote access lets a mobile device connect to this MulmoClaude's collections and feeds.", howTo: "On your phone, open {url} and sign in with the same Google account.", customViewHint: "For a mobile-friendly view, ask Claude to build a {keyword} (not a regular custom view).", qrHint: "Or scan this QR code with your phone's camera.", }, sidebarHeader: { newMessages: "New messages", home: "Go to latest chat", toolCallHistory: "Tool call history", settings: "Settings", settingsGeminiMissing: "Settings — Gemini API key missing", todayJournal: "Today's journal", todayJournalNotFound: "No journal summary yet — chat for a while and the journal will generate one.", todayJournalLoadFailed: "Failed to load journal (status {status}): {error}", copyMarkdown: "Copy chat as Markdown", copiedMarkdown: "Copied!", }, rightSidebar: { permalink: "Selected message permalink", copyPermalink: "Copy permalink to selected message", copiedPermalink: "Copied!", toggleSystemPrompt: "Toggle system prompt", systemPrompt: "System Prompt", availableTools: "Available Tools", toggleToolDescription: "Toggle tool description", toolCallHistory: "Tool Call History", copyHistory: "Copy tool call history", copiedHistory: "Copied!", noToolCalls: "No tool calls yet", arguments: "Arguments", error: "Error", result: "Result", running: "Running...", mcpHint: { title: (ctx: { named: (key: "server") => string }) => `${ctx.named("server")} setup hint`, requiredKeys: "Required keys", setupGuide: "Open setup guide", }, }, fileTreePane: { sort: "Sort:", sortByName: "Sort by name", name: "Name", sortByRecent: "Sort by modified date (newest first)", recent: "Recent", reference: "Reference", // "RO" = Read-Only. Kept short on purpose — rendered as a compact // badge next to the Reference label. readOnlyBadge: "RO", showSystemFiles: "Show system files", showSystemFilesTitle: "Show agent-internal top-level dirs (conversations/, feeds/, etc.) in addition to your user content (data/, artifacts/, config/).", }, fileTree: { dropHint: "Drop files here to save them in this folder", upload: { progress: "Uploading {done} of {total}…", done: "Saved {count} file(s)", failed: "{count} file(s) couldn't be saved", }, workspace: "(workspace)", recentlyChanged: "Recently changed", newFileMenuItem: "New file", newFileInputAria: "New file name", newFilePlaceholder: { wikiPage: "page-slug", summary: "summary-name", document: "document-name", html: "page-name", story: "story-name", }, newFileError: { empty: "Filename can't be empty.", unsafe: "Filename contains invalid characters.", exists: "A file named {filename} already exists here.", saveFailed: "Couldn't create the file. Please try again.", }, }, lockStatusPopup: { sandboxEnabledTooltip: "Sandbox enabled (Docker)", noSandboxTooltip: "No sandbox (Docker not found)", sandboxEnabledLabel: "Sandbox enabled:", sandboxEnabledBody: "Docker is running. Filesystem access is isolated.", noSandboxLabel: "No sandbox:", noSandboxBodyPrefix: "Claude can access all files on your machine. Install", noSandboxBodySuffix: "to enable filesystem isolation.", dockerDesktop: "Docker Desktop", hostCredentials: "Host credentials attached:", credsLoading: "loading…", sshAgent: "SSH agent:", forwarded: "forwarded", notForwarded: "not forwarded", mountedConfigs: "Mounted configs:", none: "none", testIsolation: "Test sandbox isolation:", }, settingsModal: { title: "Settings", version: "MulmoClaude v{version}", tabs: { gemini: "Gemini API Key", tools: "Allowed Tools", mcp: "MCP Servers", dirs: "Directories", refs: "Reference Dirs", map: "Map", photos: "Photos", google: "Google", model: "Model", voice: "Voice", chatIndex: "Chat index", journal: "Journal", notifications: "Web Push", skills: "Skills", roles: "Roles", quit: "Quit", }, groups: { llm: "LLM", servers: "Servers", workspace: "Workspace", notifications: "Notifications", plugins: "Plugins", management: "Management", server: "Server", }, navAriaLabel: "Settings sections", googleTab: { description: "Link your Google account so this machine can call Google APIs (Calendar first). The refresh token is stored only on this machine and is never sent to any server other than Google.", statusLinked: "Linked", statusNotLinked: "Not linked", statusPending: "Waiting for the browser consent to finish…", connect: "Link Google account", unlink: "Unlink", unlinkConfirm: "Unlink the Google account? The saved token is revoked and deleted from this machine.", clientSecretAmbiguous: "Multiple client_secret_*.json files were found in ~/.secrets/. Keep exactly one so the stored token stays paired with the right OAuth client.", loadError: "Failed to load the Google link status.", connectError: "Failed to start the Google authorization flow.", unlinkError: "Failed to unlink the Google account.", }, mapTab: { description: "Set the Google Maps API key used by the map plugin. The key is stored locally and never transmitted anywhere except to Google Maps.", apiKeyLabel: "Google Maps API key", apiKeyPlaceholder: "AIza…", helperText: "Create or copy a key from {consoleLink}.", requiredApis: "Enable: Maps JavaScript API, Geocoding API, Places API (New), Directions API.", configured: "Configured", notConfigured: "Not configured", clear: "Clear", loadError: "Failed to load settings", saveError: "Failed to save", }, photosTab: { description: "Privacy controls for photos uploaded via chat or a connected bridge. EXIF location data is sensitive — uncheck the box to opt out of automatic capture.", autoCaptureLabel: "Auto-capture photo location data", autoCaptureHint: "When on, every uploaded image with EXIF GPS gets a location sidecar at data/locations/. Off: nothing is captured automatically; the LLM can still extract EXIF on demand.", statusOn: "Auto-capture is ON", statusOff: "Auto-capture is OFF", loadError: "Failed to load settings", saveError: "Failed to save", }, quitTab: { description: "Stop the MulmoClaude server running on this machine. Started from the icon, it keeps running after you close this tab — this is how you stop it without a terminal.", // Message function form — skips vue-i18n's message compiler so the // literal `@` in `mulmoclaude@latest` is not parsed as a linked-message // reference (the compiler throws, and the whole tab renders as nothing). restartHint: () => "To start it again, double-click the MulmoClaude icon (or run `npx mulmoclaude@latest`).", quitLabel: "Quit MulmoClaude", confirmBody: "The server stops and this page stops working. Anything still running is interrupted.", confirmLabel: "Quit", stopping: "Stopping…", stoppedTitle: "MulmoClaude has stopped", stoppedBody: "You can close this tab. Double-click the icon to start it again.", error: "Failed to stop the server", }, notificationsTab: { description: "Get a push on your registered devices when a task you started here finishes — handy when you ask something, step away, and want to know the moment the answer is ready.", enableLabel: "Send a Web Push when a task finishes", enableHint: "Fires when a chat you started here completes. Scheduled and background tasks don't trigger it.", remoteHostNote: "Requires the RemoteHost connection (it supplies the sign-in) and at least one registered device. With either missing, this does nothing.", macosRemindersLabel: "Create a macOS Reminder when a task finishes", macosRemindersHint: "Adds the finished task to your default Reminders list. iCloud sync then mirrors it to your iPhone, which is what delivers the notification.", macosRemindersForcedOff: "Turned off at startup by --disable-macos-reminders or DISABLE_MACOS_REMINDER_NOTIFICATIONS. Remove the flag or unset the variable, then restart, to control it from here.", statusOn: "Web Push is ON", statusOff: "Web Push is OFF", loadError: "Failed to load settings", saveError: "Failed to save", }, modelTab: { description: "Control the reasoning effort Claude Code uses for each turn. Leave unset to use Claude's default.", effortLabel: "Reasoning effort", effortUnset: "(unset — use Claude's default)", helperText: "Higher levels allow more thinking time but increase latency and token usage.", configured: "Effort: {level}", notConfigured: "Not set", loadError: "Failed to load settings", saveError: "Failed to save", }, voiceTab: { description: "Dictate chat messages with your voice. Audio is transcribed locally on the machine running MulmoClaude — nothing is sent to any external service.", requirements: "Available on macOS only. It requires the whisper.cpp server — build it with yarn build:whisper (see the README, Local Voice Input section).", unsupported: "Voice input requires macOS with the whisper.cpp server installed. It is not available on this machine.", enableLabel: "Enable voice input", enableHint: "Turning this on downloads the speech model (1–3 GB) once. A mic button then appears in the chat input.", modelLabel: "Speech model", downloading: "Downloading model… {percent}%", ready: "Model ready", downloadError: "Model download failed.", retry: "Retry", loadError: "Failed to load settings", saveError: "Failed to save", }, chatIndexTab: { description: "Background AI titles + summaries for your chat history. Off by default — automation sessions (scheduler / system workers) are always skipped even when on, and human sessions only pay one summarizer call each when a turn ends.", modeLabel: "Chat index model", helperText: "Haiku is cheaper; Sonnet gives sharper titles for long, topic-shifting sessions.", mode: { off: "Off", haiku: "Haiku", sonnet: "Sonnet", }, status: { off: "Indexing is OFF", haiku: "Indexing with Haiku", sonnet: "Indexing with Sonnet", }, loadError: "Failed to load settings", saveError: "Failed to save", }, journalTab: { description: "Automated daily journal — summarises recent chat sessions into journal/*.md and extracts durable memory notes. Off by default. Automation sessions (scheduler / system workers) are always excluded regardless of this setting.", modeLabel: "Journal model", helperText: "Haiku is cheaper; Sonnet produces richer daily / topic summaries. The hourly pass runs only when this is set.", mode: { off: "Off", haiku: "Haiku", sonnet: "Sonnet", }, status: { off: "Journal is OFF", haiku: "Journal running with Haiku", sonnet: "Journal running with Sonnet", }, loadError: "Failed to load settings", saveError: "Failed to save", }, // `` slots — named `envKey` / `envFile` render as inline // `` in SettingsModal.vue, so the literal variable and file // names stay untranslated while the surrounding copy is localised. geminiRequired: "Image generation requires {envKey}. Add it to {envFile} and restart the app.", geminiAskButton: "Ask Claude", geminiAskMessage: "What is the role of the Gemini API key in this app?", toolNamesLabel: "Tool names", invalidToolNamesPrefix: "These look non-standard (expected prefix", invalidToolNamesSuffix: "):", mcpToolsError: "⚠ Could not fetch MCP tool status: {error}. Showing all tools regardless of enablement.", changesHint: "Changes apply on the next message. No restart needed.", cannotSaveTooltip: "Cannot save until settings load successfully", saving: "Saving…", loadingLabel: "Loading…", unsavedMarker: "●", unsavedToolsConfirm: "Allowed Tools has unsaved changes. Close anyway?", unsavedMcpDraftConfirm: "An MCP server draft is still open. Close anyway?", mcpSaveFailed: "Failed to save MCP server changes.", }, canvasViewToggle: { stackViewTooltip: "Stack view · click to switch to Single", singleViewTooltip: "Single view · click to switch to Stack", switchToSingle: "Switch to Single view", switchToStack: "Switch to Stack view", }, sessionHistoryToggle: { showTooltip: "Show session history panel on the left", hideTooltip: "Hide session history panel", show: "Show session history", hide: "Hide session history", }, sessionHistoryExpand: { expandTooltip: "Expand session history panel to full width", collapseTooltip: "Collapse session history panel", expand: "Expand session history", collapse: "Collapse session history", }, settingsWorkspaceDirs: { explanation: "Custom directories for organizing files under {dataDir} and {artifactsDir}. Claude uses these to route file saves.", noEntries: "No custom directories configured.", addDirTitle: "Add directory", pathPlaceholder: "data/clients or artifacts/reports", descPlaceholder: "Description (what goes in this folder)", errPathRequired: "Path required", errMustStartWith: "Must start with data/ or artifacts/", errAlreadyExists: "Already exists", }, settingsReferenceDirs: { explanation: "External directories Claude can read but not modify. In Docker mode, these are mounted read-only. Useful for referencing Obsidian vaults, project code, or document folders.", noEntries: "No reference directories configured.", addDirTitle: "Add reference directory", pathPlaceholder: "/Users/me/ObsidianVault or ~/Documents/notes", labelPlaceholder: "Label (optional — defaults to folder name)", readOnlyBadge: "read-only", errPathRequired: "Path required", errMustBeAbsolute: "Must be an absolute path or start with ~/", errAlreadyExists: "Already exists", errLabelConflict: 'Label "{label}" already exists', }, dashboard: { empty: "No favorite collections yet.", emptyHint: "Pin a collection (★) to see it here.", viewTable: "Table", viewCalendar: "Calendar", viewKanban: "Kanban", viewPickerLabel: "Choose view", openFull: "Double-click to open full view", dragHint: "Drag to reorder", resizeHint: "Drag to resize", }, pluginLauncher: { chat: { label: "Chat" }, dashboard: { label: "Dashboard" }, automations: { label: "Actions" }, wiki: { label: "Wiki" }, collections: { label: "Collections" }, feeds: { label: "Feeds" }, accounting: { label: "Accounting" }, files: { label: "Files" }, }, shortcuts: { pin: "Pin to launcher", unpin: "Unpin from launcher", zoneAriaLabel: "Pinned shortcuts", reorder: { open: "Reorder shortcuts", title: "Reorder", moveUp: "Move up", moveDown: "Move down", }, }, fileContentHeader: { showRendered: "Show rendered Markdown", showRaw: "Show raw source", rendered: "Rendered", raw: "Raw", closeFile: "Close file", revealInOs: "Show in folder", revealInOsFailed: "Failed to show in folder", }, fileContentRenderer: { download: "ZIP", downloadZip: "Download as a self-contained zip (assets bundled)", downloadError: "Download failed", selectFile: "Select a file", htmlPreview: "HTML preview", pdfPreview: "PDF preview", parseError: "parse error", editJson: "Edit JSON", jsonEditorLabel: "JSON editor", invalidJson: "Invalid JSON", undo: "Undo", redo: "Redo", editMarp: "Edit slide source", marpEditorLabel: "Marp slide source", openInOs: "Open in OS", openingInOs: "Opening…", openInOsFailed: "Failed to open in OS", }, filesView: { chatPlaceholder: "Ask about this file…", }, // SystemFileBanner copy. Each id matches a descriptor in // src/config/systemFileDescriptors.ts. `title` is the chip-style // headline above the file body; `summary` is one or two sentences // explaining what the file is, who edits it, and why the banner is // showing. Edit-policy chip labels go under `editPolicy`; the // remaining keys (schemaLabel, showDetails, hideDetails) are // banner chrome. systemFiles: { schemaLabel: "Schema", showDetails: "Show details", hideDetails: "Hide details", editPolicy: { "agent-managed-but-hand-editable": "Agent-managed (hand-edit OK)", "user-editable": "User-editable", "agent-managed": "Agent-managed", "fragile-format": "Fragile format", ephemeral: "Ephemeral", }, mcp: { title: "MCP servers", summary: "External Model Context Protocol servers attached to the agent. Add HTTP or stdio servers to expand the agent's tool surface.", }, settings: { title: "App settings", summary: "User-editable behavioural preferences for the app — Gemini API key, allowed tools, sandbox config, and similar.", }, schedulerTasks: { title: "Scheduler tasks", summary: "Recurring agent automations that fire on a schedule. Managed via the Automations UI; this file is the on-disk source of truth.", }, schedulerOverrides: { title: "Scheduler overrides", summary: "Per-task time / interval overrides applied on top of the system schedule. The agent edits this when you ask to change a recurring task's timing.", }, schedulerItems: { title: "Scheduler items queue", summary: "Active scheduled invocations ready to fire. Agent-managed; do not hand-edit unless you know exactly what each field means.", }, wikiIndex: { title: "Wiki index", summary: "Auto-generated index of every wiki page. Refreshed on each wiki edit; do not hand-edit (your changes will be overwritten).", }, wikiLog: { title: "Wiki edit log", summary: "Activity log of wiki page creates and edits. Agent-managed and append-only; useful as a recent-changes feed.", }, wikiSummary: { title: "Wiki summary", summary: "Auto-generated overview of the wiki — topic clusters, page counts, recent activity. Refreshed by the agent.", }, wikiSchema: { title: "Wiki schema", summary: "The format spec the agent reads to keep wiki pages consistent. Fragile — the agent expects a specific structure, so prefer agent-driven edits.", }, memory: { title: "Memory", summary: "Distilled facts about you, always loaded as context for new conversations. The journal extractor appends here automatically; you can also hand-edit.", }, summariesIndex: { title: "Summaries index", summary: "Browseable index linking the daily and topic summaries the journal generates. Agent-managed; refreshed on each journal pass.", }, rolesJson: { title: "Role definition (JSON)", summary: "Role configuration — model choice, MCP servers, allowed plugins, query suggestions. User-editable; restart not required.", }, rolesMd: { title: "Role description (Markdown)", summary: "The role's persona and system-prompt prose, loaded as context when this role is active. User-editable; changes apply on the next message.", }, journalDaily: { title: "Daily journal summary", summary: "Auto-generated recap of your activity for one calendar day, distilled from chat sessions by the journal pass.", }, journalTopic: { title: "Topic journal", summary: "Long-running notes for one specific topic, accumulated and revised as you keep talking about it. Agent-managed.", }, }, settingsMcpTab: { explanation: "Add external MCP servers. HTTP servers work in every mode. Stdio servers use the sandbox image's {npx} / {node} / {tsx}; paths must live under the workspace when Docker is enabled.", localhostRewrite: "In Docker mode {localhost} is rewritten to {hostDockerInternal}.", noServers: "No MCP servers configured yet.", enabled: "enabled", urlLabel: "URL:", commandLabel: "Command:", dockerStdioUnsupported: "⚠ Won't run while the Docker sandbox is enabled.", dockerStdioHostExecActive: "⚠ Runs on the host — this server escapes the Docker sandbox.", dockerStdioHostExecOptIn: "Run on the host anyway (advanced). This server runs outside the Docker sandbox via a local HTTP gateway and can access your machine.", learnMore: "Learn more", addServerButton: "+ Add MCP Server", nameLabel: "Name", namePlaceholder: "my-server", typeHttp: "HTTP", typeStdio: "Stdio (command)", urlFieldLabel: "URL", urlPlaceholder: "https://example.com/mcp", commandFieldLabel: "Command", argsLabel: "Arguments (one per line)", // Message function form — skips vue-i18n's message compiler so // the literal `@` isn't parsed as a linked-message reference. argsPlaceholder: () => "-y\n@modelcontextprotocol/server-filesystem\n/workspace/path", errNoName: "Please provide a Name, or enter a URL / args we can derive one from.", errBadName: "Name must start with a lowercase letter and contain only [a-z0-9_-].", errIdExists: 'Server id "{id}" already exists.', errBadHttpUrl: "HTTP URL must start with http:// or https://", pendingEntryWarning: "Finish or cancel the pending MCP server entry first.", customHeading: "Custom servers", catalog: { heading: "Pre-configured MCP servers", audience: { general: "🟢 General", developer: "🔵 Developer" }, risk: { low: "low", medium: "medium", high: "high" }, upstream: "📦 Source", setupGuide: "📚 Setup", entry: { memory: { displayName: "Memory", description: "Lets Claude remember conversation context across sessions.", }, sequentialThinking: { displayName: "Sequential Thinking", description: "Helps Claude work through multi-step problems by thinking step by step.", }, context7: { displayName: "Context7 (library docs)", description: "Up-to-date documentation for popular libraries — beats the model's training-cutoff memory.", }, deepwiki: { displayName: "DeepWiki (GitHub repo wiki)", description: "Ask questions about any GitHub repository and get a structured wiki-style answer.", }, notion: { displayName: "Notion", description: "Read and write your Notion workspace — pages, databases, and search.", field: { apiKey: { label: "Notion integration token", help: "Create a Notion integration and copy the Internal Integration Secret. Click 🔑 to open the integrations page.", }, }, }, slack: { displayName: "Slack", description: "List channels, post messages, and search history in your Slack workspace.", field: { botToken: { label: "Bot token", help: "Slack app → OAuth & Permissions → Bot User OAuth Token. Starts with xoxb-.", }, teamId: { label: "Team / workspace ID", help: "Run team.info or check the workspace URL — looks like T01ABC23DEF.", }, }, }, googleMaps: { displayName: "Google Maps", description: "Search places, get directions, and look up location details.", field: { apiKey: { label: "Google Maps API key", help: "Google Cloud Console → APIs & Services → Credentials → Create API key. Enable Places + Directions.", }, }, }, appleNative: { displayName: "Apple Native Apps (macOS)", description: "Read and write Reminders, Calendar, Notes, Mail and Maps via AppleScript. macOS only — no credentials needed.", }, gmail: { displayName: "Gmail", description: "Read, send, and label your Gmail. Uses a Google OAuth client you create in your own Google Cloud project (no app verification needed).", field: { credentials: { label: "Path to credentials.json", help: "Google Cloud Console → APIs & Services → Credentials → OAuth client ID (Desktop app). Download credentials.json and paste its absolute path.", }, }, }, googleCalendar: { displayName: "Google Calendar", description: "Read and create Google Calendar events. Same BYO Google OAuth credentials.json pattern as Gmail.", field: { credentials: { label: "Path to credentials.json", help: "Same Google Cloud OAuth client as Gmail. Reuse the file or create a separate Calendar-scoped one.", }, }, }, googleDrive: { displayName: "Google Drive", description: "Search and read Google Drive files. BYO Google OAuth credentials — token is cached locally next to the file.", field: { credentials: { label: "Path to credentials.json", help: "Google Cloud Console → APIs & Services → Credentials → OAuth client ID (Desktop app). Enable the Google Drive API in the same project.", }, }, }, github: { displayName: "GitHub", description: "Read repos, issues, PRs and run searches with a Personal Access Token. Scope the token narrowly — write scopes (e.g. `repo`) let the agent push to any repo you can access.", field: { token: { label: "Personal Access Token", help: "GitHub → Settings → Developer settings → Personal access tokens. Prefer fine-grained tokens limited to the repos you want the agent to touch.", }, }, }, linear: { displayName: "Linear", description: "Read and update Linear issues, projects and cycles via a personal API key.", field: { apiKey: { label: "Linear API key", help: "Linear → Settings → API → Personal API keys. Click 🔑 to open the page and click Create key.", }, }, }, weatherOpenMeteo: { displayName: "Weather (Open-Meteo)", description: "Free weather forecasts and current conditions worldwide — no API key needed.", }, spotify: { displayName: "Spotify", description: "Search tracks, manage playlists, control playback. BYO Spotify developer app — Client ID only (PKCE flow, no client secret).", field: { clientId: { label: "Client ID", help: "Spotify Developer Dashboard → Create app, set Redirect URI to http://127.0.0.1:8888/callback, copy the Client ID. Then run `SPOTIFY_CLIENT_ID= npx spotify-mcp@latest auth` once in your terminal to log in (refresh token is cached at ~/.spotify-mcp/tokens.json).", }, }, }, youtubeTranscript: { displayName: "YouTube transcript", description: "Fetch the captions for any public YouTube video by URL. No credentials needed.", }, }, config: { howToGet: "How to get this", install: "Install", errMissingRequired: "Required field(s) missing: {fields}", requiredMarker: "*", requiredAria: "required", }, }, }, pluginScheduler: { previewIcon: "📅", previewAutomations: "{count} automation | {count} automations", previewMore: "+ {count} more…", }, pluginSchedulerTasks: { recommendedFrequencies: "Recommended Frequencies", tableTaskType: "Task type", tableSuggestedSchedule: "Suggested schedule", noTasks: "No scheduled tasks", runNow: "Run now", enable: "Enable", disable: "Disable", delete: "Delete", nextRun: "Next: {time}", originSystem: "System", originUser: "User", originSkill: "Skill", runFailed: "Run failed: {error}", toggleFailed: "Toggle failed: {error}", deleteFailed: "Delete failed: {error}", detailsToggle: "Show details", promptLabel: "Prompt", roleLabel: "Role", confirmDelete: 'Delete the task "{name}"? This cannot be undone.', hintNewsRss: "News / RSS fetch", hintJournal: "Journal daily pass", hintWiki: "Wiki maintenance", hintMemory: "Memory extraction", hintCalendar: "Calendar / contact sync", }, pluginCanvas: { undo: "Undo", redo: "Redo", clear: "Clear", styleLabel: "Style:", stylePromptWithPath: "Turn the image at `{path}` into a {style} style image.", stylePromptNoPath: "Turn my drawing on the canvas into a {style} style image.", saveFailed: "Not saved", }, pluginWiki: { backToIndex: "Back to index", pdf: "PDF", pdfFailed: "⚠ PDF failed", tabIndex: "Index", tabLog: "Log", tabLint: "Lint", tabGraph: "Graph", graphEmpty: "No links to graph yet.", linkedReferences: "Linked references", empty: "Wiki is empty. Ask the Wiki Manager to ingest a source.", previewMore: "+ {count} more…", chatPlaceholder: "Ask about this page…", emptyPage: 'The page "{title}" does not exist yet.', emptyContent: 'The page "{title}" exists but has no content.', createPage: "Request creation of this wiki page", updatePage: "Request update of this wiki page", tagFilterAll: "All", noMatches: "No pages tagged #{tag}", lintChat: "Lint My Wiki", taskCountMismatch: "Wiki source and rendered output disagree on the number of tasks. Refusing to toggle to avoid corruption.", metadataCreated: "Created", metadataUpdated: "Updated", metadataEditor: "Editor", pageEditHeader: "Wiki edit", snapshotExpired: "Snapshot expired — showing current page", snapshotLoadError: "Couldn't load the snapshot — the page may still exist. Try refreshing.", pageDeleted: "Page deleted", history: { tabContent: "Content", tabHistory: "History", empty: "No history yet — edit this page and the first version will appear here.", loading: "Loading history…", backToList: "Back to history", restoreButton: "Restore this version", restoreConfirmTitle: "Restore this version?", restoreConfirmBody: "Restore page to the version from {ts} by {editor}? Current page will be replaced. Existing history is preserved.", restoreConfirmAction: "Restore", restoreConfirmCancel: "Cancel", restoreSuccessToast: "Page restored.", restoreFailureBanner: "Restore failed: {error}", compareCurrent: "Compare with current page", comparePrevious: "Compare with previous version", diffNoPrevious: "No previous version to compare against.", diffNoChanges: "No content changes between this version and the comparison.", editorBadgeUser: "User", editorBadgeLLM: "LLM", editorBadgeSystem: "System", hiddenLines: "{count} unchanged lines hidden", expandHidden: "Show", }, }, pluginPresentForm: { fallbackTitle: "Form", fieldCount: "{count} field | {count} fields", submitted: "Submitted", errorSummary: "Please fix the following errors", requiredMarker: "*", selectOption: "Select an option", charactersCount: "{current} / {max} characters", charactersCountNoMax: "{current} characters", submit: "Submit", progress: "{filled} of {total} required fields completed", }, pluginPresentSvg: { saveAsPng: "Download as PNG", png: "PNG", saveAsPdf: "Save as PDF (opens print dialog)", pdf: "PDF", untitled: "SVG Drawing", editSource: "Edit SVG Source", cancel: "Cancel", applyChanges: "Apply Changes", saving: "Saving...", saveError: "⚠ Save failed: {error}", exportError: "⚠ Export failed: {error}", loadingSource: "Loading source…", sourceError: "Failed to load source: {error}", }, photoLocations: { title: "Photo locations", summary: "{total} captured · {withGps} with GPS", mapHint: 'Ask Claude "show these on a map" to plot them with the Google Map plugin.', loading: "Loading…", empty: "No photo locations captured yet. Send a geotagged photo via chat or a connected bridge to get started.", noGps: "No GPS data", }, pluginManageSkills: { deleteProjectSkill: "Delete this project-scope skill", unstarPresetSkill: "Unstar this preset — moves it back to the catalog", heading: "Skills", previewCount: "{count} skill | {count} skills", previewMore: "+{count} more", // Function form skips vue-i18n's message compiler, which // otherwise flags the literal `` as a suspected HTML // fragment and warns about XSS on every mount. subheading: ({ named }: { named: (key: string) => unknown }) => `${named("count")} available · click one to view · "Run" invokes it as /`, emptyWithPath: "No skills found. Add skill folders under {path}.", emptySkillPath: "~/.claude/skills/", selectHint: "Select a skill on the left to view its SKILL.md.", loading: "Loading…", fieldDescription: "Description", fieldBody: "Body (Markdown)", emptyBody: "(empty body)", btnEdit: "Edit", btnDelete: "Delete", btnUnstar: "Unstar", errListFailed: "Failed to load skills: {error}", errDetailFailed: "Failed to load skill: {error}", errSaveFailed: "Save failed: {error}", errDeleteFailed: "Failed to delete", confirmDelete: 'Delete skill "{name}"? This removes ~/mulmoclaude/.claude/skills/{name}/SKILL.md.', confirmUnstar: 'Move "{name}" back to the catalog? It will stop loading into the prompt, but the catalog copy stays — you can re-star it any time.', sectionActive: "Active", sectionCatalog: "Catalog", sectionLegendActive: "Skills Claude can use right now. Claude calls them automatically in the flow of a conversation, or you can invoke one by typing its name. {system} System (mc- bundled) / {project} Project (editable, this workspace only) / {user} User (skills in ~/.claude/skills/).", sectionLegendCatalog: "Catalog: skills that become Active when you mark them with {star}. Removing {star} from an Active skill sends it back to Catalog — Claude stops using it (the skill is not deleted).", catalogEmpty: "No preset skills available.", catalogPresetHeading: "Presets", catalogStar: "Star", catalogStarred: "Starred", sourceUserTitle: "User skill (~/.claude/skills/, available in every workspace)", sourceSystemTitle: "System skill (bundled, mc- prefix — read-only, overwritten by the launcher)", sourceProjectTitle: "Project skill (workspace .claude/skills/, this workspace only)", sourcePresetTitle: "Preset catalog — click Star to activate in this workspace", errCatalogListFailed: "Failed to load catalog: {error}", errCatalogStarFailed: "Failed to star skill: {error}", errCatalogPreviewFailed: "Failed to load skill preview: {error}", catalogAddRepo: "Add skill repository", catalogAddRepoTitle: "Add a skill repository", catalogRepoUrlLabel: "GitHub URL", catalogRepoUrlPlaceholder: "https://github.com/owner/repo", catalogRepoSubpathLabel: "Subpath (optional)", catalogRepoSubpathPlaceholder: "skills", catalogAddRepoSubmit: "Install", catalogAddRepoSuggestions: "Suggested repositories", catalogUninstallRepo: "Uninstall repository", catalogUpdateRepo: "Update repository (re-fetch latest)", catalogRepoOpenLink: "Open repository on GitHub (new tab)", catalogUninstallConfirm: "Uninstall this repository? Skills you already starred stay in your active list.", catalogRepoInstalling: "Installing…", catalogRepoEmpty: "No skills found in this repository.", sourceExternalTitle: "External skill (installed from a GitHub repo — click Star to activate)", errCatalogRepoListFailed: "Failed to load installed repositories: {error}", errCatalogRepoInstallFailed: "Failed to install repository: {error}", errCatalogRepoUninstallFailed: "Failed to uninstall repository: {error}", errCatalogRepoInvalidUrl: "Enter a GitHub repository URL.", }, pluginManageRoles: { heading: "Custom Roles", roleCount: "{count} role | {count} roles", addButton: "+ Add", createPanel: "Create new role", fieldId: "ID", fieldName: "Name", fieldIcon: "Icon", fieldPrompt: "Prompt", fieldPlugins: "Plugins", fieldStarterQueries: "Starter queries", onePerLine: "(one per line)", helpLink: "?", idFormatted: "({id})", missingEnv: "(missing {env})", requiresEnv: "Requires {env} in .env", collapse: "Collapse", expand: "Expand", idPlaceholder: "unique-id", creating: "Creating…", create: "Create", updating: "Updating…", update: "Update", cancel: "Cancel", delete: "Delete", emptyHint: 'No custom roles yet. Click "+ Add" or ask Claude to create one.', errIdRequired: "ID is required.", errIdInvalid: "ID may only contain letters, numbers, '-' and '_'.", errNameRequired: "Name is required.", errIdDuplicate: "A role with ID '{id}' already exists.", errCreateFailed: "Create failed", errSaveFailed: "Save failed", errDeleteFailed: "Delete failed", errNetworkError: "Network error", errServerError: "Server error: {status}", errRefreshFailed: "Saved, but the list failed to refresh.", confirmDelete: 'Delete the role "{name}"? This cannot be undone.', }, pluginUiImage: { promptLabel: "{label}:", }, markdownMermaid: { loadFailed: "⚠ Mermaid failed to load: {error}", renderFailed: "⚠ Mermaid render failed: {error}", }, pluginTextResponse: { pdf: "PDF", pdfFailed: "⚠ PDF failed", editContent: "Edit Text Content", applyChanges: "Apply Changes", copyLabel: "Copy", speakerSystem: "System", speakerUser: "You", speakerAssistant: "Assistant", copiedLabel: "Copied!", cancel: "Cancel", seededByPlugin: "from {pkg}", seededByPluginTooltip: "This message was seeded by the {pkg} plugin, not sent by you.", truncatedForRender: "This message is unusually long ({total} chars total). Only the first portion is rendered — {omitted} chars hidden to keep the tab responsive. Use Copy for the full raw text.", }, pluginSkill: { noDescription: "(no description)", }, pluginSpreadsheet: { previewUntitled: "Spreadsheet", previewSheets: "{count} sheet | {count} sheets", untitled: "Spreadsheet", excel: "Excel", valuePlaceholder: "Value", valueOrFormulaPlaceholder: "Value or Formula (e.g., 100 or SUM(B2:B11))", formatPlaceholder: "Format (e.g., $#,##0.00)", loading: "Loading spreadsheet...", noData: "No spreadsheet data available", editData: "Edit Spreadsheet Data", applyChanges: "Apply Changes", dataMustBeArray: "Data must be an array of sheets", loadFailed: "Failed to load spreadsheet: {error}", invalidJsonAlert: "Invalid JSON format: {error}", unknownError: "Unknown error", update: "Update", stringType: "String", formulaType: "Formula", }, app: { startConversation: "Start a conversation", thinking: "Thinking…", }, suggestionsPanel: { suggestions: "Suggestions", skills: "Skills", tooltip: "Suggestions and Skills", emptySuggestions: "No suggestions.", emptySkills: "No skills installed.", skillsError: "Failed to load skills: {error}", sendEditHint: "click to send · shift+click to edit", }, settingsToolsTab: { // Rendered via with named slots `allowedTools` and `claudeMcp` // so the inline `` tags keep their styling while the copy // is translatable. explanation: "Extra tool names to pass to Claude via {allowedTools}. One per line. Useful for built-in Claude Code MCP servers like Gmail / Google Calendar after you have authenticated via {claudeMcp}.", connectorsSectionTitle: "Connected connectors", connectorsEmpty: "No connectors found.", connectorConnected: "Connected", connectorDisconnected: "Not connected", connectorsGuide: "Connectors like Slack and Gmail let Claude access your accounts. Add or remove connectors from Claude Desktop, or configure them {configLink}. (Opens claude.ai)", connectorsConfigLinkText: "here", }, confirmModal: { defaultTitle: "Confirm", defaultConfirm: "Confirm", defaultCancel: "Cancel", }, }; export default enMessages;