{
  "nav": {
    "models": "Models",
    "playground": "Playground",
    "keys": "Keys",
    "analytics": "Analytics",
    "premium": "Premium",
    "signOut": "Sign out",
    "openMenu": "Open navigation menu",
    "theme": "Theme",
    "switchLight": "Switch to light theme",
    "switchDark": "Switch to dark theme",
    "language": "Change language",
    "switchToChinese": "Switch to Chinese",
    "switchToEnglish": "Switch to English",
    "modelsMenu": "Browse model types"
  },
  "auth": {
    "createYourAccount": "Create your account",
    "signIn": "Sign in",
    "email": "Email",
    "password": "Password",
    "emailPlaceholder": "you@example.com",
    "passwordPlaceholderSetup": "at least 8 characters",
    "passwordPlaceholderLogin": "your password",
    "setupDescription": "Set the email and password that will protect this dashboard.",
    "loginDescription": "Sign in to manage your keys, routing, and analytics.",
    "createAccount": "Create account",
    "creating": "Creating…",
    "signingIn": "Signing in…",
    "loading": "Loading…",
    "newPasswordAutoComplete": "new-password",
    "currentPasswordAutoComplete": "current-password",
    "serverUnreachableBefore": "Can't reach the server. Make sure the backend is running (",
    "serverUnreachableAfter": ").",
    "setupCode": "Setup code",
    "setupCodePlaceholder": "code from the server logs",
    "setupCodeHint": "Setting up from another device needs the one-time code printed in the server logs at startup."
  },
  "common": {
    "save": "Save",
    "saving": "Saving…",
    "cancel": "Cancel",
    "discard": "Discard",
    "delete": "Delete",
    "confirm": "Confirm",
    "copy": "Copy",
    "copied": "Copied",
    "show": "Show",
    "hide": "Hide",
    "apply": "Apply",
    "applied": "Applied",
    "applying": "Applying…",
    "active": "Active",
    "inactive": "Inactive",
    "never": "never",
    "loading": "Loading…",
    "error": "Error",
    "noData": "No data yet",
    "unsavedChanges": "Unsaved changes",
    "saveChanges": "Save changes",
    "remove": "Remove",
    "unknownError": "Unknown error",
    "model": "Model",
    "provider": "Provider",
    "success": "Success",
    "failures": "Failures",
    "dismiss": "Dismiss",
    "status": "Status"
  },
  "models": {
    "title": "Models",
    "chatModelsTab": "Chat models",
    "fusionTab": "Fusion",
    "embeddingsTab": "Embeddings",
    "imageTab": "Image",
    "audioTab": "Audio",
    "imageTitle": "Image models",
    "audioTitle": "Audio models",
    "imageDesc": "Text-to-image generation models.",
    "audioDesc": "Text-to-speech generation models.",
    "mediaHint": "Generate via",
    "mediaEmpty": "No models yet. They sync from the catalog.",
    "newBadge": "New",
    "monthlyTokenBudget": "Monthly token budget",
    "remaining": "remaining",
    "used": "used",
    "of": "of",
    "showAllModels": "Show all {count} models",
    "showLess": "Show less",
    "vision": "Vision",
    "visionTitle": "Accepts image input",
    "tools": "Tools",
    "toolsTitle": "Emits structured tool calls, so it is eligible for tool-bearing requests",
    "penalty": "−{value} penalty",
    "obs": "{count} obs",
    "tokPerMonth": "{count} tok/mo",
    "rpmLimit": "{count} rpm",
    "rpdLimit": "{count} rpd",
    "noKey": "no key",
    "dragToReorder": "Drag to reorder",
    "dragToReorderGroup": "Drag to reorder model",
    "providerCount": "{count} providers",
    "servedBy": "Served by {providers}",
    "viewProviders": "View providers",
    "backToModels": "Back to models",
    "providersHeading": "Providers serving this model",
    "codeSnippetHeading": "Use this model",
    "modelNotFound": "This model is not available, or it has no configured providers.",
    "providerIdsHeading": "Provider model ids",
    "providerIdsHint": "Send the unified model id to fail over across every provider, or a provider-specific id to pin one provider.",
    "copyModelName": "Copy model name",
    "settingsHeading": "Provider settings",
    "settingsHint": "Local edits to catalog models are kept across catalog syncs. Deleted catalog models stay deleted until you add them again through config.",
    "catalogModel": "catalog",
    "customModel": "custom",
    "localOverride": "local override",
    "displayName": "Display name",
    "contextWindow": "Context",
    "inFallback": "In chain",
    "saveModelSettings": "Save model settings",
    "aggregateBudget": "{count}/mo",
    "aggregateBudgetTitle": "Total free tokens per month across this model's providers",
    "rateRpm": "{count} RPM",
    "rateRpd": "{count} RPD",
    "rateTitle": "Rate-limited — no fixed monthly token budget",
    "unifyToggle": "Unify duplicate models",
    "unifyToggleHint": "Show a model served by several providers as one item, and fail over across its providers when it's selected.",
    "columnModel": "Model",
    "columnOn": "On",
    "noModelsBefore": "No models available. Add API keys on the ",
    "keysPageLink": "Keys page",
    "noModelsAfter": " first.",
    "hiddenNoKeys": "Hidden (no keys): {platforms}",
    "searchPlaceholder": "Search models or providers…",
    "clearSearch": "Clear search",
    "ctxAny": "Any context",
    "ctxBadge": "{size} ctx",
    "ctxTitle": "Context window",
    "copyModelId": "Copy model id",
    "showingCount": "{shown} of {total} models",
    "clearFilters": "Clear filters",
    "noMatches": "No models match your filters.",
    "noModelsTitle": "No models configured"
  },
  "strategies": {
    "title": "Routing strategy",
    "description": "Pick a routing strategy. In Manual mode you drag to set the order; the other strategies route by live score across reliability, speed and intelligence.",
    "weightsSummary": "reliability {reliability}% · speed {speed}% · intelligence {intelligence}%",
    "guardrails": "Guardrails",
    "guardrailsTooltip": "Always-on guardrails: free-quota headroom × live rate-limit penalty. Below 1.0 means the model is being held back.",
    "scoreTooltip": "Final routing score = weighted average of the three axes, multiplied by the guardrails. Higher routes first.",
    "modeManualHint": "Manual mode: requests follow the order below, top-to-bottom. Drag to reorder.",
    "modeScoreHint": "Scores update from live traffic. The order below is how requests are routed right now.",
    "manual": "Manual",
    "manualBlurb": "Route in the exact order you set below. Drag the handles to reorder. No scoring; the chain is followed top-to-bottom.",
    "balanced": "Balanced",
    "balancedBlurb": "Reliability leads (50%), with speed and intelligence weighted equally (25% each). A sensible all-round default.",
    "smartest": "Smartest",
    "smartestBlurb": "Prefer the most capable model that still works. Intelligence 55%, reliability 35%, speed 10%.",
    "fastest": "Fastest",
    "fastestBlurb": "Prefer the fastest model that still works. Speed 55%, reliability 35%, intelligence 10%.",
    "reliable": "Most reliable",
    "reliableBlurb": "Maximize success rate above all. Reliability 70%, speed and intelligence 15% each.",
    "custom": "Custom",
    "customBlurb": "Set your own balance of reliability, speed and intelligence with sliders. Same engine as the presets, just your weights.",
    "adjust": "Adjust",
    "customWeights": "Custom weights",
    "customWeightsHelp": "Sliders are independent; shares auto-balance to 100%.",
    "weightReliability": "Reliability",
    "weightSpeed": "Speed",
    "weightIntelligence": "Intelligence",
    "weightRequired": "At least one weight must be above zero.",
    "scoreColumn": "Score"
  },
  "penaltyInspector": {
    "title": "Router pressure",
    "subtitle": "Penalties, cooldowns, and failures from the last {minutes} min",
    "rowCount": "{count} active",
    "offChain": "off chain",
    "penaltyValue": "Penalty {value}",
    "factor": "score x{factor}",
    "noCooldown": "No cooldown",
    "cooldownChip": "{key} - {time}",
    "errorsHeading": "{count} recent errors",
    "noRecentErrors": "No recent errors",
    "noKey": "no key",
    "reasons": {
      "penalty": "penalized",
      "cooldown": "cooldown",
      "recent_errors": "failing"
    }
  },
  "keys": {
    "pageTitle": "Keys",
    "pageDescription": "Provider credentials and the unified API key your apps connect with.",
    "tabProviders": "Providers",
    "tabQuotaSignals": "Quota signals",
    "tabApiKey": "Unified API key",
    "tabAnthropic": "Anthropic",
    "unifiedKey": "Your unified API key",
    "regenerate": "Regenerate",
    "showKey": "Show",
    "hideKey": "Hide",
    "copyKey": "Copy",
    "copiedKey": "Copied",
    "baseUrl": "Base URL",
    "endpointChat": "Chat",
    "endpointResponses": "Responses",
    "endpointEmbeddings": "Embeddings",
    "endpointEmbeddingsHint": "model: \"auto\" or a family from the Embeddings tab",
    "endpointMessages": "Messages",
    "endpointMessagesHint": "Anthropic-compatible (Claude)",
    "anthropicTitle": "Claude (Anthropic) models",
    "anthropicDesc": "Point a Claude or Anthropic SDK client at this server. Anthropic clients send Claude model names — map each to Auto (the router picks a free model) or pin a specific one.",
    "anthropicBaseUrl": "Base URL",
    "anthropicAuth": "Auth header",
    "anthropicAuto": "Auto — router picks the best model",
    "anthropicSave": "Save mapping",
    "anthropicSaved": "Saved",
    "anthropicNote": "Anthropic clients keep their built-in Claude model names; the mapping above decides how each one routes into your pool.",
    "familyDefault": "Default",
    "familyOpus": "Opus",
    "familySonnet": "Sonnet",
    "familyHaiku": "Haiku",
    "selectPlatform": "Select a provider",
    "addKey": "Add key",
    "keyAdded": "Key added",
    "export": "Export",
    "exportAll": "All keys",
    "exportHealthy": "Healthy only",
    "exportKeys": "Export API keys",
    "exportFormat": "Format",
    "exportHealthyOnly": "Healthy keys only",
    "exportHealthyOnlyDesc": "Exclude keys that are currently failing health checks",
    "exportWillExport": "Will export",
    "exportKey": "key",
    "exportKeys plural": "keys",
    "exporting": "Exporting…",
    "exportDownload": "Download",
    "exportSuccess": "Exported {count} keys",
    "adding": "Adding…",
    "selectProvider": "Select a provider…",
    "accountId": "Account ID",
    "label": "Label",
    "noKeyNeeded": "(no key needed)",
    "outboundProxy": "Outbound proxy",
    "outboundProxyDescription": "Route outbound LLM requests through a proxy. Supports SOCKS5, HTTP, and HTTPS.",
    "proxyUrl": "Proxy URL",
    "saveProxy": "Save",
    "savingProxy": "Saving…",
    "proxyLoadFailed": "Could not load proxy settings.",
    "addCustom": "Add a custom OpenAI-compatible model",
    "addCustomDescription": "Point chat, embedding, image, or audio models at any OpenAI-compatible endpoint. The API key is optional for local servers.",
    "customType": "Type",
    "customTypeChat": "Chat",
    "customTypeEmbedding": "Embedding",
    "customTypeImage": "Image",
    "customTypeAudio": "Audio",
    "customBaseUrl": "Base URL",
    "customModels": "Models",
    "customModel": "Model",
    "customDisplayName": "Display name",
    "customDisplayNamePerModel": "per-model",
    "customDisplayNameOptional": "optional",
    "customFamily": "Family",
    "customFamilyPlaceholder": "defaults to model id",
    "customApiKey": "API key",
    "customCapabilities": "Capabilities",
    "addModel": "Add model",
    "modelAdded": "Model added",
    "addModels": "Add {count} models",
    "addEmbeddingModel": "Add embedding",
    "addImageModel": "Add image model",
    "addAudioModel": "Add audio model",
    "addingCustom": "Adding…",
    "getApiKey": "Get API key",
    "deleteKeyConfirm": "Delete this key? This cannot be undone.",
    "checkAll": "Check all",
    "checking": "Checking…",
    "editLabel": "Edit label",
    "saveLabel": "Save label",
    "unifiedKeyDescBefore": "Use this as your OpenAI ",
    "unifiedKeyDescAfter": "; it authenticates requests to this proxy.",
    "serverUnreachableBefore": "Can't reach the server on ",
    "serverUnreachableAfter": ". Make sure the backend is running. npm run dev starts both, and the server logs print under the server prefix.",
    "paneProviderKey": "Provider key",
    "paneImportFile": "Import file",
    "paneCustomEndpoint": "Custom endpoint",
    "platform": "Platform",
    "apiToken": "API token",
    "noKeyNeededPlaceholder": "No API key needed",
    "bearerTokenPlaceholder": "Bearer token",
    "pasteKeyPlaceholder": "paste key here",
    "keylessHint": "No API key needed: this provider's free tier is anonymous (rate-limited per IP).",
    "enable": "Enable",
    "noProviderKeys": "No provider keys yet. Add one above to start routing.",
    "filterPlaceholder": "Filter providers or keys",
    "filterAll": "All",
    "filterHealthy": "Healthy",
    "filterIssues": "Issues",
    "filterDisabled": "Disabled",
    "noFilterMatch": "No providers match your filters.",
    "providerCountSummary": "{providers} providers · {keys} keys",
    "summaryHealthy": "{count} healthy",
    "summaryIssueOne": "{count} issue",
    "summaryIssueOther": "{count} issues",
    "providerActions": "Provider actions",
    "routeViaProxy": "Route via proxy",
    "checkNow": "Check now",
    "proxyEnvHintBefore": "Also configurable via the ",
    "proxyEnvHintAfter": " environment variable (takes precedence). Leave blank to disable. Examples:",
    "importKeys": "Import provider keys",
    "importKeysDescription": "Upload .env, JSON, JSONC, CSV, Markdown, or text files.",
    "importFiles": "Files",
    "importFileCount": "{count} selected",
    "previewFiles": "Preview",
    "previewing": "Previewing…",
    "selected": "Selected",
    "provider": "Provider",
    "keyName": "Key name",
    "keyValue": "Key value",
    "chooseProvider": "Choose provider",
    "noPreviewKeys": "No keys found in those files.",
    "skippedItems": "Skipped:",
    "moreItems": "+{count} more",
    "importSelected": "Import {count}",
    "importing": "Importing…",
    "noImportSelection": "Select at least one row with a provider.",
    "duplicate": "Duplicate",
    "duplicatesFound": "{count} duplicate(s) detected — already exist and will be skipped.",
    "importResult": "Imported {imported}; failed {failed}.",
    "confirmRemove": "Confirm?",
    "quotaSignalsTitle": "Quota signals",
    "quotaSignalsEmptyTitle": "No quota observations yet",
    "quotaSignalsEmptyDesc": "This fills in after providers return rate-limit headers, quota errors, or validation signals.",
    "quotaKeyRef": "key #{id}",
    "quotaPoolRef": "pool {pool}",
    "quotaLimit": "Limit",
    "quotaRemaining": "Remaining",
    "quotaReset": "Reset",
    "quotaObserved": "Observed"
  },
  "status": {
    "healthy": "healthy",
    "rateLimited": "rate-limited",
    "invalid": "invalid",
    "error": "error",
    "unchecked": "unchecked"
  },
  "analytics": {
    "title": "Analytics",
    "description": "Request volume, latency, token usage, and failures.",
    "range24h": "24h",
    "range7d": "7d",
    "range30d": "30d",
    "range90d": "90d",
    "requests": "Requests",
    "successRate": "Success rate",
    "inputTokens": "Input tokens",
    "outputTokens": "Output tokens",
    "avgLatency": "Avg latency",
    "p95Latency": "P95 latency",
    "avgTtft": "Avg TTFT",
    "estSavings": "Est. savings",
    "requestsByProvider": "Requests by provider",
    "avgLatencyByProvider": "Avg latency by provider",
    "ttftByProvider": "Time to first token by provider",
    "ttftEmpty": "No time to first token recorded yet.",
    "requestsOverTime": "Requests over time",
    "tokensOverTime": "Tokens over time",
    "byModel": "By model",
    "byPlatform": "By platform",
    "errors": "Errors",
    "errorDistribution": "Error distribution",
    "pinnedRequests": "Pinned requests",
    "pinnedHonored": "Honored",
    "fallbackAttempts": "Fallback attempts",
    "savingsHint": "You actually saved ${actual} over the last {range}. That is what the same tokens would have cost on paid APIs, priced per model. The number shown projects your pace from the last {span} of data to a full 30 days.",
    "savingsHintExact": "You actually saved ${actual} over the last {range}. That is what the same tokens would have cost on paid APIs, priced per model. The number shown is your real 30-day total.",
    "requestsHintPinned": "{pinned} of these requests pinned a specific model by name. {honored} were served by the pinned model; {failed} failed over to a different one. The rest were auto-routed.",
    "requestsHintAuto": "All requests in this period were auto-routed; no client pinned a specific model by name.",
    "requestsHintTypes": "{chat} chat and {embedding} embedding requests.",
    "rangeLabel24h": "24 hours",
    "rangeLabel7d": "7 days",
    "rangeLabel30d": "30 days",
    "rangeLabel90d": "90 days",
    "spanDays": "{count} days",
    "spanHours": "{count} hours",
    "latencyMs": "Latency (ms)",
    "latency": "Latency",
    "pinned": "Pinned",
    "inTokens": "In tokens",
    "outTokens": "Out tokens",
    "saved": "Saved",
    "message": "Message",
    "time": "Time",
    "perModelBreakdown": "Per-model breakdown",
    "usageByKey": "Usage by key",
    "keyColumn": "Key",
    "keyLabelFallback": "Key #{id}",
    "errorsByProvider": "Errors by provider",
    "recentErrors": "Recent errors",
    "noErrors": "No errors",
    "recentCalls": "Recent calls",
    "clientIp": "Client IP",
    "clientAgent": "Client app",
    "requestedModelHint": "Requested {model}, served by fallback"
  },
  "playground": {
    "title": "Playground",
    "description": "Send a chat completion through the router and see which provider serves it.",
    "autoModel": "Auto (fallback chain)",
    "fusionModel": "Fusion (multi-model synthesis)",
    "selectModel": "Select model",
    "searchModels": "Search models…",
    "noModelsFound": "No models found",
    "send": "Send",
    "sending": "Sending…",
    "clear": "Clear",
    "inputPlaceholder": "Type a message… (⏎ to send, ⇧⏎ for newline)",
    "emptyTitle": "Send a message to get started.",
    "emptyDescription": "Using {model}. Switch models in the selector above.",
    "noModels": "No models yet. Add an API key on the Keys page to populate this list.",
    "copyReply": "Copy reply",
    "fallback": "fallback",
    "fallbacks": "fallbacks",
    "fusionPanel": "Panel",
    "fusionJudge": "Judge",
    "fusionTrace": "{count} model responses",
    "fusionFailed": "no response",
    "fusionJudgeSynth": "synthesized the final answer",
    "errorPrefix": "Error:",
    "systemPromptLabel": "System prompt",
    "systemPromptPlaceholder": "Optional. Sent as a system message before your chat.",
    "errorTitle": "Request failed"
  },
  "embeddings": {
    "title": "Models",
    "description": "Embeddings fail over within a family only: the same model served by another provider. Vectors from different models are incompatible, so the router never swaps models on you.",
    "defaultBadge": "Default · auto",
    "makeDefault": "Make default",
    "moveUp": "Move up",
    "moveDown": "Move down",
    "usageToday": "{count} req today",
    "usageMonth": "{count} tok this month",
    "autoDescription": "model: \"auto\" on POST /v1/embeddings routes to the default family. Naming a family (or a provider model id) pins that family; providers inside it are tried in order.",
    "unsavedChanges": "Unsaved changes",
    "saveChanges": "Save changes",
    "savingChanges": "Saving…",
    "tokMax": "{tokens} tok max",
    "emptyTitle": "No embedding models yet",
    "emptyDesc": "Embedding families appear here once you add a provider key that supports embeddings.",
    "dragToReorder": "Drag to reorder provider"
  },
  "fusion": {
    "title": "Fusion",
    "description": "A panel of models answers in parallel, then a judge synthesizes one stronger answer. Use it by calling the API with model \"fusion\".",
    "panelSource": "Panel source",
    "panelSourceHelp": "Where the panel models come from when a request doesn't specify its own.",
    "mode": {
      "auto": "Auto (Fallback Chain)",
      "autoHelp": "Pick a diverse set of models from your Fallback Chain, ranked by your active routing strategy.",
      "explicit": "Explicit panel",
      "explicitHelp": "Always use the specific models you choose below."
    },
    "panelModels": "Panel models",
    "selectedCount": "{count} of {max} selected",
    "noModels": "No models are connected yet. Add an API key, then enable models in the Fallback Chain.",
    "panelSize": "Panel size",
    "panelSizeHelp": "How many diverse models to pick automatically (1–{max}).",
    "judge": "Judge model",
    "judgeHelp": "The model that synthesizes the panel's answers into one. Auto uses your top-ranked available model.",
    "judgeAuto": "Auto (top-ranked available)",
    "strategy": "Strategy",
    "strategySynthesize": "Synthesize — blend the panel into one answer",
    "strategyBestOf": "Best of — return the single strongest answer (no judge call)",
    "strategySynthesizeShort": "Synthesize",
    "strategyBestOfShort": "Best of",
    "exposePanel": "Expose panel details",
    "exposePanelHelp": "Attach each model's answer and the judge metadata under an x_fusion field in the response.",
    "howToUse": "How to use",
    "howToUseHelp": "Send a normal chat request with model \"fusion\". These settings are the default; any request can override them with a \"fusion\" field.",
    "unsavedChanges": "Unsaved changes",
    "save": "Save"
  },
  "premium": {
    "title": "Premium",
    "description": "The live model catalog, on every device.",
    "loading": "Loading…",
    "checkForUpdates": "Check for updates",
    "syncing": "Syncing…",
    "catalogFeed": "Catalog feed",
    "liveFeed": "Live feed",
    "monthlySnapshot": "Monthly snapshot",
    "lastChecked": "Last checked: {when}",
    "liveDescription": "New free models, quota changes, and quirk fixes land here within hours of being shipped. The app checks automatically twice a day; nothing to do.",
    "snapshotDescription": "Free tier: the catalog refreshes from a monthly snapshot, checked automatically twice a day. Premium switches this to the live feed, updated every 2-3 days.",
    "lastSyncProblem": "Last sync problem: {error}",
    "license": "License",
    "planAnnual": "Premium Annual",
    "planLifetime": "Premium Lifetime",
    "expired": "Expired",
    "inactive": "Inactive",
    "lifetimeNote": "Lifetime license: never expires.",
    "renewsOn": "Renews on {date}.",
    "willNotRenew": "Will not renew. Premium until {date}, then this install falls back to the free monthly catalog.",
    "keyInactive": "This key is no longer active. The app keeps working on the free monthly catalog.",
    "manageSubscription": "Manage subscription",
    "openingPortal": "Opening…",
    "removeKey": "Remove key from this device",
    "manageHint": "Manage subscription opens Stripe's billing portal: cancel, update your card, or download invoices. Removing the key only deactivates this device; your purchase is untouched.",
    "licenseKey": "License key",
    "licenseKeyPlaceholder": "fla_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "activate": "Activate",
    "activating": "Activating…",
    "keyHint": "Your key is in your purchase email and on the post-checkout page. Lost it?",
    "recoverKey": "Recover it on the website",
    "upsellTitle": "Go live for $19 a year",
    "upsellDescription": "New free models the moment they exist, instead of 30 days later. One key, every device. Cancel anytime; the router stays free forever.",
    "goPremium": "Go Premium",
    "bundled": "bundled",
    "planGeneric": "Premium"
  },
  "languages": {
    "en": "English",
    "zh-CN": "中文 (简体)",
    "fr": "Français",
    "es": "Español",
    "pt-BR": "Português (Brasil)",
    "it": "Italiano"
  },
  "errors": {
    "boundaryTitle": "Something went wrong",
    "boundaryDescription": "The dashboard hit an unexpected error. Your data is safe. Try again or go back to the models page.",
    "tryAgain": "Try again",
    "goHome": "Back to models"
  },
  "notFound": {
    "title": "Page not found",
    "description": "This page does not exist or may have moved."
  },
  "setup": {
    "title": "Getting started",
    "subtitle": "Three steps to route your first request.",
    "progress": "{done} of {total}",
    "step1Title": "Add a provider key",
    "step1Desc": "Connect at least one provider account. Free keys work.",
    "step1Cta": "Add keys",
    "step2Title": "Send a test request",
    "step2Desc": "Pick any model in the Playground and say hello.",
    "step2Cta": "Open Playground",
    "step3Title": "Connect your app",
    "step3Desc": "Use your unified API key with any OpenAI-compatible client.",
    "copyKey": "Copy API key",
    "copyUrl": "Copy base URL",
    "copiedKey": "API key copied to clipboard",
    "copiedUrl": "Base URL copied to clipboard"
  },
  "palette": {
    "title": "Command palette",
    "placeholder": "Search pages, models, actions…",
    "empty": "No results",
    "groupPages": "Pages",
    "groupActions": "Actions",
    "groupModels": "Models",
    "toggleTheme": "Toggle dark mode"
  },
  "validation": {
    "required": "Required",
    "url": "Enter a valid URL starting with http:// or https://",
    "email": "Enter a valid email address",
    "passwordMin": "At least {min} characters"
  }
}
