export default {
    accounts: {
      title: 'Account Management',
      description: 'Manage AI platform accounts and credentials',
      createAccount: 'Create Account',
      autoRefresh: 'Auto Refresh',
      enableAutoRefresh: 'Enable auto refresh',
      refreshInterval5s: '5 seconds',
      refreshInterval10s: '10 seconds',
      refreshInterval15s: '15 seconds',
      refreshInterval30s: '30 seconds',
      autoRefreshCountdown: 'Auto refresh: {seconds}s',
      listPendingSyncHint: 'List changes are pending sync. Click sync to load latest rows.',
      listPendingSyncAction: 'Sync now',
      syncFromCrs: 'Sync from CRS',
      dataExport: 'Export',
      dataExportSelected: 'Export Selected',
      dataExportIncludeProxies: 'Include proxies linked to the exported accounts',
      dataImport: 'Import',
      moreActions: 'More Actions',
      dataActions: 'Data',
      toolActions: 'Tools',
      viewColumns: 'Columns',
      selectedCount: '{count} selected',
      dataExportConfirmMessage: 'The exported data contains sensitive account and proxy information. Store it securely.',
      dataExportConfirm: 'Confirm Export',
      dataExported: 'Data exported successfully',
      dataExportedSkippedShadows: 'Data exported. Skipped {count} spark shadow account(s): their scheduling config is not included in the backup; recreate and re-tune them after restore.',
      dataExportFailed: 'Failed to export data',
      dataImportTitle: 'Import Data',
      dataImportHint: 'Upload the exported JSON file to import accounts and proxies.',
      dataImportWarning: 'Import will create new accounts/proxies; groups must be bound manually. Ensure existing data does not conflict.',
      dataImportFile: 'Data file',
      dataImportButton: 'Start Import',
      dataImporting: 'Importing...',
      dataImportSelectFile: 'Please select a data file',
      dataImportParseFailed: 'Failed to parse data file',
      dataImportParseFailedFile: 'Failed to parse {name}',
      dataImportInvalidFile: '{name} is not a supported data export file',
      dataImportIgnoredFiles: 'Ignored {count} non-JSON file(s)',
      dataImportFailed: 'Data import failed',
      dataImportResult: 'Import Result',
      dataImportResultSummary: 'Proxies created {proxy_created}, reused {proxy_reused}, failed {proxy_failed}; Accounts created {account_created}, failed {account_failed}',
      dataImportErrors: 'Error Details',
      dataImportSuccess: 'Import completed: accounts {account_created}, failed {account_failed}',
      dataImportCompletedWithErrors: 'Import completed with errors: account failed {account_failed}, proxy failed {proxy_failed}',
      syncFromCrsTitle: 'Sync Accounts from CRS',
      syncFromCrsDesc:
        'Sync accounts from claude-relay-service (CRS) into this system (CRS is called server-to-server).',
      crsVersionRequirement: '⚠️ Note: CRS version must be ≥ v1.1.240 to support this feature',
      crsBaseUrl: 'CRS Base URL',
      crsBaseUrlPlaceholder: 'e.g. http://127.0.0.1:3000',
      crsUsername: 'Username',
      crsPassword: 'Password',
      syncProxies: 'Also sync proxies (match by host/port/auth or create)',
      syncNow: 'Sync Now',
      syncing: 'Syncing...',
      syncMissingFields: 'Please fill base URL, username and password',
      syncResult: 'Sync Result',
      syncResultSummary: 'Created {created}, updated {updated}, skipped {skipped}, failed {failed}',
      syncErrors: 'Errors / Skipped Details',
      syncCompleted: 'Sync completed: created {created}, updated {updated}, skipped {skipped}',
      syncCompletedWithErrors:
        'Sync completed with errors: failed {failed} (created {created}, updated {updated}, skipped {skipped})',
      syncFailed: 'Sync failed',
      crsPreview: 'Preview',
      crsPreviewing: 'Previewing...',
      crsPreviewFailed: 'Preview failed',
      crsExistingAccounts: 'Existing accounts (will be updated)',
      crsNewAccounts: 'New accounts (select to sync)',
      crsSelectAll: 'Select all',
      crsSelectNone: 'Select none',
      crsNoNewAccounts: 'All CRS accounts are already synced.',
      crsWillUpdate: 'Will update {count} existing accounts.',
      crsSelectedCount: '{count} new accounts selected',
      crsUpdateBehaviorNote:
        'Existing accounts only sync fields returned by CRS; missing fields keep their current values. Credentials are merged by key — keys not returned by CRS are preserved. Proxies are kept when "Sync proxies" is unchecked.',
      crsBack: 'Back',
      editAccount: 'Edit Account',
      deleteAccount: 'Delete Account',
      searchAccounts: 'Search accounts...',
      notes: 'Notes',
      notesPlaceholder: 'Enter notes',
      notesHint: 'Notes are optional',
      allPlatforms: 'All Platforms',
      allTypes: 'All Types',
      allStatus: 'All Status',
      allGroups: 'All Groups',
      ungroupedGroup: 'Ungrouped',
      oauthType: 'OAuth',
      setupToken: 'Setup Token',
      apiKey: 'API Key',
      // Schedulable toggle
      schedulable: 'Schedulable',
      schedulableHint: 'Enable to include this account in API request scheduling',
      schedulableEnabled: 'Scheduling enabled',
      schedulableDisabled: 'Scheduling disabled',
      failedToToggleSchedulable: 'Failed to toggle scheduling status',
      groupCountTotal: '{count} groups total',
      platforms: {
        anthropic: 'Anthropic',
        claude: 'Claude',
        openai: 'OpenAI',
        gemini: 'Gemini',
        antigravity: 'Antigravity',
        grok: 'Grok',
        kimi: 'Kimi',
        zhipu: 'Zhipu GLM',
        deepseek: 'DeepSeek',
      },
      cnProviders: {
        accountMode: {
          title: 'Account Type',
          payg: 'Pay-as-you-go',
          paygDesc: 'Consumes account balance, billed per token. Auto-cools down on low balance and recovers after top-up.',
          coding: 'Coding Plan',
          codingDesc: 'Subscription coding package, rate-limited by 5-hour / weekly rolling usage windows.',
        },
        apiProtocol: {
          title: 'API Protocol',
          adaptive: 'Adaptive',
          adaptiveDesc: 'Uses the matching native provider endpoint for each inbound protocol, converting only when unavailable.',
          endpoints: 'Protocol endpoints',
          responsesFallbackDesc: 'Responses requests are converted to Chat Completions because this provider has no native Responses endpoint.',
          chatCompletions: 'Chat Completions',
          chatCompletionsDesc: 'Standard OpenAI-compatible endpoint; requests in other formats are converted.',
          anthropic: 'Anthropic',
          anthropicDesc: 'Native passthrough to the provider’s Anthropic endpoint — ideal for Claude Code.',
          responses: 'Responses',
          responsesDesc: 'Provider’s native Responses endpoint — ideal for Codex.',
        },
        balance: 'Balance --',
        window5h: '5-hour window',
        windowWeekly: 'Weekly window',
        probeTooltip: 'Query the provider quota endpoint for 5-hour / weekly rolling window usage',
        balanceLow: 'Insufficient balance',
        noBalanceEndpoint: 'This platform has no balance query endpoint',
        resetSoon: 'reset soon',
      },
      types: {
        oauth: 'OAuth',
        chatgptOauth: 'ChatGPT OAuth',
        responsesApi: 'Responses API',
        googleOauth: 'Google OAuth',
        codeAssist: 'Code Assist',
        antigravityOauth: 'Antigravity OAuth',
        grokOauth: 'Grok OAuth',
        antigravityApikey: 'Connect via Base URL + API Key',
        upstream: 'Upstream',
        upstreamDesc: 'Connect via Base URL + API Key'
      },
      antigravityProjectIdLabel: 'GCP Project ID (optional)',
      antigravityProjectIdPlaceholder: 'your-gcp-project-id',
      antigravityProjectIdHint:
        'Antigravity standard-tier accounts that do not receive an automatic project_id need a user-owned GCP project.',
      accountSchedulingThresholdOverride: 'Account Auto-Pause Threshold Override',
      accountSchedulingThresholdOverrideHint:
        'Override the platform auto-pause threshold for this account only. Disable to use platform settings.',
      accountSchedulingThresholdOverrideValue: 'Account threshold percent',
      accountSchedulingThresholdOverrideDisabledHint:
        'Use 1-100. The account becomes temporarily unschedulable after reaching this usage percent; 100 disables it for this account.',
      status: {
        active: 'Active',
        inactive: 'Inactive',
        expired: 'Expired',
        error: 'Error',
        cooldown: 'Cooldown',
        paused: 'Paused',
        limited: 'Limited',
        rateLimited: 'Rate Limited',
        overloaded: 'Overloaded',
        tempUnschedulable: 'Temp Unschedulable',
        quotaExceeded: 'Quota Exceeded',
        unschedulable: 'Unschedulable',
        rateLimitedUntil: 'Rate limited and removed from scheduling. Auto resumes at {time}',
        rateLimitedAutoResume: 'Auto resumes in {time}',
        modelRateLimitedUntil: '{model} rate limited until {time}',
        modelCreditOveragesUntil: '{model} using AI Credits until {time}',
        creditsExhausted: 'Credits Exhausted',
        creditsExhaustedUntil: 'AI Credits exhausted, expected recovery at {time}',
        overloadedUntil: 'Overloaded until {time}',
        viewTempUnschedDetails: 'View temp unschedulable details',
        tempUnschedulableUntil: 'Resumes {time}'
      },
      columns: {
        name: 'Name',
        id: 'Account ID',
        platformType: 'Platform/Type',
        platform: 'Platform',
        type: 'Type',
        capacity: 'Capacity',
        notes: 'Notes',
        priority: 'Priority',
        billingRateMultiplier: 'Billing Rate',
        upstreamBillingRate: 'Upstream Declared Rate',
        weight: 'Weight',
        schedulerScore: 'Scheduler Score',
        status: 'Status',
        schedulable: 'Schedulable',
        todayStats: 'Today Stats',
        groups: 'Groups',
        usageWindows: 'Usage Windows',
        proxy: 'Proxy',
        lastUsed: 'Last Used',
        createdAt: 'Created',
        expiresAt: 'Expires At',
        actions: 'Actions'
      },
      schedulerScore: {
        baseShort: 'Base',
        stickyShort: 'Sticky',
        ungrouped: 'Ungrouped',
        hint: 'Displayed as "group / base score / sticky bonus". The base score is computed within the current filtered candidate set and includes priority, load, queue depth, error rate, first-token latency, reset window, quota headroom, billing rate, and related factors. The sticky bonus applies only when sticky weighting is enabled for previous_response_id or session_hash. Higher scores are preferred.'
      },
      usageWindowsHint: '"5h / 7d" are the upstream account\'s official rolling usage windows (e.g. OpenAI ChatGPT, Claude). They are imposed by the upstream provider on the account itself — not configured by sub2api, and unrelated to the models you map. Usage resets automatically once each window rolls over, and the limit cannot be lifted from within sub2api.',
      ollamaCloud: {
        title: 'Ollama Cloud usage',
        sessionSecurityHint: 'The browser session is encrypted at rest and sent only to the fixed official settings URL.',
        configured: 'Configured',
        notConfigured: 'Not configured',
        notRefreshed: 'Not refreshed',
        encryptionKeyRequired: 'Set a persistent TOTP_ENCRYPTION_KEY before storing a browser session.',
        sessionLabel: 'Ollama browser Cookie',
        sessionPlaceholder: 'wos-session=...; __Secure-authjs.session-token.0=...',
        writeOnlyHint: 'Write-only. The saved value cannot be viewed and an empty value never replaces it.',
        deleteSession: 'Delete session',
        deleteConfirm: 'Delete the stored Ollama browser session and its usage snapshot?',
        refreshNow: 'Refresh usage',
        autoRefresh: 'Automatic usage refresh',
        autoRefreshHint: 'Runs only when the account switch and the global switch are both enabled.',
        plan: 'Plan',
        fiveHour: '5 hour',
        fiveHourShort: '5h',
        sevenDay: '7 day',
        sevenDayShort: '7d',
        balance: 'Balance',
        models: 'Models',
        status: 'Status',
        updatedAt: 'Updated',
        ok: 'Current',
        unauthorized: 'Session expired',
        failed: 'Refresh failed',
        windowWithReset: '{percent} used, resets {reset}',
        loadFailed: 'Failed to load Ollama Cloud usage settings',
        sessionSaved: 'Ollama browser session saved',
        sessionSaveFailed: 'Failed to save Ollama browser session',
        sessionDeleted: 'Ollama browser session deleted',
        sessionDeleteFailed: 'Failed to delete Ollama browser session',
        autoRefreshFailed: 'Failed to update automatic usage refresh',
        refreshSuccess: 'Ollama Cloud usage refreshed',
        refreshFailed: 'Failed to refresh Ollama Cloud usage',
        errors: {
          request_failed: 'Request failed',
          empty_response: 'Empty response',
          response_host_mismatch: 'Unexpected response host',
          redirect_blocked: 'Official settings redirected the request',
          unauthorized: 'Browser session expired',
          http_error: 'Official settings returned an error',
          response_read_failed: 'Failed to read the response',
          response_too_large: 'Settings page exceeded the response limit',
          invalid_html: 'Settings page format was not recognized',
          OLLAMA_CLOUD_USAGE_REFRESH_RATE_LIMITED: 'Refresh is limited. Try again in {retry_after_seconds} seconds.'
        }
      },
      upstreamBilling: {
        trustWarning: 'This rate is declared by the upstream site for the current API key. Sub2API cannot verify that it matches actual charges. The upstream site or an intermediary may return forged, stale, or modified data. Verify it against bills, balance changes, and actual usage.',
        autoProbe: 'Automatically probe upstream declared rate',
        autoProbeHint: 'Refresh the upstream declared rate on the global interval. This switch alone does not change the account rate.',
        syncRate: 'Sync upstream declared rate',
        syncRateHint: 'Update the account rate after each successful probe, using the base rate excluding peak hours. Failed probes or declarations outside the allowed range leave it unchanged. Enabling this also turns on "Automatically probe upstream declared rate".',
        syncRateManagedHint: 'The current rate is maintained automatically from the upstream declared base rate (excluding peak hours).',
        syncedRateTooltip: 'This account rate is synchronized from the upstream declared base rate (excluding peak hours)',
        manualProbe: 'Probe upstream rate now',
        stale: 'Stale',
        unsupported: 'Unsupported',
        failed: 'Failed',
        notProbed: 'Not probed',
        groupRate: 'Group default: {value}x',
        userRate: 'User rate: {value}x',
        peakRate: 'Peak: {start}-{end}, {value}x ({timezone})',
        noPeakRate: 'Peak rate: disabled',
        effectiveRate: 'Current rate: {value}x',
        updatedAt: 'Updated: {value}',
        nextProbeAt: 'Next probe: {value}',
        lastDetectedRate: 'Last detected rate: {value}x',
        lastDetectedAt: 'Last detected: {value}',
        elapsedSince: 'Elapsed: {value}',
        justNow: 'less than 1 minute',
        minutesAgo: '{count} minutes',
        hoursAgo: '{count} hours',
        daysAgo: '{count} days',
        accountProbeState: 'Automatic detection for this account:',
        globalProbeState: 'Global probe switch:',
        enabled: 'On',
        disabled: 'Off',
        probeFailed: 'Failed to probe upstream rate',
        noEligibleAccounts: 'Select API key accounts',
        batchLimit: 'A batch can probe at most 20 accounts',
        batchCompleted: 'Probed {count} account(s)',
        batchPartial: 'Probe partially completed: {success} succeeded, {failed} failed'
      },
      allPrivacyModes: 'All Privacy States',
      privacyUnset: 'Unset',
      privacyTrainingOff: 'Training data sharing disabled',
      privacyCfBlocked: 'Blocked by Cloudflare, training may still be on',
      privacyFailed: 'Failed to disable training',
      privacyAntigravitySet: 'Telemetry and marketing emails disabled',
      privacyAntigravityFailed: 'Privacy setting failed',
      setPrivacy: 'Set Privacy',
      subscriptionAbnormal: 'Abnormal',
      subscriptionExpires: 'Expires',
      // Capacity status tooltips
      capacity: {
        windowCost: {
          blocked: '5h window cost exceeded, account scheduling paused',
          stickyOnly: '5h window cost at threshold, only sticky sessions allowed',
          normal: '5h window cost normal'
        },
        sessions: {
          full: 'Active sessions full, new sessions must wait (idle timeout: {idle} min)',
          normal: 'Active sessions normal (idle timeout: {idle} min)'
        },
        rpm: {
          full: 'RPM limit reached',
          warning: 'RPM approaching limit',
          normal: 'RPM normal',
          tieredNormal: 'RPM limit (Tiered) - Normal',
          tieredWarning: 'RPM limit (Tiered) - Approaching limit',
          tieredStickyOnly: 'RPM limit (Tiered) - Sticky only | Buffer: {buffer}',
          tieredBlocked: 'RPM limit (Tiered) - Blocked | Buffer: {buffer}',
          stickyExemptNormal: 'RPM limit (Sticky Exempt) - Normal',
          stickyExemptWarning: 'RPM limit (Sticky Exempt) - Approaching limit',
          stickyExemptOver: 'RPM limit (Sticky Exempt) - Over limit, sticky only'
        },
        quota: {
          exceeded: 'Quota exceeded, account paused',
          normal: 'Quota normal'
        },
      },
      tempUnschedulable: {
        title: 'Temp Unschedulable',
        statusTitle: 'Temp Unschedulable Status',
        hint: 'Disable accounts temporarily when error code and keyword both match.',
        notice: 'Rules are evaluated in order and require both error code and keyword match.',
        addRule: 'Add Rule',
        ruleOrder: 'Rule Order',
        multipleErrorTrigger: '{count} matching errors in {minutes} minutes reached the trigger threshold ({threshold}).',
        multipleErrorTriggerNoWindow: '{count} matching errors reached the trigger threshold ({threshold}).',
        multipleErrorCountInWindow: '{count} matching errors occurred within {minutes} minutes.',
        multipleErrorCount: '{count} matching errors contributed to this block.',
        ruleIndex: 'Rule #{index}',
        errorCode: 'Error Code',
        errorCodePlaceholder: 'e.g. 429',
        durationMinutes: 'Duration (minutes)',
        durationPlaceholder: 'e.g. 30',
        keywords: 'Keywords',
        keywordsPlaceholder: 'e.g. overloaded, too many requests',
        keywordsHint: 'Separate keywords with commas; any keyword match will trigger.',
        description: 'Description',
        descriptionPlaceholder: 'Optional note for this rule',
        rulesInvalid: 'Add at least one rule with error code, keywords, and duration.',
        viewDetails: 'View temp unschedulable details',
        accountName: 'Account',
        triggeredAt: 'Triggered At',
        until: 'Until',
        remaining: 'Remaining',
        matchedKeyword: 'Matched Keyword',
        errorMessage: 'Error Details',
        reset: 'Recover State',
        resetSuccess: 'Account state recovered successfully',
        resetFailed: 'Failed to recover account state',
        failedToLoad: 'Failed to load temp unschedulable status',
        notActive: 'This account is not temporarily unschedulable.',
        expired: 'Expired',
        remainingMinutes: 'About {minutes} minutes',
        remainingHours: 'About {hours} hours',
        remainingHoursMinutes: 'About {hours} hours {minutes} minutes',
        presets: {
          overloadLabel: '529 Overloaded',
          overloadDesc: 'Overloaded - pause 60 minutes',
          rateLimitLabel: '429 Rate Limit',
          rateLimitDesc: 'Rate limited - pause 10 minutes',
          unavailableLabel: '503 Unavailable',
          unavailableDesc: 'Unavailable - pause 30 minutes'
        }
      },
      clearRateLimit: 'Clear Rate Limit',
      resetQuota: 'Reset Quota',
      quotaLimit: 'Quota Limit',
      quotaLimitPlaceholder: '0 means unlimited',
      quotaLimitHint: 'Set daily/weekly/total spending limits (USD). Anthropic API key accounts can also configure client affinity. Changing limits won\'t reset usage.',
      quotaLimitToggle: 'Enable Quota Limit',
      quotaLimitToggleHint: 'When enabled, account will be paused when usage reaches the set limit',
      quotaDailyLimit: 'Daily Limit',
      quotaDailyLimitHint: 'Automatically resets every 24 hours from first usage.',
      quotaWeeklyLimit: 'Weekly Limit',
      quotaWeeklyLimitHint: 'Automatically resets every 7 days from first usage.',
      quotaTotalLimit: 'Total Limit',
      quotaTotalLimitHint: 'Cumulative spending limit. Does not auto-reset — use "Reset Quota" to clear.',
      quotaResetMode: 'Reset Mode',
      quotaResetModeRolling: 'Rolling Window',
      quotaResetModeFixed: 'Fixed Time',
      quotaResetHour: 'Reset Hour',
      quotaWeeklyResetDay: 'Reset Day',
      quotaResetTimezone: 'Reset Timezone',
      quotaDailyLimitHintFixed: 'Resets daily at {hour}:00 ({timezone}).',
      quotaWeeklyLimitHintFixed: 'Resets every {day} at {hour}:00 ({timezone}).',
      dayOfWeek: {
        monday: 'Monday',
        tuesday: 'Tuesday',
        wednesday: 'Wednesday',
        thursday: 'Thursday',
        friday: 'Friday',
        saturday: 'Saturday',
        sunday: 'Sunday',
      },
      quotaLimitAmount: 'Total Limit',
      quotaLimitAmountHint: 'Cumulative spending limit. Does not auto-reset.',
      quotaNotify: {
        alert: 'Alert',
        enabled: 'Enable Alert',
        threshold: 'Alert Amount',
        thresholdPlaceholder: 'Enter percentage',
      },
      testConnection: 'Test Connection',
      reAuthorize: 'Re-Authorize',
      refreshToken: 'Refresh Token',
      noAccountsYet: 'No accounts yet',
      createFirstAccount: 'Create your first account to start using AI services.',
      tokenRefreshed: 'Token refreshed successfully',
      accountDeleted: 'Account deleted successfully',
      rateLimitCleared: 'Rate limit cleared successfully',
      bulkSchedulableEnabled: 'Successfully enabled scheduling for {count} account(s)',
      bulkSchedulableDisabled: 'Successfully disabled scheduling for {count} account(s)',
      bulkSchedulablePartial: 'Scheduling updated partially: {success} succeeded, {failed} failed',
      bulkSchedulableResultUnknown: 'Bulk scheduling result incomplete. Please retry or refresh.',
      bulkActions: {
        selected: '{count} account(s) selected',
        selectedAll: 'All {count} account(s) selected',
        selectCurrentPage: 'Select this page',
        selectAllResults: 'Select all results ({count})',
        selectingAll: 'Selecting all results...',
        selectAllFailed: 'Failed to load all accounts. The previous selection was kept.',
        clear: 'Clear selection',
        edit: 'Bulk Edit',
        delete: 'Bulk Delete',
        confirmDelete: 'Delete the selected {count} account(s)? This action cannot be undone.',
        deleteSuccess: 'Deleted {count} account(s)',
        enableScheduling: 'Enable Scheduling',
        disableScheduling: 'Disable Scheduling',
        resetStatus: 'Reset Status',
        refreshToken: 'Refresh Token',
        probeUpstreamBilling: 'Probe Upstream Rate',
        resetStatusSuccess: 'Successfully reset {count} account(s) status',
        refreshTokenSuccess: 'Successfully refreshed {count} account(s) token',
        partialSuccess: 'Partially completed: {success} succeeded, {failed} failed'
      },
      bulkEdit: {
        title: 'Bulk Edit Accounts',
        selectionInfo:
          '{count} account(s) selected. Only checked or filled fields will be updated; others stay unchanged.',
        baseUrlPlaceholder: 'https://api.anthropic.com or https://api.openai.com',
        baseUrlNotice: 'Applies to API Key accounts and the forwarding endpoint of Grok OAuth accounts; leave empty to keep existing value',
        submit: 'Update Accounts',
        updating: 'Updating...',
        success: 'Updated {count} account(s)',
        successWithInherited: 'Updated {count} account(s). {inherited} selected shadow account(s) still follow their parent account.',
        partialSuccess: 'Partially updated: {success} succeeded, {failed} failed',
        partialSuccessWithInherited: 'Partially updated: {success} succeeded, {failed} failed. {inherited} selected shadow account(s) still follow their parent account.',
        failed: 'Bulk update failed',
        noSelection: 'Please select accounts to edit',
        noFieldsSelected: 'Select at least one field to update',
        rateSyncWarning: 'Accounts with upstream rate sync enabled cannot be changed in bulk. Disable sync in the account editor first.',
        rateSyncConflict: 'Cannot change account rates: {count} target account(s) have upstream rate sync enabled.',
        longContextShadowHint: 'Long-context billing belongs to the parent account. Selected shadow accounts keep following their parent, including when targets come from a filter.',
        longContextParentRequired: 'All selected accounts are shadows. Select the parent account to change long-context billing.',
        mixedPlatformWarning: 'Selected accounts span multiple platforms ({platforms}). Model mapping presets shown are combined — ensure mappings are appropriate for each platform.'
      },
      bulkDeleteTitle: 'Bulk Delete Accounts',
      bulkDeleteConfirm: 'Delete the selected {count} account(s)? This action cannot be undone.',
      bulkDeleteSuccess: 'Deleted {count} account(s)',
      bulkDeletePartial: 'Partially deleted: {success} succeeded, {failed} failed',
      bulkDeleteFailed: 'Bulk delete failed',
      recoverState: 'Recover State',
      recoverStateHint: 'Used to recover error, rate-limit, and temporary unschedulable runtime state.',
      recoverStateSuccess: 'Account state recovered successfully',
      recoverStateFailed: 'Failed to recover account state',
      fallbackActive: 'Fallback',
      fallbackActiveTip: 'Origin proxy {origin} expired',
      revertProxy: 'Revert proxy',
      revertProxySuccess: 'Successfully reverted to original proxy',
      revertProxyFailed: 'Failed to revert proxy',
      createSparkShadow: 'Create Spark Shadow',
      createSparkShadowConfirm: 'Create a spark shadow account linked to "{name}"? It shares the parent\'s credentials and serves only spark models.',
      createSparkShadowSuccess: 'Spark shadow account created',
      createSparkShadowFailed: 'Failed to create spark shadow account',
      duplicateAccount: 'Duplicate Account',
      duplicateSuccess: 'Account duplicated as "{name}" and paused. Review its credentials before enabling it.',
      duplicateFailed: 'Failed to duplicate account',
      resetStatus: 'Reset Status',
      statusReset: 'Account status reset successfully',
      failedToResetStatus: 'Failed to reset account status',
      failedToLoad: 'Failed to load accounts',
      failedToRefresh: 'Failed to refresh token',
      failedToDelete: 'Failed to delete account',
      failedToClearRateLimit: 'Failed to clear rate limit',
      deleteConfirm: "Are you sure you want to delete '{name}'? This action cannot be undone.",
      // Create/Edit Account Modal
      platform: 'Platform',
      accountName: 'Account Name',
      enterAccountName: 'Enter account name',
      accountType: 'Account Type',
      claudeCode: 'Claude Code',
      claudeConsole: 'Claude Console',
      bedrockLabel: 'AWS Bedrock',
      bedrockDesc: 'SigV4 / API Key',
      vertexLabel: 'Vertex',
      vertexDesc: 'Service Account',
      vertexAnthropicHint: 'Use a Google Cloud Service Account JSON to call Anthropic Claude via Vertex AI. It is recommended to configure model mapping to map client Claude model names to Vertex model IDs.',
      vertexGeminiHint: 'Use a Google Cloud Service Account JSON to access Vertex AI Gemini. It is recommended to place Vertex accounts in a separate group to avoid mixing with AI Studio/Gemini OAuth on the same models.',
      vertexSaJsonLabel: 'Service Account JSON',
      vertexSaJsonLoaded: 'Service Account JSON loaded',
      vertexSaJsonDrop: 'Drop Service Account JSON here',
      vertexSaJsonKeyHidden: 'Key content is not displayed in the form.',
      vertexSaJsonDropHint: 'Drag a .json file here, or click the button to select one.',
      vertexSaJsonSelectBtn: 'Select JSON',
      vertexSaJsonUploadHint: 'After uploading or dropping a JSON file, the project_id will be auto-extracted. Key content is only used for account creation.',
      vertexSaJsonEditHint: 'Service Account JSON is not shown on the edit page; to change the JSON, delete the account and recreate it.',
      vertexProjectIdPlaceholder: 'Auto-extracted from JSON',
      vertexLocationHint: 'Available locations vary by Vertex model. Select the default endpoint location for this account.',
      vertexLocationRequired: 'Please enter a Vertex location',
      vertexSaJsonMissingFields: 'Service Account JSON is missing project_id, client_email, or private_key',
      vertexSaJsonMissingProjectId: 'Service Account JSON is missing project_id',
      vertexSaJsonMissingClientEmail: 'Service Account JSON is missing client_email',
      vertexSaJsonInvalid: 'Service Account JSON format is invalid',
      vertexSaJsonRequired: 'Please upload a Service Account JSON',
      oauthSetupToken: 'OAuth / Setup Token',
      addMethod: 'Add Method',
      setupTokenLongLived: 'Setup Token (Long-lived)',
      baseUrl: 'Base URL',
      baseUrlHint: 'Leave default for official Anthropic API',
      apiKeyRequired: 'API Key *',
      apiKeyPlaceholder: 'sk-ant-api03-...',
      apiKeyHint: 'Your Claude Console API Key',
      // OpenAI specific hints
      openai: {
        baseUrlHint: 'Leave default for official OpenAI API',
        apiKeyHint: 'Your OpenAI API Key',
        oauthPassthrough: 'Auto passthrough (auth only)',
        oauthPassthroughDesc:
          'When enabled, this OpenAI account uses automatic passthrough: the gateway forwards request/response as-is and only swaps auth, while keeping billing/concurrency/audit and necessary safety filtering.',
        flattenNamespaces: 'Flatten Codex namespace tools (compatibility)',
        flattenNamespacesDesc:
          'Disabled by default: Codex namespace tool declarations are forwarded as-is on /responses, which is what the ChatGPT Codex backend expects. Enable only when this OAuth account is routed to a relay that rejects namespace tools — flattening renames them to namespace__tool, which breaks models that address collaboration tools as functions.<namespace>.<tool>. Compaction requests always flatten regardless of this switch.',
        longContextBilling: 'API long-context pricing',
        longContextBillingDesc:
          'Disabled by default. Enable only when this account\'s upstream charges OpenAI API long-context rates above the model threshold.',
        responsesWebsocketsV2: 'Responses WebSocket v2',
        responsesWebsocketsV2Desc:
          'Disabled by default. Enable to allow responses_websockets_v2 capability (still gated by global and account-type switches).',
        wsMode: 'WS mode',
        wsModeDesc:
          'Only applies to the current OpenAI account type; account WS modes, including http_bridge, take effect only when the global gateway.openai_ws.mode_router_v2_enabled=true.',
        wsModeOff: 'Off (off)',
        wsModeCtxPool: 'Context Pool (ctx_pool)',
        wsModePassthrough: 'Passthrough (passthrough)',
        wsModeHttpBridge: 'HTTP Bridge (http_bridge)',
        wsModeShared: 'Shared (shared)',
        wsModeDedicated: 'Dedicated (dedicated)',
        wsModeConcurrencyHint:
          'When WS mode is enabled, account concurrency becomes the WS connection pool limit for this account.',
        wsModePassthroughHint: 'Passthrough mode does not use the WS connection pool.',
        oauthResponsesWebsocketsV2: 'OAuth WebSocket Mode',
        oauthResponsesWebsocketsV2Desc:
          'Only applies to OpenAI OAuth. This account can use OpenAI WebSocket Mode only when enabled.',
        apiKeyResponsesWebsocketsV2: 'API Key WebSocket Mode',
        apiKeyResponsesWebsocketsV2Desc:
          'Only applies to OpenAI API Key. This account can use OpenAI WebSocket Mode only when enabled.',
        responsesWebsocketsV2PassthroughHint:
          'Automatic passthrough is currently enabled: it only affects HTTP passthrough and does not disable WS mode.',
        responsesMode: 'Responses API support',
        responsesModeDesc:
          'Only applies to the OpenAI API Key text forwarding path. Auto follows probe results; force modes override probing.',
        responsesModeAuto: 'Auto',
        responsesModeForceResponses: 'Force Responses',
        responsesModeForceChatCompletions: 'Force Chat Completions',
        responsesModeTextDisabledHint:
          'Not applicable when the Responses / Chat Completions endpoint is not enabled.',
        endpointCapabilities: 'Endpoint capabilities',
        endpointCapabilitiesDesc:
          'Used by account routing. The text endpoint follows the Responses API support setting above and is shown as Responses, Chat Completions, or auto mode; Embeddings independently controls /v1/embeddings.',
        capabilityResponses: 'Responses',
        capabilityTextAuto: 'Responses / Chat Completions (Auto)',
        capabilityResponsesAuto: 'Responses (auto probe)',
        capabilityChatCompletions: 'Chat Completions',
        capabilityChatCompletionsAuto: 'Chat Completions (auto probe)',
        capabilityEmbeddings: 'Embeddings',
        responsesStatusAutoSupported: 'Auto probe: Responses',
        responsesStatusAutoUnsupported: 'Auto probe: Chat Completions',
        responsesStatusAutoUnknown: 'Auto probe: unknown',
        responsesStatusForcedResponses: 'Forced Responses',
        responsesStatusForcedChatCompletions: 'Forced Chat Completions',
        planType: 'Plan tier (manual override)',
        planTypeDesc:
          "Manually correct this account's ChatGPT plan tier (Plus / Pro / Free). Note: a token refresh near expiry or a 429 rate-limit response will auto-overwrite this with the real tier.",
        planTypeClear: 'Clear (auto-detect)',
        codexCLIOnly: 'Codex official clients only',
        codexCLIOnlyDesc:
          'Only applies to OpenAI OAuth. When enabled, only Codex official client families are allowed; when disabled, the gateway bypasses this restriction and keeps existing behavior.',
        codexCLIOnlyAppServer: 'Allow Codex app-server clients',
        codexCLIOnlyAppServerDesc:
          "Effective only when the switch above is on. When enabled, this account also allows third-party clients that embed the Codex engine over the app-server protocol (e.g. Claude Code's codex plugin); they still pass the global engine-fingerprint gate. OR-combined with the global app-server toggle.",
        codexFingerprintMode: 'Codex fingerprint convergence',
        codexFingerprintModeDesc: 'When multiple users share the same OAuth account, converge device/session identifiers to account-level stable values to reduce upstream-visible device and session count. Off by default (client identifiers pass through as-is); opt in explicitly when needed. Some accounts reported quota shrinkage after enabling convergence, so choose based on your own measurements.',
        codexFingerprintOff: 'Off (passthrough, default)',
        codexFingerprintDevice: 'Device only',
        codexFingerprintSession: 'Device + Session',
        codexFingerprintFull: 'Full convergence',
        codexImageTool: 'Codex image bridge policy',
        codexImageToolDesc:
          'Controls the hosted image_generation bridge and client-declared image tools on Codex /responses text requests. Hosted auto-injection applies only to non-Responses Lite requests. Account policy takes precedence over channel and global settings; standalone image-generation endpoints are unaffected.',
        codexImageToolInherit: 'Follow channel',
        codexImageToolInheritDesc: 'No account override; hosted injection for non-Lite requests follows the channel or global policy, while client-provided hosted tools and local image_gen declarations pass through.',
        codexImageToolEnabled: 'Enable hosted bridge',
        codexImageToolEnabledDesc: 'Inject the hosted image_generation tool only for non-Responses Lite requests; client-provided image tools still pass through.',
        codexImageToolDisabled: 'No hosted injection',
        codexImageToolDisabledDesc: 'Do not inject the hosted tool; client-provided hosted tools and local image_gen declarations still pass through.',
        codexImageToolBlock: 'Strip client image tools',
        codexImageToolBlockDesc: 'Do not auto-inject through the bridge, and remove client-provided hosted image_generation tools, local image_gen declarations, and matching tool_choice. Image-only model routing remains unaffected.',
        codexImageToolBadgeInherit: 'Channel policy',
        codexImageToolBadgeEnabled: 'Hosted bridge on',
        codexImageToolBadgeDisabled: 'No hosted injection',
        codexImageToolBadgeBlock: 'Client image tools stripped',
        compactMode: 'Compact mode',
        compactModeDesc:
          'Controls how this account participates in /responses/compact routing. Auto follows probe results, Force On always allows, Force Off always excludes.',
        compactModeAuto: 'Auto',
        compactModeForceOn: 'Force On',
        compactModeForceOff: 'Force Off',
        compactModelMapping: 'Compact-only model mapping',
        compactModelMappingDesc:
          'Only applies to /responses/compact. Use this when the upstream compact endpoint requires a special compact model.',
        compactSupported: 'Compact supported',
        compactUnsupported: 'Compact unsupported',
        compactAuto: 'Compact Auto',
        compactUnknown: 'Compact Auto',
        compactLastChecked: 'Last compact probe',
        testMode: 'Test mode',
        testModeDefault: 'Default request',
        testModeCompact: 'Compact probe',
        modelRestrictionDisabledByPassthrough: 'Automatic passthrough is enabled: model whitelist/mapping will not take effect.',
      },
      grok: {
        baseUrlHint: 'Grok OAuth accounts forward to the official xAI API base URL.',
        apiKeyHint: 'Grok subscription support uses OAuth refresh tokens; API keys are out of scope for this account type.',
        // Account connectivity test modal
        testMode: 'Test mode',
        testModeHint:
          'Text / image / video use the selected model. Web search, TTS, STT and Realtime hit standalone endpoints (not free-form chat tools).',
        testModeText: 'Text (Responses)',
        testModeImage: 'Image (/images/generations)',
        testModeVideo: 'Video (/videos/generations)',
        testModeSearch: 'Web search (/web_search)',
        testModeTTS: 'TTS (/tts)',
        testModeSTT: 'STT (/stt)',
        testModeRealtime: 'Realtime (WS /realtime)',
        textTestMode: 'Mode: Text (Responses)',
        searchTestMode: 'Mode: Web search (/web_search)',
        ttsTestMode: 'Mode: TTS (/tts)',
        sttTestMode: 'Mode: STT (/stt)',
        realtimeTestMode: 'Mode: Realtime (WS /realtime)',
        searchQueryLabel: 'Search query',
        searchQueryPlaceholder: 'Example: xAI Grok',
        searchQueryDefault: 'xAI Grok',
        searchTestHint:
          'Standalone web_search probe (same as gateway /v1/web_search). Not a free-form chat with tools.',
        ttsTextLabel: 'TTS text',
        ttsTextPlaceholder: 'Example: Hello from Sub2API connectivity test.',
        ttsTextDefault: 'Hello from Sub2API account connectivity test.',
        ttsTestHint: 'Standalone /v1/tts with language=en; success reports audio byte size.',
        sttTestHint: 'Standalone /v1/stt with a synthetic silent WAV; success means the endpoint is reachable.',
        realtimeTestHint:
          'Standalone WebSocket dial to /v1/realtime (model=grok-voice-latest). Handshake success = connectivity OK; may also show the first server event.',
        sendingSearchRequest: 'Sending standalone web_search request...',
        sendingTTSRequest: 'Sending standalone /tts request...',
        sendingSTTRequest: 'Sending standalone /stt request...',
        sendingRealtimeRequest: 'Dialing standalone /realtime WebSocket...',
        selectedTestMode: 'Test mode: {mode}',
        imageUploadLabel: 'Source image (optional, for edits)',
        videoFirstFrameLabel: 'First-frame / reference image (optional)',
        imageUploadHint:
          'PNG/JPEG recommended, both sides ≥ 8 px, under ~4 MB for edits. Uploading a source image switches to /images/edits (image-to-image). Leave empty for text-to-image /images/generations.',
        videoFirstFrameHint:
          'Optional first-frame / reference image for image-to-video. PNG/JPEG recommended, both sides ≥ 8 px.',
        audioUploadLabel: 'Audio file (optional for STT)',
        audioUploadHint:
          'Upload a real audio clip to transcribe. Without a file, a silent WAV is used for connectivity only.',
        mediaTooLarge: 'File is too large (max ~6 MB for admin test uploads).',
        chooseImageFile: 'Choose image',
        chooseAudioFile: 'Choose audio',
        uploadPreviewAlt: 'Upload preview',
        fileReadFailed: 'Failed to read the selected file',
        noResponseBody: 'No response body from server'
      },
      anthropic: {
        apiKeyPassthrough: 'Auto passthrough (auth only)',
        apiKeyPassthroughDesc:
          'Only applies to Anthropic API Key accounts. When enabled, messages/count_tokens are forwarded in passthrough mode with auth replacement only, while billing/concurrency/audit and safety filtering are preserved. Disable to roll back immediately.',
        apiKeyAuthScheme: 'Upstream auth scheme',
        apiKeyAuthSchemeDesc: 'Choose the API key auth header used when forwarding to an Anthropic-compatible upstream. Ollama Cloud uses Authorization: Bearer.',
        apiKeyAuthSchemeXApiKey: 'x-api-key',
        apiKeyAuthSchemeBearer: 'Authorization: Bearer',
        webSearchEmulation: 'Web Search Emulation',
        webSearchEmulationDesc:
          'Enable web search emulation for this API Key account. When a pure web_search request is detected, the gateway calls a third-party search API and constructs the response locally. Default follows channel config.',
        webSearchDefault: 'Default',
        webSearchEnabled: 'Enabled',
        webSearchDisabled: 'Disabled',
      },
      modelRestriction: 'Model Restriction (Optional)',
      modelWhitelist: 'Model Whitelist',
      modelMapping: 'Model Mapping',
      selectAllowedModels: 'Select allowed models. Leave empty to support all models.',
      mapRequestModels:
        'Map request models to actual models. Left is the requested model, right is the actual model sent to API.',
      selectedModels: 'Selected {count} model(s)',
      supportsAllModels: '(supports all models)',
      requestModel: 'Request model',
      actualModel: 'Actual model',
      addMapping: 'Add Mapping',
      mappingExists: 'Mapping for {model} already exists',
      wildcardOnlyAtEnd: 'Wildcard * can only be at the end',
      targetNoWildcard: 'Target model cannot contain wildcard *',
      searchModels: 'Search models...',
      noMatchingModels: 'No matching models',
      fillRelatedModels: 'Sync latest supported models',
      syncUpstreamModels: 'Sync upstream supported models',
      syncUpstreamModelsLoading: 'Syncing upstream...',
      syncUpstreamModelsSuccess: 'Synced {count} new model(s) from upstream ({total} upstream total)',
      syncUpstreamModelsNoChanges: 'All {count} upstream model(s) are already in the whitelist',
      syncUpstreamModelsEmpty: 'Upstream returned no models to sync',
      syncUpstreamModelsFailed: 'Failed to sync upstream models',
      syncUpstreamModelsError: 'Failed to sync upstream models: {message}',
      clearAllModels: 'Clear all models',
      customModelName: 'Custom model name',
      enterCustomModelName: 'Enter custom model name',
      addModel: 'Add',
      modelExists: 'Model already exists',
      modelCount: '{count} models',
      poolMode: 'Pool Mode',
      poolModeHint: 'Enable when upstream is an account pool; errors won\'t mark local account status',
      poolModeInfo:
        'When enabled, upstream 429/403/401 errors will auto-retry without marking the account as rate-limited or errored. Suitable for upstream pointing to another sub2api instance.',
      poolModeRetryCount: 'Same-Account Retries',
      poolModeRetryCountHint:
        'Only applies in pool mode. Use 0 to disable in-place retry. Default {default}, maximum {max}.',
      poolModeRetryStatusCodes: 'Retry Status Codes',
      poolModeRetryStatusCodesHint:
        'Comma-separated HTTP status codes (100-599) that trigger same-account retry in pool mode. Leave blank to use defaults ({default}).',
      customErrorCodes: 'Custom Error Codes',
      customErrorCodesHint: 'Only stop scheduling for selected error codes',
      customErrorCodesWarning:
        'Only selected error codes will stop scheduling. Other errors will return 500.',
      customErrorCodes429Warning:
        '429 already has built-in rate limit handling. Adding it to custom error codes will disable the account instead of temporary rate limiting. Are you sure?',
      customErrorCodes529Warning:
        '529 already has built-in overload handling. Adding it to custom error codes will disable the account instead of temporary overload marking. Are you sure?',
      selectedErrorCodes: 'Selected',
      noneSelectedUsesDefault: 'None selected (uses default policy)',
      enterErrorCode: 'Enter error code (100-599)',
      invalidErrorCode: 'Please enter a valid HTTP error code (100-599)',
      errorCodeExists: 'This error code is already selected',
      interceptWarmupRequests: 'Intercept Warmup Requests',
      interceptWarmupRequestsDesc:
        'When enabled, warmup requests like title generation will return mock responses without consuming upstream tokens',
      headerOverride: {
        title: 'Header Override',
        hint: 'Override same-named request headers on forwarding (case-insensitive)',
        info: 'Applies to outbound requests of this account only: configured headers override client/gateway-generated headers of the same name before forwarding. Auth headers (authorization, x-api-key) and connection-control headers cannot be overridden.',
        namePlaceholder: 'Header name (e.g. user-agent)',
        valuePlaceholder: 'Override value (leave empty to skip)',
        addRow: 'Add Header',
        importJson: 'Import JSON',
        importJsonApply: 'Parse & Fill',
        importJsonCancel: 'Cancel',
        importJsonHint: 'Paste a flat JSON object (header name → value). Parsing replaces the current rows.',
        importJsonInvalid: 'Invalid JSON: expected a flat object of header name → string value',
        copyJson: 'Copy as JSON',
        emptyValueHint: 'Rows with an empty value are placeholders and do not override anything.',
        bulkDisableHint: 'Saving will disable header override and clear existing configuration on the selected accounts.',
        bulkReplaceHint: 'Saving will replace the existing header override configuration on all selected accounts with the rows below.',
        bulkEmptyRows: 'Add at least one header row before saving, or turn the toggle off to clear existing configuration.',
        invalidName: 'Invalid header name (only letters, digits and !#$%&\'*+-.^_`|~ are allowed)',
        blockedName: 'This header cannot be overridden (auth and connection-control headers are managed by the system)',
        duplicateName: 'Duplicate header name (matching is case-insensitive)',
        invalidValue: 'Invalid header value (control characters are not allowed; max length 8192)',
        tooManyEntries: 'Too many header override entries (max 64)'
      },
      grokCustomBaseUrl: {
        title: 'Custom Upstream URL',
        hint: 'When enabled, account traffic (chat/media/probes) is forwarded to the specified address. OAuth authorization and token refresh are unaffected and stay on the official endpoints.',
        placeholder: 'https://relay.example.com/v1',
        required: 'An address is required when Custom Upstream URL is enabled',
        invalid: 'Invalid upstream address (must be a full http(s):// URL)',
        presets: {
          cli: 'Grok Build CLI',
          official: 'Official API'
        }
      },
      grokClientToolCache: {
        title: 'Client Tool Cache (May Change Automatic Tool Selection)',
        hint: 'For detected Grok Free OAuth accounts, this is enabled by default for client function tools such as Codex and Trae. Turn it off to opt out if the automatic tool-selection behavior is not acceptable.'
      },
      autoPauseOnExpired: 'Auto Pause On Expired',
      autoPauseOnExpiredDesc: 'When enabled, the account will auto pause scheduling after it expires',
	  autoPause5hThreshold: '5h Usage Threshold (%)',
	  autoPause7dThreshold: '7d Usage Threshold (%)',
	  autoPauseThresholdHint: 'Leave empty or set 0 to use the global default threshold (configured in Ops settings); set a value to override the global default. Reaching the threshold only skips the account during scheduling and does not modify schedulable.',
	  autoPause5hDisabled: 'Disable 5h auto-pause',
	  autoPause7dDisabled: 'Disable 7d auto-pause',
	  autoPauseDisabledHint: 'When enabled, this account is never auto-paused (even if a global default threshold is configured).',
      // Quota control (Anthropic OAuth/SetupToken only)
      quotaControl: {
        title: 'Quota Control',
        hint: 'Configure cost window, session limits, client affinity and other scheduling controls.',
        windowCost: {
          label: '5h Window Cost Limit',
          hint: 'Limit account cost usage within the 5-hour window',
          limit: 'Cost Threshold',
          limitPlaceholder: '50',
          limitHint: 'Account will not participate in new scheduling after reaching threshold',
          stickyReserve: 'Sticky Reserve',
          stickyReservePlaceholder: '10',
          stickyReserveHint: 'Additional reserve for sticky sessions'
        },
        sessionLimit: {
          label: 'Session Count Limit',
          hint: 'Limit the number of active concurrent sessions',
          maxSessions: 'Max Sessions',
          maxSessionsPlaceholder: '3',
          maxSessionsHint: 'Maximum number of active concurrent sessions',
          idleTimeout: 'Idle Timeout',
          idleTimeoutPlaceholder: '5',
          idleTimeoutHint: 'Sessions will be released after idle timeout'
        },
        rpmLimit: {
          label: 'RPM Limit',
          hint: 'Limit requests per minute to protect upstream accounts',
          baseRpm: 'Base RPM',
          baseRpmPlaceholder: '15',
          baseRpmHint: 'Max requests per minute, 0 or empty means no limit',
          strategy: 'RPM Strategy',
          strategyTiered: 'Tiered Model',
          strategyStickyExempt: 'Sticky Exempt',
          strategyTieredHint: 'Green → Yellow → Sticky only → Blocked, progressive throttling',
          strategyStickyExemptHint: 'Only sticky sessions allowed when over limit',
          strategyHint: 'Tiered: gradually restrict when exceeded; Sticky Exempt: existing sessions unrestricted',
          stickyBuffer: 'Sticky Buffer',
          stickyBufferPlaceholder: 'Default: 20% of base RPM',
          stickyBufferHint: 'Extra requests allowed for sticky sessions after exceeding base RPM. Leave empty to use default (20% of base RPM, min 1)',
          userMsgQueue: 'User Message Rate Control',
          userMsgQueueHint: 'Rate-limit user messages to avoid triggering upstream RPM limits',
          umqModeOff: 'Off',
          umqModeThrottle: 'Throttle',
          umqModeSerialize: 'Serialize',
        },
        tlsFingerprint: {
          label: 'TLS Fingerprint Simulation',
          hint: 'Simulate Node.js/Claude Code client TLS fingerprint',
          defaultProfile: 'Built-in Default',
          randomProfile: 'Random'
        },
        sessionIdMasking: {
          label: 'Session ID Masking',
          hint: 'When enabled, fixes the session ID in metadata.user_id for 15 minutes, making upstream think requests come from the same session'
        },
        cacheTTLOverride: {
          label: 'Cache TTL Override',
          hint: 'Force all cache creation tokens to be billed as the selected TTL tier (5m or 1h)',
          target: 'Target TTL',
          targetHint: 'Select the TTL tier for billing'
        },
        customBaseUrl: {
          label: 'Custom Relay URL',
          hint: 'Forward requests to a custom relay service. Proxy URL will be passed as a query parameter.',
          urlHint: 'Relay service URL (e.g., https://relay.example.com)',
        },
        clientAffinity: {
          label: 'Client Affinity Scheduling',
          hint: 'When enabled, new sessions prefer accounts previously used by this client to reduce account switching'
        }
      },
      affinityNoClients: 'No affinity clients',
      affinityClients: '{count} affinity clients:',
      affinitySection: 'Client Affinity',
      affinitySectionHint: 'Control how clients are distributed across accounts. Configure zone thresholds to balance load.',
      affinityToggle: 'Enable Client Affinity',
      affinityToggleHint: 'New sessions prefer accounts previously used by this client',
      affinityBase: 'Base Limit (Green Zone)',
      affinityBasePlaceholder: 'Empty = no limit',
      affinityBaseHint: 'Max clients in green zone (full priority scheduling)',
      affinityBaseOffHint: 'No green zone limit. All clients receive full priority scheduling.',
      affinityBuffer: 'Buffer (Yellow Zone)',
      affinityBufferPlaceholder: 'e.g. 3',
      affinityBufferHint: 'Additional clients allowed in the yellow zone (degraded priority)',
      affinityBufferInfinite: 'Unlimited',
      expired: 'Expired',
      proxy: 'Proxy',
      noProxy: 'No Proxy',
      concurrency: 'Concurrency',
      loadFactor: 'Load Factor',
      loadFactorHint: 'Higher load factor increases scheduling frequency',
      priority: 'Priority',
      priorityHint: 'Lower value accounts are used first',
      billingRateMultiplier: 'Billing Rate Multiplier',
      billingRateMultiplierHint: '0 = free, affects account billing only',
      expiresAt: 'Expires At',
      expiresAtHint: 'Leave empty for no expiration',
      higherPriorityFirst: 'Lower value means higher priority',
      mixedScheduling: 'Use in /v1/messages',
      mixedSchedulingHint: 'Enable to participate in Anthropic/Gemini group scheduling',
      mixedSchedulingTooltip:
        '!! WARNING !! Antigravity Claude and Anthropic Claude cannot be used in the same context. If you have both Anthropic and Antigravity accounts, enabling this option will cause frequent 400 errors. When enabled, please use the group feature to isolate Antigravity accounts from Anthropic accounts. Make sure you understand this before enabling!!',
      aiCreditsBalance: 'AI Credits',
      allowOverages: 'Allow Overages (AI Credits)',
      allowOveragesTooltip:
        'Only use AI Credits after free quota is explicitly exhausted. Ordinary concurrent 429 rate limits will not switch to overages.',
      creating: 'Creating...',
      updating: 'Updating...',
      accountCreated: 'Account created successfully',
      accountUpdated: 'Account updated successfully',
      failedToCreate: 'Failed to create account',
      failedToUpdate: 'Failed to update account',
      pleaseSelectStatus: 'Please select a valid account status',
      mixedChannelWarningTitle: 'Mixed Channel Warning',
      mixedChannelWarning: 'Warning: Group "{groupName}" contains both {currentPlatform} and {otherPlatform} accounts. Mixing different channels may cause thinking block signature validation issues, which will fallback to non-thinking mode. Are you sure you want to continue?',
      pleaseEnterAccountName: 'Please enter account name',
      pleaseEnterApiKey: 'Please enter API Key',
      bedrockAccessKeyId: 'AWS Access Key ID',
      bedrockSecretAccessKey: 'AWS Secret Access Key',
      bedrockSessionToken: 'AWS Session Token',
      bedrockRegion: 'AWS Region',
      bedrockRegionHint: 'e.g. us-east-1, us-west-2, eu-west-1',
      bedrockForceGlobal: 'Force Global cross-region inference',
      bedrockForceGlobalHint: 'When enabled, model IDs use the global. prefix (e.g. global.anthropic.claude-...), routing requests to any supported region worldwide for higher availability',
      bedrockAccessKeyIdRequired: 'Please enter AWS Access Key ID',
      bedrockSecretAccessKeyRequired: 'Please enter AWS Secret Access Key',
      bedrockRegionRequired: 'Please select AWS Region',
      bedrockSessionTokenHint: 'Optional, for temporary credentials',
      bedrockSecretKeyLeaveEmpty: 'Leave empty to keep current key',
      bedrockAuthMode: 'Authentication Mode',
      bedrockAuthModeSigv4: 'SigV4 Signing',
      bedrockAuthModeApikey: 'Bedrock API Key',
      bedrockApiKeyLabel: 'Bedrock API Key',
      bedrockApiKeyDesc: 'Bearer Token',
      bedrockApiKeyInput: 'API Key',
      bedrockApiKeyRequired: 'Please enter Bedrock API Key',
      bedrockApiKeyLeaveEmpty: 'Leave empty to keep current key',
      apiKeyIsRequired: 'API Key is required',
      leaveEmptyToKeep: 'Leave empty to keep current key',
      // Upstream type
      upstream: {
        baseUrl: 'Upstream Base URL',
        baseUrlHint: 'The address of the upstream Antigravity service, e.g., https://cloudcode-pa.googleapis.com',
        apiKey: 'Upstream API Key',
        apiKeyHint: 'API Key for the upstream service',
        pleaseEnterBaseUrl: 'Please enter upstream Base URL',
        pleaseEnterApiKey: 'Please enter upstream API Key'
      },
      // OAuth flow
      oauth: {
        title: 'Claude Account Authorization',
        authMethod: 'Authorization Method',
        manualAuth: 'Manual Authorization',
        cookieAutoAuth: 'Cookie Auto-Auth',
        cookieAutoAuthDesc:
          'Use claude.ai sessionKey to automatically complete OAuth authorization without manually opening browser.',
        sessionKey: 'sessionKey',
        keysCount: '{count} keys',
        batchCreateAccounts: 'Will batch create {count} accounts',
        sessionKeyPlaceholder:
          'One sessionKey per line, e.g.:\nsk-ant-sid01-xxxxx...\nsk-ant-sid01-yyyyy...',
        sessionKeyPlaceholderSingle: 'sk-ant-sid01-xxxxx...',
        howToGetSessionKey: 'How to get sessionKey',
        step1: 'Login to claude.ai in your browser',
        step2: 'Press F12 to open Developer Tools',
        step3: 'Go to Application tab',
        step4: 'Find Cookies → https://claude.ai',
        step5: 'Find the row with key sessionKey',
        step6: 'Copy the Value',
        sessionKeyFormat: 'sessionKey usually starts with sk-ant-sid01-',
        startAutoAuth: 'Start Auto-Auth',
        authorizing: 'Authorizing...',
        followSteps: 'Follow these steps to authorize your Claude account:',
        step1GenerateUrl: 'Click the button below to generate the authorization URL',
        generateAuthUrl: 'Generate Auth URL',
        generating: 'Generating...',
        regenerate: 'Regenerate',
        step2OpenUrl: 'Open the URL in your browser and complete authorization',
        openUrlDesc:
          'Open the authorization URL in a new tab, log in to your Claude account and authorize.',
        proxyWarning:
          'Note: If you configured a proxy, make sure your browser uses the same proxy to access the authorization page.',
        step3EnterCode: 'Enter the Authorization Code',
        authCodeDesc:
          'After authorization is complete, the page will display an Authorization Code. Copy and paste it below:',
        authCode: 'Authorization Code',
        authCodePlaceholder: 'Paste the Authorization Code from Claude page...',
        authCodeHint: 'Paste the Authorization Code copied from the Claude page',
        completeAuth: 'Complete Authorization',
        verifying: 'Verifying...',
        pleaseEnterSessionKey: 'Please enter at least one valid sessionKey',
        authFailed: 'Authorization failed',
        cookieAuthFailed: 'Cookie authorization failed',
        keyAuthFailed: 'Key {index}: {error}',
        successCreated: 'Successfully created {count} account(s)',
        batchSuccess: 'Successfully created {count} account(s)',
        batchPartialSuccess: 'Partial success: {success} succeeded, {failed} failed',
        batchFailed: 'Batch creation failed',
        // OpenAI specific
        openai: {
          title: 'OpenAI Account Authorization',
          followSteps: 'Follow these steps to complete OpenAI account authorization:',
          step1GenerateUrl: 'Click the button below to generate the authorization URL',
          generateAuthUrl: 'Generate Auth URL',
          step2OpenUrl: 'Open the URL in your browser and complete authorization',
          openUrlDesc:
            'Open the authorization URL in a new tab, log in to your OpenAI account and authorize.',
          importantNotice:
            'Important: The page may take a while to load after authorization. Please wait patiently. When the browser address bar changes to http://localhost..., the authorization is complete.',
          step3EnterCode: 'Enter Authorization URL or Code',
          authCodeDesc:
            'After authorization is complete, when the page URL becomes http://localhost:xxx/auth/callback?code=...:',
          authCode: 'Authorization URL or Code',
          authCodePlaceholder:
            'Option 1: Copy the complete URL\n(http://localhost:xxx/auth/callback?code=...)\nOption 2: Copy only the code parameter value',
          authCodeHint:
            'You can copy the entire URL or just the code parameter value, the system will auto-detect',
          failedToGenerateUrl: 'Failed to generate OpenAI auth URL',
          failedToExchangeCode: 'Failed to exchange OpenAI auth code',
          failedToValidateRT: 'Failed to validate refresh token',
          errors: {
            OPENAI_OAUTH_PROXY_REQUIRED:
              'No proxy is configured and this server could not reach OpenAI directly, so the OpenAI OAuth request failed. Select a proxy that can access OpenAI and retry; if the authorization code has expired, regenerate the authorization URL.'
          },
          // Refresh Token auth
          refreshTokenAuth: 'Manual RT Input',
          refreshTokenDesc: 'Enter your existing OpenAI Refresh Token(s). Supports batch input (one per line). The system will automatically validate and create accounts.',
          refreshTokenPlaceholder: 'Paste your OpenAI Refresh Token...\nSupports multiple, one per line',
          mobileRefreshTokenAuth: 'Manual Mobile RT Input',
          accessTokenAuth: 'Manual AT Input',
          codexSessionAuth: 'Codex OAuth auth.json / AT Import',
          codexSessionDesc: 'Paste a Codex OAuth auth.json or an accessToken. Accounts use the step 1 settings.',
          codexSessionInputLabel: 'Codex OAuth auth.json or accessToken',
          codexSessionPlaceholder: 'Multiple lines supported, one token or auth.json object per line',
          codexSessionHint: 'OAuth session/access-token imports retain their existing expiration behavior.',
          codexSessionImportAndCreate: 'Import & Create Account',
          codexSessionEmpty: 'Please enter a Codex auth.json or accessToken',
          codexSessionImportFailed: 'Failed to import Codex account',
          codexSessionImportSuccess: 'Import completed: created {created}, updated {updated}, skipped {skipped}',
          codexSessionImportPartial: 'Partial success: created {created}, updated {updated}, skipped {skipped}, failed {failed}',
          agentIdentityAuth: 'Agent Identity auth.json',
          agentIdentityDesc: 'Import a Codex Agent Identity auth.json. No OAuth access or refresh token is stored.',
          agentIdentityInputLabel: 'Agent Identity auth.json',
          agentIdentityPlaceholder: 'Paste one Agent Identity auth.json object',
          agentIdentityHint: 'The file must use auth_mode=agentIdentity. Upstream requests are signed dynamically.',
          agentIdentityInvalid: 'Use a Codex auth.json with auth_mode=agentIdentity.',
          codexPatAuth: 'Codex Personal Access Token',
          codexPatDesc: 'Enter a Codex at- personal access token. The system validates it with OpenAI whoami before creating the account.',
          codexPatInputLabel: 'Codex PAT',
          codexPatPlaceholder: 'at-...',
          codexPatHint: 'This is a separate auth mode. It does not save refresh_token or write an OAuth access_token expiration.',
          codexPatImportAndCreate: 'Validate & Create Codex PAT Account',
          codexPatEmpty: 'Please enter a Codex personal access token',
          codexPatImportFailed: 'Failed to create Codex PAT account',
          sessionTokenAuth: 'Manual ST Input',
          sessionTokenDesc: 'Enter your existing Session Token(s). Supports batch input (one per line). The system will automatically validate and create accounts.',
          sessionTokenPlaceholder: 'Paste your Session Token...\nSupports multiple, one per line',
          sessionTokenRawLabel: 'Raw Input',
          sessionTokenRawPlaceholder: 'Paste /api/auth/session raw payload or Session Token...',
          sessionTokenRawHint: 'You can paste full JSON. The system will auto-parse ST and AT.',
          openSessionUrl: 'Open Fetch URL',
          copySessionUrl: 'Copy URL',
          sessionUrlHint: 'This URL usually returns AT. If sessionToken is absent, copy __Secure-next-auth.session-token from browser cookies as ST.',
          parsedSessionTokensLabel: 'Parsed ST',
          parsedSessionTokensEmpty: 'No ST parsed. Please check your input.',
          parsedAccessTokensLabel: 'Parsed AT',
          validating: 'Validating...',
          validateAndCreate: 'Validate & Create Account',
          pleaseEnterRefreshToken: 'Please enter Refresh Token',
          pleaseEnterSessionToken: 'Please enter Session Token'
        },
        grok: {
          title: 'Grok Account Authorization',
          followSteps: 'Follow these steps to authorize your xAI/Grok account:',
          step1GenerateUrl: 'Generate the xAI authorization URL',
          generateAuthUrl: 'Generate Auth URL',
          step2OpenUrl: 'Open the URL in your browser and complete authorization',
          openUrlDesc: 'Open the authorization URL in a new tab, sign in to xAI, and authorize API access.',
          importantNotice: 'When the browser reaches the local callback URL, copy the full URL or the code query parameter back here.',
          step3EnterCode: 'Enter Authorization URL or Code',
          authCodeDesc: 'After authorization, paste the callback URL, query string, or authorization code:',
          authCode: 'Authorization URL or Code',
          authCodePlaceholder: 'Paste the full callback URL, ?code=... query string, or code value',
          authCodeHint: 'Full callback URLs, query strings, and bare codes are accepted.',
          refreshTokenAuth: 'Manual RT Input',
          refreshTokenDesc: 'Enter existing xAI refresh token(s). Supports batch input, one per line.',
          refreshTokenPlaceholder: 'Paste your xAI refresh token...\nSupports multiple, one per line',
          ssoCookieAuth: 'SSO Cookie Import',
          ssoCookieDesc: 'Paste one Grok Web SSO key per line. The server will complete the xAI Device Flow and convert them into Grok Build OAuth credentials.',
          ssoCookieLabel: 'Grok Web SSO Key',
          ssoCookiePlaceholder: 'One SSO key per line\nSupports multiple, one per line',
          ssoCookieHint: 'One SSO key per line. Multiple keys are imported with 3-way concurrency; expect about 90 seconds per batch. Use a matching-region proxy if needed.',
          emailPasswordAuth: 'Email + password',
          emailPasswordDesc:
            'Sign in with a Grok web email and password. The server uses the password only to obtain an ephemeral SSO cookie, then converts it to Build OAuth credentials. Neither the password nor raw SSO is stored on the account.',
          emailPasswordInputLabel: 'email----password',
          emailPasswordPlaceholder: "user{'@'}example.com----your-password\nMultiple lines supported",
          emailPasswordHint:
            'Format: email----password (password may contain -). Requires YesCaptcha keys; use a matching-region proxy when needed.',
          pleaseEnterPassword: 'Please enter email----password (one per line)',
          pleaseEnterSSOToken: 'Please enter an SSO token',
          failedToValidateSSO: 'Failed to validate Grok SSO',
          failedToAuthorizePassword: 'Grok password authorization failed',
          convertingSSO: 'Converting...',
          convertSSOAndCreate: 'Convert & Create Account',
          validating: 'Validating...',
          validateAndCreate: 'Validate & Create Account',
          pleaseEnterRefreshToken: 'Please enter Refresh Token',
          failedToGenerateUrl: 'Failed to generate Grok auth URL',
          missingExchangeParams: 'Missing authorization code, state, or OAuth session',
          failedToExchangeCode: 'Failed to exchange Grok authorization code',
          failedToValidateRT: 'Failed to validate Grok refresh token',
          failedToConvertSSO: 'Failed to convert Grok SSO cookie',
          errors: {
            GROK_OAUTH_SESSION_NOT_FOUND:
              'Grok OAuth session was not found or has expired. Generate a new auth URL and paste the newest callback URL.',
            GROK_OAUTH_INVALID_STATE:
              'Grok OAuth state does not match this session. Paste the callback URL from the same generated auth link.',
            GROK_OAUTH_STATE_REQUIRED:
              'The callback URL is missing the OAuth state. Paste the full callback URL, not only the code.',
            GROK_OAUTH_CODE_REQUIRED:
              'The Grok authorization code is missing. Paste the full callback URL, query string, or code value.',
            GROK_OAUTH_NO_REFRESH_TOKEN:
              'The Grok response did not include a refresh token. Generate a new auth URL and approve offline access again.',
            GROK_OAUTH_PROXY_NOT_AVAILABLE:
              'Grok OAuth proxy lookup is unavailable. Check the selected proxy and retry.',
            GROK_OAUTH_PROXY_NOT_FOUND:
              'The selected proxy could not be found. Choose an available proxy and retry.'
          },
          oauthOnlyHint: 'Initial Grok support is OAuth subscription-backed Responses API text and reasoning traffic only.'
        },
        // Gemini specific
	        gemini: {
	          title: 'Gemini Account Authorization',
	          followSteps: 'Follow these steps to authorize your Gemini account:',
	          step1GenerateUrl: 'Generate the authorization URL',
	          generateAuthUrl: 'Generate Auth URL',
	          projectIdLabel: 'Project ID (optional)',
	          projectIdPlaceholder: 'e.g. my-gcp-project or cloud-ai-companion-xxxxx',
	          projectIdHint:
	            'Leave empty to auto-detect after code exchange. If auto-detection fails, fill it in and re-generate the auth URL to try again.',
	          howToGetProjectId: 'How to get',
	          step2OpenUrl: 'Open the URL in your browser and complete authorization',
	          openUrlDesc:
	            'Open the authorization URL in a new tab, log in to your Google account and authorize.',
	          step3EnterCode: 'Enter Authorization URL or Code',
	          authCodeDesc:
	            'After authorization, copy the callback URL (recommended) or just the code and paste it below.',
	          authCode: 'Callback URL or Code',
	          authCodePlaceholder:
	            'Option 1 (recommended): Paste the callback URL\nOption 2: Paste only the code value',
	          authCodeHint: 'The system will auto-extract code/state from the URL.',
          redirectUri: 'Redirect URI',
          redirectUriHint:
            'This must be configured in your Google OAuth client and must match exactly.',
          confirmRedirectUri:
            'I have configured this Redirect URI in the Google OAuth client (must match exactly)',
	          invalidRedirectUri: 'Redirect URI must be a valid http(s) URL',
	          redirectUriNotConfirmed: 'Please confirm the Redirect URI is configured correctly',
	          missingRedirectUri: 'Missing redirect URI',
	          failedToGenerateUrl: 'Failed to generate Gemini auth URL',
	          missingExchangeParams: 'Missing auth code, session ID, or state',
	          failedToExchangeCode: 'Failed to exchange Gemini auth code',
	          missingProjectId: 'GCP Project ID retrieval failed: Your Google account is not linked to an active GCP project. Please activate GCP and bind a credit card in Google Cloud Console, or manually enter the Project ID during authorization.',
	          modelPassthrough: 'Gemini Model Passthrough',
	          modelPassthroughDesc:
	            'All model requests are forwarded directly to the Gemini API without model restrictions or mappings.',
	          stateWarningTitle: 'Note',
	          stateWarningDesc: 'Recommended: paste the full callback URL (includes code & state).',
	          oauthTypeLabel: 'OAuth Type',
          needsProjectId: 'Built-in OAuth (Code Assist)',
          needsProjectIdDesc: 'Requires GCP project and Project ID',
          noProjectIdNeeded: 'Custom OAuth (AI Studio)',
          noProjectIdNeededDesc: 'Requires admin-configured OAuth client',
	          aiStudioNotConfiguredShort: 'Not configured',
	          aiStudioNotConfiguredTip:
	            'AI Studio OAuth is not configured: set GEMINI_OAUTH_CLIENT_ID / GEMINI_OAUTH_CLIENT_SECRET and add Redirect URI: http://localhost:1455/auth/callback (Consent screen scopes must include https://www.googleapis.com/auth/generative-language.retriever)',
	          aiStudioNotConfigured:
	            'AI Studio OAuth is not configured: set GEMINI_OAUTH_CLIENT_ID / GEMINI_OAUTH_CLIENT_SECRET and add Redirect URI: http://localhost:1455/auth/callback'
	        },
        // Antigravity specific
        antigravity: {
          title: 'Antigravity Account Authorization',
          followSteps: 'Follow these steps to authorize your Antigravity account:',
          step1GenerateUrl: 'Generate the authorization URL',
          generateAuthUrl: 'Generate Auth URL',
          step2OpenUrl: 'Open the URL in your browser and complete authorization',
          openUrlDesc: 'Open the authorization URL in a new tab, log in to your Google account and authorize.',
          importantNotice:
            'Important: The page may take a while to load after authorization. Please wait patiently. When the browser address bar shows http://localhost..., authorization is complete.',
          step3EnterCode: 'Enter Authorization URL or Code',
          authCodeDesc:
            'After authorization, when the page URL becomes http://localhost:xxx/auth/callback?code=...:',
          authCode: 'Authorization URL or Code',
          authCodePlaceholder:
            'Option 1: Copy the complete URL\n(http://localhost:xxx/auth/callback?code=...)\nOption 2: Copy only the code parameter value',
                    authCodeHint: 'You can copy the entire URL or just the code parameter value, the system will auto-detect',
                    failedToGenerateUrl: 'Failed to generate Antigravity auth URL',
                    missingExchangeParams: 'Missing code, session ID, or state',
                    failedToExchangeCode: 'Failed to exchange Antigravity auth code',
                    // Refresh Token auth
                    refreshTokenAuth: 'Manual RT',
                    refreshTokenDesc: 'Enter your existing Antigravity Refresh Token. Supports batch input (one per line). The system will automatically validate and create accounts.',
                    refreshTokenPlaceholder: 'Paste your Antigravity Refresh Token...\nSupports multiple tokens, one per line',
                    validating: 'Validating...',
                    validateAndCreate: 'Validate & Create',
                    pleaseEnterRefreshToken: 'Please enter Refresh Token',
                    failedToValidateRT: 'Failed to validate Refresh Token'
                  }
                },      // Gemini specific (platform-wide)
      gemini: {
        helpButton: 'Help',
        helpDialog: {
          title: 'Gemini Usage Guide',
          apiKeySection: 'API Key Links'
        },
        modelPassthrough: 'Gemini Model Passthrough',
        modelPassthroughDesc:
          'All model requests are forwarded directly to the Gemini API without model restrictions or mappings.',
        baseUrlHint: 'Leave default for official Gemini API',
        apiKeyHint: 'Your Gemini API Key (starts with AIza)',
        tier: {
          label: 'Account Tier',
          hint: 'Tip: The system will try to auto-detect the tier first; if auto-detection is unavailable or fails, your selected tier is used as a fallback (simulated quota).',
          aiStudioHint:
            'AI Studio quotas are per-model (Pro/Flash are limited independently). If billing is enabled, choose Pay-as-you-go.',
          googleOne: {
            free: 'Google One Free',
            pro: 'Google One Pro',
            ultra: 'Google One Ultra'
          },
          gcp: {
            standard: 'GCP Standard',
            enterprise: 'GCP Enterprise'
          },
          aiStudio: {
            free: 'Google AI Free',
            paid: 'Google AI Pay-as-you-go'
          }
        },
        accountType: {
          oauthTitle: 'OAuth (Gemini)',
          oauthDesc: 'Authorize with your Google account and choose an OAuth type.',
          apiKeyTitle: 'API Key (AI Studio)',
          apiKeyDesc: 'Fastest setup. Use an AIza API key.',
          apiKeyNote:
            'Best for light testing. Free tier has strict rate limits and data may be used for training.',
          apiKeyLink: 'Get API Key',
          quotaLink: 'Quota guide'
        },
        oauthType: {
          builtInTitle: 'Built-in OAuth (Gemini CLI / Code Assist)',
          builtInDesc: 'Uses Google built-in client ID. No admin configuration required.',
          builtInRequirement: 'Requires a GCP project and Project ID.',
          googleOneDesc: 'Personal account with Google One subscription quota',
          codeAssistDesc: 'Enterprise-grade, requires a GCP project',
          codeAssistRequirement: 'Requires an active GCP project with billing enabled',
          showAdvanced: 'Show advanced options (custom OAuth Client)',
          hideAdvanced: 'Hide advanced options (custom OAuth Client)',
          gcpProjectLink: 'Create project',
          customTitle: 'Custom OAuth (AI Studio OAuth)',
          customDesc: 'Uses admin-configured OAuth client for org management.',
          customRequirement: 'Admin must configure Client ID and add you as a test user.',
          badges: {
            recommended: 'Recommended',
            highConcurrency: 'High concurrency',
            individuals: 'Recommended for individuals',
            noGcp: 'No GCP required',
            enterprise: 'Enterprise users',
            noAdmin: 'No admin setup',
            orgManaged: 'Org managed',
            adminRequired: 'Admin required'
          }
        },
        setupGuide: {
          title: 'Gemini Setup Checklist',
          checklistTitle: 'Checklist',
          checklistItems: {
            usIp: 'Use a US IP and ensure your account country is set to US.',
            age: 'Account must be 18+.'
          },
          activationTitle: 'One-click Activation',
          activationItems: {
            geminiWeb: 'Activate Gemini Web to avoid User not initialized.',
            gcpProject: 'Activate a GCP project and get the Project ID for Code Assist.'
          },
          links: {
            countryCheck: 'Check country association',
            countryChange: 'Change country association',
            geminiWebActivation: 'Activate Gemini Web',
            gcpProject: 'Open GCP Console'
          }
        },
        quotaPolicy: {
          title: 'Gemini Quota & Limit Policy (Reference)',
          note: 'Note: Gemini does not provide an official quota inquiry API. The "Daily Quota" shown here is an estimate simulated by the system based on account tiers for scheduling reference only. Please refer to official Google errors for actual limits.',
          columns: {
            channel: 'Auth Channel',
            account: 'Account Status',
            limits: 'Limit Policy',
            docs: 'Official Docs'
          },
          docs: {
            codeAssist: 'Code Assist Quotas',
            aiStudio: 'AI Studio Pricing',
            vertex: 'Vertex AI Quotas'
          },
          simulatedNote: 'Simulated quota, for reference only',
          rows: {
            googleOne: {
              channel: 'Google One OAuth (Individuals / Code Assist for Individuals)',
              limitsFree: 'Shared pool: 1000 RPD / 60 RPM',
              limitsPro: 'Shared pool: 1500 RPD / 120 RPM',
              limitsUltra: 'Shared pool: 2000 RPD / 120 RPM'
            },
            gcp: {
              channel: 'GCP Code Assist OAuth (Enterprise)',
              limitsStandard: 'Shared pool: 1500 RPD / 120 RPM',
              limitsEnterprise: 'Shared pool: 2000 RPD / 120 RPM'
            },
            cli: {
              channel: 'Gemini CLI (Official Google Login / Code Assist)',
              free: 'Free Google Account',
              premium: 'Google One AI Premium',
              limitsFree: 'RPD ~1000; RPM ~60 (soft)',
              limitsPremium: 'RPD ~1500+; RPM ~60+ (priority queue)'
            },
            gcloud: {
              channel: 'GCP Code Assist (gcloud auth)',
              account: 'No Code Assist subscription',
              limits: 'RPD ~1000; RPM ~60 (preview)'
            },
            aiStudio: {
              channel: 'AI Studio API Key / OAuth',
              free: 'No billing (free tier)',
              paid: 'Billing enabled (pay-as-you-go)',
              limitsFree: 'RPD 50; RPM 2 (Pro) / 15 (Flash)',
              limitsPaid: 'RPD unlimited; RPM 1000 (Pro) / 2000 (Flash) (per model)'
            },
            customOAuth: {
              channel: 'Custom OAuth Client (GCP)',
              free: 'Project not billed',
              paid: 'Project billed',
              limitsFree: 'RPD 50; RPM 2 (project quota)',
              limitsPaid: 'RPD unlimited; RPM 1000+ (project quota)'
            }
          }
        },
        rateLimit: {
          ok: 'Not rate limited',
          unlimited: 'Unlimited',
          limited: 'Rate limited {time}',
          now: 'now'
        }
      },
      // Re-Auth Modal
      reAuthorizeAccount: 'Re-Authorize Account',
      claudeCodeAccount: 'Claude Code Account',
      openaiAccount: 'OpenAI Account',
      geminiAccount: 'Gemini Account',
      antigravityAccount: 'Antigravity Account',
      grokAccount: 'Grok Account',
      inputMethod: 'Input Method',
      reAuthorizedSuccess: 'Account re-authorized successfully',
      // Test Modal
      testAccountConnection: 'Test Account Connection',
      errorPrefix: 'Error: {message}',
      imagePreviewAlt: 'Test image {index}',
      imageLightboxAlt: 'Image preview',
      account: 'Account',
      readyToTest: 'Ready to test. Click "Start Test" to begin...',
      connectingToApi: 'Connecting to API...',
      testCompleted: 'Test completed successfully!',
      testFailed: 'Test failed',
      connectedToApi: 'Connected to API',
      usingModel: 'Using model: {model}',
      sendingTestMessage: 'Sending test message: "hi"',
      sendingImageRequest: 'Sending image generation test request...',
      response: 'Response:',
      startTest: 'Start Test',
      testing: 'Testing...',
      retry: 'Retry',
      copyOutput: 'Copy output',
      outputCopied: 'Output copied',
      startingTestForAccount: 'Starting test for account: {name}',
      testAccountTypeLabel: 'Account type: {type}',
      selectTestModel: 'Select Test Model',
      testModel: 'Test model',
      testPrompt: 'Prompt: "hi"',
      imagePromptLabel: 'Image prompt',
      imagePromptPlaceholder: 'Example: Generate an orange cat astronaut sticker in pixel-art style on a solid background.',
      imagePromptDefault: 'Generate a cute orange cat astronaut sticker on a clean pastel background.',
      imageTestHint:
        'Calls standalone /v1/images/generations and shows the returned image below.',
      imageTestMode: 'Mode: Image generation test',
      videoPromptLabel: 'Video prompt',
      videoPromptPlaceholder: 'Example: A red ball bouncing once on a white floor, short simple motion.',
      videoPromptDefault: 'A red ball bouncing once on a white floor, short simple motion.',
      videoTestHint:
        'Calls standalone /v1/videos/generations, polls until done, then downloads the finished video for on-page preview.',
      videoTestMode: 'Mode: Video generation test',
      sendingVideoRequest: 'Sending video generation request...',
      imagePreview: 'Generated images:',
      imageReceived: 'Received test image #{count}',
      audioPreview: 'Generated audio:',
      audioReceived: 'Received test audio #{count}',
      videoPreview: 'Generated video:',
      videoReceived: 'Received test video #{count}',
      // Stats Modal
      viewStats: 'View Stats',
      usageStatistics: 'Usage Statistics',
      last30DaysUsage: 'Last 30 days usage statistics (based on actual usage days)',
      stats: {
        totalCost: '30-Day Total Cost',
        accumulatedCost: 'Accumulated cost',
        standardCost: 'Standard',
        totalRequests: '30-Day Total Requests',
        totalCalls: 'Total API calls',
        avgDailyCost: 'Daily Avg Cost',
        basedOnActualDays: 'Based on {days} actual usage days',
        avgDailyRequests: 'Daily Avg Requests',
        avgDailyUsage: 'Average daily usage',
        todayOverview: 'Today Overview',
        cost: 'Cost',
        requests: 'Requests',
        tokens: 'Tokens',
        highestCostDay: 'Highest Cost Day',
        highestRequestDay: 'Highest Request Day',
        date: 'Date',
        accumulatedTokens: 'Accumulated Tokens',
        totalTokens: '30-Day Total',
        dailyAvgTokens: 'Daily Average',
        performance: 'Performance',
        avgResponseTime: 'Avg Response',
        daysActive: 'Days Active',
        recentActivity: 'Recent Activity',
        todayRequests: 'Today Requests',
        todayTokens: 'Today Tokens',
        todayCost: 'Today Cost',
        usageTrend: '30-Day Cost & Request Trend',
        noData: 'No usage data available for this account'
      },
      usageWindow: {
        statsTitle: '5-Hour Window Usage Statistics',
        statsTitleDaily: 'Daily Usage Statistics',
        geminiProDaily: 'Pro',
        geminiFlashDaily: 'Flash',
        gemini3Pro: 'G3P',
        gemini3Flash: 'G3F',
        gemini3Image: 'G31FI',
        claude: 'Claude',
        grokRequests: 'Req',
        grokTokens: 'Tok',
        grokFreeQuota24hHint: 'Estimated from local token usage over the rolling 24-hour window ({limit} limit)',
        grokWeeklyUsage: 'Weekly {percent}%',
        grokUsed: 'Used $',
        grokBalance: 'Bal $',
        grokPrepaid: 'Prepaid balance',
        grokMonthlyLimit: 'Monthly used / limit (USD)',
        grokOverage: 'Overage onDemandUsed/onDemandCap',
        grokOverageShort: 'OD $',
        grokUnknown: 'Grok quota is unknown until the first upstream response includes xAI rate-limit headers.',
        grokRetryAfter: 'Retry after {time}',
        grokProbe: 'Probe',
        grokProbeTooltip: 'Send a minimal xAI Responses probe and read quota headers',
        grokResetUnsupported: 'Reset unsupported',
        grokResetUnsupportedTooltip: 'xAI does not expose reset credits for Grok OAuth accounts',
        grokNoHeaders: 'No quota headers observed',
        grokLastStatus: 'Status {status}',
        grokLastProbe: 'Probe {time}',
        grokLastHeadersSeen: 'Headers {time}',
        passiveSampled: 'Passive',
        activeQuery: 'Query'
      },
      openaiQuotaReset: {
        count: 'Credits',
        reset: 'Reset',
        countTooltipLoad: 'Click to load the available reset-credit count',
        countTooltipRefresh: 'Click to refresh the available reset-credit count',
        resetTooltipReady: 'Consume 1 reset credit to immediately restore the window',
        resetTooltipNeedQuery: 'Click Credits first to load the available count',
        resetTooltipNoCredits: 'No reset credits available',
        resetTooltipShadow: 'Spark shadow accounts cannot reset credits; reset on the parent account',
        expiresAt: 'Expires {time}',
        expiresAtFull: 'Reset credit expires at {time}',
        expandExpirations: 'Expand the other {count} reset credit expiration(s)',
        collapseExpirations: 'Collapse reset credit expirations',
        expirationDetails: 'Reset credit expiration details',
        noCreditsAvailable: 'No reset credits available',
        resetSuccess: 'Reset {windows} window(s); credits and account state updated',
        resetCacheRefreshFailed: 'The window was reset and account state recovered, but the reset-credit count could not be read back. Query it again.',
        resetAccountRecoveryFailed: 'The window was reset, but account state recovery failed. Recover the account state manually.',
        resetAccountRefreshFailed: 'The window, account state, and reset-credit cache were updated, but the latest account display could not be loaded.',
        refreshCachePersistFailed: 'Showing the live count, but its expiration details were unavailable, so the cached details were kept.',
        confirmTitle: 'Confirm Weekly Limit Reset',
        confirmMessage: 'This will consume 1 reset credit to immediately restore the current window ({count} remaining). This action cannot be undone. Continue?'
      },
      tier: {
        free: 'Free',
        pro: 'Pro',
        ultra: 'Ultra',
        aiPremium: 'AI Premium',
        standard: 'Standard',
        basic: 'Basic',
        personal: 'Personal',
        unlimited: 'Unlimited'
      },
      ineligibleWarning:
        'This account is not eligible for Antigravity, but API forwarding still works. Use at your own risk.',
      forbidden: 'Forbidden',
      forbiddenValidation: 'Verification Required',
      forbiddenViolation: 'Violation Ban',
      openVerification: 'Open Verification Link',
      copyLink: 'Copy Link',
      linkCopied: 'Link Copied',
      needsReauth: 'Re-auth Required',
      rateLimited: 'Rate Limited',
      usageError: 'Fetch Error'
    },

    // Scheduled Tests
}
