import { defineStore } from 'pinia'

import type { AppLaunchOrigin, LaunchablePhoneAppId } from '@/types/apps'
import type {
  DeviceBootstrap,
  DeviceSecurity,
  PhoneDevice,
} from '@/types/device'
import { clampPage } from '@/utils/pages'
import { cloneJsonData } from '@/utils/clone'
import { nuiCall } from '@/utils/nui'
import type { NuiResponse } from '@/utils/nui'
import {
  DEFAULT_PHONE_PREFERENCES,
  PHONE_SETUP_LAST_STEP,
  clampPhoneScale,
  ensureAppNotificationPreferences,
  parsePhonePreferences,
  type AppNotificationPreferences,
  type PhonePreferencesV1,
  type WallpaperId,
  type WallpaperTarget,
} from '@/utils/preferences'

type LocaleTree = Record<string, unknown>

export type PasscodeResponseData = {
  attemptsRemaining?: number
  retryAfter?: number
  security?: DeviceSecurity
}

export type PhoneOpenPayload = {
  account?: DeviceBootstrap['account']
  device?: PhoneDevice
  fallbackLocales?: LocaleTree
  lang?: string
  locales?: LocaleTree
  memos?: DeviceBootstrap['memos']
  notes?: DeviceBootstrap['notes']
  phoneNumberFormat?: DeviceBootstrap['phoneNumberFormat']
  player?: DeviceBootstrap['player']
  security?: DeviceSecurity
  token?: string
}

const namespaceQueues = new Map<string, Promise<boolean>>()
let nextPersistenceSession = 0

const companiesFallbackLocales = {
  name: 'Companies',
  navigation: 'Companies navigation',
  actions: 'Request actions',
  request: 'Service Request',
  verified: 'Verified company',
  back: 'Back',
  close: 'Close',
  tryAgain: 'Try Again',
  loadMore: 'Load More',
  routeSet: 'GPS route set.',
  tabs: {
    directory: 'Discover',
    requests: 'Requests',
    work: 'Work',
  },
  availability: {
    available: 'Available',
    busy: 'Busy',
    closed: 'Closed',
  },
  requestStatuses: {
    new: 'New',
    assigned: 'Assigned',
    in_progress: 'In Progress',
    waiting_customer: 'Waiting',
    completed: 'Completed',
    cancelled: 'Cancelled',
  },
  roles: {
    employee: 'Employee',
    manager: 'Manager',
  },
  categories: {
    gastronomy: 'Food & Drink',
    vehicles: 'Vehicles',
    transport: 'Transport',
    crafts: 'Trades',
    retail: 'Retail',
    real_estate: 'Real Estate',
    media: 'Media',
    nightlife: 'Nightlife',
    public_services: 'Public Services',
    emergency: 'Emergency Services',
    medical: 'Medical',
    mechanics: 'Mechanics',
    government: 'Government',
  },
  days: {
    monday: 'Monday',
    tuesday: 'Tuesday',
    wednesday: 'Wednesday',
    thursday: 'Thursday',
    friday: 'Friday',
    saturday: 'Saturday',
    sunday: 'Sunday',
  },
  directory: {
    searchPlaceholder: 'Search companies or services',
    categories: 'Company categories',
    allCategories: 'All',
    allCompanies: 'All Companies',
    availableNow: 'Available now',
    availableNowHint: 'Only show companies taking calls right now.',
    availableSection: 'Available Now',
    moreFilters: 'More filters',
    hasLocation: 'Location',
    acceptsRequests: 'Requests',
    resetFilters: 'Reset Filters',
  },
  loading: {
    directory: 'Loading companies...',
    profile: 'Loading company profile...',
    request: 'Loading request...',
    requests: 'Loading your requests...',
    work: 'Loading workspace...',
  },
  states: {
    directoryError: 'Companies are unavailable',
    requestsError: 'Requests are unavailable',
    workError: 'Workspace unavailable',
    profileError: 'Company unavailable',
    requestError: 'Request unavailable',
    noCompanies: 'No companies listed',
    noCompaniesBody: 'Public companies will appear here when configured.',
    noResults: 'No matching companies',
    noResultsBody: 'Try another search or reset your filters.',
    noOpenRequests: 'No open requests',
    noClosedRequests: 'No completed requests',
    noRequestsBody: 'Requests you create with a company will appear here.',
  },
  actionUnavailable: {
    call: 'This company cannot be called right now.',
    message: 'This service line does not accept text messages.',
    route: 'This company has no public location.',
    request: 'This company is not accepting service requests.',
  },
  profile: {
    updated: 'Status updated {time}',
    announcement: 'Latest Update',
    location: 'Location',
    noLocation: 'No public location',
    hours: 'Opening Hours',
    closed: 'Closed',
    byAvailability: 'Open by availability',
    services: 'Services',
    noServices: 'No public services listed',
    call: 'Call',
    message: 'Message',
    route: 'Route',
    request: 'Request',
  },
  composer: {
    title: 'New Service Request',
    chooseService: 'Choose a Service',
    subject: 'Subject',
    subjectPlaceholder: 'What do you need?',
    description: 'Details',
    descriptionPlaceholder:
      'Describe what happened and how the company can help.',
    contact: 'Contact number',
    registeredSim: 'Replies go to your registered SIM {number}.',
    registeredSimRequired: 'A registered SIM is required to send a request.',
    addPhotos: 'Add Photos ({count}/3)',
    selectedPhoto: 'Selected request photo',
    removePhoto: 'Remove photo',
    review: 'Review Request',
    confirmTitle: 'Send this request?',
    confirmBody: 'Send your {service} request to {company}.',
    edit: 'Keep Editing',
    send: 'Send Request',
  },
  requests: {
    open: 'Open',
    closed: 'Completed',
    findCompany: 'Find a Company',
    generalService: 'General service',
    attachments: 'Request photos',
    attachedPhoto: 'Attached request photo',
    timeline: 'Timeline',
    conversation: 'Conversation',
    noMessages: 'No replies yet',
    replyPlaceholder: 'Write a reply',
    sendReply: 'Send reply',
    call: 'Call Company',
    cancel: 'Cancel Request',
    cancelTitle: 'Cancel this request?',
    cancelBody: 'The company will be notified and can no longer complete it.',
    keep: 'Keep Request',
    cancelConfirm: 'Cancel Request',
  },
  timeline: {
    created: 'Request created',
    assigned: 'Request assigned',
    cancelled: 'Request cancelled',
    completed: 'Request completed',
    statusChanged: 'Status changed to {status}',
  },
  time: {
    justNow: 'just now',
    minutesAgo: '{count} min ago',
    hoursAgo: '{count} hr ago',
    daysAgo: '{count} days ago',
  },
  work: {
    notAuthorized: 'No company workspace',
    notAuthorizedBody:
      'Your current job is not connected to a configured company.',
    workspace: 'Company Workspace',
    publicAvailability: 'Public Availability',
    takeCalls: 'Take company calls',
    takeCallsBody: 'Route new service-line calls to this active SIM.',
    dialServiceLine: 'Call from service line',
    dialServiceLineBody: 'Make an outgoing call that displays {number}.',
    dialServiceLineHint:
      'The recipient will see {number} as the incoming caller.',
    targetNumber: 'Phone number',
    targetNumberHint: 'Enter a phone number',
    callNow: 'Call Now',
    overview: 'Today at a Glance',
    metrics: {
      new: 'New',
      assigned: 'Mine',
      waiting: 'Waiting',
      completedToday: 'Done Today',
    },
    queue: 'Request Queue',
    filters: {
      new: 'New',
      assigned: 'Mine',
      in_progress: 'In Progress',
      waiting_customer: 'Waiting',
      completed: 'Done',
    },
    unassigned: 'Unassigned',
    emptyQueue: 'Queue is clear',
    emptyQueueBody: 'No requests match this filter.',
  },
  workActions: {
    claim: 'Claim Request',
    assign: 'Assign Employee',
    callCustomer: 'Call Customer',
    setStatus: 'Set status: {status}',
  },
  assignment: {
    title: 'Assign Employee',
    online: 'Online',
    offline: 'Offline',
    unknownMember: 'Unknown employee',
    confirm: 'Assign Request',
  },
  assignmentLabels: {
    you: 'Assigned to you',
    assigned: 'Assigned to a colleague',
  },
  messageAuthors: {
    you: 'You',
    customer: 'Customer',
    company: 'Company',
  },
  manager: {
    title: 'Company Profile',
    subtitle: 'Manage public information, services, and announcements.',
    revision: 'Server revision {revision}',
    availability: 'Availability',
    profile: 'Public Profile',
    description: 'Description',
    descriptionPlaceholder: 'Describe the company and what it offers.',
    phoneNumber: 'Service number',
    phoneNumberManaged: 'Configured by the server and cannot be edited here.',
    noPhoneNumber: 'No service number configured',
    chooseCover: 'Choose cover photo',
    chooseLogo: 'Choose logo',
    coverPhoto: 'Company cover photo',
    logoPhoto: 'Company logo',
    locationLabel: 'Location name',
    locationReady: 'A map position is attached to this profile.',
    useCurrentLocation: 'Use Current Location',
    address: 'Address',
    district: 'District',
    acceptRequests: 'Accept service requests',
    acceptRequestsBody: 'Allow registered SIMs to open structured requests.',
    saveProfile: 'Save Profile',
    hours: 'Opening Hours',
    dayOpen: 'Company is open on this day',
    opensAt: 'Opens',
    closesAt: 'Closes',
    saveHours: 'Save Hours',
    services: 'Services',
    serviceTitle: 'Service name',
    serviceDescription: 'Description',
    priceText: 'Price text',
    serviceActive: 'Publicly visible',
    serviceRequests: 'Accept requests for this service',
    removeService: 'Remove Service',
    addService: 'Add Service',
    saveServices: 'Save Services',
    announcement: 'Current Announcement',
    announcementText: 'Announcement',
    announcementPlaceholder: 'Share a short, timely update.',
    expiresAt: 'Expires at',
    publish: 'Publish Announcement',
  },
  feedback: {
    requestCreated: 'Request sent.',
    requestCancelled: 'Request cancelled.',
    requestClaimed: 'Request claimed.',
    requestAssigned: 'Request assigned.',
    statusUpdated: 'Request status updated.',
    availabilityUpdated: 'Public availability updated.',
    locationUpdated: 'Current location selected.',
    callsEnabled: 'Company calls enabled.',
    callsDisabled: 'Company calls disabled.',
    profileSaved: 'Company profile saved.',
    hoursSaved: 'Opening hours saved.',
    servicesSaved: 'Services saved.',
    announcementPublished: 'Announcement published.',
  },
  conflict: {
    title: 'Newer changes are available',
    body: 'Another manager updated this information. Reload before saving again.',
    reload: 'Reload',
  },
  notifications: {
    newRequest: 'New company request',
    requestUpdated: 'A company request was updated.',
    newMessage: 'New reply to your company request.',
    assigned: 'A company request was assigned to you.',
  },
  errors: {
    anonymous_sim: 'A registered SIM is required for service requests.',
    call_unavailable: 'This service line is currently unavailable.',
    company_not_found: 'This company is no longer available.',
    invalid_profile: 'Check the company profile fields.',
    invalid_request: 'Check the subject and request details.',
    invalid_media: 'One or more attached photos are unavailable.',
    invalid_expiration: 'Choose a valid announcement expiration.',
    invalid_service: 'This service is no longer available.',
    invalid_status: 'That status change is not allowed.',
    messaging_unavailable: 'This company does not accept text messages.',
    no_sim: 'Insert a SIM card to continue.',
    not_authorized: 'You are not authorized for this company action.',
    rate_limited: 'Please wait before trying again.',
    request_not_found: 'This request is no longer available.',
    revision_conflict:
      'The data changed on another device. Reload and try again.',
    service_unavailable: 'The Companies service is temporarily unavailable.',
    too_many_open_requests: 'You already have too many open requests.',
    request_failed: 'Companies could not complete the request.',
  },
}

const healthFallbackLocales = {
  name: 'Health',
  navigation: 'Health navigation',
  tabs: { today: 'Today', trends: 'Trends', medicalId: 'Medical ID' },
  loading: 'Loading health data...',
  errorTitle: 'Health is unavailable',
  errorBody: 'Your activity data could not be loaded.',
  tryAgain: 'Try Again',
  goal: 'of {count}',
  steps: 'Steps',
  distance: 'Distance',
  active: 'Active',
  energy: 'Energy',
  kilometers: '{count} km',
  minutes: '{count} min',
  kilocalories: '{count} kcal',
  thisWeek: 'This week',
  snapshot: 'Health snapshot',
  trends: {
    title: 'Trends',
    week: 'Week',
    month: 'Month',
    total: '{count} steps',
    more: '{count}% more than last week',
    less: '{count}% less than last week',
    same: 'Same as last week',
    dailyAverage: 'Daily average',
    activeTime: 'Active time',
    dailyActivity: 'Daily activity',
    goal: 'Goal',
  },
  medicalId: {
    title: 'Medical ID',
    edit: 'Edit',
    done: 'Done',
    resident: 'Los Santos resident',
    emergencyInformation: 'Emergency information',
    bloodType: 'Blood type',
    allergies: 'Allergies',
    conditions: 'Conditions',
    medication: 'Medication',
    noneRecorded: 'None recorded',
    emergencyContact: 'Emergency contact',
    contactName: 'Contact name',
    relation: 'Relation',
    phoneNumber: 'Phone number',
    emergencyCall: 'Emergency call',
    callContact: 'Call emergency contact',
    saveFailed: 'Medical ID could not be saved.',
  },
  errors: {
    invalid_phone_number: 'Enter a valid in-game phone number.',
    invalid_request: 'Check the Medical ID fields.',
    rate_limited: 'Please wait before trying again.',
    request_failed: 'Health could not complete the request.',
  },
}

const cryptoFallbackLocales = {
  name: 'VaultX',
  subtitle: 'Fictional asset exchange',
  loading: 'Opening secure exchange…',
  logout: 'Sign out',
  logoutConfirm: {
    title: 'Sign out of VaultX?',
    body: 'You will need your VaultX password to unlock this profile again.',
    cancel: 'Cancel',
    confirm: 'Sign out',
    signingOut: 'Signing out…',
  },
  refresh: 'Refresh portfolio',
  navigation: 'VaultX navigation',
  auth: {
    eyebrow: 'Character-bound exchange',
    loginTitle: 'Welcome back',
    registerTitle: 'Create your VaultX profile',
    body: 'Trade and transfer fictional digital assets through a protected, server-controlled account.',
    network: 'VAULT NETWORK ONLINE',
    serverSecured: 'Server secured',
    personalKey: 'Personal crypto key',
    characterBound: 'Character bound',
    login: 'Log in',
    register: 'Register',
    accountEmail: 'Sky Cloud email',
    accountMissing: 'No Sky Cloud account',
    loginPanelTitle: 'Secure account access',
    loginPanelBody: 'Your character identity is detected automatically.',
    registerPanelTitle: 'Set up your private vault',
    registerPanelBody: 'Choose a profile identity and secure access phrase.',
    handle: 'VaultX handle',
    handlePlaceholder: '3–20 letters or numbers',
    password: 'Password',
    passwordPlaceholder: '8–72 characters',
    confirmPassword: 'Confirm password',
    confirmPasswordPlaceholder: 'Enter the same password again',
    showPassword: 'Show password',
    hidePassword: 'Hide password',
    security: 'Use an in-character password. Never reuse a real password.',
    ruleLength: '8+ characters',
    ruleMixed: 'Upper & lowercase',
    ruleNumber: 'Contains a number',
    ruleSpecial: 'Special character',
    strength0: 'Start entering a secure password',
    strength1: 'Weak protection',
    strength2: 'Basic protection',
    strength3: 'Strong protection',
    strength4: 'Excellent protection',
    acceptTitle: 'Create a fictional wallet',
    acceptBody: 'I understand that VaultX assets have no real-world value.',
    footer: 'Encrypted session · Rate-limited access · Memory-hard password',
    loginAction: 'Unlock VaultX',
    registerAction: 'Create secure profile',
  },
  tabs: {
    portfolio: 'Portfolio',
    markets: 'Markets',
    activity: 'Activity',
    profile: 'Profile',
  },
  portfolio: {
    total: 'Total portfolio',
    performance: 'Profit / loss',
    return: 'Total return',
    current: 'Current position',
    history: 'Performance',
    forecast: 'Forecast',
    chartLabel: 'Profit and loss performance with dashed forecast',
    cash: 'Available cash',
    invested: 'Invested assets',
    allocation: 'Portfolio allocation',
    inMarket: 'in markets',
    assets: 'assets',
    holdings: 'Your assets',
    empty: 'No assets yet',
    emptyBody: 'Open Markets to request a protected quote.',
    avg: 'Avg.',
    insightTitle: 'Portfolio insight',
    insightBody:
      'Your holdings are valued against the latest synthetic server prices.',
  },
  quick: { trade: 'Trade', send: 'Send', more: 'More' },
  actions: { deposit: 'Deposit', withdraw: 'Withdraw' },
  markets: {
    title: 'Fictional markets',
    live: 'Server priced',
    noticeTitle: 'In-game exchange',
    notice:
      'Prices are synthetic and have no real-world value. Every trade is checked against finite exchange liquidity.',
    eyebrow: 'Market pulse',
    movers: 'Top movers',
    today: 'Today',
  },
  marketDetail: {
    back: 'Back to markets',
    today: 'Today',
    description:
      'The reference price is generated by the server. Executable prices use a protected short-lived quote.',
    investment: 'Your investment',
    totalValue: 'Current value',
    statistics: 'Market statistics',
    high24h: '24h high',
    low24h: '24h low',
    supply: 'Issued supply',
    liquidity: 'Exchange liquidity',
  },
  trade: {
    actions: 'Trade actions',
    buy: 'Buy',
    buyBody: 'Add',
    sell: 'Sell',
    sellBody: 'Reduce',
    quantity: 'Quantity',
    available: 'Available:',
    estimated: 'Estimated value',
    marketPrice: 'Market price',
    protected: 'Protected server quote',
    protectedBody:
      'VaultX validates price, liquidity, balance and limits before execution.',
    review: 'Order review',
    price: 'Quote price',
    gross: 'Gross',
    fee: 'Exchange fee',
    total: 'Total',
    quoteExpiry:
      'This server quote expires after 8 seconds and can only be used once.',
    getQuote: 'Get protected quote',
    confirm: 'Confirm trade',
  },
  settlement: {
    depositBody: 'Move bank funds into your settled VaultX cash balance.',
    withdrawBody:
      "Return settled VaultX cash to your character's bank account.",
    amount: 'Whole bank amount',
    confirm: 'Confirm securely',
  },
  transfer: {
    title: 'Send crypto',
    subtitle: 'VaultX transfer',
    keyTitle: 'Public receiving key',
    keyBody:
      'Verify the recipient before selecting an asset. Cash can never be sent here.',
    walletKey: 'Recipient crypto key',
    verify: 'Verify',
    verifiedRecipient: 'Verified VaultX recipient',
    asset: 'Choose asset',
    quantity: 'Crypto amount',
    available: 'Available balance',
    password: 'Confirm with password',
    protected: 'Atomic wallet transfer',
    protectedBody:
      'VaultX rechecks recipient, ownership, quantity and balance on the server, then books both wallets together.',
    confirm: 'Send crypto securely',
  },
  activity: {
    title: 'Financial activity',
    empty: 'No activity',
    emptyBody:
      'Deposits, withdrawals, trades and crypto transfers will appear here.',
    volume: 'Lifetime trading volume',
    completedTrades: 'completed trades',
    cash: 'Cash',
    filters: { all: 'All', trades: 'Trades', wallet: 'Wallet' },
  },
  profile: {
    verified: 'Character ownership verified',
    walletKey: 'Public crypto key',
    walletKeyBody:
      'Share this key to receive crypto. It cannot access your account.',
    copyKey: 'Copy',
    copied: 'Copied',
    shareKey: 'Share',
    shareTitle: 'My VaultX crypto key',
    shareText: 'Send crypto to my public VaultX key:',
    trades: 'Trades',
    volume: 'Volume',
    memberSince: 'Member since',
    preferences: 'Preferences',
    priceAlerts: 'Price alerts',
    priceAlertsBody: 'Notify me about notable market moves.',
    confirmations: 'Trade confirmations',
    confirmationsBody: 'Keep an extra confirmation before execution.',
    hideBalances: 'Privacy mode',
    hideBalancesBody: 'Hide balances across VaultX.',
    identity: 'Profile identity',
    edit: 'Edit',
    account: 'Account & security',
    editTitle: 'Edit profile',
    editBody:
      'Update your handle or set a new password. Your current password confirms identity changes.',
    currentPassword: 'Current password',
    currentPasswordPlaceholder: 'Required for handle or password changes',
    newPassword: 'New password',
    newPasswordPlaceholder: 'Leave blank to keep your current password',
    passwordSecurity:
      'Passwords use memory-hard hashing and are never shown again.',
    saved: 'Profile saved securely.',
    save: 'Save profile',
    saveChanges: 'Save changes',
    securityTitle: 'Protected profile',
    securityBody:
      'Your profile stays bound to this framework character and financial session.',
  },
  activityTypes: {
    buy: 'Asset purchase',
    sell: 'Asset sale',
    deposit: 'Bank deposit',
    withdrawal: 'Bank withdrawal',
    transfer_in: 'Crypto received',
    transfer_out: 'Crypto sent',
  },
  statuses: {
    completed: 'Completed',
    pending: 'Pending review',
    failed: 'Failed',
    manual_review: 'Manual review',
  },
  errors: {
    invalid_handle: 'Use 3–20 letters, numbers, dots or underscores.',
    handle_taken: 'That VaultX handle is already taken.',
    profile_exists: 'This character already owns a VaultX profile.',
    invalid_password:
      'Use 8–72 characters with uppercase, lowercase, a number, and a special character.',
    password_mismatch: 'The passwords do not match.',
    accept_terms: 'Confirm that this is a fictional in-game wallet.',
    invalid_credentials: 'The password is incorrect.',
    locked: 'Too many attempts. Try again later.',
    not_authenticated: 'Sign in to VaultX first.',
    invalid_amount: 'Enter a positive whole amount.',
    invalid_quantity: 'Enter a quantity with up to six decimals.',
    invalid_wallet_key: 'Enter a complete VX receiving key.',
    recipient_not_found: 'No active VaultX account uses this key.',
    self_transfer: 'You cannot send crypto to your own key.',
    invalid_transfer: 'Verify a recipient, asset and valid crypto amount.',
    recipient_limit_exceeded: 'The recipient wallet cannot hold that amount.',
    insufficient_funds: 'Your available balance is too low.',
    insufficient_liquidity: 'The exchange cannot fill that amount right now.',
    quote_expired: 'This quote expired. Request a new one.',
    quote_unavailable: 'The quote is no longer available.',
    market_unavailable: 'This market is temporarily paused.',
    limit_exceeded: 'This request exceeds an exchange limit.',
    duplicate_request: 'This request was already processed.',
    rate_limited: 'Too many requests. Try again shortly.',
    settlement_pending: 'A money transfer is already pending review.',
    service_unavailable: 'VaultX is temporarily unavailable.',
    request_failed: 'The secure exchange request failed.',
    invalid_profile: 'Check your profile settings and handle.',
    default: 'VaultX could not complete the request.',
  },
}

const citywarnFallbackLocales = {
  name: 'CityWarn',
  navigation: 'CityWarn navigation',
  loading: 'Loading warnings...',
  tabs: {
    active: 'Current',
    map: 'Map',
    archive: 'History',
    settings: 'Settings',
  },
  hero: {
    safe: 'No immediate danger',
    safeBody: 'There are no active warnings for your location.',
    active: '{count} active warnings',
    activeBody: 'Follow the guidance from the responsible authorities.',
  },
  issuedBy: 'Issued by',
  updated: 'Updated',
  expires: 'Valid until',
  affectedArea: 'Affected area',
  currentLocation: 'Current location',
  details: 'Warning details',
  instructions: 'What you should do now',
  timeline: 'Updates',
  publishedBy: 'Published by {name}',
  radius: '{count} m radius',
  status: {
    active: 'Active',
    resolved: 'Resolved',
    expired: 'Expired',
  },
  severity: {
    information: 'Information',
    warning: 'Warning',
    danger: 'Danger',
    extreme: 'Extreme danger',
  },
  categories: {
    public_safety: 'Public safety',
    police: 'Police incident',
    fire: 'Fire & rescue',
    medical: 'Health',
    infrastructure: 'Infrastructure',
    evacuation: 'Evacuation',
  },
  emptyArchive: 'No history yet',
  emptyArchiveBody: 'Resolved and expired warnings will appear here.',
  emptyFiltered: 'No matching warnings',
  emptyFilteredBody: 'Your personal filters hide the current alerts.',
  mapTitle: 'Warning areas',
  mapBody: 'Highlighted areas show the approximate scope of active alerts.',
  publisher: {
    title: 'Authority tools',
    body: 'You are on duty as {job} ({grade}) and may publish warnings.',
    offDuty: 'You must be on duty to publish warnings.',
    unavailable: 'Authority tools are not available for your current job.',
  },
  compose: {
    new: 'New warning',
    step: 'Step {step} of 4',
    categoryTitle: 'Type of danger',
    categoryBody: 'Choose the category that best matches the incident.',
    severityTitle: 'Danger level',
    areaTitle: 'Affected area',
    areaBody: 'Define where this alert applies.',
    areaTypes: {
      radius: 'Radius',
      district: 'District',
      city: 'Whole city',
    },
    areaLabel: 'Area name',
    areaPlaceholder: 'e.g. Mission Row',
    radius: 'Radius in metres',
    useLocation: 'Use current location',
    locationSet: 'Location captured',
    contentTitle: 'Warning content',
    title: 'Short title',
    titlePlaceholder: 'What happened?',
    body: 'Description',
    bodyPlaceholder: 'Describe the situation clearly and factually...',
    instructions: 'Recommended action',
    instructionsPlaceholder: 'What should residents do now?',
    duration: 'Validity',
    durationMinutes: '{count} minutes',
    previewTitle: 'Review & send',
    recipients: 'About {count} people are currently reachable.',
    legal: 'Your name, job and every change are recorded permanently.',
    back: 'Back',
    next: 'Continue',
    publish: 'Publish warning',
    publishing: 'Publishing...',
    success: 'The warning was published and distributed.',
  },
  manage: {
    update: 'Send update',
    resolve: 'Resolve warning',
    updateTitle: 'Update situation',
    resolveTitle: 'Resolve warning',
    updatePlaceholder: 'What changed?',
    resolvePlaceholder: 'Why has the warning ended?',
    send: 'Publish update',
    confirm: 'Resolve warning',
    success: 'The warning was updated.',
    resolved: 'The warning was resolved.',
  },
  settings: {
    locationTitle: 'Location-based warnings',
    locationBody:
      'Shows alerts for radiuses and districts. City-wide alerts always remain visible.',
    levelTitle: 'Minimum level',
    levelBody: 'Alerts below this level are hidden from the current view.',
    categoryTitle: 'Warning types',
    categoryBody: 'Choose which types of alerts you want to see.',
    notificationHint:
      'Critical alerts may bypass focus modes. Manage sounds in the phone settings.',
  },
  notifications: {
    published: 'New population warning',
    update: 'A warning was updated',
    resolved: 'A warning was resolved',
  },
  errors: {
    feature_disabled: 'CityWarn is disabled on this server.',
    not_authorized: 'You are not allowed to perform this action.',
    invalid_warning: 'Check all warning details.',
    invalid_update: 'Enter a valid update.',
    active_limit: 'Too many warnings are already active.',
    revision_conflict: 'The warning changed in the meantime. Reload it.',
    rate_limited: 'Please wait before trying again.',
    request_failed: 'CityWarn could not complete the request.',
    not_found: 'This warning is no longer available.',
    device_not_open: 'Open and unlock this phone before trying again.',
    device_not_owned: 'This phone is no longer available.',
    device_locked: 'Unlock this phone before trying again.',
    default: 'CityWarn is temporarily unavailable.',
  },
}

