// English resources — translated from zh-CN.ts. Keep keys in sync.

import type { zhCN } from './zh-CN';

// Type-level guarantee that en mirrors the zh-CN shape.
export const en: typeof zhCN = {
  app: {
    name: 'OpenLess',
    tagline: 'Speak naturally, write perfectly',
  },
  common: {
    loading: 'Loading…',
    retry: 'Retry',
    settingsLoadFailed: 'Settings load failed',
    refresh: 'Refresh',
    clear: 'Clear',
    copy: 'Copy',
    delete: 'Delete',
    later: 'Later',
    cancel: 'Cancel',
    close: 'Close',
    show: 'Show',
    hide: 'Hide',
    saved: 'Saved',
    saving: 'Saving…',
    copied: 'Copied',
    operationFailed: 'Operation failed',
    add: 'Add',
    durationSeconds: '{{value}}s',
    durationMinutes: '{{value}}m',
  },
  capsule: {
    thinking: 'thinking',
    cancelled: 'Cancelled',
    error: 'Something went wrong',
    inserted: 'Inserted {{count}}',
    translating: 'Translating',
  },
  qa: {
    thinking: 'Thinking…',
    error: 'Something went wrong. Please try again.',
    errorRetry: 'Retry',
    errorRetryHint: 'Press {{recordHotkey}} again to retry.',
    pinTooltip: 'Pin (stay open)',
    unpinTooltip: 'Unpin',
    closeTooltip: 'Close',
    selectionPreview: 'From selected text:',
    emptyTitle: 'Press {{recordHotkey}} to ask',
    emptyDesc: 'Select text in any app, press {{recordHotkey}} once to start recording, press it again to submit. Answers appear here. You can ask follow-up questions in the same panel.',
    recordingHint: 'Recording… press {{recordHotkey}} again to submit',
    statusIdle: 'Press {{recordHotkey}} to ask',
    statusRecording: 'Recording',
    statusThinking: 'Thinking',
    statusError: 'Error',
  },
  nav: {
    overview: 'Overview',
    history: 'History',
    vocab: 'Vocabulary',
    style: 'Style',
    marketplace: 'Marketplace',
    translation: 'Translation',
    selectionAsk: 'Ask',
    localAsr: 'Models',
  },
  marketplace: {
    kicker: 'MARKETPLACE',
    title: 'Style Pack Marketplace',
    desc: 'Browse, install, and share community style packs.',
    searchPlaceholder: 'Search name / description / tags…',
    sortPopular: 'Popular',
    sortNew: 'Newest',
    uploadBtn: 'Upload',
    uploadDisabledHint: 'Set your GitHub login in Settings → Marketplace first',
    refreshBtn: 'Refresh',
    empty: 'No style packs yet',
    emptyHint: 'Try a different keyword, or upload your own',
    loadFailed: 'Load failed: {{err}}',
    noDescription: '(no description)',
    installBtn: 'Install',
    likeBtn: 'Like',
    installed: 'Installed "{{name}}" locally',
    uploaded: 'Uploaded — waiting for review',
    uploadTitle: 'Pick a style pack to upload',
    uploadHint: 'Uploading as {{login}}. Content goes to the cloud review queue.',
    uploadNoLocal: 'No local style packs to upload',
    errors: {
      detail: 'Detail load failed: {{err}}',
      install: 'Install failed: {{err}}',
      like: 'Like failed: {{err}}',
      upload: 'Upload failed: {{err}}',
      loadLocal: 'Load local packs failed: {{err}}',
    },
    sortLiked: 'Liked',
    likedEmpty: 'You have not liked any style packs yet',
    likedEmptyHint: 'Open any pack and tap the star — liked packs appear here',
    derivativeBadge: 'Derived from @{{login}}',
    detail: {
      withdrawBtn: 'Withdraw',
      withdrawConfirm: 'Withdraw "{{name}}" from the marketplace? Your local copy is kept.',
      withdrawSuccess: 'Withdrawn from marketplace',
      withdrawFailed: 'Withdraw failed: {{err}}',
    },
    myPacks: {
      buttonLabel: 'My Packs',
      buttonTitle: 'View {{login}}\'s publications',
      buttonTitleEmpty: 'Set publisher identity in Settings → Marketplace first',
      searchPlaceholder: 'Search name or tags',
      notLoggedIn: 'Set publisher identity in Settings → Marketplace first',
      emptyTitle: 'You have not published any style packs yet',
      emptyHint: 'Edit a pack in the Style page and click "Publish to Marketplace", or upload a local pack from the top-right.',
      noMatch: 'No matching style packs',
      summary: '{{count}} published',
      summaryPending: '{{count}} published · {{pending}} pending review',
      versionDate: 'v{{version}} · {{date}}',
      stats: '★ {{likes}} · ↓ {{downloads}}',
      actions: {
        update: 'Update',
        withdraw: 'Withdraw',
      },
      loadFailed: 'Failed to load my packs: {{err}}',
      loadingTitle: 'Loading…',
      loadingHint: 'Fetching your latest publications from the marketplace.',
      loadErrorTitle: 'Load failed',
      loadErrorRetry: 'Retry',
    },
    upload: {
      confirmBtn: 'Confirm upload',
      updateTitle: 'Update "{{name}}"',
      updateHint: 'Pick the local newer version, then click "Confirm upload". A same-name pack is pre-selected.',
      recommendedBadge: 'Recommended',
    },
    state: {
      pending: 'Pending',
      approved: 'Published',
      rejected: 'Rejected',
      withdrawn: 'Withdrawn',
      superseded: 'Superseded',
      unknown: 'Unknown',
    },
    oauth: {
      title: 'Sign in with GitHub',
      generating: 'Generating device code…',
      browserHint: 'Open {{uri}} in your browser and enter this code:',
      copyBtn: 'Copy',
      copied: 'Device code copied',
      copyFailed: 'Copy failed: {{err}}',
      openBrowserBtn: 'Open browser',
      cancelBtn: 'Cancel',
      waiting: 'Waiting for browser authorization…',
      successAs: 'Signed in as @{{login}}',
      retryBtn: 'Retry',
      closeBtn: 'Close',
      loginBtn: 'Sign in',
      loginTooltip: 'Sign in with GitHub',
      reloginTooltip: 'Click to re-sign-in / switch account (current @{{login}})',
    },
    modal: {
      loggedIn: 'Current sign-in identity — change in Settings → Recording → Marketplace',
      notLoggedIn: 'Not signed in — go to Settings → Recording → Marketplace to set publisher name',
      notLoggedInLabel: 'Not signed in',
    },
  },
  shell: {
    shortcutLabel: 'Recording shortcut',
    shortcutHint: 'Start / Stop',
    betaTag: 'BETA',
    betaNote: 'All data stays on this device.',
    footer: {
      account: 'Account',
      feedback: 'Feedback',
      settings: 'Settings',
      help: 'Help',
      version: 'Version {{version}}',
      helpPopover: {
        tagline: 'Local-first voice input layer',
        releaseNotes: 'Release notes ↗',
        docs: 'Help center ↗',
      },
    },
    providerPrompt: {
      title: 'Set up speech providers',
      body: 'No ASR or LLM provider is configured yet. Voice input and polishing will not work until you add credentials.',
      later: 'Later',
      openSettings: 'Open Settings',
    },
    hotkeyModePrompt: {
      title: 'Review your recording mode',
      body: 'Default is now Toggle. If you changed the trigger mode before, please confirm it in Recording settings.',
      later: 'Remind me later',
      openSettings: 'Open Recording',
    },
  },
  onboarding: {
    welcome: 'Welcome to OpenLess',
    intro: 'Speak locally, type locally. Two system permissions are needed before you start.',
    accessibilityTitle: 'Accessibility',
    hotkeyTitle: 'Global hotkey',
    accessibilityDesc: 'Used to listen to the global hotkey (default {{trigger}}) and write transcripts at the cursor.',
    hotkeyDesc: 'Used to confirm that the global hotkey listener is available.',
    micTitle: 'Microphone',
    micDesc: 'Used to capture your voice input.',
    actionNotApplicable: 'Not required',
    actionGranted: 'Granted',
    actionOpenSystem: 'Open System Settings',
    actionGrant: 'Grant',
    actionRequestMic: 'Request access',
    accessibilityHint: 'After granting, you must **fully quit OpenLess** and reopen it (a macOS TCC requirement).',
    footerHint: 'This onboarding closes automatically once both permissions are granted. If it persists, quit OpenLess from the menu bar and relaunch.',
  },
  overview: {
    kicker: 'DASHBOARD',
    title: "Today's overview",
    desc: 'Today\'s dictation stats and system status.',
    pressPrefix: 'Press',
    pressSuffix: 'to start',
    asrKind: 'ASR',
    llmKind: 'LLM',
    asrName: 'Volcengine',
    asrSubname: 'bigmodel',
    llmName: 'OpenAI-compatible',
    llmConfigured: 'Active LLM configured',
    llmNotConfigured: 'Not configured',
    statusConfigured: 'Configured',
    statusNotConfigured: 'Not configured',
    statusUnknown: 'Unavailable',
    credentialsLoadError: 'Could not read credential status',
    metricChars: 'Characters today',
    metricSegments: '{{count}} segments',
    metricDuration: 'Total duration today',
    metricAvg: 'Avg per segment',
    metricAvgTrend: "Today's average",
    metricNoData: 'No data',
    historyLoadError: 'History load failed',
    metricTotal: 'Total records',
    metricTotalTrend: 'Local archive (max 200)',
    weekTitle: 'Last 7 days',
    weekUnit: 'count / day',
    recentTitle: 'Recent transcripts',
    recentAll: 'View all →',
    recentEmpty: 'No records yet. Press {{trigger}} to start your first recording.',
    recentLoadFailed: 'Could not load recent transcripts. Please retry.',
    historyRetry: 'Retry',
    weekDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
  },
  history: {
    kicker: 'HISTORY',
    title: 'History',
    desc: 'Locally stored transcripts.',
    filterAll: 'All',
    summary: '{{total}} total · showing {{shown}}',
    empty: 'No history yet. Press {{trigger}} to record one.',
    loadFailed: 'Failed to load history: {{err}}',
    retry: 'Retry',
    clearFailed: 'Failed to clear history: {{err}}',
    deleteFailed: 'Failed to delete entry: {{err}}',
    copyFailed: 'Failed to copy: {{err}}',
    playRecording: 'Play recording',
    audioLoading: 'Loading…',
    exportRecording: 'Export recording',
    exportFailed: 'Failed to export: {{err}}',
    rawLabel: 'Raw',
    rawEmpty: '(empty)',
    selectHint: 'Select an entry on the left to see details.',
    insertedTo: 'Inserted into',
    chars: '{{count}} chars',
    vocabHits: '{{count}} vocab hits',
    inserted: 'Inserted',
    pasteSent: 'Paste sent',
    copiedFallback: 'Copied (use {{shortcut}})',
    insertFailed: 'Insert failed',
    confirmClear: 'Delete all {{count}} history entries? This cannot be undone.',
  },
  vocab: {
    kicker: 'VOCABULARY',
    title: 'Vocabulary',
    desc: 'Add terms or jargon to improve recognition accuracy.',
    sectionTitle: 'Entries',
    placeholder: 'Type a word, press Enter or click Add…',
    tip: 'Mixed Chinese/English supported · numeric prefixes are matched literally · hits counted automatically',
    loadFailed: 'Load failed: {{err}}',
    empty: 'No entries yet. Add a new term or piece of jargon above so the model can prioritize it.',
    tipDisabled: 'Click to disable this entry',
    tipEnabled: 'Click to enable this entry',
    removeAria: 'Remove',
    corrections: {
      title: 'Correction rules',
      tip: 'Fix common ASR mistakes. Supports {num} number wildcard.',
      patternPlaceholder: 'Mistaken text, e.g. {num}粒',
      replacementPlaceholder: 'Target text, e.g. {num}例',
      empty: 'No correction rules yet.',
      invalid: 'Only literal replacements or one {num} number wildcard are supported, for example {num}粒 → {num}例.',
      tipDisabled: 'Click to disable this rule',
      tipEnabled: 'Click to enable this rule',
      removeAria: 'Remove correction rule',
    },
    presets: {
      title: 'Scenario presets',
      tip: 'Multi-select to apply in batch. Supports edit and create.',
      create: 'New preset',
      apply: 'Apply selected',
      save: 'Save preset',
      edit: 'Edit {{name}}',
      newPreset: 'New preset',
      namePlaceholder: 'Preset name',
      wordsPlaceholder: 'Terms (comma or newline separated)',
    },
  },
  style: {
    kicker: 'STYLE',
    title: 'Output style',
    desc: 'Choose the default output style for recording.',
    masterToggle: 'Master switch',
    currentDefault: 'Current default',
    ariaSetDefault: 'Set as default',
    saveFailed: 'Save failed: {{error}}',
    customPromptTitle: 'Custom prompt',
    customPromptPlaceholder: 'Optional. Appended to this style’s built-in system prompt.',
    customPromptHint: 'Leave empty to preserve current behavior. After saving, it applies to both this style’s live polish path and repolish. Press Ctrl/Cmd+Enter to save as well.',
    customPromptSave: 'Save prompt',
    customPromptDirty: 'Unsaved',
    systemPromptMovedHint: 'Full system prompt editing has moved to Settings -> Providers. This page now only controls which styles are enabled and which one is the default.',
    modes: {
      raw: { name: 'Raw', desc: 'Only adds punctuation and natural breaks — no rewriting or expansion.', sample: "Keeps spoken cadence; fillers like 'um' or 'you know' get dropped, but sentences stay intact." },
      light: { name: 'Light polish', desc: 'Drops fillers, adds punctuation, and produces sendable natural prose.', sample: "Makes the transcript flow well without sounding scripted — your tone and habits remain." },
      structured: { name: 'Structured', desc: 'Auto-organizes into a numbered outline when you cover several topics or steps.', sample: '1. Topic one\na. Point\nb. Point\n2. Topic two\na. Point\nb. Point' },
      formal: { name: 'Formal', desc: 'Email and workplace tone — more complete, more professional.', sample: 'Detects greetings/sign-offs in email contexts; avoids empty pleasantries.' },
    },
    pack: {
      kicker: 'STYLE PACKS',
      title: 'Style Packs',
      desc: 'Manage local style packs.',
      marketplaceBtn: 'Marketplace',
      loadFailed: 'Failed to load style packs: {{err}}',
      importZip: 'Import ZIP',
      exportZip: 'Export ZIP',
      exportShort: 'Export',
      publishMarketplace: 'Publish to Marketplace',
      updateMarketplace: 'Update Marketplace version',
      publishDisabledHint: 'Configure your GitHub login in Settings → Marketplace first',
      publishSuccess: 'Published — pending review on marketplace',
      publishFailed: 'Publish failed: {{err}}',
      publishBuiltinRejected: 'Built-in packs cannot be published. Clone first via edit.',
      builtin: 'Built-in',
      imported: 'Imported',
      active: 'Active',
      activate: 'Activate',
      edit: 'Edit',
      closeEditor: 'Close',
      unsaved: 'Unsaved',
      listTitle: 'Local Packs',
      listDesc: 'Browse and switch packs.',
      listCount: '{{count}} packs',
      addPackTileTitle: 'New Pack',
      addPackTileHint: 'Start from a blank template.',
      createSuccess: 'New pack created.',
      createFailed: 'Failed to create pack: {{err}}',
      save: 'Save',
      revert: 'Revert',
      saveSuccess: 'Style pack saved.',
      saveFailed: 'Failed to save style pack: {{err}}',
      activateSuccess: 'Set "{{name}}" as current.',
      activateFailed: 'Failed to set current style pack: {{err}}',
      importSuccess: 'Imported "{{name}}".',
      importFailed: 'Failed to import ZIP: {{err}}',
      exportSuccess: 'Exported to {{path}}',
      exportFailed: 'Failed to export ZIP: {{err}}',
      exportDirtyFirst: 'Save this pack before exporting ZIP.',
      resetBuiltin: 'Reset',
      resetSuccess: 'Reset "{{name}}".',
      resetFailed: 'Failed to reset pack: {{err}}',
      deleteImported: 'Delete',
      deleteConfirm: 'Delete "{{name}}"? This cannot be undone.',
      deleteSuccess: 'Deleted "{{name}}".',
      deleteFailed: 'Failed to delete pack: {{err}}',
      summaryCurrentEmpty: 'No pack selected yet',
      editorTitle: 'Edit Pack',
      editorDesc: 'Edit this pack.',
      metaTitle: 'Installation Info',
      metaSource: 'Source',
      metaBaseMode: 'Base Mode',
      metaUpdatedAt: 'Updated',
      fieldName: 'Name',
      fieldAuthor: 'Author',
      fieldAuthorPlaceholder: 'Optional source label',
      fieldVersion: 'Version',
      fieldTags: 'Tags',
      fieldTagsPlaceholder: 'Comma-separated tags, e.g. community, voiceover, formal',
      fieldDescription: 'Description',
      fieldModel: 'Recommended Model (Metadata)',
      fieldModelPlaceholder: 'Optional, e.g. gpt-4.1 / deepseek-v3',
      fieldModelHint: 'Metadata only. Does not switch model.',
      fieldCompatibility: 'Compatible App Version',
      fieldCompatibilityPlaceholder: 'Optional, e.g. >=1.3.0',
      fullPromptTitle: 'System Prompt',
      fullPromptHint: 'The prompt owned by this pack.',
      promptChars: '{{count}} chars',
      runtimeTitle: 'OpenLess Runtime Directives',
      runtimeDesc: 'Read-only runtime helpers.',
      runtimeContextTitle: 'Context premise',
      runtimeContextDesc: 'From language and app context',
      runtimeContextEmpty: 'Not added in the current preview.',
      runtimeHotwordTitle: 'Hotword block',
      runtimeHotwordDesc: 'From enabled hotwords',
      runtimeHotwordEmpty: 'Not added in the current preview.',
      runtimeHistoryTitle: 'Multi-turn history guardrail',
      runtimeHistoryDesc: 'Only for live multi-turn polish',
      runtimeHistoryEmpty: 'Only added when prior turns exist.',
      runtimeActive: 'Active',
      runtimeInactive: 'Inactive',
      runtimePreviewFailed: 'Failed to build runtime preview: {{err}}',
      runtimePreviewOmittedFrontApp: 'Preview omits the front-app label.',
      examplesTitle: 'Effect Examples',
      examplesDesc: 'Exported with the pack.',
      addExample: 'Add Example',
      examplesEmpty: 'No examples yet.',
      exampleTitlePlaceholder: 'Example {{index}} title',
      exampleInput: 'Input',
      exampleOutput: 'Output',
      examplesCount: '{{count}} examples',
      discardCloseConfirm: 'Discard unsaved changes and close the editor?',
      discardSwitchConfirm: 'Discard unsaved changes and switch to "{{name}}"?',
      derivativeBadge: 'Derived from @{{login}}',
    },
  },
  translation: {
    kicker: 'TRANSLATION',
    title: 'Translation',
    desc: 'Auto-translate recordings into a target language before insertion.',
    statusEnabled: 'Enabled',
    statusDisabled: 'Disabled',
    working: {
      title: 'Working languages',
      desc: 'Select languages you use regularly to improve polish and translation.',
    },
    target: {
      title: 'Translation target language',
      desc: 'Press Shift during recording to trigger translation. "Disabled" makes Shift a no-op.',
      disabled: 'Disabled (Shift does nothing)',
    },
    save: {
      workingFailed: 'Failed to save working languages. Please try again.',
      targetFailed: 'Failed to save translation target. Please try again.',
      hotkeyRegisterFailed: 'Failed to register the translation shortcut. The preference was not saved.',
      hotkeySaveFailed: 'Failed to save the translation shortcut. Please try again.',
    },
    howto: {
      title: 'How to use',
      step1: 'Place cursor in any text field.',
      step2: 'Press {{trigger}} to start recording.',
      step3: 'Press Shift once during recording to activate translation.',
      step4: 'Press {{trigger}} again to stop.',
      step5: 'Translated text is inserted at the cursor.',
      indicatorTitle: 'How to confirm translation mode is on',
      indicatorDesc: 'A blue "Translating" indicator appears at the bottom of the screen after pressing Shift.',
      fallbackTitle: 'Safety fallbacks',
      fallbackDesc: 'If translation fails, the raw transcript is inserted instead.',
    },
  },
  selectionAsk: {
    kicker: 'SELECTION ASK',
    title: 'Selection Ask',
    desc: 'Select text and ask questions by voice, with multi-turn follow-ups.',
    statusEnabled: 'Enabled',
    statusDisabled: 'Disabled',
    hotkey: {
      title: 'Hotkey to open the panel',
      desc: 'Opens/closes the panel. Recording uses {{recordHotkey}}.',
      optionDisabled: 'Disabled',
      chordWarning: '',
    },
    save: {
      hotkeyRegisterFailed: 'Failed to register the Selection Ask shortcut. The preference was not saved.',
      hotkeySaveFailed: 'Failed to save the Selection Ask shortcut. Please try again.',
      historySaveFailed: 'Failed to save the Q&A history setting. Please try again.',
    },
    history: {
      title: 'Save history',
      desc: 'Save Q&A records locally when enabled. Off by default.',
    },
    howto: {
      title: 'How to use',
      step1: 'Press {{hotkey}} to open the panel.',
      step2: 'Select text in any app.',
      step3: 'Press {{recordHotkey}} to record, press again to submit.',
      step4: 'Continue pressing {{recordHotkey}} for follow-up questions.',
      step5: 'Press Esc to close and clear history.',
      windowTitle: 'Position, drag, and pin',
      windowDesc: 'The panel is draggable and remembers its position. Pin to keep it open.',
      privacyTitle: 'Privacy contract',
      privacyDesc: 'Selected text lives only in memory until the panel closes. Text over 4000 chars is truncated.',
    },
  },
  settings: {
    kicker: 'SETTINGS',
    title: 'Settings',
    desc: 'Recording, providers, shortcuts, and permissions.',
    sections: {
      recording: 'Recording',
      providers: 'Providers',
      shortcuts: 'Shortcuts',
      permissions: 'Permissions',
      language: 'Language',
      advanced: 'Advanced',
      about: 'About',
    },
    recording: {
      title: 'Recording',
      desc: 'Global recording hotkey and trigger mode.',
      hotkeyLabel: 'Recording hotkey',
      hotkeyDescAcc: 'Press to capture voice globally (requires Accessibility permission).',
      hotkeyDescNoAcc: 'Press to capture voice globally.',
      modeLabel: 'Trigger mode',
      modeDesc: 'Toggle = tap once to start, again to stop. Push-to-talk = hold to record.',
      modeToggle: 'Toggle',
      modeHold: 'Push-to-talk',
      migrationNoticeTitle: 'Default recording mode is now Toggle',
      migrationNoticeDesc: 'This update changes the default; if you prefer push-to-talk, switch it back here.',
      microphoneLabel: 'Preferred microphone',
      microphoneDesc: 'Choose the preferred input device; falls back to system default when unavailable.',
      microphoneDefault: 'System default microphone',
      microphoneDefaultDesc: 'Use the system default input device',
      microphoneSystemDefault: 'system default',
      microphoneUnavailable: 'unavailable',
      microphoneLoadError: 'Failed to load microphones: {{message}}',
      microphoneDialogTitle: 'Microphone',
      microphoneDialogDesc: 'Choose a microphone that can pick up your voice.',
      microphoneMonitorError: 'Failed to monitor input level: {{message}}',
      capsuleLabel: 'Recording capsule',
      capsuleDesc: 'Show a translucent capsule at the bottom of the screen while recording.',
      muteDuringRecordingLabel: 'Mute while recording',
      muteDuringRecordingDesc: 'Temporarily mute system output during voice input to avoid speaker echo.',
      insertGroupTitle: 'Insertion & clipboard',
      restoreClipboardLabel: 'Restore clipboard after insert',
      restoreClipboardDesc: 'Restore your original clipboard after a successful paste (Windows / Linux only).',
      pasteShortcutLabel: 'Simulated paste shortcut',
      pasteShortcutDesc: 'Which paste combo to simulate when inserting; some terminals need Ctrl+Shift+V (Windows / Linux only).',
      pasteShortcutCtrlV: 'Ctrl+V (default / most apps)',
      pasteShortcutCtrlShiftV: 'Ctrl+Shift+V (kitty / alacritty / wezterm / most terminals)',
      pasteShortcutShiftInsert: 'Shift+Insert (xterm / urxvt)',
      comboRecordLabel: 'Record shortcut',
      comboRecordDesc: 'Click, then press your desired key combination (e.g. \u2318\u21E7D). Supports Toggle and Push-to-talk modes.',
      comboRecordBtn: 'Record shortcut',
      comboRecordHint: 'Press your shortcut combination\u2026',
      comboRecorded: 'Recorded',
      comboClear: 'Clear',
      comboConflict: 'This shortcut combination is not available',
      allowNonTsfFallbackLabel: 'Allow non-TSF fallback',
      allowNonTsfFallbackDesc: 'Windows: when TSF insertion fails, use paced Unicode SendInput; if that still fails, copy the text to the clipboard.',
      historyGroupTitle: 'History & context',
      historyRetentionLabel: 'History retention (days)',
      historyRetentionDesc: 'Entries older than this are pruned on new writes; 0 = no time-based pruning.',
      historyMaxEntriesLabel: 'Max history entries',
      historyMaxEntriesDesc: 'Max sessions retained locally. Blank = 200. Range 5–200.',
      polishContextWindowLabel: 'Polish context window (minutes)',
      polishContextWindowDesc: 'Use the last N minutes of polished transcripts as multi-turn context; 0 = disabled.',
      recordAudioForDebugLabel: 'Keep raw recording (debug)',
      recordAudioForDebugDesc: 'Save raw microphone audio as wav for diagnosing recognition issues.',
      audioRecordingMaxEntriesLabel: 'Max raw recordings',
      audioRecordingMaxEntriesDesc: 'Max wav files retained locally. Blank = 200.',
      startupGroupTitle: 'Startup',
      startMinimizedLabel: 'Start minimized (no main window)',
      startMinimizedDesc: 'No main window on any launch path — menu bar / tray only.',
      autoUpdateCheckLabel: 'Auto-check for updates',
      autoUpdateCheckDesc: 'Check for updates on launch and every 60 minutes.',
      marketplaceGroupTitle: 'Style Pack Marketplace',
      marketplaceBaseUrlLabel: 'Backend URL',
      marketplaceBaseUrlDesc: 'Marketplace backend URL. Blank uses the default.',
      marketplaceDevLoginLabel: 'GitHub login (upload identity)',
      marketplaceDevLoginDesc: 'Identifies the uploader. Blank disables upload and likes.',
      startupAtBoot: 'Launch at login',
      startupAtBootDesc: 'Start OpenLess automatically when you sign in.',
      startupAtBootError: 'Failed to toggle launch at login: {{message}}',
    },
    providers: {
      llmTitle: 'LLM (polishing)',
      llmDesc: 'OpenAI-compatible protocol. Multiple vendors supported.',
      providerLabel: 'Provider',
      llmProviderDesc: 'Selecting a preset auto-fills the default Base URL.',
      credentialStorageNotice: 'Credentials are stored in the OS credential vault.',
      codexOAuthNotice: 'Codex OAuth uses the local Codex login state (~/.codex/auth.json). OpenLess does not store an API key or Base URL for this provider.',
      asrProviderDesc: 'Switching providers automatically loads the matching credentials.',
      asrTitle: 'ASR (transcription)',
      asrDesc: 'Used to turn speech into text in real time.',
      presets: {
        ark: 'ARK (Volcengine Ark)',
        deepseek: 'DeepSeek',
        siliconflow: 'SiliconFlow',
        openai: 'OpenAI',
        gemini: 'Google Gemini',
        codexOAuth: 'Codex OAuth',
        mimo: 'Xiaomi MiMo',
        cometapi: 'CometAPI',
        openrouterFree: 'OpenRouter (free models)',
        alibabaCoding: 'Alibaba Cloud Coding Plan',
        codingPlanX: 'CodingPlanX',
        custom: 'Custom',
        asrVolcengine: 'Volcengine bigasr',
        asrBailian: 'Alibaba Bailian realtime ASR',
        asrSiliconflow: 'SiliconFlow SenseVoice',
        asrZhipu: 'Zhipu GLM-ASR',
        asrGroq: 'Groq Whisper-large-v3',
        asrWhisper: 'OpenAI Whisper (compatible)',
        asrFoundryLocalWhisper: 'Local Whisper (Foundry Local)',
        asrLocalQwen3: 'Local Qwen3-ASR',
      },
      volcengineAppKeyLabel: 'APP ID',
      volcengineAccessKeyLabel: 'Access Token',
      volcengineResourceIdLabel: 'Resource ID',
      volcengineMappingNote: 'Secret Key is not required right now. Resource ID defaults to volc.bigasr.sauc.duration.',
      localAsrActiveNotice: 'Local ASR ({{name}}) is currently active. Switch or disable it from the Advanced tab.',
      localAsrTakeoverHint: 'Once "{{name}}" is enabled, the ASR provider will be taken over.',
      asrProviderTakenOver: 'ASR provider taken over',
      localAsrHint: 'Runs on this machine, no API key needed. Download the model from HuggingFace.',
      foundryLocalAsrHint: 'Runs on this device, no ASR API key needed. First use downloads runtime components and model.',
      localAsrPerformanceWarning: 'Local inference is slower than cloud ASR with potentially lower Chinese accuracy. Best for offline or privacy-sensitive use.',
      localAsrReady: '{{model}} downloaded',
      localAsrNotReady: '{{model}} not downloaded',
      localAsrGoDownload: 'Open Models page to download',
      localAsrManage: 'Open Models page',
      localAsrDownloadedTitle: 'Downloaded models',
      localAsrDelete: 'Delete',
      fillDefault: 'Fill default value',
      readFailed: 'Read failed',
      apiKeyLabel: 'API Key',
      baseUrlLabel: 'Base URL',
      modelLabel: 'Model',
      thinkingModeLabel: 'Thinking',
      thinkingModeOn: 'On',
      thinkingModeOff: 'Off',
      thinkingModeHint: 'Off disables or minimizes thinking with provider-level official parameters. On enables thinking by channel defaults. No prompt injection or per-model adapters.',
      bailianVocabularyIdLabel: 'Hotword Vocabulary ID (optional)',
      bailianVocabularyIdNote: 'If you have created a DashScope hotword vocabulary, enter its vocab-... ID. Leave blank to skip hotwords.',
      appIdLabel: 'App ID',
      accessKeyLabel: 'Access Key',
      resourceIdLabel: 'Resource ID',
      toolsLabel: 'Connection check',
      toolsDesc: 'Save the fields above, then validate the selected model or fetch models. Manual model input remains available if fetching fails.',
      validate: 'Validate',
      validating: 'Validating…',
      fetchModels: 'Fetch models',
      loadingModels: 'Fetching models…',
      modelMissing: 'No model is configured. Please enter a model ID first.',
      modelsEmpty: 'Credentials are valid, but no models were returned.',
      modelsLoaded: 'Fetched {{count}} models.',
      selectModel: 'Select a model to fill the field above',
      modelSaved: 'Saved model {{model}}.',
      validateSuccess: 'Connection check passed.',
      providerHttpStatus: 'Provider returned HTTP {{status}}. Check the API key permissions or endpoint.',
      endpointMustUseHttps: 'Endpoint must use HTTPS (localhost/127.0.0.1 are allowed for local testing).',
      endpointInvalid: 'Endpoint format is invalid.',
      responseTooLarge: 'Provider response is too large to validate safely.',
      asrInvalidJson: 'ASR response is not valid JSON.',
      asrMissingTextField: 'ASR response is missing the text field.',
      apiKeyMissing: 'API Key is empty.',
      endpointMissing: 'Endpoint is empty.',
      requestTimeout: 'Request timed out. Try again later.',
    },
    shortcuts: {
      title: 'Shortcut reference',
      descAcc: 'All shortcuts apply globally. Accessibility permission must be granted in Permissions.',
      descNoAcc: 'All shortcuts apply globally. If unresponsive, check the global hotkey status in Permissions.',
      startStop: 'Start / Stop recording',
      cancel: 'Cancel current recording',
      confirm: 'Confirm capsule insertion',
      switchStyle: 'Switch to previous style',
      openApp: 'Open OpenLess',
      confirmHint: 'Click ✓ on the capsule',
      notSupported: 'Not yet supported',
    },
    permissions: {
      title: 'Permissions',
      descAcc: 'OpenLess needs the following system permissions to work. After granting, fully quit and relaunch the app for changes to take effect.',
      descNoAcc: 'OpenLess needs microphone access and uses the global hotkey listener state to verify the native hook is running.',
      micLabel: 'Microphone',
      micDesc: 'Used to capture your voice input.',
      accLabel: 'Accessibility',
      accDesc: 'Used to listen to the global hotkey and write transcripts at the cursor.',
      hotkeyLabel: 'Global hotkey',
      hotkeyDescWithAdapter: 'Active adapter: {{adapter}}. Used to confirm the hotkey listener is installed.',
      hotkeyDescPlain: 'Used to confirm the hotkey listener is installed.',
      networkLabel: 'Network',
      networkDesc: 'Required for cloud ASR / LLM calls. Disable for local-only mode.',
      networkOk: 'Available',
      networkOffline: 'Unavailable',
      checking: 'Checking…',
      granted: 'Granted',
      notApplicable: 'Not required',
      denied: 'Not granted',
      indeterminate: 'Undetermined',
      openSystem: 'Open System Settings',
      grant: 'Grant',
      hotkeyInstalled: 'Installed',
      hotkeyStarting: 'Installing…',
      hotkeyFailed: 'Listener failed',
      windowsImeLabel: 'Windows input method backend',
      windowsImeDesc: 'Temporarily switches to the OpenLess TSF IME during voice sessions to avoid clipboard insertion limits.',
      windowsImeInstalled: 'Installed',
      windowsImeUnavailable: 'Unavailable',
      windowsIme: {
        installed: 'Installed. Voice input temporarily switches to the OpenLess IME.',
        notInstalled: 'Not installed. OpenLess is using the clipboard/WM_PASTE fallback.',
        registrationBroken: 'Registration is broken. Reinstall the OpenLess IME.',
        notWindows: 'Only available on Windows.',
      },
    },
    advanced: {
      streamingInsertTitle: 'Streaming insertion',
      streamingInsertTitleLinux: 'Streaming insertion (experimental)',
      streamingInsertDesc:
        'Streams text to cursor character by character, reducing perceived latency. Falls back to one-shot paste when conditions are not met.',
      streamingInsertLabel: 'Streaming insertion',
      streamingInsertHintMac:
        'Temporarily switches the input source to ABC so CJK IMEs cannot intercept keystrokes; restored on session end.',
      streamingInsertHintWindows:
        'SendInput Unicode types directly, bypassing TSF / IME — no input-method switching needed.',
      streamingInsertHintLinux:
        'Uses fcitx5 plugin for text submission; streaming insertion uses enigo + XTest for keystroke synthesis.',
      streamingInsertSaveClipboardLabel: 'Copy to clipboard',
      streamingInsertSaveClipboardHint: 'After a successful insert, write the final text to the clipboard so Cmd+V can paste it again. Off = clipboard is never touched.',
      localAsrTitle: 'Local ASR models (experimental)',
      localAsrDesc: 'Move transcription from cloud ASR to on-device inference. Offline / privacy-sensitive use only.',
      localAsrWarningShort: 'Local inference is slower; under-spec hardware may drop words.',
      qwen3Desc: 'Once enabled, the ASR provider will be taken over.',
      foundryDesc: 'Once enabled, the ASR provider will be taken over.',
      notSupportedHere: 'Not supported on this platform — no inference module bundled.',
      enable: 'Enable',
      alreadyActive: 'Active',
      disableLocalLabel: 'Disable local ASR',
      disableLocalDesc: 'Switch back to cloud ASR (defaults to Volcengine bigasr).',
      disable: 'Disable',
      platformNotSupported: 'Local ASR model integration is not supported on this platform.',
      confirmEnableLocalTitle: 'Enable local ASR?',
      confirmEnableLocalBody: 'Transcription will be slower than cloud and potentially less accurate.',
      confirm: 'Enable',
    },
    language: {
      title: 'Interface language',
      desc: 'Switch the UI language. Applies to the current session immediately and persists across launches.',
      label: 'Language',
      labelDesc: 'Choose "Follow system" to match the OS language at launch.',
      followSystem: 'Follow system',
      zh: '简体中文',
      zhTW: '繁體中文',
      en: 'English',
      ja: '日本語 (Beta)',
      ko: '한국어 (Beta)',
      restartHint: 'Some native menus (system tray, etc.) may require an app restart to fully switch.',
    },
    about: {
      tagline: 'Speak naturally, write perfectly',
      checkUpdate: 'Check for updates',
      checkUpdateBtn: 'Check',
      checkingUpdate: 'Checking…',
      upToDate: 'You are already on the latest version.',
      updateError: 'Update check or install failed. Please try again later.',
      retryBtn: 'Retry',
      openReleases: 'Open Releases',
      source: 'Source',
      docs: 'Docs',
      feedback: 'Feedback',
      qq: 'QQ community group',
      qqDesc: 'Search the group number in QQ to join, or scan the QR code.',
      copyQq: 'Copy group number',
      privacy: 'Privacy',
      privacyDesc: 'All data stays on this device. Cloud APIs do not retain recordings.',
      localFirst: 'Local-first',
      betaChannelLabel: 'Join Beta channel',
      betaChannelDesc: 'Receive Beta updates when enabled. May be unstable — recommended only for early adopters.',
      betaChannelFetching: 'Fetching the latest Beta…',
      betaChannelFetchBtn: 'Look up latest Beta',
      betaChannelLatestPrefix: 'Latest Beta:',
      betaChannelDownloadBtn: 'Open download page',
      betaChannelRefresh: 'Refresh',
      betaChannelNoBeta: 'No Beta release has been published yet.',
      betaChannelFetchError: 'Failed to fetch Beta release info. Please try again later.',
      betaChannelUpToDate: 'Up to date',
      betaChannelUpdateNow: 'Update now',
      betaChannelUpdateNowTitle: 'Check and download the latest Beta, then show the update dialog',
      betaChannelChecking: 'Checking…',
      updateDialog: {
        available: {
          title: 'Update available',
          desc: 'OpenLess {{version}} is available. Update now?',
        },
        downloading: {
          title: 'Downloading update',
          desc: 'Downloading OpenLess {{version}}. Keep the app open.',
        },
        downloaded: {
          title: 'Update ready',
          desc: 'OpenLess {{version}} has been installed. Restart automatically now to apply it?',
        },
        installing: {
          title: 'Installing update',
          desc: 'Installing OpenLess {{version}}. Keep the app open.',
        },
        install: 'Update now',
        downloadingLabel: 'Downloading…',
        installingLabel: 'Installing…',
        later: 'Restart manually later',
        restartNow: 'Restart now',
        progress: '{{progress}}% · {{downloaded}} / {{total}}',
        progressUnknown: '{{downloaded}} downloaded',
      },
    },
  },
  modal: {
    sections: {
      account: 'Account',
      settings: 'Settings',
      personalize: 'Personalize',
      about: 'About',
      helpCenter: 'Help center',
      releaseNotes: 'Release notes',
    },
    account: {
      localUser: 'Local user',
      localUserDesc: 'Not signed in · all data stays local',
      loginSync: 'Sign in / Sync',
      footer: 'Runs fully locally by default. Sign in to sync vocabulary and style presets across devices.',
    },
    personalize: {
      appearance: 'Appearance',
      appearanceDesc: 'Follow system / Light / Dark',
      appearanceSystem: 'Follow system',
      appearanceLight: 'Light',
      appearanceDark: 'Dark',
      font: 'Font size',
      fontDesc: 'Scale the entire UI font size — applies instantly.',
      fontSmall: 'Small',
      fontMedium: 'Medium',
      fontLarge: 'Large',
      blur: 'Glass blur intensity',
      blurDesc: 'Affects the inner backdrop-filter strength (the macOS system frosted layer can not be tuned at runtime).',
      startupOpen: 'On launch',
      startupOverview: 'Overview',
      startupLast: 'Last position',
      startupAtBoot: 'Launch at login',
    },
    about: {
      tagline: 'Speak naturally, write perfectly',
      checkUpdate: 'Check for updates',
      checkUpdateBtn: 'Check',
      docs: 'Docs',
      docsBtn: 'openless.app/docs ↗',
      feedback: 'Feedback channel',
      feedbackBtn: 'GitHub Issues ↗',
      source: 'Source',
      qq: 'Community QQ Group',
      qqDesc: 'Search the group number on QQ to join, or scan the QR code.',
      copyQq: 'Copy group number',
      exportErrorLog: 'Export error log',
      exportErrorLogDesc: 'Save the current session log to disk for debugging or sending us feedback.',
      exportErrorLogBtn: 'Export',
      exporting: 'Exporting…',
      exportSuccess: 'Saved',
      exportFailed: 'Export failed',
      privacy: 'Privacy',
      privacyDesc: 'All transcripts stay on this device. Cloud APIs are used only for real-time calls.',
      localFirst: 'Local-first',
    },
  },
  windowChrome: {
    minimize: 'Minimize',
    maximize: 'Maximize',
    close: 'Close',
  },
  hotkey: {
    triggers: {
      rightOption: 'Right Option',
      leftOption: 'Left Option',
      rightControl: 'Right Control',
      leftControl: 'Left Control',
      rightCommand: 'Right Command',
      fn: 'Fn (Globe key)',
      rightAlt: 'Right Alt',
      custom: 'Custom combination\u2026',
    },
    fallback: 'Global hotkey',
    modeHoldSuffix: ' (push-to-talk)',
    modeToggleSuffix: ' (start / stop)',
    usageHold: 'Hold {{trigger}} to talk, release to stop.',
    usageToggle: 'Press {{trigger}} to start, press again to stop.',
    adapter: {
      macEventTap: 'macOS Event Tap',
      windowsLowLevel: 'Windows low-level keyboard hook',
      fcitx5: 'fcitx5 input method plugin',
    },
  },
  localAsr: {
    kicker: 'LOCAL ASR',
    title: 'Models',
    desc: 'Manage on-device speech recognition models.',
    qwenTitle: 'Qwen3-ASR model manager',
    qwenExperimentalBadge: 'Experimental',
    engineUnavailable: 'The Qwen3-ASR inference engine is not bundled on this platform. You can still download models, but Qwen3-ASR cannot be activated here yet.',
    qwenUnavailableOnWindows: 'Qwen3-ASR is not supported on Windows yet. Please use Foundry Local Whisper above instead.',
    foundryTitle: 'Windows Foundry Local Whisper',
    foundryDesc: 'On-device speech recognition, no ASR API key needed. First use requires downloading runtime and model.',
    foundryAvailable: 'Available on Windows',
    foundryUnavailable: 'Windows only',
    foundryRuntimeReady: 'Runtime components downloaded',
    foundryRuntimeMissing: 'Runtime components not downloaded',
    foundryRuntimeSourceLabel: 'Runtime component source',
    foundryRuntimeSourceAuto: 'Auto (NuGet first)',
    foundryRuntimeSourceNuget: 'NuGet official feed',
    foundryRuntimeSourceOrtNightly: 'Microsoft ORT-Nightly feed',
    foundryRuntimeSourceDesc: 'Runtime components are downloaded before first use.',
    foundrySelectedModel: 'Selected model',
    foundryActiveModel: 'Current default alias',
    foundryLoadedModel: 'Loaded model',
    foundryNotLoaded: 'Not loaded',
    foundryError: 'Foundry status',
    foundrySetDefault: 'Set default / Enable Windows local ASR',
    foundryEnabling: 'Enabling…',
    foundryPrepare: 'Prepare / Download / Load',
    foundryPreparing: 'Preparing…',
    foundryReleasing: 'Releasing…',
    foundryRetryPrepare: 'Continue / Retry prepare',
    foundryCancelPrepare: 'Cancel prepare',
    foundryCancelRequested: 'Cancel requested',
    foundryCancelling: 'Cancelling…',
    foundryCancelBestEffort: 'Cancellation requested. Will stop after the current step completes. Retry later.',
    foundryPrepareRuntime: 'Prepare runtime components',
    foundryPrepareModel: 'Download model',
    foundryPrepareLoad: 'Load model',
    foundryPrepareModelSkipped: 'Model already downloaded; download skipped',
    foundryPrepareDone: 'Done',
    foundryPrepareWaiting: 'Waiting',
    foundryApproxSizeMb: 'about {{mb}} MB',
    foundryLanguageLabel: 'Recognition language',
    foundryLanguageAuto: 'Auto',
    foundryLanguageZh: 'Chinese zh',
    foundryLanguageEn: 'English en',
    foundryLanguageDesc: 'Choose Chinese for Chinese dictation, Auto for mixed use.',
    foundryModelSmall: 'Whisper Small (default / balanced)',
    foundryModelSmallDesc: 'Default balanced option for quality and resource use.',
    foundryModelMedium: 'Whisper Medium (higher quality)',
    foundryModelMediumDesc: 'Higher accuracy for stronger devices that can handle larger downloads and slower inference.',
    foundryModelLarge: 'Whisper Large V3 Turbo (best quality)',
    foundryModelLargeDesc: 'Large-model option for high-end devices and quality-first use.',
    foundryModelBase: 'Whisper Base (faster / lower resource)',
    foundryModelBaseDesc: 'Faster with lower resource use for lightweight daily dictation.',
    foundryModelTiny: 'Whisper Tiny (fastest / smoke test)',
    foundryModelTinyDesc: 'Fastest check option for confirming the Foundry path works.',
    mirrorLabel: 'Download mirror',
    mirrorDesc: 'huggingface.co is the official source; hf-mirror.com is a community mirror friendlier to Mainland China networks.',
    mirrorHuggingface: 'HuggingFace official (huggingface.co)',
    mirrorHfMirror: 'Mainland mirror (hf-mirror.com)',
    activeBadge: 'In use',
    downloadedBadge: 'Downloaded',
    notDownloadedBadge: 'Not downloaded',
    download: 'Download',
    resume: 'Resume',
    cancel: 'Cancel',
    delete: 'Delete',
    setActive: 'Set as default',
    failed: 'Failed',
    cancelled: 'Cancelled',
    files: 'files',
    sizeLoading: 'Fetching size…',
    sizeUnknown: 'Size unknown',
    performanceWarning: 'Local ASR is best for offline or privacy-sensitive use. First use requires model download.',
    test: 'Load & Test',
    testRunning: 'Testing…',
    testHeading: 'Built-in audio test',
    testExpected: 'Expected',
    testActual: 'Got',
    testStats: 'Audio {{audio}}s · Load {{load}}s · Transcribe {{transcribe}}s · Backend {{backend}}',
    testFailed: 'Test failed',
    engineStatusLabel: 'Engine in memory',
    engineLoaded: 'Loaded: {{model}}',
    engineUnloaded: 'Not loaded (first transcription must load the model)',
    loadNow: 'Load now',
    releaseNow: 'Release now',
    keepLoadedLabel: 'Keep loaded for',
    keepLoadedDesc: 'How long Qwen3-ASR stays in memory after the last use, before being freed.',
    keepImmediate: 'Release immediately',
    keep1min: '1 minute after last use',
    keep5min: '5 minutes after last use (default)',
    keep30min: '30 minutes after last use',
    keepForever: 'Never release (always loaded)',
  },
};