const adminPanelFallbackLocales = {
  name: 'Phone Admin',
  subtitle: 'Administration',
  navigation: 'Admin navigation',
  refresh: 'Refresh admin data',
  loading: 'Loading protected data...',
  tabs: {
    overview: 'Overview',
    players: 'Players',
    devices: 'Devices',
    apps: 'Apps',
    accounts: 'Accounts',
    messages: 'Messages',
    calls: 'Calls',
    moderation: 'Moderation',
    audit: 'Audit',
    configurator: 'Phone configurator',
  },
  overview: {
    eyebrow: 'Server',
    title: 'Dashboard',
    body: 'Players, devices, apps, and phone data.',
    stats: 'Server phone statistics',
    online: 'Online',
    devices: 'Devices',
    accounts: 'Accounts',
    audit: 'Audit entries',
    control: 'Navigation',
    features: 'Modules',
    featuresBody: 'Open an administration module.',
    recent: 'Recent activity',
    playerFeature: 'Identity, finances, job, and duty',
    deviceFeature: 'IMEI, SIM, number, and activity',
    appFeature: 'Install or remove phone apps',
    accountFeature: 'Account access and protected credentials',
    messageFeature: 'Review recent SMS activity',
    callFeature: 'Review recent call activity',
    moderationFeature: 'Reset access, number, or device data',
    auditFeature: 'Review sensitive admin actions',
    configuratorFeature: 'Manage config.lua and media.lua through SQL',
  },
  appearance: {
    eyebrow: 'Appearance',
    title: 'Interface',
    body: 'Personalize colors and typography across the admin workspace.',
    colors: {
      emerald: 'Emerald',
      blue: 'Blue',
      violet: 'Violet',
      orange: 'Orange',
      red: 'Red',
    },
    controls: {
      customColor: 'Custom color',
      customColorBody: 'Choose any RGB accent or enter its channel values.',
      red: 'R',
      green: 'G',
      blue: 'B',
      hex: 'HEX color',
      value: 'value',
      fontFamily: 'Font family',
      fontFamilyBody: 'Choose the typeface used by the complete admin panel.',
      fontSize: 'Font size',
      fontSizeBody: 'Scale text and controls for comfortable readability.',
      fonts: {
        inter: 'Inter',
        system: 'System',
        classic: 'Classic',
        verdana: 'Verdana',
        tahoma: 'Tahoma',
        trebuchet: 'Trebuchet',
        georgia: 'Georgia',
        mono: 'Monospace',
      },
    },
  },
  configurator: {
    context: 'Runtime configuration',
    eyebrow: 'System tool',
    sections: 'Configuration',
    search: 'Search settings or paths',
    configScope: 'config.lua',
    mediaScope: 'media.lua',
    noResults: 'No matching settings',
    loading: 'Loading SQL configuration...',
    title: 'Phone configurator',
    body: 'Manage phone and media settings from the protected admin workspace.',
    disabledTitle: 'SQL configuration is not active',
    disabledBody:
      'Enable the configurator at the beginning of config.lua and restart sky_phone. Until then, file values remain active and editing is locked.',
    manualSave: 'Manual save',
    refreshNotice:
      'Nothing is written automatically. The green check verifies config.lua and media.lua in SQL and refreshes the active server, client, media and UI configuration immediately.',
    fieldCount: '{count} fields',
    secretConfigured: 'Secret configured · enter a replacement',
    invalidValue: 'Check the highlighted table or number value.',
    saved: 'SQL configuration saved and applied.',
    descriptions: {
      featureToggle: 'Turns {name} on or off.',
      boolean: 'Controls whether {name} is allowed.',
      number: 'Sets the numeric value for {name}.',
      text: 'Sets the text value used for {name}.',
      optionalText:
        'Sets the optional value for {name}; switch it off to disable it.',
      list: 'Manages all entries used for {name}.',
      table: 'Groups the related settings for {name}.',
      credential: 'Stores the protected credential used by {name}.',
      url: 'Sets the URL or endpoint used by {name}.',
      hosts: 'Defines which domains are allowed for {name}.',
      milliseconds: 'Sets the timing for {name} in milliseconds.',
      seconds: 'Sets the timing for {name} in seconds.',
      rateLimit: 'Limits how many {name} actions are allowed per minute.',
      byteLimit: 'Sets the maximum data size allowed for {name}.',
      textLimit: 'Sets the maximum text length allowed for {name}.',
      distance: 'Sets the world distance used for {name}.',
      coordinates: 'Sets the world coordinates or orientation for {name}.',
      gameAsset: 'Sets the GTA model or prop used for {name}.',
      animation: 'Sets the animation asset used for {name}.',
      access: 'Defines the jobs, groups or permission level for {name}.',
      integration: 'Selects the connected framework or provider for {name}.',
      path: 'Sets the storage or resource path used for {name}.',
      color: 'Sets the interface color used for {name}.',
      displayText: 'Sets the text shown to players for {name}.',
      phoneNumber: 'Sets the phone or service number used for {name}.',
      routing: 'Controls how incoming requests are routed for {name}.',
      command: 'Sets the chat command used to open or run {name}.',
      locale: 'Selects the language used for {name}.',
      debug: 'Controls detailed diagnostic output for {name}.',
      mediaQuality: 'Sets the media quality or volume used for {name}.',
      amount: 'Sets the maximum or displayed amount for {name}.',
    },
    table: {
      list: 'List',
      table: 'Key table',
      vector: 'Vector',
      entry: 'Entry',
      general: 'General',
      subtabs: {
        Dictionaries: 'Dictionaries',
        Clips: 'Clips',
        Transforms: 'Transforms',
        RateLimits: 'Rate Limits',
        Publishers: 'Publishers',
        ExternalPingResources: 'External Ping Resources',
        Markets: 'Markets',
        TrustedAdapters: 'Trusted Adapters',
        AllowedDisappearTimers: 'Allowed Disappear Timers',
        MusicTracks: 'Music Tracks',
        VehicleImages: 'Vehicle Images',
        Custom: 'Custom',
        Valet: 'Valet',
        AutoPriority: 'Automatic Priority',
        Camera: 'Camera',
        Categories: 'Categories',
        Districts: 'Districts',
        PhotoGradients: 'Photo Gradients',
        LbPhone: 'LB Phone',
        Tracks: 'Tracks',
        Props: 'Props',
        CustomLocations: 'Custom Locations',
        Animation: 'Animation',
        ReportReasons: 'Report Reasons',
        DisplayName: 'Display Name',
        Hud: 'HUD',
        Badge: 'Badge',
        LockedChannels: 'Locked Channels',
        NumberGroups: 'Number Groups',
        CustomFare: 'Custom Fare',
        DriverJobs: 'Driver Jobs',
        Services: 'Services',
        QuickLocations: 'Quick Locations',
        AllowedJobs: 'Allowed Jobs',
        Websites: 'Websites',
      },
      addRow: 'Add row',
      addField: 'Add field',
      remove: 'Remove',
      emptyList: 'No rows yet. Add the first row with plus.',
      emptyTable: 'No fields yet. Add the first key below.',
      keyPlaceholder: 'New key',
      convertToList: 'Use list',
      convertToMap: 'Use typed key table',
      convertToTable: 'Use key table',
      types: {
        string: 'Text',
        number: 'Number',
        boolean: 'Switch',
        list: 'List',
        table: 'Table',
      },
    },
  },
  players: {
    eyebrow: 'Active sessions',
    title: 'Online players',
    online: 'Online now',
    empty: 'No players found',
    emptyBody: 'Adjust the search or refresh the live player list.',
  },
  search: {
    players: 'Search name, ID, job, or number',
    apps: 'Search apps',
    clear: 'Clear search',
  },
  detail: {
    character: 'Character profile',
    data: 'Player data overview',
    cash: 'Cash',
    bank: 'Bank',
    job: 'Job',
    duty: 'Duty',
    onDuty: 'On duty',
    offDuty: 'Off duty',
    identity: 'Identity',
    playerData: 'Player data',
    identifier: 'Character identifier',
    birthdate: 'Birthdate',
    grade: 'Job grade',
    unknown: 'Unknown',
  },
  devices: {
    eyebrow: 'Device control',
    title: 'Phones',
    body: 'Inspect every phone assigned to the selected player.',
    choose: 'Choose phone',
    empty: 'No phone found',
    emptyBody: 'This player currently has no phone device that can be managed.',
    noNumber: 'No phone number',
    noSim: 'No SIM',
    imei: 'IMEI',
    updated: 'Last activity',
    apps: 'Claimed apps',
    account: 'Linked account',
  },
  credentials: {
    eyebrow: 'Protected data',
    title: 'Credentials',
    email: 'iFruit email',
    password: 'iFruit password',
    reveal: 'Reveal',
    copy: 'Copy password',
    copied: 'Password copied.',
    noAccount: 'No iFruit account is linked to this phone.',
    passcode: 'Device passcode',
    passcodeHashed: '{length}-digit PIN · securely hashed and not recoverable',
    passcodeDisabled: 'No passcode configured',
    revealTitle: 'Reveal protected password?',
    revealBody:
      'This action is server-authorized, rate-limited, and written to the admin audit log.',
    cancel: 'Cancel',
    confirmReveal: 'Reveal password',
  },
  apps: {
    eyebrow: 'Remote management',
    title: 'App access',
    description:
      'Stage app access for this device. Nothing changes until you save.',
    installed: 'Installed',
    available: 'Available',
    protected: 'System app',
    changes: '{count} pending changes',
  },
  activity: {
    protected: 'Protected activity',
    messagesTitle: 'Messages',
    messagesBody: 'Recent SMS activity for the selected SIM.',
    callsTitle: 'Calls',
    callsBody: 'Recent call activity for the selected SIM.',
    loading: 'Loading activity...',
    incoming: 'Incoming',
    outgoing: 'Outgoing',
    mediaMessage: '{type} message',
    noMessages: 'No message activity found.',
    noCalls: 'No call activity found.',
    status: {
      completed: 'Completed',
      missed: 'Missed',
      rejected: 'Rejected',
      busy: 'Busy',
      unanswered: 'Unanswered',
      cancelled: 'Cancelled',
      failed: 'Failed',
      ringing: 'Ringing',
    },
  },
  moderation: {
    eyebrow: 'Device administration',
    title: 'Moderation actions',
    body: 'Every action is server-authorized, rate-limited, and audited.',
    resetPasscode: 'Reset passcode',
    resetPasscodeBody: 'Remove the device PIN and clear failed attempts.',
    changeNumber: 'Change number',
    changeNumberBody: 'Assign a new unique number to the current SIM.',
    factoryReset: 'Factory reset',
    factoryResetBody: 'Clear local device data and disconnect the account.',
    saveFirst: 'Save or discard pending app changes first.',
    phoneNumber: 'New phone number',
    phoneNumberPlaceholder: 'Enter the full configured number',
    typeToConfirm: 'Type {word} to confirm the factory reset.',
    confirmWord: 'RESET',
    cancel: 'Cancel',
    'reset-passcodeSuccess': 'Passcode reset.',
    'change-numberSuccess': 'Phone number changed.',
    'factory-resetSuccess': 'Phone factory reset completed.',
    dialogs: {
      'reset-passcodeTitle': 'Reset device passcode?',
      'reset-passcodeBody':
        'The player can unlock this phone without the previous PIN afterward.',
      'change-numberTitle': 'Change phone number?',
      'change-numberBody':
        'The new number must match the configured server number format and be unique.',
      'factory-resetTitle': 'Factory reset this phone?',
      'factory-resetBody':
        'This clears local device data, app settings, security, and the linked account. This cannot be undone.',
    },
    confirm: {
      'reset-passcode': 'Reset passcode',
      'change-number': 'Change number',
      'factory-reset': 'Factory reset',
    },
  },
  editor: {
    brand: 'SKY PHONE',
    workspace: 'ADMIN',
    players: 'Player directory',
    audit: 'Audit log',
    selectPlayer:
      'Select a player to inspect identity, devices, credentials, and app access.',
    save: 'Save changes',
    saveHint: 'Apply pending changes',
    saved: 'Changes saved.',
    unsaved: 'Unsaved changes',
    close: 'Close admin panel',
    refresh: 'Refresh live data',
    online: 'LIVE',
    profile: 'PROFILE',
    financial: 'FINANCIAL',
    device: 'DEVICE',
    security: 'SECURITY',
    noAutoSave: 'Manual save',
    noAutoSaveBody: 'Changes stay local until the green check is pressed.',
    discardTitle: 'Discard unsaved changes?',
    discardBody:
      'Your staged app or configuration changes have not been saved.',
    keepEditing: 'Keep editing',
    discard: 'Discard changes',
    saveFailed: 'Some changes could not be saved.',
    noSelection: 'No player selected',
  },
  audit: {
    eyebrow: 'Accountability',
    title: 'Audit trail',
    body: 'Sensitive reveals and remote app changes are recorded here.',
    empty: 'No admin actions yet',
    emptyBody: 'Protected actions will appear here after they are performed.',
    by: '{actor} · target ID {target}',
    actions: {
      grant_app: 'App installed',
      revoke_app: 'App removed',
      reveal_account_password: 'Password revealed',
      view_messages: 'Messages viewed',
      view_calls: 'Calls viewed',
      reset_passcode: 'Passcode reset',
      change_number: 'Phone number changed',
      factory_reset: 'Phone factory reset',
      save_configuration: 'Configuration saved',
    },
  },
  errors: {
    not_authorized: 'You do not have access to the admin panel.',
    rate_limited: 'Too many admin requests. Please wait.',
    player_unavailable: 'That player is no longer online.',
    device_not_owned: 'That phone no longer belongs to the selected player.',
    invalid_app: 'That app is not registered on the server.',
    app_protected: 'This system app cannot be removed.',
    revision_conflict:
      'The data changed in the meantime. Reopen the section and try again.',
    configurator_disabled: 'Enable the phone configurator in config.lua first.',
    invalid_field: 'That configuration field is no longer available.',
    invalid_value: 'A configuration value is invalid.',
    account_not_found: 'No iFruit account is linked to this phone.',
    invalid_phone_number:
      'Enter a phone number in the configured server format.',
    phone_number_unchanged: 'This SIM already uses that phone number.',
    phone_number_taken: 'That phone number is already assigned.',
    no_sim: 'This phone has no SIM that can be changed.',
    passcode_not_set: 'This phone has no passcode configured.',
    device_not_found: 'This phone no longer exists.',
    metadata_unsupported: 'The phone inventory metadata could not be updated.',
    invalid_request: 'The admin request was invalid.',
    request_failed: 'The admin request failed.',
    default: 'The admin panel is temporarily unavailable.',
  },
}

const defaultLocales: LocaleTree = {
  AdminPanel: adminPanelFallbackLocales,
  Apps: {
    citywarn: citywarnFallbackLocales,
    crypto: cryptoFallbackLocales,
    easyShare: {
      name: 'EasyShare',
      incoming: 'Incoming Share',
      recentChats: 'Contacts and Chats',
      destinations: 'Share destinations',
      newMessage: 'New Message',
      shareProfile: 'Share Profile',
      share: 'Share',
      kinds: {
        contact: 'Contact',
        document: 'Document',
        link: 'Link',
        location: 'Location',
        media: 'Media',
        note: 'Note',
        photo: 'Photo',
        playlist: 'Playlist',
        post: 'Post',
        profile: 'Profile',
        text: 'Message',
        track: 'Track',
        video: 'Video',
      },
      chooseConversation: 'Choose a conversation',
      sentToChat: 'Sent to chat.',
      savedToNotes: 'Saved to Notes.',
      copy: 'Copy',
      copyLink: 'Copy Link',
      nearby: 'People Nearby',
      history: 'Transfer History',
      noHistory: 'No transfers yet.',
      noNearby: 'No visible players are nearby.',
      visibility: 'Visibility',
      requestSent: 'Waiting for acceptance...',
      incomingFrom: '{name} wants to share',
      distance: '{distance} m away',
      cancel: 'Cancel',
      visibilityOptions: {
        everyone: 'Everyone',
        contacts: 'Contacts Only',
        hidden: 'Receiving Off',
      },
      status: {
        pending: 'Waiting for acceptance',
        transferring: 'Transferring',
        completed: 'Completed',
        declined: 'Declined',
        cancelled: 'Cancelled',
        expired: 'Expired',
        failed: 'Failed',
        accepted: 'Accepted',
      },
      errors: {
        disabled: 'EasyShare is disabled.',
        invalid_payload: 'This item cannot be shared.',
        invalid_target: 'Choose a valid recipient.',
        target_unavailable: 'That player is no longer available.',
        transfer_not_found: 'That transfer is no longer available.',
        too_far: 'The recipient is too far away.',
        not_owned: 'This item no longer belongs to this phone.',
        unsupported_payload: 'This item is not available for EasyShare.',
        payload_too_large: 'This item is too large to share.',
        location_unavailable: 'Your location is unavailable.',
        rate_limited: 'Too many share requests. Try again shortly.',
        request_failed: 'EasyShare is temporarily unavailable.',
      },
    },
    weazelNews: {
      name: 'Weazel News',
      brand: 'WEAZEL NEWS',
      navigation: 'Weazel News navigation',
      tabs: {
        home: 'Home',
        categories: 'Categories',
        search: 'Search',
        editorial: 'Editorial',
      },
      back: 'Back',
      retry: 'Try Again',
      loadMore: 'Load More',
      home: {
        eyebrow: 'Los Santos, live',
        latest: 'Latest News',
      },
      categories: {
        title: 'Categories',
        subtitle: 'Browse every story from the Weazel News desk.',
        all: 'All Stories',
        official: 'Official Notices',
        events: 'Events & Leisure',
        jobs: 'Jobs',
        news: 'News & Events',
        business: 'Business',
      },
      search: {
        placeholder: 'Search articles...',
        title: 'Search Weazel News',
        emptyTitle: 'No Articles Found',
        emptyBody: 'Try another headline, topic, or category.',
      },
      states: {
        loading: 'Loading Weazel News...',
        emptyTitle: 'No News Yet',
        emptyBody:
          'The Weazel News desk has not published any articles here yet.',
        errorTitle: 'Weazel News Is Unavailable',
        readOnlyTitle: 'Read-only Access',
        readOnlyBody:
          'Your current job can read Weazel News, but cannot manage articles.',
        noManagedTitle: 'No Editorial Articles',
        noManagedBody:
          'Create an article or change the current editorial filter.',
      },
      article: {
        readMore: 'Read Article',
        byline: 'By {author}',
        updated: 'Updated {date}',
        published: 'Published {date}',
        coverAlt: 'Cover image for {title}',
      },
      editorial: {
        title: 'Editorial Desk',
        subtitle: 'Create, publish, and maintain Weazel News articles.',
        newArticle: 'New Article',
        published: 'Published',
        drafts: 'Drafts',
        all: 'All',
        jobAccess: 'Signed in as {job}',
        edit: 'Edit Article',
        delete: 'Delete Article',
      },
      composer: {
        newTitle: 'New Article',
        editTitle: 'Edit Article',
        title: 'Headline',
        titlePlaceholder: 'Write a clear headline',
        body: 'Article',
        bodyPlaceholder: 'Write the full story...',
        category: 'Category',
        status: 'Publication Status',
        cover: 'Cover Image',
        photos: 'Article Photos',
        photoCount: '{count} of {maximum} photos',
        addPhotos: 'Add Photos',
        choosePhotos: 'Choose from Photos',
        takePhoto: 'Take Photo',
        photoSourceHint:
          'Choose several photos from your library or take a new one.',
        primaryPhoto: 'Cover',
        makePrimary: 'Use as Cover',
        chooseCover: 'Choose from Photos',
        changeCover: 'Change Cover',
        removeCover: 'Remove Cover',
        coverAlt: 'Selected article cover',
        publish: 'Publish Article',
        saveDraft: 'Save Draft',
        saveChanges: 'Save Changes',
        statusPublished: 'Published',
        statusDraft: 'Draft',
      },
      delete: {
        title: 'Delete Article?',
        body: 'This article will be removed from Weazel News.',
        cancel: 'Cancel',
        confirm: 'Delete',
      },
      feedback: {
        created: 'Article created.',
        updated: 'Article updated.',
        deleted: 'Article deleted.',
      },
      accessibility: {
        articleList: 'Weazel News articles',
        categoryList: 'News categories',
        editorialList: 'Editorial articles',
        openArticle: 'Open {title}',
        openCategory: 'Open {category}',
        openEditorial: 'Open the editorial desk',
        newArticle: 'Create a new article',
        editArticle: 'Edit {title}',
        deleteArticle: 'Delete {title}',
        search: 'Search Weazel News',
        clearSearch: 'Clear article search',
        articleImages: 'Article photos',
        previousPhoto: 'Show the previous article photo',
        nextPhoto: 'Show the next article photo',
        showPhoto: 'Show article photo {current} of {total}',
        coverPreview: 'Article cover preview',
        removeCover: 'Remove the selected cover image',
        removePhoto: 'Remove this article photo',
        status: 'Article status: {status}',
      },
      errors: {
        feature_disabled: 'Weazel News is currently disabled.',
        invalid_article: 'Enter a valid headline, article, and category.',
        invalid_draft:
          'Enter a headline and article text before saving the draft.',
        invalid_publish: 'Enter a headline and article text before publishing.',
        invalid_request: 'Check the article details and try again.',
        invalid_image: 'Choose a valid photo from this phone.',
        invalid_attachment: 'Choose a valid photo from this phone.',
        not_authorized: 'Your current job cannot manage Weazel News articles.',
        article_not_found: 'This article is no longer available.',
        not_found: 'This article is no longer available.',
        revision_conflict:
          'This article changed on another device. Reload it and try again.',
        rate_limited: 'Too many requests. Try again shortly.',
        request_failed: 'Weazel News could not complete the request.',
        default: 'Weazel News is temporarily unavailable.',
      },
    },
    companies: companiesFallbackLocales,
    health: healthFallbackLocales,
    crewlink: {
      name: 'CrewLink',
      connecting: 'Connecting your crew...',
      privateNetwork: 'Private location network',
      signInTitle: 'Connect your Sky Cloud account',
      signInBody:
        'CrewLink uses your private Sky Cloud identity to keep groups and roles available across your phones.',
      openSettings: 'Open Sky Cloud settings',
      authEyebrow: 'Private crew network',
      authTitle: 'Welcome to CrewLink',
      authBody:
        'Your iFruit email is linked automatically. Enter your password to continue.',
      login: 'Log in',
      register: 'Register',
      ifruitEmail: 'iFruit address',
      password: 'Password',
      passwordPlaceholder: '8–72 characters',
      gallery: 'Photos',
      camera: 'Camera',
      backToLogin: 'Back to CrewLink login',
      authErrors: {
        no_ifruit_account:
          'Sign in to your Sky Cloud account in Settings first.',
        invalid_username: 'Use 3–20 letters, numbers, dots, or underscores.',
        invalid_password: 'Password must be 8–72 characters.',
        invalid_credentials: 'The password for this iFruit email is incorrect.',
        profile_not_found: 'No CrewLink profile exists for this iFruit email.',
        profile_exists: 'This iFruit email already has a CrewLink profile.',
        username_taken: 'That CrewLink username is already taken.',
        invalid_profile_image: 'Choose a valid photo from this phone.',
        rate_limited: 'Too many attempts. Try again shortly.',
        request_failed: 'CrewLink could not complete the request.',
      },
      welcomeEyebrow: 'Your crew. One signal.',
      welcomeTitle: 'Find your people',
      welcomeBody:
        'Choose a private CrewLink username. Only confirmed group members can see your presence and shared location.',
      username: 'CrewLink username',
      usernamePlaceholder: 'e.g. Skyline',
      createProfile: 'Create CrewLink Profile',
      profileCreated: 'Your CrewLink profile is ready.',
      privacyNote: 'Private by design. No public player search.',
      noGroupEyebrow: 'No active crew',
      noGroupTitle: 'Build your private network',
      noGroupBody:
        'Create a crew or join friends with a private invitation code.',
      createGroup: 'Create Group',
      createGroupBody: 'Give your crew a recognizable name and signal colour.',
      joinGroup: 'Join Group',
      joinWithCode: 'Join with Code',
      joinWithCodeBody:
        'Enter the private eight-character code shared by a coordinator.',
      groupName: 'Group name',
      groupNamePlaceholder: 'e.g. Night Shift',
      groupColour: 'Signal colour',
      createCrew: 'Create Crew',
      joinCrew: 'Join Crew',
      inviteCode: 'Invitation code',
      inviteCodePlaceholder: 'AB12CD34',
      groupCreated: 'Crew created.',
      joinedGroup: 'You joined the crew.',
      activeGroupChanged: 'Active crew changed.',
      pendingInvitations: 'Invitations',
      invitedBy: 'Invited by @{username}',
      inviteAccepted: 'Invitation accepted.',
      inviteDeclined: 'Invitation declined.',
      navigation: 'CrewLink navigation',
      map: 'Map',
      crew: 'Crew',
      pings: 'Pings',
      profile: 'Profile',
      onlineNow: 'online now',
      zoomIn: 'Zoom in',
      zoomOut: 'Zoom out',
      myLocation: 'Center my location',
      member: 'Member',
      members: 'Members',
      openCrew: 'View team',
      newPing: 'New Ping',
      shareLocation: 'Mark a place',
      locationUnavailable: 'Your live location is currently hidden.',
      activeCrew: 'Active private crew',
      groupSummarySingle: '{online} of {total} member online',
      groupSummary: '{online} of {total} members online',
      private: 'Private',
      nearby: 'Nearby',
      copyCode: 'Copy code',
      shareInvite: 'Invite by message',
      shareInviteTitle: 'Join {group} on CrewLink',
      shareInviteBody: 'Use invitation code {code} to join {group}.',
      manage: 'Manage',
      refresh: 'Refresh',
      codeCopied: 'Invitation code copied.',
      codeRotated: 'A new invitation code is active.',
      peopleNearby: 'People Nearby',
      peopleNearbyBody:
        'Only CrewLink users within {distance} meters appear here.',
      metersAway: '{distance} m away',
      invite: 'Invite',
      inviteSent: 'Invitation sent to @{username}.',
      nobodyNearby: 'Nobody in range',
      nobodyNearbyBody: 'Move closer to another CrewLink user and scan again.',
      scanAgain: 'Scan Again',
      liveCoordination: 'Live coordination',
      noPings: 'No active pings',
      noPingsBody: 'Share a meeting point, warning, or target with your crew.',
      expiresMinutes: '{count} min left',
      expiresSeconds: '{count} sec left',
      sharedBy: 'Shared by @{username}',
      setRoute: 'Set Route',
      routeSet: 'GPS route set.',
      pingCreated: 'Ping shared with your crew.',
      pingRemoved: 'Ping removed.',
      newPingBody: 'Choose a point on the map, then add a label and ping type.',
      pingLabel: 'Ping label',
      pingLabelPlaceholder: 'e.g. Meet at the garage',
      placeOnMap: 'Place ping on map',
      placeOnMapBody: 'Move the map until the crosshair marks the exact spot.',
      positionSelected: 'Position selected · tap to change',
      addHere: 'Place Here',
      sharePing: 'Share Ping',
      pingTypes: {
        meeting: 'Meet',
        danger: 'Danger',
        help: 'Help',
        target: 'Target',
        info: 'Info',
      },
      status: { live: 'Live', hidden: 'Location hidden', offline: 'Offline' },
      roles: {
        owner: 'Owner',
        coordinator: 'Coordinator',
        moderator: 'Moderator',
        member: 'Member',
        guest: 'Guest',
      },
      roleDescriptions: {
        coordinator: 'Manage invitations, settings, roles, and pings.',
        moderator: 'Invite nearby users and moderate group pings.',
        member: 'Share presence and create pings when enabled.',
        guest: 'View the active crew without coordination permissions.',
      },
      manageCrew: 'Manage Crew',
      manageCrewBody: 'Update identity, permissions, and invitations.',
      memberPings: 'Member pings',
      memberPingsBody: 'Allow members and guests to create pings.',
      allowOverhead: 'Overhead labels',
      allowOverheadBody: 'Permit nearby opt-in member labels in the world.',
      groupSaved: 'Crew settings saved.',
      assignRole: 'Assign Role',
      roleUpdated: 'Member role updated.',
      transferOwnership: 'Transfer Ownership',
      removeMember: 'Remove from Crew',
      yourCrewLinkId: 'Your CrewLink ID',
      privacyVisibility: 'Privacy & Visibility',
      shareOnMap: 'Share on group map',
      shareOnMapBody: 'Active members can see your live position.',
      overheadLabels: 'Nearby overhead label',
      overheadLabelsBody: 'Show your CrewLink name to nearby opted-in members.',
      yourGroups: 'Your Groups',
      createAnotherGroup: 'Create another group',
      account: 'CrewLink Account',
      externalApi: 'Connected resources',
      externalApiBody: 'Approved scripts may add temporary group pings.',
      editUsername: 'Edit Username',
      editUsernameBody: 'Your username is unique across CrewLink.',
      editProfile: 'Edit CrewLink Profile',
      editProfileBody: 'Change your username and profile photo.',
      removeProfilePhoto: 'Remove profile photo',
      profileSaved: 'CrewLink profile saved.',
      deleteGroup: 'Delete Group',
      leaveGroup: 'Leave Group',
      confirmAction: 'Confirm',
      confirm: {
        'delete-group': {
          title: 'Delete this crew?',
          body: 'The group, memberships, invitations, and active pings will be permanently removed.',
        },
        'leave-group': {
          title: 'Leave this crew?',
          body: 'You will lose access to its members, map, and pings.',
        },
        'remove-member': {
          title: 'Remove this member?',
          body: 'They will immediately lose access to this crew.',
        },
        'transfer-owner': {
          title: 'Transfer ownership?',
          body: 'The selected member becomes owner and you become coordinator.',
        },
      },
      'delete-groupDone': 'Crew deleted.',
      'leave-groupDone': 'You left the crew.',
      'remove-memberDone': 'Member removed.',
      'transfer-ownerDone': 'Ownership transferred.',
      notifications: {
        invite: '{actor} invited you to {group}.',
        member_joined: '{actor} joined {group}.',
        ping: '{actor} shared “{ping}” with {group}.',
        role: 'Your CrewLink role changed in {group}.',
        removed: 'You were removed from {group}.',
        default: 'Your CrewLink group has an update.',
      },
      errors: {
        not_authenticated: 'Sign in to your Sky Cloud account first.',
        profile_required: 'Create your CrewLink profile first.',
        invalid_username: 'Use 3–20 letters, numbers, dots, or underscores.',
        invalid_profile: 'Check your profile details.',
        invalid_profile_image: 'Choose a valid photo from this phone.',
        username_taken: 'That CrewLink username is already taken.',
        invalid_group: 'Choose a valid group name and signal colour.',
        group_limit: 'You have reached your group limit.',
        member_limit: 'This group has reached its member limit.',
        group_not_found: 'This group is no longer available.',
        invalid_code: 'That invitation code is invalid.',
        already_member: 'You are already a member of this group.',
        forbidden: 'Your current role cannot perform this action.',
        player_not_nearby: 'That player is no longer nearby.',
        player_unavailable: 'That player cannot receive a CrewLink invitation.',
        invalid_invitation: 'This invitation is invalid.',
        invitation_expired: 'This invitation has expired.',
        invalid_role: 'That role cannot be assigned.',
        owner_must_transfer: 'Transfer ownership before leaving this group.',
        invalid_ping: 'Choose a valid ping type, label, and position.',
        ping_limit: 'This crew already has too many active pings.',
        ping_not_found: 'That ping is no longer active.',
        rate_limited: 'Too many CrewLink actions. Try again shortly.',
        request_failed: 'CrewLink is temporarily unavailable.',
      },
    },
    flare: {
      name: 'Flare',
      signInTitle: 'Sign in to Flare',
      signInBody:
        'Flare profiles and matches are linked to your private Sky Cloud account.',
      createProfile: 'Create your profile',
      welcome: 'Find your spark',
      welcomeBody: 'Build a profile and meet people across Los Santos.',
      photo: 'Profile photo',
      profilePhotos: 'Profile photos',
      profilePhotosBody:
        'Add one to six photos from Photos or Camera. Your first photo is shown first.',
      profilePhotoRequired: 'Add at least one profile photo to continue.',
      addPhotos: 'Add photos',
      choosePhotos: 'Choose from Photos',
      primaryPhoto: 'Main',
      removePhoto: 'Remove photo {number}',
      profilePhotoFallback: 'Flare profile avatar',
      profilePhotoNumber: 'Profile photo {number} of {count}',
      viewProfilePhoto: 'Show profile photo {number} of {count}',
      yourName: 'Your name',
      namePlaceholder: 'What should people call you?',
      age: 'Age',
      bio: 'About you',
      bioPlaceholder: 'A short line that starts a conversation...',
      gender: 'I am',
      showMe: 'Show me',
      woman: 'Woman',
      man: 'Man',
      nonbinary: 'Non-binary',
      women: 'Women',
      men: 'Men',
      nonbinaryPeople: 'Non-binary people',
      everyone: 'Everyone',
      minimumAge: 'Minimum age',
      maximumAge: 'Maximum age',
      start: 'Start exploring',
      saveProfile: 'Save profile',
      editProfileBody: 'Keep your profile fresh and unmistakably you.',
      navigation: 'Flare navigation',
      discover: 'Discover',
      matches: 'Matches',
      profile: 'Profile',
      tabs: {
        discover: 'Swipe',
        explore: 'Explore',
        likes: 'Likes',
        matches: 'Chat',
        profile: 'Profile',
      },
      filters: 'Discovery settings',
      nearby: 'Nearby',
      rewind: 'Rewind',
      superLike: 'Super Like',
      explore: 'Explore',
      clearExploreFilter: 'Show all profiles',
      exploreTitle: 'Explore',
      exploreBody: 'Find people who are into the same things.',
      exploreModes: {
        forYou: 'For You',
        dateNight: 'Date Night',
        freeTonight: 'Free Tonight',
        longTerm: 'Long-term thing',
        newFriends: 'New Friends',
        weekend: 'Weekend plans',
      },
      likesTitle: 'Likes You',
      likesBody: 'See who already likes you, then make your choice.',
      matched: 'Matched',
      newMatches: 'New Matches',
      messages: 'Messages',
      settings: 'Settings',
      editProfile: 'Edit profile',
      editRelationshipGoal: 'Edit relationship goal',
      profileGoalBody: 'Shown on your discovery card.',
      accountActions: 'Account',
      signOut: 'Sign Out',
      signOutTitle: 'Sign out of Sky Cloud?',
      signOutBody:
        'This signs the whole phone out of Sky Cloud. Your Flare profile, matches and chats stay stored.',
      signingOut: 'Signing Out...',
      signOutHint:
        'Signing out affects every app that uses Sky Cloud on this phone.',
      deleteAccount: 'Delete Flare Account',
      deleteAccountTitle: 'Delete your Flare account?',
      deleteAccountBody:
        'Your Flare profile, swipes, matches and conversations will be permanently deleted. Your Sky Cloud account and Photos library stay available.',
      deletingAccount: 'Deleting...',
      relationshipGoal: 'Relationship goal',
      interests: 'Interests',
      interestsPlaceholder: 'Music, travel, coffee',
      interestsHint: 'Separate up to five interests with commas.',
      discoverySettings: 'Discovery',
      discoveryPreferences: 'Who you want to meet',
      showProfile: 'Show me on Flare',
      showProfileBody: 'Let new people discover your profile.',
      discoveryPrivacyNote:
        'Turning Discovery off hides you from new people. Your existing matches and chats stay available.',
      discoveryOffTitle: 'Discovery is off',
      discoveryOffBody:
        'Your profile is hidden from new people. Your existing matches and chats stay available.',
      discoveryOffShort: 'Only existing matches can still reach you.',
      enableDiscovery: 'Enable Discovery',
      saveSettings: 'Save settings',
      likedYou: 'Likes you',
      superLikedYou: 'Super Liked you',
      noLikes: 'No new likes',
      noLikesBody: 'People who like you before you decide will appear here.',
      matchActions: 'Match actions',
      messagePhoto: 'Photo',
      gif: 'GIF',
      video: 'Video',
      attachPhoto: 'Attach Photo',
      takePhoto: 'Take Photo',
      emoji: 'Emoji',
      attachGif: 'Attach GIF',
      attachVideo: 'Attach Video',
      gifs: 'GIFs',
      searchGifs: 'Search GIPHY',
      loadMore: 'Load More',
      retryGifs: 'Try Again',
      moreActions: 'More Actions',
      unmatch: 'Unmatch',
      unmatchTitle: 'Unmatch this person?',
      unmatchBody:
        'You and {name} will disappear from each other’s match lists. This cannot be undone.',
      like: 'Like',
      pass: 'Pass',
      noProfiles: "You're all caught up",
      noProfilesBody: 'Come back later when new people join Flare.',
      yourMatches: 'Your matches',
      matchesBody: 'A spark goes both ways.',
      newMatch: 'You matched — say hello!',
      noMatches: 'No sparks yet',
      noMatchesBody: 'When someone likes you back, they will appear here.',
      messagePlaceholder: 'Write a message',
      itsAMatch: "It's a spark!",
      matchBody: 'You and {name} liked each other.',
      sayHello: 'Say hello',
      keepSwiping: 'Keep exploring',
      newMatchNotification: 'You and {sender} found a spark!',
      newMessageNotification: 'New message from {sender}',
      lookingFor: {
        longTerm: 'Long-term connection',
        dates: 'Good dates',
        friends: 'New friends',
      },
      errors: {
        invalid_profile: 'Check your name, age and profile text.',
        profile_not_found: 'This Flare account is no longer available.',
        invalid_profile_photos:
          'Choose or take at least one and up to six photos saved in your own Photos library.',
        request_failed: 'Flare could not save those changes. Try again.',
        invalid_target: 'This profile is no longer available.',
        invalid_choice: 'That swipe could not be saved.',
        invalid_discovery: 'That Discovery setting is invalid.',
        discovery_disabled: 'Enable Discovery before swiping.',
        nothing_to_rewind: 'There is no recent swipe to rewind.',
        cannot_rewind_match: 'A swipe that created a match cannot be rewound.',
        match_not_found: 'This match is no longer available.',
        invalid_message: 'Write a message before sending.',
        invalid_attachment: 'This attachment is unavailable.',
        gif_provider_unconfigured: 'GIF search is not configured.',
        gif_provider_unauthorized: 'The GIF provider key is invalid.',
        gif_provider_rate_limited: 'GIF search is busy. Try again shortly.',
        gif_provider_failed: 'GIFs are temporarily unavailable.',
        rate_limited: 'Slow down for a moment and try again.',
        default: 'Flare could not complete the request.',
      },
    },
    picstagram: {
      name: 'Picstagram',
      loading: 'Loading Picstagram',
      home: 'Home',
      explore: 'Explore',
      create: 'Create',
      activity: 'Activity',
      profile: 'Profile',
      following: 'Following',
      city: 'City',
      posts: 'Posts',
      followers: 'Followers',
      verified: 'Verified profile',
      editProfile: 'Edit profile',
      saved: 'Saved',
      photos: 'Photos',
      allPosts: 'All posts',
      videos: 'Videos',
      taggedPosts: 'Tagged posts',
      emptyBio: 'No bio yet.',
      authTitle: 'Your Picstagram profile',
      login: 'Sign In',
      register: 'Register',
      createAccount: 'Create Profile',
      logout: 'Sign Out',
      signingOut: 'Signing Out...',
      loginBody:
        'Sign in to continue with your photos, stories, follows, and saved posts.',
      registerBody: 'Create a private Picstagram login for your photo profile.',
      registrationHint:
        'A Sky Cloud account is required once to create and own a Picstagram profile.',
      displayName: 'Name',
      username: 'Username',
      password: 'Password',
      confirmPassword: 'Confirm password',
      passwordsMismatch: 'The passwords do not match.',
      displayNamePlaceholder: 'Your name',
      usernamePlaceholder: 'username',
      passwordPlaceholder: 'At least 8 characters',
      confirmPasswordPlaceholder: 'Enter password again',
      signOutTitle: 'Sign out of Picstagram?',
      signOutBody:
        'Your profile and posts stay online. This phone returns to the sign-in screen.',
      stories: 'Stories',
      yourStory: 'Your story',
      noStories: 'No active stories',
      storyViews: '{count} views',
      storyViewers: 'Story viewers',
      seenBy: 'Seen by {count}',
      nextStory: 'Next story',
      closeStory: 'Close story',
      removeStory: 'Remove story',
      storyRemoved: 'Story removed.',
      emptyFeed: 'Your feed is quiet',
      emptyFeedBody: 'Discover profiles or share the first photo.',
      discoverPeople: 'Discover people',
      searchPlaceholder: 'Search profiles, captions, places, or tags',
      noResults: 'No results',
      noResultsBody: 'Try another username, caption, place, or hashtag.',
      newPost: 'New Post',
      newStory: 'New Story',
      post: 'Post',
      story: 'Story',
      choosePhotos: 'Choose photos',
      choosePhoto: 'Choose photo',
      photo: 'Photo',
      video: 'Video',
      gallery: 'Gallery',
      camera: 'Camera',
      choosePhotosHint: 'Select up to five photos or one video',
      chooseStoryHint: 'Select one photo or video',
      selectedPhotos: '{count} selected',
      changePhotos: 'Change selection',
      previousPhoto: 'Previous photo',
      nextPhoto: 'Next photo',
      caption: 'Caption',
      captionPlaceholder: 'Write a caption...',
      storyTextPlaceholder: 'Add a short story text...',
      location: 'Location',
      locationPlaceholder: 'Add a place',
      allowComments: 'Allow comments',
      publishing: 'Publishing...',
      published: 'Your post is live.',
      storyPublished: 'Your story is live.',
      comments: 'Comments',
      comment: 'Comment',
      noComments: 'No comments yet',
      addComment: 'Add a comment...',
      reply: 'Reply',
      replyPlaceholder: 'Write a reply...',
      replyingTo: 'Replying to {handle}',
      removeComment: 'Remove comment',
      likes: '{count} likes',
      viewComments: 'View all {count} comments',
      showReplies: 'Show {count} replies',
      hideReplies: 'Hide replies',
      like: 'Like',
      unlike: 'Unlike',
      save: 'Save',
      unsave: 'Remove from saved',
      more: 'More',
      archive: 'Archive',
      restore: 'Restore',
      deletePost: 'Delete post',
      deletePostTitle: 'Delete this post?',
      deletePostBody: 'The post and its reactions will no longer be available.',
      privateProfile: 'This profile is private',
      privateProfileBody: 'Follow this profile to see its posts and stories.',
      follow: 'Follow',
      requested: 'Requested',
      unfollow: 'Following',
      accept: 'Accept',
      decline: 'Decline',
      noConnections: 'No profiles here yet',
      shareProfile: 'Share profile',
      share: 'Share',
      followRequests: 'Follow requests',
      requestAccepted: 'Follow request accepted.',
      requestDeclined: 'Follow request declined.',
      noActivity: 'No activity yet',
      markRead: 'Mark as read',
      publicProfile: 'Public profile',
      privateProfileSetting: 'Private profile',
      profileVisibility: 'Profile visibility',
      privacyHint: 'Only accepted followers can see your posts and stories.',
      bio: 'Bio',
      avatar: 'Profile photo',
      chooseAvatar: 'Choose profile photo',
      removeAvatar: 'Remove profile photo',
      saveProfile: 'Save profile',
      profileSaved: 'Profile updated.',
      cancel: 'Cancel',
      done: 'Done',
      report: 'Report',
      reportTarget: 'Report {target}',
      reportReason: 'Reason',
      reportDetails: 'Additional details (optional)',
      submitReport: 'Submit report',
      reported: 'Report submitted.',
      block: 'Block profile',
      unblock: 'Unblock profile',
      blocked: 'Profile blocked.',
      blockTitle: 'Block @{handle}?',
      blockBody:
        "You will no longer see or interact with each other's Picstagram content.",
      moderation: 'Moderation',
      reports: 'Open reports',
      noReports: 'No open reports',
      noDetails: 'No additional details',
      hide: 'Hide',
      remove: 'Remove',
      restoreAction: 'Restore',
      dismiss: 'Dismiss',
      reportTargets: {
        post: 'Post',
        profile: 'Profile',
        comment: 'Comment',
        story: 'Story',
      },
      reportReasons: {
        spam: 'Spam or misleading',
        harassment: 'Harassment or bullying',
        dangerous: 'Dangerous activity',
        illegal: 'Illegal content',
        other: 'Something else',
      },
      activityKinds: {
        follow_request: 'requested to follow you',
        follow: 'started following you',
        request_accepted: 'accepted your follow request',
        like: 'liked your post',
        comment: 'commented on your post',
        comment_like: 'liked your comment',
        reply: 'replied to your comment',
        verified: 'verification changed',
      },
      notifications: {
        follow_request: '{actor} requested to follow you.',
        follow: '{actor} started following you.',
        request_accepted: '{actor} accepted your follow request.',
        like: '{actor} liked your post.',
        comment: '{actor} commented on your post.',
        comment_like: '{actor} liked your comment.',
        reply: '{actor} replied to your comment.',
        verified: 'Your Picstagram profile is now verified.',
        default: 'You have new Picstagram activity.',
      },
      errors: {
        invalid_post: 'Check the post details.',
        invalid_story: 'Check the story details.',
        invalid_media: 'Choose photos owned by this phone.',
        invalid_comment: 'Enter a valid comment.',
        comments_disabled: 'Comments are disabled.',
        invalid_profile: 'Check your profile details.',
        invalid_handle: 'Use 3–24 letters, numbers, dots, or underscores.',
        invalid_display_name: 'Enter a display name.',
        invalid_password: 'Password must be 8–72 characters.',
        invalid_credentials: 'Username or password is incorrect.',
        already_registered:
          'This Sky Cloud account already owns a Picstagram profile.',
        handle_taken: 'This username is already taken.',
        post_not_found: 'This post is unavailable.',
        story_not_found: 'This story is unavailable.',
        profile_not_found: 'This profile is unavailable.',
        request_not_found: 'This follow request is no longer open.',
        profile_unavailable: 'This profile is unavailable.',
        blocked: 'This profile is blocked.',
        already_reported: 'You already reported this content.',
        invalid_report: 'Choose a valid report reason.',
        invalid_search: 'Enter a valid search.',
        invalid_cursor: 'The feed changed. Refresh and try again.',
        invalid_status: 'This action is not available.',
        not_authorized: 'You are not allowed to do that.',
        report_not_found: 'This report is no longer open.',
        rate_limited: 'Too many actions. Try again shortly.',
        not_authenticated: 'Sign in to Sky Cloud first.',
        picstagram_not_authenticated: 'Sign in to Picstagram first.',
        request_failed: 'The request failed.',
        default: 'Picstagram could not complete the request.',
      },
    },
    feather: {
      name: 'Feather',
      loading: 'Loading Feather',
      home: 'Home',
      explore: 'Explore',
      activity: 'Notifications',
      activityNav: 'Alerts',
      profile: 'Profile',
      back: 'Back',
      settings: 'Settings',
      settingsEyebrow: 'Feather preferences',
      compactMode: 'Compact timeline',
      compactModeBody: 'Show more conversation on the screen.',
      showSuggestions: 'Profile suggestions',
      showSuggestionsBody: 'Show people to follow on your profile.',
      forYou: 'For you',
      following: 'Following',
      add: 'Add',
      network: 'Network',
      networkTitle: 'Find your people',
      networkBody:
        'Follow voices from across Los Santos and build a timeline that feels like yours.',
      networkSearchPlaceholder: 'Search names or @usernames',
      postSearchPlaceholder: 'Search posts or #hashtags',
      searchResults: 'Search results',
      suggestedPeople: 'Suggested for you',
      noPeopleFound: 'No people found',
      noPeopleFoundBody: 'Try another name or username.',
      noSuggestions: 'No suggestions yet',
      noSuggestionsBody: 'New Feather accounts will appear here.',
      all: 'All',
      mentions: 'Mentions',
      media: 'Media',
      trending: 'Trending',
      peopleTab: 'People',
      searchPlaceholder: 'Search Feather',
      search: 'Search',
      cancel: 'Cancel',
      done: 'Done',
      authEyebrow: 'Feather network',
      authWelcome: 'Welcome to Feather',
      authBody:
        'Your iFruit email is linked automatically. Use your Feather username to continue.',
      login: 'Log in',
      register: 'Register',
      loginTitle: 'Good to see you again',
      loginBody:
        'Use your Feather username. Your iFruit email is linked automatically.',
      registerTitle: 'Create your Feather profile',
      registerBody:
        'Choose a username and optional profile photo. No password is needed.',
      email: 'iFruit address',
      emailPlaceholder: 'yourname',
      password: 'Password',
      passwordPlaceholder: '6–64 characters',
      confirmPassword: 'Confirm password',
      confirmPasswordPlaceholder: 'Enter it again',
      showPassword: 'Show password',
      hidePassword: 'Hide password',
      loginAction: 'Continue to Feather',
      registerAction: 'Create profile',
      noAccount: 'New to Feather?',
      haveAccount: 'Already registered?',
      registerNow: 'Create a profile',
      loginNow: 'Log in',
      authTrust:
        'Your Feather profile is linked to your Sky Cloud account through your iFruit email.',
      profileStep: 'Step 2 of 2',
      accountConnected: 'Sky Cloud account connected',
      authErrors: {
        no_ifruit_account:
          'Sign in to your Sky Cloud account in Settings first.',
        invalid_handle: 'Use 3–30 letters, numbers or underscores.',
        invalid_username: 'That username does not match your Feather profile.',
        profile_not_found: 'No Feather profile exists for this iFruit email.',
        already_registered: 'This iFruit email already has a Feather profile.',
        handle_taken: 'That Feather username is already taken.',
        invalid_media: 'Choose a valid photo from this phone.',
        rate_limited: 'Too many attempts. Try again in a minute.',
        default: 'Feather could not complete the request. Please try again.',
      },
      welcome: 'Find your voice',
      welcomeBody:
        'Join the live conversation in Los Santos. Your Feather profile stays linked to your Sky Cloud account.',
      createProfile: 'Create Feather profile',
      profileDetailsHint: 'Choose how people will recognize you.',
      displayName: 'Display name',
      displayNamePlaceholder: 'Your name',
      handle: 'Username',
      handlePlaceholder: 'your_username',
      handleHint: '3-30 letters, numbers or underscores',
      bio: 'Bio',
      bioPlaceholder: 'What should people know about you?',
      start: 'Join Feather',
      chooseAvatar: 'Choose profile photo',
      changeAvatar: 'Change photo',
      newPost: 'New feather',
      reply: 'Reply',
      replyingTo: 'Replying to @{handle}',
      quote: 'Quote',
      post: 'Post',
      posting: 'Posting...',
      composerPlaceholder: "What's happening?",
      replyPlaceholder: 'Post your reply',
      addPhotos: 'Add photos',
      removePhoto: 'Remove photo {number}',
      charactersLeft: '{count} left',
      composerAudience: 'Everyone can join the conversation',
      mediaTitle: 'Add to your feather',
      mediaBody: 'Share up to four photos from your phone or take a new one.',
      chooseGallery: 'Photos',
      chooseGalleryBody: 'Choose one or more saved photos',
      takePhoto: 'Camera',
      takePhotoBody: 'Take a new photo now',
      selectedPhotos: 'Selected photos',
      photoLimit: 'You can add up to four photos.',
      emptyFeed: 'Your timeline is quiet',
      emptyFeedBody: 'Follow people or post the first feather.',
      emptyExplore: 'No results found',
      emptyExploreBody: 'Try another search or explore a topic.',
      noTrendingHashtags: 'No hashtags yet',
      noTrendingHashtagsBody: 'Hashtags from published posts will appear here.',
      happeningNow: "What's happening",
      trends: 'Trends for you',
      trendingIn: 'Trending in Los Santos',
      openTopic: 'View conversation',
      postsCount: '{count} posts',
      exploreTabs: {
        explore: 'Explore',
        trending: 'Trends',
        news: 'News',
      },
      trendKinds: {
        explore: 'Trending in Los Santos',
        trending: 'Popular right now',
        news: 'News · Trending',
        sports: 'Sports · Trending',
        entertainment: 'Entertainment · Trending',
      },
      people: 'Who to follow',
      posts: 'Posts',
      followers: 'Followers',
      followingCount: 'Following',
      removeConnection: 'Remove',
      followerRemoved: 'Follower removed.',
      followingRemoved: 'Account unfollowed.',
      noConnections: 'Nobody here yet',
      noConnectionsBody: 'Profiles will appear here as connections are made.',
      follow: 'Follow',
      unfollow: 'Following',
      editProfile: 'Edit profile',
      saveProfile: 'Save',
      joined: 'Joined Feather',
      joinedDate: 'Joined August 2026',
      shareProfile: 'Share profile',
      profileCopied: 'Profile handle copied.',
      postCopied: 'Post copied.',
      verified: 'Verified profile',
      noBio: 'No bio yet.',
      showMore: 'Show more',
      moreActions: 'More actions',
      now: 'now',
      minutesShort: '{count}m',
      hoursShort: '{count}h',
      daysShort: '{count}d',
      comments: 'Comments',
      noComments: 'No comments yet',
      addComment: 'Add a comment...',
      postComment: 'Post comment',
      likeComment: 'Like comment by {name}',
      likesCount: '{count} likes',
      replies: 'Replies',
      noReplies: 'No replies yet. Start the conversation.',
      likes: 'Likes',
      bookmarks: 'Bookmarks',
      noBookmarks: 'No saved posts yet',
      noBookmarksBody: 'Posts you bookmark will appear here.',
      imagePreview: 'Image preview',
      openImage: 'Open image {number}',
      postImage: 'Post image {number}',
      previousImage: 'Previous image',
      nextImage: 'Next image',
      delete: 'Delete',
      report: 'Report',
      block: 'Block @{handle}',
      reportTitle: 'Report feather',
      reportBody: 'Tell us why this feather should be reviewed.',
      reportSubmit: 'Submit report',
      reported: 'Report submitted.',
      blocked: 'Profile blocked.',
      deleted: 'Feather deleted.',
      reasons: {
        spam: 'Spam',
        harassment: 'Harassment',
        dangerous: 'Dangerous content',
        illegal: 'Illegal content',
        other: 'Other',
      },
      noActivity: 'No activity yet',
      activityBody: 'Likes, replies and new followers will appear here.',
      activityKinds: {
        like: 'liked your feather',
        reply: 'replied to your feather',
        follow: 'followed you',
        quote: 'quoted your feather',
      },
      errors: {
        accountRequired: 'Sign in to Sky Cloud in Settings first.',
        invalidHandle: 'Use 3–30 letters, numbers or underscores.',
        handleTaken: 'That handle is already taken.',
        invalidPost: 'Write something or attach a photo.',
        generic: 'Something went wrong. Please try again.',
      },
      notifications: {
        default: 'You have new activity.',
        like: '{actor} liked your feather.',
        reply: '{actor} replied to your feather.',
        follow: '{actor} followed you.',
        quote: '{actor} quoted your feather.',
      },
    },
    fliptok: {
      name: 'FlipTok',
      loading: 'Loading FlipTok',
      following: 'Following',
      forYou: 'For You',
      verified: 'Verified account',
      originalSound: 'original sound',
      save: 'Save',
      home: 'Home',
      discover: 'Discover',
      create: 'Create',
      activity: 'Activity',
      profile: 'Profile',
      navigation: 'FlipTok navigation',
      emptyFeed: 'No videos yet',
      emptyFeedBody: 'Follow creators or post the first FlipTok.',
      searchPlaceholder: 'Search creators and videos',
      clearSearch: 'Clear search',
      trendLosSantos: '# Los Santos',
      trendRoleplay: '# Roleplay',
      trendTrending: '# Trending',
      noActivity: 'No activity yet',
      followers: 'Followers',
      videos: 'Videos',
      emptyBio: 'No bio yet.',
      editProfile: 'Edit profile',
      more: 'More',
      newVideo: 'New FlipTok',
      createTitle: 'Create your next FlipTok',
      createBody: 'Record a clip or turn photos into a swipeable FlipTok.',
      chooseMedia: 'Choose media',
      chooseMediaHint:
        'Record a video, choose a clip, or select up to 10 photos.',
      recordVideo: 'Record video',
      camera: 'Camera',
      chooseVideo: 'Choose video',
      photoSlideshow: 'Photos as FlipTok',
      changeMedia: 'Change',
      previousPhoto: 'Previous photo',
      nextPhoto: 'Next photo',
      caption: 'Caption',
      captionPlaceholder: 'Write a caption...',
      location: 'Add location',
      whoCanWatch: 'Who can watch',
      public: 'Everyone',
      followersOnly: 'Followers',
      private: 'Only me',
      allowComments: 'Allow comments',
      saveDraft: 'Drafts',
      publishing: 'Posting...',
      post: 'Post',
      draftSaved: 'Draft saved.',
      published: 'Your FlipTok is live.',
      videoDeleted: 'Video removed.',
      deleteVideoTitle: 'Remove this video?',
      deleteVideoBody:
        'The video disappears from FlipTok and cannot be restored.',
      deletingVideo: 'Removing...',
      linkCopied: 'Video link copied.',
      reported: 'Report submitted.',
      blocked: 'Creator blocked.',
      comments: 'Comments',
      noComments: 'No comments yet',
      addComment: 'Add comment...',
      reply: 'Reply',
      showReplies: 'Show {count} replies',
      hideReplies: 'Hide {count} replies',
      replyPlaceholder: 'Write a reply...',
      replyingTo: 'Replying to {handle}',
      cancelReply: 'Cancel reply',
      report: 'Report video',
      reportReason: 'Reason',
      reportDetails: 'Additional details (optional)',
      submitReport: 'Submit report',
      reportDiscordNote:
        'This report is sent directly to the moderation team through Discord.',
      reportReasons: {
        spam: 'Spam or misleading',
        harassment: 'Harassment or bullying',
        dangerous: 'Dangerous activity',
        illegal: 'Illegal content',
        other: 'Something else',
      },
      block: 'Block creator',
      follow: 'Follow',
      unfollow: 'Following',
      backToProfile: 'Back',
      sounds: 'Sound',
      chooseSound: 'Choose music',
      originalOnly: 'Original sound only',
      noMusic: 'No music tracks are configured.',
      customSound: 'Custom sound',
      customSoundLink: 'YouTube or audio link',
      customSoundHint:
        'Paste a YouTube link or a direct HTTPS audio-file link.',
      customSoundPlaceholder:
        'https://youtu.be/... or https://example.com/song.mp3',
      customSoundFormats:
        'YouTube, YouTube Music, Shorts, MP3, M4A, AAC, OGG, OPUS, WAV, or WEBM',
      useCustomSound: 'Use sound',
      loadingSound: 'Loading sound',
      invalidCustomSoundLink:
        'Use a YouTube link or a direct public HTTPS audio-file link.',
      customSoundLoadFailed: 'This sound could not be loaded.',
      trimAndCover: 'Trim & cover',
      trimStart: 'Start',
      trimEnd: 'End',
      coverFrame: 'Cover',
      originalVolume: 'Original sound',
      musicVolume: 'Music',
      moderation: 'Moderation',
      reports: 'Open reports',
      noReports: 'No open reports',
      removeVideo: 'Remove video',
      dismissReport: 'Dismiss',
      cancel: 'Cancel',
      done: 'Done',
      savingProfile: 'Saving...',
      displayName: 'Name',
      username: 'Username',
      bio: 'Bio',
      accountType: 'Account type',
      profilePhoto: 'Profile photo',
      changePhoto: 'Change photo',
      chooseFromGallery: 'Photos',
      takePhoto: 'Camera',
      removePhoto: 'Remove photo',
      noConnections: 'No profiles to show',
      authTitle: 'Your FlipTok account',
      login: 'Sign In',
      register: 'Register',
      createAccount: 'Create Account',
      logout: 'Sign Out',
      loginBody:
        'Sign in to continue with your videos, follows, and saved posts.',
      registerBody: 'Create a private FlipTok login for this profile.',
      password: 'Password',
      confirmPassword: 'Confirm password',
      passwordsMismatch: 'The passwords do not match.',
      displayNamePlaceholder: 'Your name',
      usernamePlaceholder: 'username',
      passwordPlaceholder: 'At least 8 characters',
      confirmPasswordPlaceholder: 'Enter password again',
      registrationHint:
        'A Sky Cloud account is required once to create and own a FlipTok profile.',
      accountDetails: 'Account details',
      profileDetails: 'Profile details',
      account: 'Account',
      signOutTitle: 'Sign out of FlipTok?',
      signOutBody:
        'Your profile and videos stay online. This phone will return to the FlipTok sign-in screen.',
      signingOut: 'Signing Out...',
      accountTypes: {
        person: 'Person',
        business: 'Business',
        organization: 'Organization',
        media: 'Media',
        event: 'Event',
      },
      activityKinds: {
        like: 'liked your video',
        comment: 'commented on your video',
        follow: 'started following you',
        verified: 'verification changed',
      },
      notifications: {
        like: '{actor} liked your video.',
        comment: '{actor} commented on your video.',
        follow: '{actor} started following you.',
        verified: 'Your FlipTok account is now verified.',
        default: 'You have new FlipTok activity.',
      },
      errors: {
        invalid_video: 'Check the video details.',
        invalid_music_url:
          'Use a valid YouTube or direct HTTPS audio-file link.',
        invalid_media: 'Choose valid media from this phone.',
        invalid_comment: 'Enter a valid comment.',
        comments_disabled: 'Comments are disabled.',
        invalid_profile: 'Check your profile details.',
        invalid_profile_image: 'Choose a valid photo from this phone.',
        invalid_handle: 'Use 3–24 letters, numbers, dots, or underscores.',
        invalid_display_name: 'Enter a display name.',
        invalid_password: 'Password must be 8–72 characters.',
        invalid_credentials: 'Username or password is incorrect.',
        already_registered:
          'This Sky Cloud account already owns a registered FlipTok profile.',
        handle_taken: 'This username is already taken.',
        video_not_found: 'This video is unavailable.',
        profile_not_found: 'This profile is unavailable.',
        rate_limited: 'Too many actions. Try again shortly.',
        not_authenticated: 'Sign in to Sky Cloud first.',
        blocked: 'This account is blocked.',
        not_authorized: 'You do not have moderation access.',
        report_not_found: 'This report is no longer open.',
        report_unavailable: 'Discord reporting is not configured yet.',
        request_failed:
          'Your FlipTok request could not be completed. Try again.',
        default: 'FlipTok could not complete the request.',
      },
    },
    darkchat: {
      name: 'DarkChat',
      newMessage: 'New DarkChat message from {sender}',
      privateNotification: 'New DarkChat message',
      signInBody:
        'DarkChat identities are linked to your private Sky Cloud account.',
      signInHint: 'Sign in through Settings to continue.',
      createIdentity: 'Create Dark Identity',
      createIdentityBody:
        'Create a private identity before starting a DarkChat conversation.',
      security: 'Security',
      privateNetwork: 'Private invitation-only network',
      noResults: 'No Results',
      noResultsBody: 'Try another alias or Dark-ID.',
      noChats: 'No Private Chats',
      noChatsBody:
        'Connect with a Dark-ID or invitation code. There is no public search.',
      newChat: 'New Chat',
      to: 'To:',
      connectPrivately: 'Connect privately',
      newChatBody:
        "Enter another person's Dark-ID or invitation code. You can share your own ID from your profile.",
      darkIdOrInvite: 'Dark-ID or invitation code',
      continue: 'Continue',
      contacts: 'DarkChat Contacts',
      contactsHint:
        'Saved private identities appear only on your DarkChat account.',
      noContacts: 'No saved DarkChat contacts',
      private: 'Private',
      shareIdentity: 'Tap to share your private identity',
      message: 'Dark message',
      activeNow: 'Activity shared',
      encryptedSession: 'Private session',
      serverPrivate: 'Private server-stored conversation',
      emoji: 'Emoji',
      gif: 'GIF',
      gifs: 'GIFs',
      photo: 'Photo',
      video: 'Video',
      attachPhoto: 'Attach Photo',
      takePhoto: 'Take Photo',
      attachGif: 'Attach GIF',
      attachVideo: 'Attach Video',
      searchGifs: 'Search GIFs',
      loadMore: 'Load More',
      voiceMessage: 'Voice message',
      sending: 'Sending',
      failed: 'Not delivered',
      delivered: 'Delivered',
      read: 'Read',
      replying: 'Replying to',
      messageDeleted: 'Message deleted',
      securityUpdate: 'Security settings updated',
      timerChanged: 'Disappearing messages: {timer}',
      timerOff: 'Off',
      timerAfterRead: 'After reading',
      timerMinute: '1 minute',
      timerFiveMinutes: '5 minutes',
      timerHour: '1 hour',
      timerDay: '24 hours',
      timerWeek: '7 days',
      copied: 'Copied',
      reply: 'Reply',
      copy: 'Copy',
      deleteForMe: 'Delete for me',
      deleteForBoth: 'Delete for both',
      report: 'Report',
      messageActions: 'Message actions',
      contactSecurity: 'Contact & Security',
      chatSettings: 'Chat Settings',
      contactActions: 'Contact Actions',
      chatSince: 'Private chat since {date}',
      notifications: 'Notifications',
      readReceipts: 'Read receipts',
      disappearing: 'Disappearing messages',
      contactAlias: 'Contact alias',
      saveContact: 'Save Contact',
      addContact: 'Add Contact',
      contactSaved: 'Contact saved',
      removeContact: 'Remove Contact',
      returnToCall: 'Return to Call',
      block: 'Block User',
      unblock: 'Unblock User',
      clearChat: 'Clear Chat',
      chatCleared: 'Chat cleared',
      myIdentity: 'My Dark Identity',
      alias: 'Alias',
      privacySettings: 'Privacy Settings',
      notificationPrivacy: 'Notification privacy',
      notificationFull: 'Full · alias and message',
      notificationPrivate: 'Private · generic message',
      notificationHidden: 'Invisible · badge only',
      shareActivity: 'Share activity status',
      inviteCode: 'Private invitation code',
      copyInvite: 'Copy Invite',
      shareInvite: 'Share Invite',
      privacyDisclaimer:
        'DarkChat stores messages on the server and does not claim end-to-end encryption.',
      profileActions: 'Account Actions',
      signOut: 'Sign Out',
      signOutTitle: 'Sign out of Sky Cloud?',
      signOutBody:
        'This signs the whole phone out of Sky Cloud. Your DarkChat profile and messages remain stored.',
      signingOut: 'Signing Out...',
      signOutHint:
        'Signing out affects every app that uses Sky Cloud on this phone.',
      deleteProfile: 'Delete DarkChat Profile',
      deleteProfileTitle: 'Delete DarkChat profile?',
      deleteProfileBody:
        'Your identity, contacts and DarkChat conversations will be permanently deleted.',
      deletingProfile: 'Deleting...',
      unknownIdentity: 'Unknown Identity',
      unknownIdentityBody:
        'Only continue if you expected this identity. The account is not discoverable through public search.',
      openSecureChat: 'Open Private Chat',
      reportUser: 'Report User',
      reportSpam: 'Spam',
      reportHarassment: 'Harassment',
      reportThreats: 'Threats',
      reportIllegal: 'Illegal content',
      reportOther: 'Other',
      reportDetails: 'Optional details',
      submitReport: 'Submit Report',
      reported: 'Report submitted',
      microphoneUnavailable: 'The microphone is unavailable.',
      recordingTooLarge: 'The voice message is too large.',
      errors: {
        not_authenticated: 'Sign in to your Sky Cloud account first.',
        invalid_dark_id: 'Enter a valid Dark-ID or invitation code.',
        profile_not_found: 'This private identity was not found.',
        self_chat: 'You cannot message your own identity.',
        conversation_not_found: 'This conversation is unavailable.',
        blocked: 'Messages are blocked in this conversation.',
        invalid_message: 'Enter a valid message.',
        invalid_gif: 'This GIF is invalid.',
        invalid_voice: 'This voice message is invalid.',
        invalid_attachment: 'This photo or video is unavailable.',
        invalid_profile: 'Check your alias and privacy settings.',
        sign_out_failed: 'Sky Cloud could not sign out.',
        rate_limited: 'Too many requests. Try again shortly.',
        gif_provider_unconfigured: 'GIF search is not configured.',
        gif_provider_unauthorized: 'The GIF provider key is invalid.',
        gif_provider_rate_limited: 'GIF search is busy. Try again shortly.',
        gif_provider_failed: 'GIFs are temporarily unavailable.',
        default: 'DarkChat could not complete the request.',
      },
    },
    messages: {
      name: 'Messages',
      newMessage: 'New message from {sender}',
      compose: 'New Message',
      search: 'Search',
      to: 'To:',
      message: 'Message',
      send: 'Send',
      details: 'Details',
      officialContact: 'Official company contact',
      messagingUnavailable: 'This company contact does not accept messages.',
      filterUnread: 'Show Unread Messages',
      filterLabel: 'Conversation Filter',
      allMessages: 'All',
      unreadMessages: 'Unread',
      smsLabel: 'Text Message · SMS',
      photo: 'Photo',
      gif: 'GIF',
      video: 'Video',
      contact: 'Contact',
      attachPhoto: 'Attach Photo',
      takePhoto: 'Take Photo',
      attachGif: 'Attach GIF',
      attachVideo: 'Attach Video',
      photos: 'Photos',
      gifs: 'GIFs',
      videos: 'Videos',
      contacts: 'Contacts',
      shareContact: 'Share Contact',
      noContactsToShare: 'Save a contact first to share it here.',
      contactSaved: 'Contact Saved',
      noPhotos: 'Take a photo first to attach it here.',
      noVideos: 'Record a video in Camera first.',
      searchGifs: 'Search GIPHY',
      loadMore: 'Load More',
      retryGifs: 'Try Again',
      moreActions: 'More Actions',
      inboxActions: 'Conversation Actions',
      sortLabel: 'Conversation Sort',
      sortNewest: 'Newest First',
      sortOldest: 'Oldest First',
      unreadConversation: '{name}, {count} unread messages',
      attachmentPreview: 'Selected attachments',
      attachmentLimit: 'You can attach up to {count} photos or videos.',
      removeAttachment: 'Remove attachment {number}',
      contactDetails: 'Contact Details',
      contactName: 'Name',
      phoneNumber: 'Phone Number',
      company: 'Company',
      contactActions: 'Contact Actions',
      call: 'Call',
      messageAction: 'Message',
      addContact: 'Add Contact',
      showInContacts: 'Show in Contacts',
      blockContact: 'Block Contact',
      blockContactTitle: 'Block this contact?',
      blockContactBody:
        '{name} will no longer be able to call or message this SIM.',
      blockContactFailed: 'The contact could not be blocked.',
      contactBlocked: 'Contact blocked.',
      deleteContact: 'Delete Contact',
      selectedCount: '{count} Selected',
      deleteSelected: 'Delete',
      contactSaveFailed: 'The contact could not be saved.',
      contactDeleteFailed: 'The contact could not be deleted.',
      callFailed: 'The call could not be started.',
      emoji: 'Emoji',
      emojiPicker: 'Emoji picker',
      emojiCategories: 'Emoji categories',
      skinTone: 'Skin tone {number}',
      gifLabels: {
        celebrate: 'Celebrate!',
        hearts: 'Love it',
        party: 'Party time',
        thumbsUp: 'Perfect',
        wow: 'WOW',
      },
      voiceMessage: 'Audio Message',
      recordVoice: 'Record Audio',
      playAudio: 'Play Audio',
      pauseAudio: 'Pause Audio',
      seekAudio: 'Seek Audio',
      recording: 'Recording',
      stopAndSend: 'Stop and Send',
      cancelRecording: 'Cancel Recording',
      sending: 'Sending...',
      delivered: 'Delivered',
      notDelivered: 'Not Delivered',
      microphoneUnavailable: 'The microphone is unavailable.',
      recordingTooLarge: 'The recording is too large.',
      noSim: 'No SIM',
      noSimBody: 'Insert a SIM card in Settings to send and receive messages.',
      noMessages: 'No Messages',
      noMessagesBody: 'Start a conversation with a phone number.',
      noResults: 'No Results',
      today: 'Today',
      yesterday: 'Yesterday',
      errors: {
        invalid_number: 'Enter a valid phone number.',
        invalid_message: 'Enter a message.',
        invalid_voice: 'The audio message is invalid.',
        invalid_attachment: 'The attachment is invalid.',
        invalid_contact: 'The contact is invalid.',
        contact_not_found: 'This contact is no longer available.',
        media_provider_unconfigured: 'Photo uploads are not configured.',
        capture_provider_unavailable: 'The screenshot resource is unavailable.',
        capture_failed: 'The photo could not be captured.',
        video_provider_unavailable:
          'Video capture requires the screencapture resource.',
        video_capture_failed: 'The video could not be recorded.',
        recording_in_progress: 'A video is already being recorded.',
        recording_not_found: 'No active video recording was found.',
        gif_provider_unconfigured: 'GIF search is not configured.',
        gif_provider_unauthorized: 'The configured GIPHY API key is invalid.',
        gif_provider_rate_limited: 'GIPHY is busy. Try again in a moment.',
        gif_provider_failed: 'GIF search is temporarily unavailable.',
        self_message: 'You cannot message your own number.',
        recipient_not_found: 'That number is unavailable.',
        blocked: 'This contact has blocked calls and messages from your SIM.',
        messaging_unavailable: 'This company contact does not accept messages.',
        no_sim: 'This phone has no SIM card.',
        rate_limited: 'Too many messages. Try again in a minute.',
        request_failed: 'Messages are temporarily unavailable.',
        default: 'The message could not be sent.',
      },
    },
    appStore: {
      name: 'App Store',
      get: 'GET',
      open: 'OPEN',
      installing: 'Installing',
      player: 'Player',
      profileLabel: '{name} profile',
      searchPlaceholder: 'Games, apps, stories and more',
      appsTitle: 'Built-in Apps',
      gamesTitle: 'Games',
      selected: 'Apps available for your Sky Phone',
      tabs: {
        today: 'Today',
        apps: 'Apps',
        games: 'Games',
        search: 'Search',
      },
      today: {
        featured: 'Featured today',
        gameHighlight: 'Game highlight',
        appHighlight: 'App highlight',
        curatedForYou: 'Curated for you',
        editorsChoice: "Editor's Choice",
        discoverTitle: 'Discover {app}',
        playTitle: 'Play something new with {app}',
        description: '{app} is in the spotlight today on Sky Phone.',
        categoryDescription: '{category} · Made for Sky Phone',
        moreHighlights: "Today's Highlights",
        topToday: 'Top Today',
        topTodayDescription: 'Popular picks from today’s edition',
        oneMoreThing: 'One more thing',
      },
      filters: {
        all: 'All',
        social: 'Social',
        utilities: 'Utilities',
        shopping: 'Shopping',
        productivity: 'Productivity',
        arcade: 'Arcade',
        puzzle: 'Puzzle',
        classic: 'Classics',
      },
      browse: {
        categories: 'Store categories',
        availableNow: 'Available now',
        featureTitle: 'Discover {app}',
        featuredPages: 'Featured apps',
        handPicked: 'Hand-picked for you',
        essentialApps: 'Essential Apps',
        essentialGames: 'Essential Games',
      },
      taglines: {
        citywarn: 'Live city alerts and public safety updates',
        crypto: 'Synthetic markets, portfolio and wallet transfers',
        health: 'Health, activity and medical information',
        'weazel-news': 'Local news and city stories',
        companies: 'Businesses, jobs and services',
        music: 'Songs, playlists and audio',
        picstagram: 'Photo sharing and social feed',
        feather: 'Short posts and city conversations',
        fliptok: 'Short videos and trends',
        flare: 'Social posts and live moments',
        calendar: 'Events, schedules and reminders',
        radio: 'Live radio and team communication',
        'local-pages': 'Local businesses and community pages',
        crewlink: 'Crews, members and coordination',
        phone: 'Calls, contacts and voicemail',
        messages: 'Private texts and media',
        darkchat: 'Private messaging and secure groups',
        garage: 'Vehicles, parking and valet',
        house: 'Homes, access and management',
        map: 'Navigation and nearby places',
        skyride: 'City rides and driver booking',
        banking: 'Accounts, transfers and payments',
        billing: 'Invoices and payment requests',
        mail: 'Email and attachments',
        notes: 'Notes, lists and ideas',
        memos: 'Voice recordings and memos',
        calculator: 'Everyday and scientific calculations',
        camera: 'Photos and video capture',
        clock: 'Time, alarms and timers',
        weather: 'Forecasts and current conditions',
        photos: 'Photo and video library',
        settings: 'Phone preferences and security',
        snake: 'Classic arcade snake',
        memory: 'Card matching challenge',
        'number-merge': 'Number puzzle and strategy',
        minesweeper: 'Classic logic puzzle',
        'tower-stack': 'Precision tower-building game',
        'sky-flappy': 'Fast arcade flying challenge',
        citymarkt: 'Local marketplace and listings',
        'neon-drop': 'Neon block-dropping puzzle',
      },
      previews: {
        citywarn: {
          first: 'Live alerts',
          second: 'Safety zones',
          third: 'Incident updates',
        },
        crypto: {
          first: 'Synthetic markets',
          second: 'Portfolio',
          third: 'Wallet transfers',
        },
        health: {
          first: 'Activity rings',
          second: 'Medical ID',
          third: 'Health records',
        },
        'weazel-news': {
          first: 'Top stories',
          second: 'Local reports',
          third: 'Breaking news',
        },
        companies: {
          first: 'Business directory',
          second: 'Job requests',
          third: 'Services',
        },
        music: {
          first: 'Now playing',
          second: 'Playlists',
          third: 'Music library',
        },
        picstagram: {
          first: 'Photo feed',
          second: 'Stories',
          third: 'Profiles',
        },
        feather: {
          first: 'Short posts',
          second: 'Following feed',
          third: 'Conversations',
        },
        fliptok: {
          first: 'Video feed',
          second: 'Creator tools',
          third: 'Trends',
        },
        flare: {
          first: 'Discover people',
          second: 'Matches',
          third: 'Live moments',
        },
        calendar: {
          first: 'Upcoming events',
          second: 'Day planner',
          third: 'Reminders',
        },
        radio: {
          first: 'Live channels',
          second: 'Team radio',
          third: 'Favorites',
        },
        'local-pages': {
          first: 'Local pages',
          second: 'Reviews',
          third: 'City discovery',
        },
        crewlink: {
          first: 'Crew roster',
          second: 'Shared locations',
          third: 'Coordination',
        },
        phone: {
          first: 'Recent calls',
          second: 'Contacts',
          third: 'Voicemail',
        },
        messages: {
          first: 'Conversations',
          second: 'Media sharing',
          third: 'Quick replies',
        },
        darkchat: {
          first: 'Private chats',
          second: 'Secure groups',
          third: 'Invitations',
        },
        garage: {
          first: 'Vehicle list',
          second: 'Parking locations',
          third: 'Valet',
        },
        house: {
          first: 'Property access',
          second: 'Residents',
          third: 'Management',
        },
        map: {
          first: 'Live navigation',
          second: 'Nearby places',
          third: 'Route guidance',
        },
        skyride: {
          first: 'Ride booking',
          second: 'Driver tracking',
          third: 'Trip history',
        },
        banking: {
          first: 'Account balance',
          second: 'Transfers',
          third: 'Transactions',
        },
        billing: {
          first: 'Open invoices',
          second: 'Payment requests',
          third: 'Payment history',
        },
        mail: { first: 'Inbox', second: 'Attachments', third: 'Mailboxes' },
        notes: { first: 'Notes', second: 'Checklists', third: 'Pinned ideas' },
        memos: {
          first: 'Voice recordings',
          second: 'Playback',
          third: 'Favorites',
        },
        calculator: {
          first: 'Basic calculation',
          second: 'Scientific tools',
          third: 'History',
        },
        camera: {
          first: 'Photo mode',
          second: 'Video capture',
          third: 'Zoom controls',
        },
        clock: { first: 'World clock', second: 'Alarms', third: 'Timers' },
        weather: {
          first: 'Current weather',
          second: 'Hourly forecast',
          third: 'Seven-day outlook',
        },
        photos: {
          first: 'Media library',
          second: 'Albums',
          third: 'Shared media',
        },
        settings: {
          first: 'Device controls',
          second: 'Privacy',
          third: 'Personalization',
        },
        snake: { first: 'High score', second: 'Speed', third: 'Classic grid' },
        memory: {
          first: 'Matched pairs',
          second: 'Best time',
          third: 'Card themes',
        },
        'number-merge': {
          first: 'Highest tile',
          second: 'Score',
          third: 'Strategy grid',
        },
        minesweeper: {
          first: 'Mine counter',
          second: 'Best time',
          third: 'Difficulty',
        },
        'tower-stack': {
          first: 'Tower height',
          second: 'Perfect drops',
          third: 'High score',
        },
        'sky-flappy': {
          first: 'Flight score',
          second: 'Best run',
          third: 'Obstacles',
        },
        citymarkt: {
          first: 'Listings',
          second: 'Categories',
          third: 'Saved offers',
        },
        'neon-drop': {
          first: 'Lines cleared',
          second: 'Level',
          third: 'Neon pieces',
        },
      },
      search: {
        recommended: 'Recommended',
        discover: 'Discover',
        results: 'Search Results',
        noResults: 'No Results',
        noResultsBody: 'Try searching for a different app or game.',
        ad: 'Ad',
        topApps: 'Top Apps',
        topGames: 'Top Games',
        bestApps: 'Bestselling Apps',
        bestGames: 'Bestselling Games',
        productivity: 'Productivity',
        photoVideo: 'Photo & Video',
      },
      account: {
        account: 'Account',
        title: 'App Management',
        skyAccount: 'Sky Phone Account',
        apps: 'Installed Apps',
        games: 'Games',
        library: 'Your Library',
        myApps: 'My Apps',
        downloadedOn: 'Downloaded {date}',
        uninstall: 'Uninstall',
        uninstallApp: 'Uninstall {app}',
        uninstallTitle: 'Uninstall this app?',
        uninstallBody:
          '{app} will be removed from this phone. You can download it again from the App Store.',
      },
      details: {
        skyStudios: 'Sky Studios',
        share: 'Share app',
        openDetails: 'View {app}',
        shareReady: 'App information is ready to share.',
        shareCopy: 'Take a look at {app} in the App Store.',
        ratings: '{count} ratings',
        age: 'Age Rating',
        years: 'Years',
        chart: 'Chart',
        whatsNew: "What's New",
        version: 'Version {version}',
        updatedToday: 'Today',
        releaseNotes:
          '{app} now includes fresh content, faster loading and several interface improvements.',
        preview: 'Preview',
        testData: 'Preview data',
        previewLive: 'Live Preview',
        previousPreview: 'Previous preview',
        nextPreview: 'Next preview',
        previewOverview: 'Everything important at a glance',
        previewCommunity: 'Community',
        previewInsights: 'Insights',
        today: 'Today',
        progress: 'Progress',
        communityTitle: 'Together in {app}',
        communityBody: 'Discover activity from players around Los Santos.',
        featuredMoment: 'Featured moment from today',
        weeklyActivity: 'Weekly Activity',
        fromLastWeek: 'from last week',
        about: 'About this App',
        description:
          '{app} is a detailed {category} experience made for Sky Phone. Explore its features, connect with the city and keep everything close at hand.',
        crypto: {
          releaseNotes:
            'VaultX now offers clearer portfolio performance, live synthetic market updates and improved wallet transfers with server-validated quotes.',
          previewOverview: 'Portfolio, balance and performance at a glance',
          previewCommunity: 'Markets',
          markets: 'Markets',
          serverPrices: 'Server prices',
          communityTitle: 'Trade synthetic markets',
          communityBody:
            'Follow fictional assets, review short-lived quotes and trade against available exchange liquidity.',
          featuredMoment: 'Live market movers',
          previewInsights: 'Portfolio',
          weeklyActivity: 'Simulated performance',
          fromLastWeek: 'fictional weekly portfolio change',
          description:
            "VaultX is Sky Phone's fictional crypto exchange and wallet. Track synthetic markets, manage your character-bound portfolio, deposit or withdraw funds, trade with short-lived server quotes and transfer assets using public VaultX keys. All prices are fictional and have no real-world value.",
        },
      },
    },
    phone: {
      name: 'Phone',
      recents: 'Recents',
      contacts: 'Contacts',
      keypad: 'Keypad',
      noSim: 'No SIM',
      noSimBody: 'Insert a SIM card in Settings to make calls.',
      noRecents: 'No Recent Calls',
      allCalls: 'All',
      missedCalls: 'Missed',
      searchRecents: 'Search Calls',
      contactDetails: 'Contact Details',
      unknownCaller: 'Unknown Caller',
      callHistory: 'Call History',
      noCallHistory: 'No calls with this number yet.',
      noContacts: 'No Contacts',
      favorites: 'Favorites',
      searchContacts: 'Search Contacts',
      myCard: 'My Card',
      myNumber: 'My Number',
      device: 'Device',
      contactIndex: 'Contact Index',
      addContact: 'New Contact',
      newContact: 'New',
      editContact: 'Edit Contact',
      contactName: 'Name',
      firstName: 'First Name',
      lastName: 'Last Name',
      companyOrGroup: 'Company or Group',
      phoneNumber: 'Phone Number',
      officialContact: 'Official company contact',
      choosePhoto: 'Choose Contact Photo',
      chooseGallery: 'Photos',
      takePhoto: 'Camera',
      removePhoto: 'Remove Photo',
      call: 'Call',
      callFailed: 'The call could not be started.',
      message: 'Message',
      video: 'Video',
      mail: 'Mail',
      defaultLabel: 'Default',
      privateLabel: 'Private',
      contactCard: 'Contact Card',
      removeContact: 'Remove Contact',
      mobile: 'Mobile',
      messagesProfile: 'Messages',
      notes: 'Notes',
      sendMessage: 'Send Message',
      shareContact: 'Share Contact',
      addFavorite: 'Add to Favorites',
      removeFavorite: 'Remove from Favorites',
      addEmergency: 'Add to Emergency Contacts',
      blockContact: 'Block Contact',
      block: 'Block',
      blockCaller: 'Block Caller',
      blockCallerTitle: 'Block this caller?',
      blockCallerBody: '{number} will no longer be able to call this SIM.',
      faceTime: 'FaceTime',
      hideKeypad: 'Hide Keypad',
      more: 'More',
      mute: 'Mute',
      speaker: 'Speaker',
      viewContact: 'View Contact',
      calling: 'calling...',
      incoming: 'Incoming Call',
      incomingDirection: 'Incoming',
      outgoingDirection: 'Outgoing',
      connected: 'Connected',
      missed: 'Missed',
      declined: 'Declined',
      busy: 'Busy',
      unavailable: 'Unavailable',
      noAnswer: 'No Answer',
      cancelled: 'Cancelled',
      disconnected: 'Disconnected',
      sim_removed: 'SIM Removed',
      completed: 'Call Ended',
      answer: 'Answer',
      decline: 'Decline',
      hangup: 'End',
      addToContacts: 'Add to Contacts',
      deleteContact: 'Delete Contact',
      replaceTitle: 'Replace SIM?',
      replaceBody: 'The current SIM will be returned to your inventory.',
      choosePhone: 'Choose a Phone',
      choosePhoneBody: 'Select the phone that should receive {number}.',
      emptyPhone: 'No SIM inserted',
      errors: {
        invalid_contact: 'Enter a name and valid phone number.',
        invalid_number: 'Enter a valid phone number.',
        invalid_sim: 'The active SIM card is unavailable.',
        message_unavailable: 'The conversation could not be opened.',
        contact_remove_failed: 'The contact could not be removed.',
        contact_favorite_failed: 'The favorite could not be updated.',
        no_sim: 'This phone has no SIM card.',
        airplane_mode: 'Turn off Airplane Mode to make calls.',
        self_call: 'You cannot call your own number.',
        blocked: 'This number is blocked.',
        recipient_not_found: 'This number is not known.',
        busy: 'The line is busy.',
        company_unavailable: 'This company service line is unavailable.',
        readonly_contact: 'Official company contacts cannot be changed.',
        rate_limited: 'Too many calls. Try again in a minute.',
        voice_unavailable: 'The configured phone voice service is unavailable.',
        call_not_connected:
          'Connect the call before changing its audio controls.',
        speaker_unavailable:
          'Speaker mode is not available for the configured phone voice service.',
        speaker_unsupported:
          'The configured phone voice service does not support speaker mode.',
        mute_unavailable:
          'Mute is not available for the configured phone voice service.',
        mute_unsupported:
          'The configured phone voice service does not support mute.',
        inventory_full: 'There is no room for the ejected SIM card.',
        operation_in_progress:
          'Another phone operation is already in progress.',
        sim_request_expired:
          'The SIM selection expired. Use the SIM card again.',
        sim_not_owned: 'That SIM card is no longer in your inventory.',
        phone_not_owned: 'That phone is no longer in your inventory.',
        metadata_unsupported:
          'The configured inventory cannot store unique SIM metadata.',
        request_failed: 'The phone request failed.',
        default: 'The phone request failed.',
      },
    },
    radio: {
      name: 'Radio',
      disconnected: 'Not connected',
      connectedTo: 'Connected - {frequency} MHz',
      noProvider: 'Voice service unavailable',
      channel: 'Channel',
      primaryFrequency: 'Primary frequency',
      secondaryFrequency: 'Secondary frequency',
      frequencyPlaceholder: 'e.g. 120.5',
      optional: 'Optional',
      mhz: 'MHz',
      volume: 'Volume',
      connect: 'Connect',
      disconnect: 'Disconnect',
      speaker: 'Speaker',
      speakerDescription: 'Play radio traffic through speaker mode',
      providerFeatureUnavailable: 'Not supported by this voice service',
      notSupported: 'Not supported',
      members: 'Currently connected ({count})',
      noMembers: 'No participants',
      history: 'Recently connected',
      noHistory: 'No history',
      badge: 'Service number',
      badgePlaceholder: 'e.g. 231',
      profileSaved: 'Radio profile saved',
      displayName: 'Radio display name',
      displayNamePlaceholder: 'Character name',
      displayNameDescription:
        'Leave empty to use your character name. This name is shown to other participants and in the built-in radio overlay.',
      displayNameNotAllowed:
        'Your current job or grade is not allowed to change the radio display name.',
      otherSettings: 'Other',
      autoRejoin: 'Automatic rejoin',
      autoRejoinDescription:
        'Reconnect after spawning or restarting the phone resource',
      radioNotifications: 'Radio notifications',
      notificationsDescription: 'Show joins and leaves on your current channel',
      memberJoined: '{name} joined the radio',
      memberLeft: '{name} left the radio',
      unknownMember: 'Unknown',
      tabs: { radio: 'Radio', settings: 'Settings' },
      errors: {
        invalid_frequency: 'Enter a valid frequency.',
        invalid_volume: 'Enter a valid volume.',
        channel_locked: 'You do not have access to this channel.',
        secondary_locked: 'You do not have access to the secondary channel.',
        voice_unavailable: 'The configured radio voice service is unavailable.',
        player_unavailable: 'Your player data is not available.',
        rate_limited: 'Please wait before changing channel again.',
        invalid_setting: 'This setting is invalid.',
        radio_not_connected:
          'Connect to a radio channel before enabling speaker mode.',
        speaker_unavailable:
          'Speaker mode is not available for the configured radio voice service.',
        speaker_unsupported:
          'The configured radio voice service does not support speaker mode.',
        badge_disabled: 'Service numbers are disabled.',
        badge_forbidden: 'This service number is not allowed.',
        display_name_disabled: 'Radio display names are disabled.',
        display_name_forbidden:
          'Your job or grade cannot change the radio display name.',
        invalid_display_name: 'Enter a valid radio display name.',
        request_failed: 'The radio request failed.',
        default: 'The radio request failed.',
      },
    },
    banking: {
      name: 'Banking',
      welcome: 'Welcome back',
      totalBalance: 'Total Balance',
      recentPeriod: 'in recent activity',
      actions: 'Banking actions',
      send: 'Send',
      accounts: 'Accounts',
      bankAccount: 'Bank account',
      cash: 'Cash',
      latestTransactions: 'Latest Transactions',
      allTransactions: 'All Transactions',
      viewAll: 'View all',
      noTransactions: 'Your banking activity will appear here.',
      home: 'Home',
      activity: 'Activity',
      chartDaySummary: '{day}: {incoming} incoming, {outgoing} outgoing',
      navigation: 'Banking navigation',
      incoming: 'Incoming',
      outgoing: 'Outgoing',
      refresh: 'Refresh banking data',
      unavailable: 'Banking unavailable',
      tryAgain: 'Try Again',
      recipientPhone: 'Phone number',
      recipientPhonePlaceholder: "Enter the recipient's number",
      chooseContact: 'Choose from contacts',
      noContacts: 'No contacts saved yet.',
      amount: 'Amount',
      amountPlaceholder: 'Enter an amount',
      transactionDetails: 'Transaction details',
      transactionDate: 'Date',
      transactionDirection: 'Direction',
      transactionReference: 'Reference',
      notifications: {
        receivedTitle: 'Money received',
        received: 'You received {amount} from {sender}.',
      },
      transactions: {
        deposit: 'Cash deposit',
        withdrawal: 'Cash withdrawal',
        transfer_in: 'Incoming transfer',
        transfer_out: 'Outgoing transfer',
      },
      forms: {
        transfer: {
          title: 'Send money',
          body: 'Transfer money from your bank account to an online player.',
          submit: 'Send transfer',
        },
      },
      errors: {
        invalid_request: 'Enter a valid phone number and whole amount.',
        insufficient_funds: 'There is not enough money in this account.',
        target_not_found: 'The recipient is not online.',
        self_transfer: 'You cannot send money to yourself.',
        rate_limited: 'Please wait before making another transaction.',
        reload_cooldown:
          'Too many refreshes. Please wait a moment before trying again.',
        transfer_failed: 'The transfer could not be completed.',
        banking_unavailable: 'Banking is currently unavailable.',
        request_failed: 'The banking request failed.',
        default: 'The banking request failed.',
      },
    },
    billing: {
      name: 'Billing',
      back: 'Back',
      navigation: 'Billing navigation',
      search: 'Search invoices',
      loadMore: 'Load more',
      tryAgain: 'Try again',
      tabs: { overview: 'Overview', inbox: 'Inbox', history: 'History' },
      direction: { inbox: 'Received', sent: 'Sent' },
      summary: { open: 'Open invoices', due: 'Amount due', overdue: 'Overdue' },
      overview: {
        eyebrow: 'Payment center',
        urgent: 'Needs attention',
        viewAll: 'View all invoices',
      },
      filters: {
        scope: 'Invoice source',
        status: 'Status',
        all: 'All',
        open: 'Open',
        overdue: 'Overdue',
        paid: 'Paid',
      },
      status: {
        open: 'Open',
        overdue: 'Overdue',
        processing: 'Processing',
        paid: 'Paid',
        disputed: 'Disputed',
        cancelled: 'Cancelled',
        refunded: 'Refunded',
      },
      detail: {
        title: 'Invoice details',
        noDueDate: 'No due date',
        total: 'Total',
        reason: 'Reason',
        issued: 'Issued',
        due: 'Due',
        paidAt: 'Paid',
        paidOn: 'Payment on {date}',
        issuer: 'Issuer',
        invoiceNumber: 'Invoice number',
        invoiceInformation: 'Invoice information',
        paymentInformation: 'Payment information',
        note: 'Invoice note',
        paymentReference: 'Payment reference',
        dispute: 'Dispute invoice',
        disputedSuccess: 'The invoice was disputed.',
      },
      payment: {
        payNow: 'Pay this invoice',
        title: 'Confirm payment',
        body: 'Pay this invoice from {issuer} with your bank account.',
        confirm: 'Pay invoice',
        cancel: 'Cancel',
        success: 'Invoice paid successfully.',
      },
      empty: {
        openTitle: "You're all caught up",
        openBody: 'Invoices requiring your attention will appear here.',
        inboxTitle: 'No invoices found',
        inboxBody: 'Received invoices matching your filters will appear here.',
        historyTitle: 'No payment history',
        historyBody: 'Completed and closed invoices will appear here.',
      },
      notifications: { newInvoice: 'New invoice from {issuer}: {amount}' },
      errors: {
        billing_unavailable: 'Billing is currently unavailable.',
        invoice_not_found: 'This invoice could not be found.',
        invoice_not_payable: 'This invoice can no longer be paid.',
        invoice_already_paid: 'This invoice has already been paid.',
        insufficient_funds: 'There is not enough money in your bank account.',
        payment_in_progress: 'This invoice is already being processed.',
        payment_failed: 'The payment could not be completed.',
        dispute_unavailable: 'This invoice cannot be disputed.',
        rate_limited: 'Please wait before trying again.',
        request_failed: 'The billing request failed.',
        default: 'The billing request failed.',
      },
    },
    garage: {
      name: 'Garage',
      subtitle: 'Your vehicle collection',
      myVehicles: 'My Vehicles',
      searchPlaceholder: 'Search name, plate or garage',
      tryAgain: 'Try Again',
      unavailable: 'Garage unavailable',
      noVehicles: 'No Vehicles',
      noVehiclesBody: 'Vehicles owned by this character will appear here.',
      noResults: 'No Results',
      noResultsBody: 'Try another vehicle, plate or location.',
      unknownVehicle: 'Unknown Vehicle',
      unknownLocation: 'Location unavailable',
      location: 'Current location',
      fuel: 'Fuel',
      engine: 'Engine',
      body: 'Body',
      vin: 'Vehicle Identification Number',
      notAvailable: '—',
      filtersLabel: 'Filter vehicles',
      valet: {
        title: 'Sky Valet',
        eyebrow: 'Premium delivery',
        body: 'A professional driver brings this vehicle directly to your current location.',
        deliver: 'Deliver Vehicle',
        liveEyebrow: 'Live delivery',
        tracked: 'Live tracked',
        onDemand: 'On demand',
        cancel: 'Cancel',
        confirmTitle: 'Request Valet?',
        confirmBody: '{vehicle} will be delivered to you for {price}.',
        confirm: 'Request Valet',
        ordering: 'Requesting…',
        account: 'The fee is charged to your {account} account.',
        connecting: 'Locating driver',
        arriving: 'Arriving now',
        eta: 'About {seconds}s away',
        distance: '{distance} m away',
        unavailableStatus: 'Only parked vehicles can be delivered.',
        unsupported: 'Valet delivery is disabled for this vehicle type.',
        activeOrder: 'A valet driver is already delivering another vehicle.',
        status: {
          ordering: 'Confirming order',
          preparing: 'Preparing vehicle',
          en_route: 'Driver en route',
          arriving: 'Arriving now',
          delivered: 'Vehicle delivered',
          cancelled: 'Delivery cancelled',
          failed: 'Delivery failed',
        },
      },
      filters: {
        all: 'All',
        garaged: 'Parked',
        out: 'Outside',
        impounded: 'Impound',
      },
      status: {
        garaged: 'Parked',
        out: 'Outside',
        impounded: 'Impounded',
      },
      kinds: {
        car: 'Vehicle',
        bike: 'Motorcycle',
        boat: 'Boat',
        plane: 'Aircraft',
        helicopter: 'Helicopter',
      },
      errors: {
        garage_unavailable: 'Vehicle data is unavailable for this character.',
        valet_disabled: 'Valet service is currently unavailable.',
        valet_active: 'A valet delivery is already active.',
        valet_cooldown: 'Please wait before requesting another vehicle.',
        invalid_vehicle: 'Select a valid vehicle.',
        vehicle_not_owned: 'This vehicle does not belong to this character.',
        vehicle_not_garaged: 'This vehicle is not parked in a garage.',
        valet_vehicle_type: 'Valet supports cars and motorcycles only.',
        insufficient_funds: 'You do not have enough money for this delivery.',
        valet_status_unsupported:
          'This garage system cannot release the vehicle.',
        valet_not_found: 'The valet order is no longer active.',
        valet_not_cancellable: 'The delivery can no longer be cancelled.',
        invalid_vehicle_model: 'The vehicle model could not be loaded.',
        vehicle_already_out: 'This vehicle is already nearby.',
        valet_spawn_failed: 'The driver could not prepare your vehicle.',
        valet_no_road: 'No suitable road was found near you.',
        valet_interrupted: 'The valet delivery was interrupted.',
        valet_timeout:
          'The driver could not reach you in time. Your fee was refunded.',
        valet_completion_failed: 'The handover could not be confirmed.',
        valet_vehicle_unverified:
          'The delivered vehicle could not be verified.',
        rate_limited: 'Please wait before refreshing again.',
        device_not_open: 'Open the phone again to view your vehicles.',
        request_failed: 'The garage request failed.',
        default: 'The garage request failed.',
      },
    },
    skyride: {
      name: 'SkyRide',
      loading: 'Loading SkyRide',
      unavailable: 'SkyRide unavailable',
      tryAgain: 'Try Again',
      mode: {
        rider: 'Ride',
        driver: 'Drive',
      },
      riderEyebrow: 'Ride anywhere',
      whereTo: 'Where to?',
      pickup: 'Pickup',
      destination: 'Destination',
      currentLocation: 'Current location',
      notSelected: 'Not selected',
      location: 'Choose a location',
      chooseLocation: {
        pickup: 'Set pickup',
        destination: 'Choose destination',
      },
      chooseOnMap: 'Choose on map',
      chooseOnMapBody: 'Tap a precise point on the Los Santos map.',
      pickOnMap: {
        pickup: 'Tap your pickup point',
        destination: 'Tap your destination',
      },
      tapMapHint: 'Drag and zoom the map, then tap to place the pin.',
      mapPoint: 'Map pin',
      map: 'Map',
      useGps: 'Use your live position',
      zoomIn: 'Zoom in',
      zoomOut: 'Zoom out',
      resetMap: 'Reset map view',
      savedPlaces: 'Saved places',
      savedPlace: 'Saved place',
      quickDestinations: 'Quick destinations',
      quickLocations: {
        'legion-square': 'Legion Square',
        'diamond-casino': 'Diamond Casino',
        airport: 'Los Santos Airport',
        vinewood: 'Vinewood',
      },
      viewRides: 'View Ride Options',
      change: 'Change',
      requestRide: 'Request SkyRide',
      playerDriverNotice:
        'SkyRide matches you with real player drivers. A driver must be online and accept your request.',
      serviceMeta: '{eta} min away · {seats} seats',
      distanceMeters: '{distance} m',
      distanceKilometers: '{distance} km',
      distanceMiles: '{distance} mi',
      durationMinutes: '{minutes} min',
      fare: 'Trip fare',
      calculatedFare: 'Calculated',
      customFare: 'Own price',
      calculatedFareBody: 'Distance, time, and selected service included.',
      customFareRange: 'Allowed range: {minimum} to {maximum}',
      applyCustomFare: 'Apply Own Price',
      perKilometer: '{price} / km',
      perMile: '{price} / mi',
      rideStatus: 'Ride status',
      services: {
        taxi: { name: 'Taxi' },
        comfort: { name: 'Comfort' },
        xl: { name: 'XL' },
        premium: { name: 'Premium' },
      },
      ride: 'SkyRide',
      driver: {
        online: 'You are online',
        offline: 'You are offline',
        onlineBody: 'New ride requests can reach you now.',
        offlineBody: 'Go online when you are ready to drive.',
        toggleStatus: 'Toggle driver status',
        today: 'Today',
        requests: 'Nearby requests',
        accept: 'Accept Ride',
        noRequests: 'No nearby requests',
        noRequestsBody: 'New requests will appear here automatically.',
        goOnline: 'Go online to receive rides',
        goOnlineBody: 'Switch your driver status on to see nearby requests.',
        statusBody: {
          searching: 'A passenger is waiting for a driver.',
          accepted: 'Navigate to the passenger pickup.',
          driver_arriving: 'You are heading to the passenger.',
          arrived: 'Confirm when the passenger is in the vehicle.',
          in_progress: 'Follow the route to the destination.',
          completed: 'The ride has been completed.',
          cancelled: 'This ride was cancelled.',
        },
        actions: {
          arrive: 'I Have Arrived',
          start: 'Start Ride',
          complete: 'Complete Ride',
        },
      },
      passenger: 'Passenger',
      vehicle: 'Vehicle',
      navigate: 'Navigate',
      call: 'Call',
      message: 'Message',
      cancelRide: 'Cancel Ride',
      cancelTitle: 'Cancel this ride?',
      cancelBody:
        'Your driver or passenger will be notified. A cancellation fee may apply.',
      safetyNote: 'Trip details are shared only while this ride is active.',
      waypointSet: 'Route added to your GPS.',
      status: {
        searching: 'Finding a driver',
        accepted: 'Driver found',
        driver_arriving: 'Driver en route',
        arrived: 'Driver arrived',
        in_progress: 'On the way',
        completed: 'Completed',
        cancelled: 'Cancelled',
      },
      statusBody: {
        searching:
          'Your request is waiting for an available player driver to accept it.',
        accepted: 'Your driver is preparing to pick you up.',
        driver_arriving: 'Your driver is on the way to your pickup.',
        arrived: 'Your driver is waiting at the pickup point.',
        in_progress: 'You are on the way to your destination.',
        completed: 'You have arrived at your destination.',
        cancelled: 'This ride is no longer active.',
      },
      navigation: 'SkyRide navigation',
      tabs: {
        home: 'Home',
        rides: 'Rides',
        activity: 'Activity',
        messages: 'Chat',
        profile: 'Profile',
      },
      rides: 'Your Rides',
      ridesBody: 'Review completed and cancelled rides.',
      noRides: 'No rides yet',
      noRidesBody: 'Completed and cancelled rides will appear here.',
      activity: 'Activity',
      activityBody: 'Recent trip updates and milestones.',
      noActivity: 'No recent activity',
      noActivityBody: 'Ride updates will appear here.',
      messages: 'Messages',
      messagesBody: 'Contact your active driver or passenger.',
      noMessages: 'No active contact',
      noMessagesBody: 'Book or accept a ride to contact the other person.',
      openMessages: 'Open Messages',
      account: 'Account',
      paymentMethod: 'Payment Method',
      paymentMethods: {
        bank: 'Bank',
        cash: 'Cash',
        default: 'Payment method',
      },
      safety: 'Safety Center',
      safetyBody: 'Help, trip sharing, and emergency information',
      driverMode: 'Driver Mode',
      driverModeBody: 'Accept ride requests and track your earnings',
      completedRides: 'Completed',
      cancelledRides: 'Cancelled',
      acceptance: 'Acceptance',
      rating: 'Rating',
      notAvailable: 'N/A',
      profile: 'Your profile',
      editProfile: 'Edit Profile',
      editProfileBody: 'Change your public name and profile photo',
      profilePhoto: 'Profile photo',
      gallery: 'Photos',
      camera: 'Camera',
      profileDetails: 'Profile details',
      profileName: 'Name',
      profileNamePlaceholder: 'Your SkyRide name',
      removeProfilePhoto: 'Remove profile photo',
      saveProfile: 'Save Profile',
      profileSaved: 'Your SkyRide profile was updated.',
      memberSince: 'SkyRide member since {date}',
      rideComplete: 'Ride Complete',
      rateRide: 'Rate your ride',
      rateRideBody: 'How was your trip? Your feedback helps every rider.',
      ratingValue: '{rating} stars',
      tip: 'Add a tip',
      noTip: 'No tip',
      comment: 'Comment',
      commentPlaceholder: 'Share an optional note...',
      submitRating: 'Submit Rating',
      notNow: 'Not Now',
      ratingSaved: 'Thanks for your feedback.',
      errors: {
        skyride_unavailable: 'SkyRide is currently unavailable.',
        invalid_location: 'Choose a valid pickup and destination.',
        pickup_too_far: 'Move closer to the selected pickup point.',
        invalid_route: 'No suitable route was found for this ride.',
        invalid_custom_fare: 'Choose an own price inside the allowed range.',
        quote_not_found: 'These ride options are no longer available.',
        quote_expired: 'These ride options expired. Request a new quote.',
        active_ride_exists: 'Finish or cancel your active ride first.',
        invalid_driver_status: 'That driver status is not available.',
        driver_not_eligible: 'This account is not eligible for Driver Mode.',
        driver_offline: 'Go online before accepting a ride.',
        driver_unavailable: 'That driver is no longer available.',
        driver_vehicle_required:
          "Sit in the driver's seat of a vehicle before accepting a ride.",
        ride_not_found: 'This ride is no longer available.',
        invalid_ride_status: 'This action is not available for the ride.',
        not_ride_participant: 'You are not part of this ride.',
        driver_too_far: 'Move closer to the required ride location.',
        passenger_too_far: 'The passenger is not close enough to start.',
        destination_too_far:
          'Move closer to the destination to complete the ride.',
        payout_failed: 'The driver payout could not be completed.',
        refund_failed: 'The ride refund could not be completed.',
        invalid_cancel_reason: 'Choose a valid cancellation reason.',
        invalid_rating: 'Choose a rating from one to five stars.',
        invalid_tip: 'Choose a valid tip amount.',
        invalid_comment: 'The rating comment is not valid.',
        invalid_profile: 'Choose a valid profile name and photo.',
        invalid_profile_name:
          'Your profile name must contain 2 to 50 characters.',
        invalid_profile_image: 'Choose a profile photo from your own gallery.',
        tip_failed: 'The tip could not be processed.',
        insufficient_funds:
          'Your selected payment method has insufficient funds.',
        rate_limited: 'Please wait a moment before trying again.',
        device_not_open: 'Open the phone again to use SkyRide.',
        device_not_owned: 'This phone does not belong to this character.',
        device_locked: 'Unlock this phone to use SkyRide.',
        request_failed: 'SkyRide could not complete the request.',
        default: 'SkyRide could not complete the request.',
      },
    },
    house: {
      name: 'House',
      subtitle: 'Manage homes and access',
      myHomes: 'Your Homes',
      properties: 'Properties',
      owned: 'Owned',
      shared: 'Shared with you',
      locked: 'Locked',
      unlocked: 'Unlocked',
      lockDoor: 'Lock Door',
      unlockDoor: 'Unlock Door',
      owner: 'Owner',
      keyholder: 'Key holder',
      openDetails: 'Open property details',
      tryAgain: 'Try Again',
      refresh: 'Refresh',
      offline: 'House is offline',
      offlineBody:
        'Start a supported housing resource or review Config.Housing.',
      unavailable: 'Homes unavailable',
      empty: 'No homes yet',
      emptyBody: 'Homes you own or can access will appear here automatically.',
      provider: 'Housing data · {system}',
      access: 'Access',
      status: 'Status',
      actions: 'Quick Actions',
      setWaypoint: 'Get Directions',
      viewCamera: 'View Camera',
      garage: 'Garage',
      garageEnabled: 'Enabled',
      garageDisabled: 'Unavailable',
      storedVehicles: '{count} stored',
      camera: 'Camera',
      cameraAvailable: 'Ready',
      cameraUnavailable: 'Unavailable',
      keys: 'Keys',
      manageKeys: 'Manage Keys',
      addKey: 'Add Key',
      revokeKey: 'Revoke',
      noKeys: 'No shared keys',
      noKeysBody: 'Only you currently have access to this home.',
      chooseResident: 'Give a Key',
      chooseResidentBody: 'Choose a person currently inside this property.',
      noCandidates: 'Nobody is waiting inside',
      noCandidatesBody:
        'A person must be inside the property before you can give them a key.',
      revokeTitle: 'Revoke Key?',
      revokeBody: '{name} will lose access to this property.',
      offlineKey: 'Offline',
      onlineKey: 'Online',
      cancel: 'Cancel',
      confirm: 'Confirm',
      lockSuccess: 'Door status updated.',
      waypointSuccess: 'Route set on your map.',
      keyGranted: 'Key granted.',
      keyRevoked: 'Key revoked.',
      cameraStarting: 'Opening camera…',
      errors: {
        provider_unavailable: 'The housing provider is unavailable.',
        provider_error: 'The housing provider could not be reached.',
        housing_unavailable: 'Housing data is unavailable for this character.',
        property_not_found: 'This property no longer exists.',
        property_access_denied: 'You no longer have access to this property.',
        owner_required: 'Only the property owner can do that.',
        invalid_property: 'Select a valid property.',
        invalid_action: 'This housing action is unavailable.',
        capability_unavailable:
          'This property provider does not support that control.',
        invalid_target: 'Select a valid resident.',
        target_not_in_property: 'That person is no longer inside the property.',
        key_not_found: 'That key no longer exists.',
        key_already_exists: 'That person already has access to this property.',
        keyholder_offline:
          'Offline keys must be revoked through the property menu.',
        cctv_unavailable: 'The camera is unavailable for this property.',
        provider_rejected: 'The housing provider rejected the request.',
        action_failed: 'The property change could not be saved.',
        rate_limited: 'Please wait before using another housing control.',
        reload_cooldown:
          'Too many refreshes. Please wait a moment before trying again.',
        device_not_open: 'Open the phone again to manage your homes.',
        device_locked: 'Unlock the phone to manage your homes.',
        request_failed: 'The housing request failed.',
        default: 'The housing request failed.',
      },
    },
    calculator: {
      name: 'Calculator',
      history: 'Calculation history',
      changeMode: 'Change calculator mode',
      scientificFunctions: 'Scientific functions',
      edit: 'Edit',
      done: 'Done',
      closeHistory: 'Close history',
      today: 'Today',
      yesterday: 'Yesterday',
      earlier: 'Earlier',
      noHistory: 'No calculations yet',
      clearHistory: 'Clear History',
    },
    snake: {
      name: 'Snake',
      backToMenu: 'Back to game menu',
      board: 'Snake game board',
      controls: 'Direction controls',
      directions: {
        down: 'Move down',
        left: 'Move left',
        right: 'Move right',
        up: 'Move up',
      },
      gameOver: 'Game Over',
      highScore: 'High Score',
      menu: 'Main Menu',
      pause: 'Pause game',
      paused: 'Paused',
      readyBody: 'Collect fruit, grow longer, and stay clear of every wall.',
      readyTitle: 'Ready to play?',
      restart: 'Play Again',
      resume: 'Resume game',
      score: 'Score',
      speed: 'Speed',
      speeds: { fast: 'Fast', normal: 'Normal', relaxed: 'Relaxed' },
      start: 'Start Game',
      swipeHint: 'Swipe, tap the controls, or use arrow keys / WASD',
    },
    memory: {
      name: 'Memory',
      backToMenu: 'Back to board selection',
      board: 'Memory card board',
      chooseBody: 'Find every matching pair with as few moves as possible.',
      chooseTitle: 'Choose a board',
      completeEyebrow: 'Board cleared',
      completeTitle: 'Great memory!',
      difficulties: { large: 'Expert', medium: 'Classic', small: 'Quick' },
      eyebrow: 'Matching game',
      hiddenCard: 'Hidden card',
      menu: 'Board Selection',
      moves: 'Moves',
      mute: 'Mute game sounds',
      noBest: 'No best score yet',
      playAgain: 'Play Again',
      restart: 'Restart',
      revealedCard: 'Revealed card',
      time: 'Time',
      unmute: 'Turn on game sounds',
    },
    numberMerge: {
      name: '2048',
      backToMenu: 'Back to game menu',
      best: 'Best',
      board: '2048 game board',
      cancel: 'Keep Current Game',
      confirmBody:
        'Your current board will be replaced. This cannot be undone.',
      confirmNew: 'Start New Game',
      confirmTitle: 'Replace current game?',
      continueGame: 'Continue Game',
      controls: 'Move controls',
      directions: {
        down: 'Move tiles down',
        left: 'Move tiles left',
        right: 'Move tiles right',
        up: 'Move tiles up',
      },
      eyebrow: 'Number puzzle',
      gameOverBody: 'No empty spaces or matching neighbours remain.',
      gameOverTitle: 'No more moves',
      highest: 'Highest Tile',
      howToBody:
        'Swipe to combine matching tiles. Each pair becomes one larger tile.',
      howToExample: '2 + 2 = 4  ·  4 + 4 = 8  ·  …  ·  1024 + 1024 = 2048',
      howToGoal:
        'Goal: create the 2048 tile before the board has no moves left.',
      howToTitle: 'How to play',
      keepPlaying: 'Keep Playing',
      mainMenu: 'Main Menu',
      menuBody: 'Slide matching numbers together and work your way up to 2048.',
      menuTitle: 'Build the 2048 tile',
      mute: 'Mute game sounds',
      newGame: 'New Game',
      score: 'Score',
      swipeHint: 'Swipe the board, use the buttons, or press arrow keys / WASD',
      unmute: 'Turn on game sounds',
      wonBody:
        'You reached the legendary tile. Continue climbing or begin again.',
      wonTitle: 'You made 2048!',
    },
    minesweeper: {
      name: 'Minesweeper',
      backToMenu: 'Back to game menu',
      board: 'Minesweeper board',
      chooseBody:
        'Reveal every safe field. Numbers show how many mines touch that field.',
      chooseTitle: 'Choose a minefield',
      difficulties: { classic: 'Classic', expert: 'Expert', quick: 'Quick' },
      emptyCell: 'Revealed empty field',
      eyebrow: 'Mine puzzle',
      flaggedCell: 'Flagged field',
      gameHint: 'Tap to reveal · Hold or right-click to flag',
      hiddenCell: 'Hidden field',
      longPressHint: 'Tap to reveal · Hold to place a flag',
      lostBody:
        'A mine was hidden there. Mark suspicious fields and try again.',
      lostTitle: 'Mine triggered',
      mainMenu: 'Main Menu',
      mineCell: 'Revealed mine',
      mines: 'Mines',
      mute: 'Mute game sounds',
      noBest: 'No best time yet',
      numberCell: 'Revealed field with {count} adjacent mines',
      playAgain: 'Play Again',
      restart: 'Restart',
      resume: 'Continue Game',
      time: 'Time',
      unmute: 'Turn on game sounds',
      wonBody: 'Minefield cleared in {time}.',
      wonTitle: 'Field cleared!',
    },
    towerStack: {
      name: 'Tower Stack',
      backToMenu: 'Back to game menu',
      bestHeight: 'Best Height',
      bestScore: 'Best Score',
      blocks: 'Blocks',
      eyebrow: 'Timing challenge',
      finalScore: 'Final Score',
      gameHint: 'Tap the playfield to place the moving block',
      gameOver: 'Tower collapsed',
      height: 'Height',
      mainMenu: 'Main Menu',
      menuBody:
        'Stop each moving block above the tower. Only the overlapping part remains.',
      menuTitle: 'How high can you build?',
      mute: 'Mute game sounds',
      newGame: 'Start New Game',
      pause: 'Pause or resume game',
      paused: 'Paused',
      perfect: 'PERFECT!',
      placeBlock: 'Place moving block',
      playAgain: 'Play Again',
      ready: 'Ready to stack?',
      resume: 'Continue Game',
      score: 'Score',
      start: 'Start Game',
      tapHint: 'Every tap places one block · perfect hits earn bonus points',
      unmute: 'Turn on game sounds',
    },
    skyFlappy: {
      name: 'Sky Flappy',
      backToMenu: 'Back to game menu',
      best: 'Best',
      design: 'Sky design',
      designs: { dawn: 'Dawn', neon: 'Neon', storm: 'Storm' },
      eyebrow: 'Sky challenge',
      firstTap: 'Tap to launch',
      flap: 'Fly upward',
      gameHint: 'Tap anywhere in the sky to fly upward',
      gameOver: 'Flight ended',
      highScore: 'High Score',
      mainMenu: 'Main Menu',
      menuBody:
        'Guide the sky bird through every tower opening without touching the edges.',
      menuTitle: 'Fly between the towers',
      mute: 'Mute game sounds',
      pause: 'Pause or resume game',
      paused: 'Paused',
      playAgain: 'Fly Again',
      points: 'Points',
      ready: 'Ready for takeoff?',
      resume: 'Continue Flight',
      score: 'Score',
      start: 'Start Flight',
      tapHint: 'Tap to flap · gravity pulls the bird down',
      unmute: 'Turn on game sounds',
    },
    neonDrop: {
      name: 'Neon Drop',
      backToMenu: 'Back to game menu',
      bestLines: 'Best Lines',
      bestScore: 'Best Score',
      board: 'Neon Drop reactor board',
      controls: 'Block controls',
      eyebrow: 'Energy block puzzle',
      gameHint: 'Swipe the reactor or use the five controls',
      gameOver: 'Reactor overloaded',
      hardDrop: 'Drop block immediately',
      howToBody:
        'Fill complete horizontal rows to dissolve them and keep the reactor clear.',
      howToTitle: 'Charge the reactor',
      left: 'Move block left',
      level: 'Level',
      lines: 'Lines',
      mainMenu: 'Main Menu',
      menuBody:
        'Rotate and place falling energy shapes. Every cleared row increases your charge.',
      menuTitle: 'Build complete energy lines',
      mute: 'Mute game sounds',
      newGame: 'Start New Game',
      next: 'Next',
      pause: 'Pause or resume game',
      paused: 'Paused',
      playAgain: 'Play Again',
      points: 'Points',
      ready: 'Reactor ready',
      resume: 'Continue Game',
      right: 'Move block right',
      rotate: 'Rotate block',
      score: 'Score',
      softDrop: 'Move block down',
      start: 'Start Reactor',
      unmute: 'Turn on game sounds',
    },
    citymarkt: {
      name: 'CityMarkt',
      eyebrow: 'Los Santos marketplace',
      tabs: {
        discover: 'Discover',
        search: 'Search',
        sell: 'Sell',
        inbox: 'Inbox',
        profile: 'Me',
      },
      categories: {
        vehicles: 'Vehicles',
        property: 'Property',
        electronics: 'Electronics',
        clothing: 'Clothing',
        tools: 'Tools',
        leisure: 'Leisure',
        services: 'Services',
        jobs: 'Jobs',
        wanted: 'Wanted',
        other: 'Other',
      },
      conditions: {
        new: 'New',
        very_good: 'Very good',
        used: 'Used',
        defective: 'Defective',
      },
      priceTypes: {
        fixed: 'Fixed price',
        negotiable: 'Negotiable',
        free: 'Free',
      },
      offerStatus: {
        pending: 'Open',
        accepted: 'Accepted',
        declined: 'Declined',
        countered: 'Negotiated',
      },
      districts: {
        los_santos: 'Los Santos',
        vinewood: 'Vinewood',
        vespucci: 'Vespucci',
        south_los_santos: 'South Los Santos',
        sandy_shores: 'Sandy Shores',
        paleto_bay: 'Paleto Bay',
        blaine_county: 'Blaine County',
      },
      status: {
        active: 'Active',
        reserved: 'Reserved',
        sold: 'Sold',
        expired: 'Expired',
        removed: 'Removed',
      },
      reportReasons: {
        prohibited: 'Prohibited item',
        fraud: 'Suspected fraud',
        spam: 'Spam',
        offensive: 'Offensive content',
        other: 'Other',
      },
      searchPlaceholder: 'What are you looking for?',
      allCategories: 'All categories',
      allDistricts: 'All districts',
      sortNewest: 'Newest first',
      sortPriceAsc: 'Lowest price',
      sortPriceDesc: 'Highest price',
      freshOffers: 'Fresh offers',
      offers: 'offers',
      compactView: 'Compact view',
      largeView: 'Large view',
      noListings: 'No offers found',
      noListingsBody: 'Try another search or category.',
      noDistrict: 'No district',
      free: 'Free',
      negotiablePrice: '${price} negotiable',
      money: '${price}',
      hoursAgo: '{count}h ago',
      daysAgo: '{count}d ago',
      photos: 'photos',
      activeListings: 'active listings',
      signInTitle: 'Sign in to Sky Cloud',
      signInBody: 'Log in to your CityMarkt profile to sell, save or message.',
      authEyebrow: 'CityMarkt account',
      authLoginTitle: 'Welcome back',
      authRegisterTitle: 'Create your profile',
      authLoginBody: 'Sign in with the iFruit account linked to this phone.',
      authRegisterBody:
        'Confirm your linked iFruit account to create a CityMarkt profile.',
      login: 'Sign in',
      register: 'Create profile',
      authPassword: 'iFruit password',
      authPasswordPlaceholder: 'Enter your iFruit password',
      authPasswordHelp: 'Use 6–64 characters.',
      authConfirmPassword: 'Repeat password',
      authConfirmPlaceholder: 'Repeat your iFruit password',
      passwordsMismatch: 'The passwords do not match.',
      authErrors: {
        no_ifruit_account: 'Connect a Sky Cloud account in Settings first.',
        invalid_password: 'Password must be 6–64 characters.',
        invalid_credentials: 'The iFruit email or password is incorrect.',
        invalid_profile_image: 'Choose a valid photo from this phone.',
        profile_not_found: 'No CityMarkt profile exists for this iFruit email.',
        profile_exists:
          'A CityMarkt profile already exists. Use Sign in instead.',
        rate_limited: 'Too many attempts. Try again in a minute.',
        default: 'CityMarkt authentication failed.',
      },
      noMessages: 'No conversations',
      noMessagesBody: 'Messages about offers will appear here.',
      myListings: 'My listings',
      favorites: 'Favorites',
      addFavorite: 'Add to favorites',
      removeFavorite: 'Remove from favorites',
      noProfileListings: 'Nothing here yet',
      createProfile: 'Create your CityMarkt profile',
      editProfile: 'Edit profile',
      profileIntro: 'Your iFruit email stays linked to this profile.',
      profileEmail: 'iFruit email',
      displayName: 'Display name',
      profileBio: 'About you',
      saveProfile: 'Save profile',
      cancel: 'Cancel',
      profileSaved: 'Your profile was saved.',
      removeProfilePhoto: 'Remove photo',
      phone: 'Phone',
      contactSeller: 'Contact seller',
      messagePlaceholder: 'Hi, is this still available?',
      signInToMessage: 'Sign in to your Sky Cloud account to send a message.',
      edit: 'Edit',
      makeActive: 'Make active',
      markSold: 'Mark sold',
      remove: 'Remove',
      createListing: 'Create listing',
      editListing: 'Edit listing',
      save: 'Save',
      step: 'Step {current} of {total}',
      next: 'Next',
      previous: 'Back',
      publish: 'Publish',
      addPhotos: 'Add photos',
      addPhotosBody:
        'Choose up to six photos from Photos or take new ones. Photos are optional and the first becomes the cover.',
      chooseGallery: 'Choose from Photos',
      chooseGalleryBody: 'Use photos saved on this phone.',
      takePhotos: 'Take photos',
      takePhotosBody: 'Take several new photos for this listing.',
      selectedPhotos: 'Selected photos',
      gallery: 'Photos',
      camera: 'Camera',
      takePhoto: 'Take photo',
      cameraHint:
        'Take as many photos as you need. Each shot is added to the listing automatically.',
      noPhoto: 'No photo available',
      noPhotoBody: 'The seller did not add a photo to this listing.',
      noPhotoOptional:
        'You can publish without a photo or add one from the gallery or camera.',
      previousPhoto: 'Previous photo',
      nextPhoto: 'Next photo',
      photo: 'Photo',
      removePhoto: 'Remove photo {number}',
      photoLimit: 'You can add up to six photos.',
      describeOffer: 'Describe your offer',
      title: 'Title',
      description: 'Description',
      category: 'Category',
      condition: 'Condition',
      characterCount: '{current} / {maximum} characters',
      minimumCharacters: 'min. {minimum}',
      priceAndPlace: 'Price and location',
      priceType: 'Price type',
      price: 'Price',
      district: 'District',
      showPhone: 'Show my current phone number',
      preview: 'Preview',
      published: 'Your listing is live.',
      writeMessage: 'Write a message',
      reserveForBuyer: 'Reserve for this buyer',
      statusChanged: 'Listing updated.',
      offer: 'Offer',
      counterOffer: 'Counteroffer',
      makeOffer: 'Make an offer',
      offeredByYou: 'You sent this offer.',
      offeredToYou: 'You received this offer.',
      acceptOffer: 'Accept',
      declineOffer: 'Decline',
      negotiateOffer: 'Counter',
      offerFor: 'Your offer for',
      offerAmount: 'Offer amount',
      sendOffer: 'Send offer',
      offerSent: 'Your offer was sent.',
      offerAccepted: 'Offer accepted.',
      offerDeclined: 'Offer declined.',
      reportListing: 'Report listing',
      reportWhy: 'Why are you reporting this?',
      reportDetails: 'Add details (optional)',
      sendReport: 'Send report',
      blockSeller: 'Block seller',
      reported: 'Report sent.',
      blocked: 'Seller blocked.',
      newMessage: 'New CityMarkt message from {sender}',
      newOffer: '{sender} offered ${price}.',
      offerAcceptedNotification: '{sender} accepted your ${price} offer.',
      offerRejectedNotification: '{sender} declined your ${price} offer.',
      errors: {
        invalid_listing: 'Check the listing details.',
        invalid_price: 'Enter a valid price.',
        invalid_images: 'Choose valid photos from this phone.',
        phone_unavailable: 'Insert a SIM or hide your number.',
        listing_limit: 'You have reached the active listing limit.',
        listing_not_found: 'This listing is no longer available.',
        invalid_message: 'Enter a valid message.',
        inquiry_not_found: 'This conversation is no longer available.',
        invalid_offer: 'Enter a valid whole-number offer.',
        invalid_offer_response: 'This offer action is invalid.',
        offer_listing_unavailable:
          'This listing is no longer available for offers.',
        offer_closed: 'This negotiation is already complete.',
        offer_waiting: 'Wait for the other person to respond.',
        offer_not_allowed: 'You cannot make an offer right now.',
        offer_not_actionable: 'This offer can no longer be changed.',
        offer_conflict: 'The offer changed. Please try again.',
        blocked: 'Messages are blocked between these accounts.',
        already_reported: 'You already reported this listing.',
        rate_limited: 'Too many requests. Try again shortly.',
        not_authenticated: 'Sign in to your Sky Cloud account first.',
        conflict: 'The listing changed. Open it again.',
        request_failed: 'CityMarkt is temporarily unavailable.',
        default: 'The CityMarkt request failed.',
      },
    },
    localPages: {
      name: 'Local Pages',
      eyebrow: 'Your city. Your stories.',
      cityPulse: 'Live from Los Santos',
      heroTitle: 'What is happening nearby?',
      heroBody: 'Discover places, people and local tips.',
      searchPlaceholder: 'Search posts and places',
      search: 'Search',
      allCategories: 'All categories',
      allLosSantos: 'Los Santos',
      hoursAgo: '{count}h ago',
      daysAgo: '{count}d ago',
      categories: {
        recommendation: 'Recommended',
        wanted: 'Wanted',
        service: 'Service',
        event: 'Event',
        place: 'Place',
        community: 'Community',
        citymarkt: 'CityMarkt',
      },
      discover: 'Discover',
      create: 'Post',
      profile: 'Profile',
      post: 'Post',
      posts: 'posts',
      noPosts: 'Nothing here yet',
      noPostsBody: 'Be the first to share something with the city.',
      noPhoto: 'No photo attached',
      signInTitle: 'Your Local Pages profile',
      signInBody: 'Sign in to Sky Cloud in Settings to publish and save posts.',
      authEyebrow: 'Local Pages account',
      authWelcome: 'Welcome to Local Pages',
      authBody:
        'Your iFruit email is linked automatically. Use your Local Pages username to continue.',
      login: 'Sign in',
      register: 'Register',
      loginTitle: 'Continue with Sky Cloud',
      loginBody:
        'Your posts, saved items and profile stay linked to your account.',
      registerTitle: 'Create your Local Pages profile',
      registerBody:
        'Choose a username and optional profile photo. Your iFruit email stays linked.',
      authEmail: 'iFruit address',
      authEmailPlaceholder: 'your.name',
      authPassword: 'Password',
      authPasswordPlaceholder: 'At least 6 characters',
      authConfirmPassword: 'Confirm password',
      authConfirmPlaceholder: 'Enter the password again',
      showPassword: 'Show password',
      hidePassword: 'Hide password',
      localCreator: 'Local creator',
      createProfile: 'Create your profile',
      editProfile: 'Edit profile',
      profileSetupBody: 'Your iFruit email is linked automatically.',
      profileEmail: 'iFruit email',
      profileHandle: 'Username',
      profileHandlePlaceholder: 'your.name',
      profileHandleHint:
        'Use 3–24 lowercase letters, numbers, dots or underscores.',
      profileBio: 'Bio',
      profileBioPlaceholder: 'Tell the city a little about yourself...',
      profilePhoto: 'Profile photo',
      profilePhotoHint: 'Choose a photo from Photos or take a new one.',
      removeProfilePhoto: 'Remove photo',
      profileSave: 'Save profile',
      profileCancel: 'Cancel',
      profileSaved: 'Your profile was saved.',
      myPosts: 'My posts',
      saved: 'Saved',
      save: 'Save',
      likes: 'likes',
      location: 'Location',
      sharedFrom: 'Shared from CityMarkt',
      openCityMarkt: 'Open CityMarkt listing',
      newPost: 'New local post',
      shareWithCity: 'Share with the city',
      publish: 'Publish',
      published: 'Your post is live.',
      deleted: 'Post deleted.',
      title: 'Title',
      body: 'Your story',
      category: 'Category',
      titlePlaceholder: 'What should people know?',
      bodyPlaceholder: 'Add details, a recommendation or directions...',
      photos: 'Photos',
      optional: 'optional',
      camera: 'Camera',
      gallery: 'Photos',
      photoLimit: 'You can add up to six photos.',
      cityMarktShare: 'Share to Local Pages',
      cityMarktShared: 'Shared to Local Pages.',
      cityMarktShareHint: 'One CityMarkt share per day',
      cityMarktAlreadyShared: 'Already published',
      cityMarktOpenSharedHint: 'Open the Local Pages post',
      cityMarktAppMissing: 'Local Pages is not installed',
      cityMarktInstallHint: 'Install Local Pages to share this listing',
      cityMarktAccountMissing: 'Local Pages profile required',
      cityMarktAccountHint: 'Create a Local Pages profile before sharing',
      cityMarktComposeTitle: 'Share CityMarkt listing',
      cityMarktComposeNavTitle: 'Share listing',
      cityMarktComposeHint:
        'Review the listing and publish it when you are ready.',
      cityMarktPhotosHint: 'The CityMarkt listing photos will be included.',
      authErrors: {
        no_ifruit_account:
          'Sign in to your Sky Cloud account in Settings first.',
        invalid_username:
          'Use 3–24 lowercase letters, numbers, dots or underscores.',
        profile_not_found:
          'No Local Pages profile exists for this iFruit email.',
        profile_exists: 'This iFruit email already has a Local Pages profile.',
        invalid_profile: 'Check your Local Pages username.',
        invalid_profile_image: 'Choose a valid photo from this phone.',
        profile_handle_taken: 'This Local Pages username is already taken.',
        rate_limited: 'Too many attempts. Try again shortly.',
        default: 'Local Pages could not complete the request.',
      },
      errors: {
        profile_required: 'Create your Local Pages profile first.',
        invalid_profile: 'Check your username and bio.',
        invalid_profile_image: 'Choose a valid photo from this phone.',
        profile_handle_taken: 'This username is already taken.',
        invalid_post: 'Add a title and a little more detail.',
        invalid_images: 'Choose valid photos from this phone.',
        invalid_request: 'This action is not valid.',
        post_not_found: 'This post is no longer available.',
        citymarkt_not_found: 'This CityMarkt listing is unavailable.',
        citymarkt_daily_limit: 'You already shared a CityMarkt listing today.',
        citymarkt_already_shared: 'This listing was already shared.',
        not_authenticated: 'Sign in to Sky Cloud first.',
        rate_limited: 'Too many requests. Try again shortly.',
        request_failed: 'The post could not be saved.',
        default: 'Local Pages is temporarily unavailable.',
      },
    },
    map: {
      name: 'Map',
      controls: 'Map controls',
      currentLocation: 'Current Location',
      imageError: 'The map image could not be loaded.',
      switchStyle: 'Switch Map Type',
      addMarker: 'Add Marker',
      placeMarker: 'Place Marker',
      placeMarkerHint:
        'Move the map until the crosshair is over the destination.',
      addHere: 'Add Here',
      newMarker: 'New Marker',
      newMarkerDescription: 'Give this saved place a name and color.',
      markerName: 'Name',
      markerNamePlaceholder: 'e.g. Meeting point',
      markerColor: 'Marker Color',
      saveMarker: 'Save Marker',
      deleteMarker: 'Delete Marker',
      setWaypoint: 'Set Waypoint',
      waypointSet: 'Waypoint set.',
      markerSaved: 'Marker saved.',
      markerDeleted: 'Marker deleted.',
      colors: {
        blue: 'Blue',
        green: 'Green',
        orange: 'Orange',
        purple: 'Purple',
        red: 'Red',
      },
      errors: {
        invalid_marker: 'Enter a valid marker name and position.',
        marker_limit: 'This phone has reached its marker limit.',
        marker_not_found: 'This marker no longer exists.',
        rate_limited: 'Too many changes. Try again shortly.',
        request_failed: 'The marker could not be saved.',
      },
      styles: {
        default: 'Default Map',
        satellite: 'Satellite Map',
        atlas: 'Atlas Map',
        roads: 'Road Map',
      },
    },
    weather: {
      name: 'Weather',
      now: 'Now',
      today: 'Today',
      refresh: 'Refresh weather',
      details: 'Weather details',
      feelsLike: 'Feels Like',
      wind: 'Wind',
      humidity: 'Humidity',
      rain: 'Precipitation',
      hourly: 'Next Hours',
      unavailable: 'Weather is unavailable',
      stale: 'Showing the last available weather update.',
      errors: {
        reload_cooldown:
          'Too many refreshes. Please wait a moment before trying again.',
      },
      tryAgain: 'Try Again',
      regions: {
        los_santos: 'Los Santos',
        blaine_county: 'Blaine County',
        cayo_perico: 'Cayo Perico',
      },
      conditions: {
        sunny: 'Sunny',
        clear: 'Clear',
        partly_cloudy: 'Partly Cloudy',
        cloudy: 'Cloudy',
        rain: 'Rain',
        thunder: 'Thunderstorms',
        fog: 'Foggy',
        snow: 'Snow',
      },
      summaries: {
        sunny: 'Bright skies throughout the current period.',
        clear: 'Calm and clear conditions across the region.',
        partly_cloudy: 'Sunshine mixed with passing clouds.',
        cloudy: 'Cloud cover will remain over the region.',
        rain: 'Wet conditions are moving through the area.',
        thunder: 'Stormy conditions with heavy bursts of rain.',
        fog: 'Reduced visibility in low-lying areas.',
        snow: 'Cold conditions with snow across the region.',
      },
    },
    camera: {
      name: 'Camera',
      flash: 'Flash',
      flip: 'Flip camera',
      landscape: 'Switch to landscape',
      portrait: 'Switch to portrait',
      photo: 'Photo',
      video: 'Video',
      microphoneOn: 'Microphone on',
      microphoneOff: 'Microphone muted',
      focusHelp: 'Space for movement',
      returnHelp: 'Space to return',
      uploading: '{count} uploading',
      saving: 'Saving video...',
      openGallery: 'Open Photos',
      takePhoto: 'Take photo',
      startRecording: 'Start recording',
      stopRecording: 'Stop recording',
      saved: 'Saved to Photos.',
      zoom: 'Set camera zoom to {zoom}',
      errors: {
        cancelled: 'Capture cancelled.',
        capture_failed: 'Unable to capture the game view.',
        invalid_media_type: 'The uploaded media type is invalid.',
        invalid_upload: 'The upload could not be verified.',
        invalid_upload_token: 'The upload session is no longer valid.',
        media_provider_failed: 'The camera upload service is unavailable.',
        media_provider_rate_limited:
          'The camera upload service is busy. Try again shortly.',
        media_provider_unauthorized:
          'The configured FiveManage API key was rejected.',
        missing_config: 'Camera uploads are not configured.',
        microphone_unavailable:
          'Allow microphone access or mute the microphone before recording.',
        not_found: 'The media item no longer exists.',
        operation_in_progress:
          'Another media operation is already in progress.',
        owner_changed: 'The active phone account changed during upload.',
        rate_limited: 'Too many media actions. Try again shortly.',
        request_failed: 'The camera request failed.',
        request_timeout: 'The media service timed out.',
        unsupported: 'Video recording is not supported.',
        upload_failed: 'The media upload failed.',
        upload_timeout: 'The media upload timed out.',
      },
    },
    calendar: {
      name: 'Calendar',
      today: 'Today',
      calendars: 'Calendars',
      calendar: 'Calendar',
      calendarName: 'Sky Cloud',
      searchPlaceholder: 'Search events',
      previousMonth: 'Previous month',
      nextMonth: 'Next month',
      eyebrow: 'Your time. Clearly planned.',
      schedule: 'Schedule',
      event: 'Event',
      appointment: 'Appointment',
      newEvent: 'New event',
      editEvent: 'Edit event',
      deleteEvent: 'Delete event',
      details: 'Event details',
      title: 'Title',
      titlePlaceholder: 'What is planned?',
      date: 'Date',
      allDay: 'All-day',
      starts: 'Starts',
      ends: 'Ends',
      reminder: 'Reminder',
      note: 'Note',
      notePlaceholder: 'Add details, an address or anything to remember...',
      noEvents: 'Nothing planned',
      noEventsBody:
        'This day is still free. Add an event whenever you are ready.',
      signInTitle: 'Your Sky Cloud calendar',
      signInBody:
        'Sign in to Sky Cloud in Settings to sync appointments and receive reminders.',
      reminderNotification: 'Upcoming: {title}',
      views: {
        compact: 'Compact',
        stacked: 'Stacked',
        details: 'Details',
        list: 'List',
      },
      reminders: {
        none: 'No reminder',
        atStart: 'At start time',
        tenMinutes: '10 minutes before',
        thirtyMinutes: '30 minutes before',
        oneHour: '1 hour before',
        oneDay: '1 day before',
      },
      errors: {
        invalid_event:
          'Enter a title and make sure the end is after the start.',
        invalid_range: 'This calendar period is invalid.',
        conflict: 'This event changed on another phone. Open it again.',
        rate_limited: 'Too many changes. Try again shortly.',
        not_authenticated: 'Sign in to your Sky Cloud account first.',
        request_failed: 'Calendar is temporarily unavailable.',
        default: 'The calendar request failed.',
      },
    },
    clock: {
      name: 'Clock',
      lap: 'Lap',
      minutes: 'Minutes',
      add: 'Add alarm',
      location: 'Los Santos',
      tabs: {
        world: 'Clock',
        alarm: 'Alarm',
        stopwatch: 'Stopwatch',
        timer: 'Timer',
      },
      alarm: {
        add: 'Add Alarm',
        edit: 'Edit Alarm',
        ringing: 'Alarm',
        time: 'Time',
        hours: 'Hours',
        repeat: 'Repeat',
        note: 'Note',
        notePlaceholder: 'Alarm',
        sound: 'Sound',
        delete: 'Delete Alarm',
        never: 'Never',
        everyDay: 'Every Day',
        weekdays: 'Weekdays',
        weekends: 'Weekends',
        days: {
          sunday: 'Sunday',
          monday: 'Monday',
          tuesday: 'Tuesday',
          wednesday: 'Wednesday',
          thursday: 'Thursday',
          friday: 'Friday',
          saturday: 'Saturday',
        },
        daysShort: {
          sunday: 'Sun',
          monday: 'Mon',
          tuesday: 'Tue',
          wednesday: 'Wed',
          thursday: 'Thu',
          friday: 'Fri',
          saturday: 'Sat',
        },
        sounds: {
          radar: 'Radar',
          beacon: 'Beacon',
          chimes: 'Chimes',
          apex: 'Apex',
          aurora: 'Aurora',
          circuit: 'Circuit',
          constellation: 'Constellation',
          daybreak: 'Daybreak',
          uplift: 'Uplift',
        },
      },
      timer: {
        time: 'Timer duration',
        hours: 'Hours',
        hoursShort: 'hr',
        minutes: 'Minutes',
        minutesShort: 'min',
        seconds: 'Seconds',
        secondsShort: 'sec',
        description: 'Description',
        note: 'Note',
        notePlaceholder: 'Timer',
        sound: 'Sound',
        ringing: 'Timer',
      },
    },
    mail: {
      name: 'Mail',
      login: 'Sign In',
      register: 'Create Account',
      registerLink: 'Register',
      loginTitle: 'iFruit Mail',
      loginBody: 'Sign in to use your shared iFruit mailbox.',
      registerBody: 'Choose your new @ifruit.com address.',
      localPart: 'Email address',
      email: 'Email',
      password: 'Password',
      confirmPassword: 'Confirm password',
      accountEyebrow: 'Your Sky Cloud account',
      emailPlaceholder: 'name@ifruit.com',
      passwordPlaceholder: 'Enter password',
      passwordWarning:
        'Use an in-character password. Do not reuse a real-world password.',
      mailboxes: 'Mailboxes',
      editMailboxes: 'Edit',
      newMailbox: 'New Mailbox',
      mailboxName: 'Name',
      mailboxNamePlaceholder: 'Mailbox name',
      mailboxLocation: 'Mailbox Location',
      deleteMailbox: 'Delete {mailbox}',
      deleteMailboxTitle: 'Delete Mailbox?',
      deleteMailboxBody:
        'Messages in {mailbox} will return to their original mailbox.',
      moveToMailbox: 'Move to Mailbox',
      movedToMailbox: 'Moved to {mailbox}.',
      inbox: 'Inbox',
      sent: 'Sent',
      drafts: 'Drafts',
      trash: 'Trash',
      compose: 'New Message',
      recipients: 'To',
      recipientHint: 'Separate up to 10 addresses with commas.',
      recipientPlaceholder: 'name@ifruit.com',
      subject: 'Subject',
      subjectPlaceholder: "What's this about?",
      body: 'Message',
      messagePlaceholder: 'Write a message...',
      search: 'Search mail',
      messages: 'messages',
      toolbar: 'Mail tools',
      filterTitle: 'Filter',
      filteredBy: 'Filtered by',
      filterActiveLabel: 'Filtered by {filter}',
      filterStatus: 'Status',
      filterAddressed: 'Addressed',
      filterDirection: 'Direction',
      filterOther: 'Other',
      filterUnreadLabel: 'Unread',
      filterReadLabel: 'Read',
      filterReceived: 'Received',
      filterSent: 'Sent',
      filterToMe: 'To Me',
      filterFromMe: 'From Me',
      filterToday: 'Today',
      filterMultiple: '{count} Filters',
      select: 'Select',
      selectedCount: '{count} Selected',
      deleteSelected: 'Delete Selected',
      selectionLimit: 'You can select up to {count} messages at once.',
      noMessageContent: 'No message content',
      noMail: 'No Mail',
      noMailBody: 'Messages in this mailbox will appear here.',
      noResults: 'No Results',
      noResultsBody: 'Try a different search.',
      untitled: '(no subject)',
      loadMore: 'Load More',
      logout: 'Sign Out',
      from: 'From',
      to: 'To',
      reply: 'Reply',
      replyAll: 'Reply All',
      forward: 'Forward',
      formatBold: 'Bold',
      formatItalic: 'Italic',
      formatBulletList: 'Bullet list',
      formatNumberedList: 'Numbered list',
      formatQuote: 'Quote',
      undo: 'Undo',
      redo: 'Redo',
      markRead: 'Read',
      markUnread: 'Mark as Unread',
      delete: 'Delete',
      moveToTrash: 'Move to Trash',
      restore: 'Restore',
      deleteForever: 'Delete Forever',
      emptyTrash: 'Empty Trash',
      emptyTrashTitle: 'Empty Trash?',
      emptyTrashBody: 'Every message in Trash will be permanently deleted.',
      deleteDraft: 'Delete Draft',
      sentSuccess: 'Message sent.',
      newMessage: 'New mail from {sender}',
      passwordsMismatch: 'Passwords do not match.',
      errors: {
        invalid_email: 'Choose a valid 3–32 character iFruit address.',
        invalid_password: 'Password must be 6–64 characters.',
        invalid_credentials: 'Email or password is incorrect.',
        email_taken: 'That iFruit address is already registered.',
        rate_limited: 'Too many attempts. Try again in a minute.',
        invalid_message: 'Add a valid recipient and a subject or message.',
        recipient_not_found: 'One or more recipients do not exist.',
        invalid_draft: 'This draft contains invalid content.',
        not_authenticated: 'Your mail session has ended. Sign in again.',
        request_failed: 'Mail is temporarily unavailable.',
        invalid_request: 'The mail request was invalid.',
        invalid_mailbox: 'Enter a mailbox name with up to 50 characters.',
        mailbox_exists: 'A mailbox with that name already exists.',
        mailbox_limit: 'You have reached the mailbox limit.',
        mailbox_not_found: 'That mailbox is no longer available.',
        message_not_found: 'That message is no longer available.',
        invalid_filter: 'That mail filter is not available.',
        default: 'The mail request failed.',
      },
    },
    music: {
      name: 'Music',
      loading: 'Loading Music...',
      navigation: 'Music navigation',
      tabs: { library: 'Library', playlists: 'Playlists', search: 'Search' },
      librarySubtitle: 'Your music, all in one place.',
      playlistsSubtitle: 'Mix server tracks and your YouTube favorites.',
      featured: 'Made for the city',
      recentlyAdded: 'Recently Added',
      songs: 'Songs',
      play: 'Play',
      previous: 'Previous track',
      next: 'Next track',
      nowPlaying: 'Now Playing',
      progress: 'Playback position',
      volume: 'Music volume',
      addMusic: 'Add music',
      addYouTube: 'Add from YouTube',
      youtubeBody:
        'Paste a public YouTube link. Leave the optional fields empty to use the video title and channel automatically.',
      youtubeUrl: 'YouTube URL',
      youtubePlaceholder: 'https://youtube.com/watch?v=...',
      youtubeTitle: 'Song title (optional)',
      youtubeTitlePlaceholder: 'Use the YouTube video title',
      youtubeArtist: 'Artist (optional)',
      youtubeArtistPlaceholder: 'Use the YouTube channel',
      addToLibrary: 'Add to Library',
      songAdded: 'Song added to your library.',
      songRemoved: 'Song removed from your library.',
      newPlaylist: 'New Playlist',
      renamePlaylist: 'Rename Playlist',
      addSongs: 'Add Songs',
      addSongsBody: 'Choose songs from your library for this playlist.',
      allSongsAdded: 'All Songs Added',
      allSongsAddedBody:
        'Every song in your library is already in this playlist.',
      playlistBody: 'Give this playlist a name you will recognize.',
      playlistName: 'Playlist Name',
      playlistPlaceholder: 'My Playlist',
      playlistCreated: 'Playlist created.',
      playlistCreatedWithSong: 'Playlist created and song added.',
      playlistRenamed: 'Playlist renamed.',
      playlistDeleted: 'Playlist deleted.',
      playlistActions: 'Playlist actions',
      deletePlaylist: 'Delete Playlist',
      deletePlaylistTitle: 'Delete Playlist?',
      deletePlaylistBody:
        'The playlist will be deleted. Songs stay in your library.',
      choosePlaylist: 'Choose a Playlist',
      addToPlaylist: 'Add to Playlist',
      addedToPlaylist: 'Added to playlist.',
      alreadyAdded: 'Added',
      removeFromPlaylist: 'Remove from Playlist',
      removedFromPlaylist: 'Removed from playlist.',
      createPlaylistFirst: 'Create a playlist before adding this song.',
      songActions: 'Song actions',
      removeFromLibrary: 'Remove from Library',
      removeSongTitle: 'Remove Song?',
      removeSongBody:
        'This YouTube song will also be removed from your playlists.',
      songCount: '{count} songs',
      emptyLibrary: 'Your Library is Empty',
      emptyLibraryBody:
        'Add a YouTube song or ask the server owner to publish MP3 and OGG tracks.',
      emptyPlaylist: 'No Songs Yet',
      emptyPlaylistBody: 'Add songs from your library to this playlist.',
      noPlaylists: 'No Playlists',
      noPlaylistsBody: 'Create a playlist for your favorite songs.',
      searchPlaceholder: 'Artists, Songs and Playlists',
      noResults: 'No Results',
      noResultsBody: 'Try another song title or artist.',
      errors: {
        invalid_youtube_url: 'Paste a valid public YouTube video link.',
        invalid_song_metadata: 'Use a shorter song title or artist name.',
        song_exists: 'That YouTube song is already in your library.',
        song_limit: 'Your personal song limit has been reached.',
        song_not_found: 'That song is no longer in your library.',
        invalid_playlist: 'Choose a valid playlist name.',
        playlist_limit: 'Your playlist limit has been reached.',
        playlist_not_found: 'That playlist no longer exists.',
        playlist_song_limit: 'That playlist has reached its song limit.',
        invalid_song: 'That song cannot be added to this playlist.',
        song_already_in_playlist: 'That song is already in this playlist.',
        rate_limited: 'Too many music changes. Try again shortly.',
        playback_failed: 'This song could not be played.',
        request_failed: 'Music is temporarily unavailable.',
        default: 'The Music request failed.',
      },
    },
    notes: {
      name: 'Notes',
      note: 'Note',
      back: 'Back',
      actions: 'Note actions',
      newNote: 'New Note',
      searchPlaceholder: 'Search Notes',
      title: 'Title',
      titlePlaceholder: 'Note title',
      body: 'Note',
      bodyPlaceholder: 'Start writing...',
      untitled: 'Untitled',
      noText: 'No additional text',
      emptyBadge: 'ON DEVICE',
      emptyTitle: 'No Notes',
      emptyBody: 'Create a note to keep important details close at hand.',
      noResults: 'No Results',
      noResultsBody: 'Try searching for a different word or phrase.',
      pin: 'Pin note',
      unpin: 'Unpin note',
      deleteNote: 'Delete note',
      deleteTitle: 'Delete Note?',
      deleteBody: 'This note will be permanently deleted.',
      tools: {
        bold: 'Bold',
        bulletList: 'Bullet list',
        decreaseText: 'Smaller text',
        increaseText: 'Larger text',
        italic: 'Italic',
        numberedList: 'Numbered list',
        quote: 'Quote',
        redo: 'Redo',
        strike: 'Strikethrough',
        toolbar: 'Formatting tools',
        underline: 'Underline',
        undo: 'Undo',
      },
    },
    memos: {
      name: 'Memos',
      memo: 'Voice Memo',
      back: 'Back',
      actions: 'Memo actions',
      newMemo: 'New Recording',
      newMemoWithDate: 'New Recording · {date}',
      searchPlaceholder: 'Search Recordings',
      emptyTitle: 'No Recordings',
      emptyBody: 'Tap the record button to capture your first voice memo.',
      noResults: 'No Results',
      noResultsBody: 'Try searching for a different title or note.',
      recording: 'Recording',
      paused: 'Paused',
      preparing: 'Preparing microphone…',
      saving: 'Saving Recording…',
      stop: 'Stop',
      pause: 'Pause',
      resume: 'Resume',
      cancel: 'Cancel',
      done: 'Done',
      play: 'Play recording',
      pausePlayback: 'Pause recording',
      skipBack: 'Back 15 seconds',
      skipForward: 'Forward 15 seconds',
      playbackSpeed: 'Playback Speed',
      title: 'Title',
      titlePlaceholder: 'Recording title',
      note: 'Note',
      notePlaceholder: 'Add details…',
      delete: 'Delete memo',
      deleteTitle: 'Delete Voice Memo?',
      deleteBody: 'This recording will be permanently deleted.',
      discardTitle: 'Discard Recording?',
      discardBody: 'The current recording will not be saved.',
      discard: 'Discard',
      keepRecording: 'Keep Recording',
      deleted: 'Memo deleted.',
      microphoneUnavailable: 'The microphone is unavailable.',
      recordingTooLarge: 'The recording is too large to save.',
      maximumDuration: 'The maximum recording duration was reached.',
      errors: {
        conflict: 'This memo changed on another device. It has been reloaded.',
        invalid_memo: 'The voice memo contains invalid data.',
        invalid_request: 'The memo request is invalid.',
        invalid_upload: 'The uploaded recording could not be verified.',
        invalid_upload_token: 'The recording upload could not be verified.',
        memo_limit: 'The limit of 100 voice memos has been reached.',
        memo_not_found: 'This voice memo no longer exists.',
        media_provider_failed: 'The voice memo upload service is unavailable.',
        media_provider_rate_limited:
          'The voice memo upload service is busy. Try again shortly.',
        media_provider_unauthorized:
          'The configured FiveManage API key was rejected.',
        missing_config: 'Voice memo uploads are not configured.',
        operation_in_progress: 'Finish the current recording first.',
        owner_changed: 'The active phone changed while saving the memo.',
        rate_limited: 'Too many memo changes. Try again shortly.',
        recording_failed: 'The recording could not be captured.',
        request_timeout: 'The memo request timed out.',
        upload_failed: 'The recording could not be uploaded.',
        upload_timeout: 'The recording upload timed out.',
        request_failed: 'Memos are temporarily unavailable.',
      },
    },
    photos: {
      name: 'Photos',
      library: 'Library',
      counts: {
        all: '{count} Objects',
        allOne: '{count} Object',
        favorite: '{count} Favorite',
        favorites: '{count} Favorites',
        photo: '{count} Photo',
        photos: '{count} Photos',
        video: '{count} Video',
        videos: '{count} Videos',
      },
      sorting: {
        action: 'Sort Photos',
        title: 'Sort Order',
        show: 'Show',
        allItems: 'All Items',
        favorites: 'Favorites',
        newestFirst: 'Newest First',
        oldestFirst: 'Oldest First',
      },
      selection: {
        action: 'Select',
        selected: '{count} Selected',
        share: 'Share Selected',
        shareTitle: '{count} Media Items',
        shareCopy: '{count} photos and videos shared from Photos.',
        delete: 'Delete Selected',
        deleteTitle: 'Delete Selected Media?',
        deleteBody:
          'The selected photos and videos will be permanently deleted.',
        deleted: '{count} media items deleted.',
        limit: 'You can select up to 50 media items.',
      },
      loading: 'Loading Photos...',
      emptyTitle: 'No Photos or Videos',
      emptyBody: 'Captures from Camera will appear here.',
      photo: 'Photo',
      video: 'Video',
      photoAlt: 'Photo library image',
      videoAlt: 'Photo library video',
      today: 'Today',
      yesterday: 'Yesterday',
      addFavorite: 'Add to Favorites',
      removeFavorite: 'Remove from Favorites',
      delete: 'Delete media',
      deleteTitle: 'Delete Media?',
      deleteBody: 'This photo or video will be permanently deleted.',
      deleted: 'Media deleted.',
      zoomIn: 'Zoom in',
      zoomOut: 'Zoom out',
      resetZoom: 'Reset zoom',
      filters: { all: 'All', photos: 'Photos', videos: 'Videos' },
      import: {
        action: 'Import',
        title: 'Import',
        chooseSource: 'Choose a website',
        linkTitle: 'Import from Link',
        linkBody: 'Paste a direct link to an image or video from this website.',
        linkLabel: 'Image or video link',
        linkPlaceholder: 'https://...',
        linkCompleted: 'Media imported.',
        loading: 'Loading media...',
        emptyTitle: 'No Media',
        emptyBody: 'This website has no media of this type.',
        loadMore: 'Load More',
        alreadyImported: 'Imported',
        failed: 'Failed',
        completed: 'Imported {imported} media.',
        partial: 'Imported {imported} of {total} media.',
      },
      errors: {
        cancelled: 'The media action was cancelled.',
        capture_failed: 'Unable to capture the game view.',
        invalid_import_request: 'The import request is invalid.',
        invalid_import_media: 'This media item cannot be imported.',
        invalid_media_type: 'The media type is invalid.',
        invalid_upload: 'The upload could not be verified.',
        invalid_upload_token: 'The upload session is no longer valid.',
        missing_config: 'Photos uploads are not configured.',
        import_media_not_allowed: 'This media item is not allowed.',
        import_media_too_large: 'This media item is too large.',
        import_media_unavailable: 'This media item is no longer available.',
        import_provider_failed: 'The website could not load its media.',
        import_provider_unauthorized: 'The website credentials are invalid.',
        import_source_not_found: 'This website is not registered.',
        import_source_unavailable: 'This website is temporarily unavailable.',
        invalid_import_url: 'Enter a valid HTTPS media link.',
        import_url_not_allowed: 'This link is not from the selected website.',
        import_url_unavailable: 'The linked media could not be reached.',
        import_size_unavailable: 'The website did not provide the media size.',
        not_found: 'The media item no longer exists.',
        profile_photo_required:
          'This is the last photo on your Flare profile. Add another profile photo before deleting it.',
        operation_in_progress:
          'Another media operation is already in progress.',
        owner_changed: 'The active phone account changed.',
        rate_limited: 'Too many media actions. Try again shortly.',
        request_failed: 'The Photos request failed.',
        request_timeout: 'The media service timed out.',
        unsupported: 'This media format is not supported.',
        upload_failed: 'The media upload failed.',
        upload_timeout: 'The media upload timed out.',
      },
    },
    settings: {
      name: 'Settings',
      searchPlaceholder: 'Search',
      airplaneMode: 'Airplane Mode',
      streamerMode: 'Streamer Mode',
      wallpaper: 'Wallpaper',
      on: 'On',
      off: 'Off',
      accountName: 'Sky Cloud',
      accountDetail: 'Important Data & Cloud',
      accountLocalDetail: 'Not signed in',
      accountCloudDetail: 'Important data syncs through Sky Cloud',
      accountLoginBody:
        'Sign in to keep your important data safe. Without a Sky Cloud account, it will be lost with the phone.',
      accountInformation: 'Account Information',
      accountStatus: 'Account Status',
      accountStatusValue: 'Active',
      accountStorage: 'Cloud Storage',
      accountStorageValue: 'On Device',
      accountPurchases: 'Media & Purchases',
      accountPurchasesValue: 'Available',
      notifications: 'Notifications',
      sounds: 'Sounds & Haptics',
      general: 'General Settings',
      security: 'Passcode & Security',
      appearance: 'Appearance',
      connectivity: 'Connectivity',
      connections: 'Connections',
      wifi: 'Wi-Fi',
      bluetooth: 'Bluetooth',
      cellular: 'Cellular',
      connectivityDescription:
        'Wi-Fi, Bluetooth, and cellular settings are saved on this phone.',
      focus: 'Focus',
      focusMode: 'Focus',
      focusDescription:
        'Focus silences non-critical notifications while keeping alarms and important alerts available.',
      allowNotifications: 'Allow Notifications',
      notificationSounds: 'Sounds',
      notificationDuration: 'Notification Duration',
      seconds: '{seconds} seconds',
      ringtoneVolume: 'Ringtone Volume',
      notificationVolume: 'Notification Volume',
      ringtone: 'Ringtone',
      notificationSound: 'Notification Sound',
      graphicsMode: 'Graphics Mode',
      performanceMode: 'Performance Mode',
      performanceModeDescription:
        'Blur-free glass simulation for better CEF performance',
      ultimateMode: 'Ultimate Mode',
      ultimateModeDescription:
        'Full blur and glass effects for compatible FiveM clients',
      appearanceMode: 'Appearance Mode',
      automatic: 'Automatic',
      light: 'Light',
      dark: 'Dark',
      phoneScale: 'Phone Scale',
      phoneFrame: 'Phone Frame',
      screenBrightness: 'Screen Brightness',
      development: 'Development',
      skyUiKitchenSink: 'Sky UI Kitchen Sink',
      skyUiKitchenSinkDescription:
        'Interactive catalog of first-party Sky UI components',
      about: 'About',
      deviceName: 'Device Name',
      deviceNameValue: 'Sky Phone',
      softwareVersion: 'Software Version',
      language: 'Language',
      languageValue: 'English',
      localStorage: 'Local Storage',
      localStorageValue: 'On Device',
      deviceInformation: 'Device Information',
      imei: 'IMEI',
      simCard: 'SIM Card',
      simNumber: 'Phone Number',
      simType: 'SIM Type',
      registeredSim: 'Registered',
      anonymousSim: 'Anonymous',
      noSim: 'No SIM',
      ejectSim: 'Eject SIM',
      ejectSimBody: 'Return this SIM card to your inventory?',
      linkedDevices: 'Linked Devices',
      thisDevice: 'This Phone',
      removeDevice: 'Remove Device',
      removeDeviceBody:
        'Enter your Sky Cloud password to remove this device from the account.',
      signOut: 'Sign Out',
      reset: 'Transfer or Reset Phone',
      transferOrReset: 'Transfer or Reset Phone',
      transferOrResetDescription: 'Prepare this phone for a fresh setup',
      resetHeroTitle: 'A clean start, without losing what follows you',
      resetHeroBody:
        'Erase local content and settings from this phone. Information attached to your SIM or stored in supported accounts remains available.',
      erasedFromPhone: 'Erased From This Phone',
      eraseDeviceSettings: 'Passcode, preferences and Home Screen layout',
      eraseLocalContent: 'Photos, notes and content stored only locally',
      eraseLocalApps: 'Downloaded apps and local app sessions',
      keptSafe: 'Kept Safe',
      keepSimData: 'SIM Card & Phone Number',
      keepSimDataBody:
        'Your number and SIM-backed communication remain on the SIM.',
      keepCloudData: 'Account & Cloud Data',
      keepCloudDataBody:
        'Supported information remains in its app or Sky Cloud account.',
      resetSetupAssistant:
        'When erasing is complete, Setup Assistant starts automatically.',
      factoryReset: 'Erase All Content and Settings',
      factoryResetBody:
        'This cannot be undone. Local settings, apps and unsynced content are erased. Your SIM number and account-backed data remain available.',
      factoryResetProgress: 'Erasing Sky Phone',
      factoryResetWarning:
        'Keep this phone open. Your SIM and cloud data are safe.',
      factoryResetSystemProcess: 'Secure system process',
      factoryResetPreparing: 'Preparing secure reset',
      factoryResetPreparingDetail: 'Checking device data',
      factoryResetRemoving: 'Removing personal data',
      factoryResetRemovingDetail: 'Clearing apps and local content',
      factoryResetSecuring: 'Protecting linked services',
      factoryResetSecuringDetail: 'Preserving SIM and cloud data',
      factoryResetFinishing: 'Finishing setup',
      factoryResetFinishingDetail: 'Preparing the welcome screen',
      factoryResetSimSafe: 'SIM protected',
      factoryResetCloudSafe: 'Cloud protected',
      passcode: {
        description:
          'A passcode protects the contents of this phone. It stays with the device when the SIM or Sky Cloud account changes.',
        status: 'Passcode',
        codeLength: 'Code Length',
        sixDigit: '6-Digit Code',
        fourDigit: '4-Digit Code',
        turnOn: 'Turn Passcode On',
        turnOff: 'Turn Passcode Off',
        change: 'Change Passcode',
        enterNew: 'Enter New Passcode',
        confirmNew: 'Verify New Passcode',
        enterCurrent: 'Enter Current Passcode',
        screenSubtitle: 'Use 4 or 6 numbers.',
        incorrect: 'Incorrect passcode.',
        mismatch: 'The passcodes did not match.',
        locked: 'Too many incorrect attempts. Try again later.',
        rateLimited: 'Too many attempts. Please wait.',
        failed: 'The passcode could not be updated.',
        saved: 'Passcode saved.',
        disabled: 'Passcode turned off.',
      },
      accountErrors: {
        invalid_email: 'Choose a valid 3–32 character iFruit address.',
        invalid_password: 'Password must be 6–64 characters.',
        invalid_credentials: 'Email or password is incorrect.',
        email_taken: 'That iFruit address is already registered.',
        rate_limited: 'Too many attempts. Try again in a minute.',
        current_device: 'Sign out instead of removing the current phone.',
        device_not_found: 'That device is no longer linked.',
        default: 'The account request failed.',
      },
      back: 'Settings',
      wallpaperPicker: 'Sky Wallpapers',
      wallpaperFromPhotos: 'Choose from Photos',
      wallpaperFromPhotosDescription: 'Use one of your photos',
      wallpaperFromCamera: 'Take Photo',
      wallpaperFromCameraDescription: 'Create a new wallpaper',
      wallpaperCustomUpload: 'Upload Custom Image',
      wallpaperCustomUploadDescription: 'Use a verified HTTPS image link',
      wallpaperHistory: 'Recent',
      wallpaperCustom: 'Photo wallpaper',
      wallpaperTarget: 'Wallpaper destination',
      wallpaperHomeScreen: 'Home Screen',
      wallpaperLockScreen: 'Lock Screen',
      toggle: {
        airplaneMode: 'Toggle Airplane Mode',
        streamerMode: 'Toggle Streamer Mode',
        focusMode: 'Toggle Focus',
        wifiEnabled: 'Toggle Wi-Fi',
        bluetoothEnabled: 'Toggle Bluetooth',
        cellularEnabled: 'Toggle Cellular Data',
        notifications: 'Toggle notifications for {app}',
        notificationSounds: 'Toggle notification sounds for {app}',
      },
      frames: {
        black: 'Black',
        blue: 'Blue',
        green: 'Green',
        lavender: 'Lavender',
        red: 'Red',
        white: 'White',
        orange: 'Orange',
        yellow: 'Yellow',
        lime: 'Lime',
        teal: 'Teal',
        cyan: 'Cyan',
        purple: 'Purple',
        pink: 'Pink',
        gold: 'Gold',
        rgb: 'RGB',
      },
      ringtones: {
        skyline: 'Skyline',
        horizon: 'Horizon',
        pulse: 'Pulse',
      },
      notificationSoundsList: {
        chime: 'Chime',
        signal: 'Signal',
        soft: 'Soft',
      },
      wallpapers: {
        midnight: 'Midnight',
        aurora: 'Aurora',
        ember: 'Ember',
        ocean: 'Ocean',
        sunrise: 'Sunrise',
        violet: 'Violet',
        forest: 'Forest',
        cobalt: 'Cobalt',
        rose: 'Rose',
        sand: 'Sand',
        graphite: 'Graphite',
        prism: 'Prism',
      },
    },
  },
  ControlCenter: {
    airplaneMode: 'Airplane Mode',
    bluetooth: 'Bluetooth',
    brightness: 'Brightness',
    calculator: 'Calculator',
    camera: 'Camera',
    cellular: 'Cellular Data',
    close: 'Close Control Center',
    easyShareContact: 'Share my contact with nearby players',
    flashlight: 'Flashlight',
    focus: 'Focus',
    label: 'Control Center',
    media: 'Media',
    muteRingtone: 'Mute ringtone and notifications',
    next: 'Next track',
    notPlaying: 'Not Playing',
    open: 'Open Control Center',
    play: 'Play',
    previous: 'Previous track',
    quickActions: 'Quick actions',
    timer: 'Timer',
    unmuteRingtone: 'Unmute ringtone and notifications',
    volume: 'Volume',
    wifi: 'Wi-Fi',
  },
  Setup: {
    title: 'Sky Phone Setup Assistant',
    ownerFallback: 'Sky Phone Owner',
    getStarted: 'Get Started',
    setUpLater: 'Set Up Later',
    development: { skip: 'Skip setup' },
    welcome: {
      title: 'Welcome to Sky Phone',
      eyebrow: 'Designed around you',
      hello: 'hello',
      hallo: 'hallo',
      bonjour: 'bonjour',
      body: 'Hello, {name}. Let’s make this phone unmistakably yours.',
      private: 'Private by design',
      personal: 'Made for you',
    },
    connection: {
      eyebrow: 'Mobile Connection',
      title: 'Your connection is ready',
      body: 'Sky Phone automatically detects the SIM installed in this device.',
      noSim: 'No SIM installed',
      ready: 'Connected to the Sky network',
      offline: 'Phone works offline; calls and messages require a SIM',
      preserved:
        'Your SIM number and SIM-backed conversations remain with the SIM, even if this phone is erased.',
    },
    cloud: {
      eyebrow: 'Sky Cloud',
      title: 'Keep important data with you',
      body: 'Sign in to sync supported app data and recover it on another Sky Phone.',
      signIn: 'Sign In',
      create: 'Create Account',
      signInTitle: 'Sign in to Sky Cloud',
      createTitle: 'Create your Sky Cloud account',
      signInBody:
        'Access your protected data, purchases and settings on this phone.',
      createBody:
        'Choose your personal iFruit address and keep important data available across devices.',
      accountPreview: 'Your Sky Cloud ID',
      accountName: 'Account name',
      addressPlaceholder: 'alex.morgan',
      email: 'Sky Cloud Email',
      password: 'Password',
      confirmPassword: 'Confirm Password',
      passwordMismatch: 'The passwords do not match.',
      strength0: 'At least 6 characters',
      strength1: 'Basic password',
      strength2: 'Good password',
      strength3: 'Strong password',
      strength4: 'Excellent password',
      signInAction: 'Sign In Securely',
      createAction: 'Create Sky Cloud Account',
      securityNote: 'Encrypted connection · Your password stays private',
      connected: 'Sky Cloud connected',
      invalid: 'Enter a valid email and a password with at least 6 characters.',
      errors: {
        invalid_email: 'Enter a valid Sky Cloud email.',
        invalid_password: 'Your password must contain at least 6 characters.',
        invalid_credentials: 'Email or password is incorrect.',
        email_taken: 'This Sky Cloud email is already registered.',
        rate_limited: 'Too many attempts. Try again shortly.',
        request_failed: 'Sky Cloud is temporarily unavailable.',
      },
    },
    security: {
      eyebrow: 'Privacy & Security',
      title: 'Protect this phone',
      body: 'Choose a four- or six-digit passcode to keep your apps and local information private.',
      local:
        'The passcode belongs to this physical phone and is never transferred with the SIM.',
      create: 'Create Passcode',
      createSelected: 'Create {count}-Digit Passcode',
      lengthTitle: 'Choose passcode length',
      fourDigit: '4-Digit Code',
      sixDigit: '6-Digit Code',
      enter: 'Create a Passcode',
      confirm: 'Verify Your Passcode',
      codeHint: 'Enter a memorable six-digit code.',
      fourDigitHint: 'Enter a memorable four-digit code.',
      sixDigitHint: 'Enter a memorable six-digit code.',
      mismatch: 'The passcodes did not match. Try again.',
      failed: 'The passcode could not be saved.',
    },
    appearance: {
      eyebrow: 'Display',
      title: 'Choose your appearance',
      body: 'Automatic follows your system. You can change this at any time in Settings.',
    },
    performance: {
      eyebrow: 'Performance',
      title: 'Choose how your phone feels',
      body: 'Balance responsiveness and visual effects for your FiveM setup.',
      performance: 'Fastest response with optimized, blur-free glass.',
      ultimate: 'Rich depth, live blur and the complete glass experience.',
      changeLater: 'You can switch modes later under Settings › Appearance.',
    },
    wallpaper: {
      eyebrow: 'Personalize',
      title: 'Make it yours',
      body: 'Choose one of twelve crafted Sky Phone backgrounds.',
    },
    notifications: {
      eyebrow: 'Stay Informed',
      title: 'Notifications your way',
      body: 'Choose how apps may reach you. Critical alarms and calls stay available.',
      allow: 'Allow App Notifications',
      allowBody: 'Show banners, lock-screen updates and badges.',
      sounds: 'Notification Sounds',
      soundsBody: 'Play a subtle sound for incoming updates.',
    },
    apps: {
      eyebrow: 'Suggested for You',
      title: 'Start with your favorites',
      body: 'Choose optional apps. Every essential phone app is already included.',
      install: 'Add {count} Apps',
      descriptions: {
        banking: 'Secure city banking',
        garage: 'Your vehicles at a glance',
        skyride: 'Request a ride',
        citymarkt: 'Shop local listings',
        picstagram: 'Share photos',
        snake: 'A timeless game',
      },
    },
    ready: {
      eyebrow: 'Setup Complete',
      title: 'Welcome, {name}',
      body: 'Your Sky Phone is configured and ready. Your choices can always be refined in Settings.',
      localOnly: 'Stored on this phone',
      enter: 'Enter Sky Phone',
      review: 'Review Setup',
      saveFailed: 'Setup could not be saved. Try again.',
      saving: 'Saving setup',
    },
  },
  Common: {
    add: 'Add',
    cancel: 'Cancel',
    clear: 'Clear',
    close: 'Close',
    continue: 'Continue',
    back: 'Back',
    delete: 'Delete',
    done: 'Done',
    edit: 'Edit',
    home: 'Home',
    pause: 'Pause',
    phone: 'Phone',
    phoneStatus: 'Phone status',
    reset: 'Reset',
    loading: 'Loading',
    search: 'Search',
    save: 'Save',
    send: 'Send',
    start: 'Start',
    stop: 'Stop',
    signOut: 'Sign Out',
    signingOut: 'Signing Out...',
    signOutTitle: 'Sign out of {app}?',
    signOutBody:
      'You will only be signed out of {app}. Your other iFruit apps stay signed in.',
    signOutFailed: 'Could not sign out. Please try again.',
    appAuth: {
      eyebrow: 'iFruit account',
      title: 'Continue to {app}',
      body: 'Use your iFruit email and password. This login applies only to {app}.',
      login: 'Login',
      register: 'Register',
      email: 'iFruit email',
      password: 'Password',
      confirm: 'Confirm password',
      loginAction: 'Log in',
      registerAction: 'Create account',
      errors: {
        invalid_email: 'Enter a valid iFruit email.',
        invalid_password: 'Password must be 6–64 characters.',
        invalid_credentials: 'Email or password is incorrect.',
        email_taken: 'That iFruit email is already registered.',
        rate_limited: 'Too many attempts. Try again in a minute.',
        default: 'The account request failed.',
      },
    },
    use: 'Use',
  },
  Notifications: {
    clearAll: 'Clear All',
    clear: 'Clear',
    now: 'now',
    open: 'Open notification',
  },
  LockScreen: {
    label: 'Lock Screen',
    flashlight: 'Flashlight',
    camera: 'Camera',
    swipeUp: 'Swipe up to open',
    passcode: {
      enter: 'Enter Passcode',
      unlockSubtitle: 'Enter the passcode for this phone.',
      cancel: 'Cancel',
      delete: 'Delete digit',
      incorrect: 'Incorrect passcode',
      locked: 'Too many attempts. Try again in {seconds} seconds.',
      tryAgain: 'Try again in {seconds} seconds',
      rateLimited: 'Too many attempts. Please wait.',
    },
  },
  HardwareButtons: {
    lock: 'Lock phone',
    mute: 'Mute ringtone and notifications',
    unlock: 'Unlock phone',
    unmute: 'Unmute ringtone and notifications',
    volumeDown: 'Volume down',
    volumeLevel: 'Volume {value} percent',
    volumeUp: 'Volume up',
  },
  Home: {
    appLibrary: 'App Library',
    appLibrarySearch: 'Search apps',
    allApps: 'All Apps',
    apps: 'Apps',
    dock: 'Dock',
    noApps: 'No apps found',
    removeApp: 'Remove {app} from Home Screen',
    addToHome: 'Add {app} to Home Screen',
    addPage: 'Add Home Screen page',
    deletePage: 'Delete current Home Screen page',
    removedFromHome: 'Removed from Home Screen',
    page: 'Page',
    pages: 'Home screen pages',
    folders: {
      defaultName: 'Folder',
      close: 'Close folder',
      rename: 'Rename Folder',
      name: 'Folder name',
      pages: 'Folder pages',
    },
    groups: {
      suggestions: 'Suggestions',
      recentlyAdded: 'Recently Added',
      games: 'Games',
      productivity: 'Productivity',
      shopping: 'Shopping',
      social: 'Social Networks',
      utilities: 'Utilities',
    },
    widgets: {
      label: 'Widgets',
      weather: { city: 'Los Santos', condition: 'Partly Cloudy' },
      calendar: { event: 'No more events today' },
      battery: { label: 'Phone' },
      media: {
        title: 'Night Drive',
        artist: 'Sky Radio',
        play: 'Play',
        pause: 'Pause',
      },
    },
    widgetSystem: {
      galleryTitle: 'Widgets',
      search: 'Search Widgets',
      size: 'Size',
      add: 'Add Widget',
      addWidget: 'Add Widget',
      editWidget: 'Edit Widget',
      removeWidget: 'Remove Widget',
      remove: 'Remove widget',
      configure: 'Configure Widget',
      noResults: 'No widgets found',
      sizes: { small: 'Small', medium: 'Medium', large: 'Large' },
      categories: {
        essentials: 'Essentials',
        information: 'Information',
        media: 'Media',
        finance: 'Finance',
        people: 'People',
      },
      clock: {
        name: 'Clock',
        description: 'The current time, with an optional date.',
        showDate: 'Show Date',
      },
      date: {
        name: 'Date',
        description: 'Weekday, month, and date at a glance.',
      },
      weather: {
        name: 'Weather',
        description: 'Current conditions, location, and high and low.',
        range: 'H: {high}° L: {low}°',
      },
      music: {
        name: 'Now Playing',
        description: 'Music controls and the current track.',
        empty: 'No recently played music',
      },
      wallet: {
        name: 'Wallet',
        description: 'Your current bank or cash balance.',
        balance: 'Displayed Balance',
        bank: 'Bank',
        cash: 'Cash',
      },
      transactions: {
        name: 'Transactions',
        description: 'Your latest incoming and outgoing payments.',
      },
      contacts: {
        name: 'Favorites',
        description: 'Call or message your favorite contacts.',
        choose: 'Favorite Contacts',
      },
    },
  },
}

function getByPath(source: LocaleTree, path: string): unknown {
  return path.split('.').reduce<unknown>((current, segment) => {
    if (
      typeof current !== 'object' ||
      current === null ||
      Array.isArray(current)
    )
      return undefined
    return (current as LocaleTree)[segment]
  }, source)
}

export const usePhoneStore = defineStore('phone', {
  state: () => ({
    cameraLandscape: false,
    currentPage: 1,
    device: null as PhoneDevice | null,
    deviceRevisions: {} as Record<string, number>,
    deviceSessionToken: null as string | null,
    isOpen: false,
    lang: 'en',
    launchOrigin: null as AppLaunchOrigin | null,
    fallbackLocales: defaultLocales,
    locales: defaultLocales,
    preferences: cloneJsonData(DEFAULT_PHONE_PREFERENCES),
    persistenceGeneration: 0,
    persistenceSession: ++nextPersistenceSession,
    player: {
      firstName: '',
      lastName: '',
    } as DeviceBootstrap['player'],
    security: {
      enabled: false,
      length: null,
      lockedUntil: 0,
    } as DeviceSecurity,
    systemDarkMode: window.matchMedia('(prefers-color-scheme: dark)').matches,
  }),
  getters: {
    isDarkMode(state): boolean {
      if (state.preferences.settings.appearanceMode === 'dark') return true
      if (state.preferences.settings.appearanceMode === 'light') return false
      return state.systemDarkMode
    },
  },
  actions: {
    close(): void {
      this.cameraLandscape = false
      this.isOpen = false
    },
    setLocale(
      lang: string,
      locales: LocaleTree,
      fallbackLocales: LocaleTree,
    ): void {
      this.lang = lang
      this.locales = locales
      this.fallbackLocales = fallbackLocales
    },
    open(payload: PhoneOpenPayload = {}): void {
      const nextImei = payload.device?.imei ?? this.device?.imei ?? null
      const nextToken = payload.token ?? this.deviceSessionToken
      if (
        nextImei !== (this.device?.imei ?? null) ||
        nextToken !== this.deviceSessionToken
      ) {
        this.persistenceGeneration += 1
      }
      this.deviceSessionToken = nextToken
      this.lang = payload.lang ?? 'en'
      this.fallbackLocales = payload.fallbackLocales ?? defaultLocales
      this.locales = payload.locales ?? this.fallbackLocales
      if (payload.device) this.hydrateDevice(payload.device)
      if (payload.player) this.player = payload.player
      this.security = payload.security ?? {
        enabled: false,
        length: null,
        lockedUntil: 0,
      }
      this.isOpen = true
    },
    endDeviceSession(): void {
      this.close()
      this.player = { firstName: '', lastName: '' }
      if (this.deviceSessionToken !== null) {
        this.deviceSessionToken = null
        this.persistenceGeneration += 1
      }
    },
    hydrateDevice(device: PhoneDevice): void {
      this.device = device
      this.deviceRevisions = Object.fromEntries(
        Object.entries(device.data).map(([key, value]) => [
          key,
          value?.revision ?? 0,
        ]),
      )
      this.preferences = parsePhonePreferences(
        JSON.stringify(device.data.settings?.payload ?? null),
      )
    },
    saveDeviceNamespace(namespace: string, payload: unknown): Promise<boolean> {
      const imei = this.device?.imei
      if (!imei) {
        console.error(
          `[Phone persistence] Could not save ${namespace} without an active device.`,
        )
        return Promise.resolve(false)
      }
      const generation = this.persistenceGeneration
      const session = this.persistenceSession
      const token = this.deviceSessionToken
      const queuedPayload = cloneJsonData(payload)
      const queueKey = `${session}:${generation}:${imei}:${namespace}`
      const isCurrentScope = (): boolean =>
        this.persistenceSession === session &&
        this.persistenceGeneration === generation &&
        this.device?.imei === imei &&
        this.deviceSessionToken === token
      const previous = namespaceQueues.get(queueKey) ?? Promise.resolve()
      const queued = previous.then(async () => {
        if (!isCurrentScope()) return false
        const response = await nuiCall<{ revision: number }>('device:save', {
          imei,
          namespace,
          payload: queuedPayload,
          revision: this.deviceRevisions[namespace] ?? 0,
          sessionToken: token,
        })
        if (
          isCurrentScope() &&
          response.success &&
          Number.isInteger(response.data?.revision) &&
          Number(response.data?.revision) >= 0
        ) {
          this.deviceRevisions[namespace] = Number(response.data?.revision)
          return true
        }
        if (isCurrentScope()) {
          console.error(
            `[Phone persistence] Could not save ${namespace}: ${response.error ?? 'request_failed'}`,
          )
        }
        return false
      })
      const tracked = queued.finally(() => {
        if (namespaceQueues.get(queueKey) === tracked)
          namespaceQueues.delete(queueKey)
      })
      namespaceQueues.set(queueKey, tracked)
      return tracked
    },
    async flushDevicePersistence(): Promise<void> {
      const imei = this.device?.imei
      if (!imei) return
      const queuePrefix = `${this.persistenceSession}:${this.persistenceGeneration}:${imei}:`
      while (true) {
        const activeQueues = [...namespaceQueues.entries()]
          .filter(([key]) => key.startsWith(queuePrefix))
          .map(([, queue]) => queue)
        if (!activeQueues.length) return
        await Promise.all(activeQueues)
      }
    },
    setCurrentPage(page: number, pageCount?: number): void {
      this.currentPage = clampPage(page, pageCount)
    },
    setCameraLandscape(landscape: boolean): void {
      this.cameraLandscape = landscape
    },
    setLaunchOrigin(origin: AppLaunchOrigin | null): void {
      this.launchOrigin = origin
    },
    ensureAppNotificationPreferences(appIds: LaunchablePhoneAppId[]): void {
      ensureAppNotificationPreferences(this.preferences, appIds)
    },
    setAppNotification(
      appId: LaunchablePhoneAppId,
      key: keyof AppNotificationPreferences,
      value: boolean,
    ): void {
      this.preferences.settings.notifications[appId][key] = value
      this.saveDeviceNamespace('settings', this.preferences)
    },
    setPreference<K extends keyof PhonePreferencesV1['settings']>(
      key: K,
      value: PhonePreferencesV1['settings'][K],
    ): void {
      this.preferences.settings[key] = (
        key === 'phoneScale' ? clampPhoneScale(Number(value)) : value
      ) as PhonePreferencesV1['settings'][K]
      this.saveDeviceNamespace('settings', this.preferences)
    },
    setAlertVolumes(value: number): void {
      const volume = Math.min(100, Math.max(0, Math.round(value)))
      this.preferences.settings.notificationVolume = volume
      this.preferences.settings.ringtoneVolume = volume
      this.saveDeviceNamespace('settings', this.preferences)
    },
    setAllAppNotifications(enabled: boolean, sounds: boolean): void {
      for (const preferences of Object.values(
        this.preferences.settings.notifications,
      )) {
        preferences.enabled = enabled
        preferences.sounds = enabled && sounds
      }
      this.saveDeviceNamespace('settings', this.preferences)
    },
    setSetupStep(step: number): void {
      this.preferences.settings.setupStep = Math.min(
        PHONE_SETUP_LAST_STEP,
        Math.max(0, Math.floor(step)),
      )
      this.saveDeviceNamespace('settings', this.preferences)
    },
    async completeSetup(): Promise<boolean> {
      const completedPreferences = cloneJsonData(this.preferences)
      completedPreferences.settings.setupCompleted = true
      completedPreferences.settings.setupStep = PHONE_SETUP_LAST_STEP
      const saved = await this.saveDeviceNamespace(
        'settings',
        completedPreferences,
      )
      if (saved) this.preferences = completedPreferences
      return saved
    },
    resetAfterFactoryReset(): void {
      this.persistenceGeneration += 1
      this.preferences = cloneJsonData(DEFAULT_PHONE_PREFERENCES)
      this.deviceRevisions = {}
      if (this.device) this.device.data = {}
      this.security = { enabled: false, length: null, lockedUntil: 0 }
    },
    setSystemDarkMode(value: boolean): void {
      this.systemDarkMode = value
    },
    setWallpaper(
      wallpaper: WallpaperId,
      imageUrl: string | null = null,
      target: WallpaperTarget | 'both' = 'home',
    ): void {
      const normalizedImageUrl =
        wallpaper === 'custom' ? imageUrl?.trim() : null
      if (wallpaper === 'custom' && !normalizedImageUrl) {
        throw new Error('Custom wallpapers require a photo URL.')
      }

      if (target === 'home' || target === 'both') {
        this.preferences.settings.wallpaper = wallpaper
        this.preferences.settings.wallpaperImageUrl = normalizedImageUrl ?? null
      }
      if (target === 'lock' || target === 'both') {
        this.preferences.settings.lockWallpaper = wallpaper
        this.preferences.settings.lockWallpaperImageUrl =
          normalizedImageUrl ?? null
      }
      this.preferences.settings.wallpaperHistory = [
        { imageUrl: normalizedImageUrl ?? null, wallpaper },
        ...this.preferences.settings.wallpaperHistory.filter((entry) =>
          wallpaper === 'custom'
            ? entry.wallpaper !== 'custom' ||
              entry.imageUrl !== normalizedImageUrl
            : entry.wallpaper !== wallpaper,
        ),
      ].slice(0, 4)
      this.saveDeviceNamespace('settings', this.preferences)
    },
    async unlockWithPasscode(
      passcode: string,
    ): Promise<NuiResponse<PasscodeResponseData>> {
      const response = await nuiCall<PasscodeResponseData>('security:unlock', {
        passcode,
      })
      if (response.success && response.data?.security) {
        this.security = response.data.security
      }
      return response
    },
    async setPasscode(
      passcode: string,
    ): Promise<NuiResponse<PasscodeResponseData>> {
      const response = await nuiCall<PasscodeResponseData>(
        'security:set-passcode',
        { passcode },
      )
      if (response.success && response.data?.security) {
        this.security = response.data.security
      }
      return response
    },
    async changePasscode(
      currentPasscode: string,
      newPasscode: string,
    ): Promise<NuiResponse<PasscodeResponseData>> {
      const response = await nuiCall<PasscodeResponseData>(
        'security:change-passcode',
        { currentPasscode, newPasscode },
      )
      if (response.success && response.data?.security) {
        this.security = response.data.security
      }
      return response
    },
    async disablePasscode(
      passcode: string,
    ): Promise<NuiResponse<PasscodeResponseData>> {
      const response = await nuiCall<PasscodeResponseData>(
        'security:disable-passcode',
        { passcode },
      )
      if (response.success && response.data?.security) {
        this.security = response.data.security
      }
      return response
    },
    t(path: string, replacements: Record<string, string> = {}): string {
      const translated = getByPath(this.locales, path)
      const english = getByPath(this.fallbackLocales, path)
      const emergencyFallback = getByPath(defaultLocales, path)
      const value =
        typeof translated === 'string'
          ? translated
          : typeof english === 'string'
            ? english
            : typeof emergencyFallback === 'string'
              ? emergencyFallback
              : path
      return Object.entries(replacements).reduce(
        (result, [key, replacement]) =>
          result.split(`{${key}}`).join(replacement),
        value,
      )
    },
  },
})
