## 1. Project Overview & Quickstart (cjpais/Handy)

## File: README.md

# Handy

[](https://discord.com/invite/WVBeWsNXK4)

**A free, open source, and extensible speech-to-text application that works completely offline.**

Handy is a cross-platform desktop application that provides simple, privacy-focused speech transcription. Press a shortcut, speak, and have your words appear in any text field. This happens on your own computer without sending any information to the cloud.

## Why Handy?

Handy was created to fill the gap for a truly open source, extensible speech-to-text tool. As stated on [handy.computer](https://handy.computer):

- **Free**: Accessibility tooling belongs in everyone's hands, not behind a paywall
- **Open Source**: Together we can build further. Extend Handy for yourself and contribute to something bigger
- **Private**: Your voice stays on your computer. Get transcriptions without sending audio to the cloud
- **Simple**: One tool, one job. Transcribe what you say and put it into a text box

## How It Works

1. **Press** a configurable keyboard shortcut: hold it to record and release to stop, or tap it to toggle recording on and off (Hold-only and Toggle-only modes are also available)
2. **Speak** your words while the shortcut is active
3. **Release** and Handy processes your speech using Whisper
4. **Get** your transcribed text pasted directly into whatever app you're using

The process is entirely local:

- Silence is filtered using VAD (Voice Activity Detection) with Silero
- Transcription uses your choice of models:
  - **Whisper models** (Small/Medium/Turbo/Large) with GPU acceleration when available
  - **Parakeet V3** - CPU-optimized model with excellent performance and automatic language detection
- Works on Windows, macOS, and Linux

## Quick Start

### Installation

1. Download the latest release from the [releases page](https://github.com/cjpais/Handy/releases) or the [website](https://handy.computer)
   - **macOS**: Also available via [Homebrew cask](https://formulae.brew.sh/cask/handy): `brew install --cask handy`
   - **Windows**: Also available via [winget](https://github.com/microsoft/winget-pkgs): `winget install cjpais.Handy` \
     **Note:** The Homebrew cask and winget package are not maintained by the Handy developers.
   - **Debian/Ubuntu**: Install the downloaded `.deb` with APT so required dependencies are installed automatically:
     ```bash
     sudo apt install ./Handy_*.deb
     ```
     Do not use `dpkg -i` unless the dependencies are already installed. If you already used it, run `sudo apt --fix-broken install`.
2. Install the application
3. Launch Handy and grant necessary system permissions (microphone, accessibility)
4. Configure your preferred keyboard shortcuts in Settings
5. Start transcribing!

### Development Setup

For detailed build instructions including platform-specific requirements, see [BUILD.md](BUILD.md).

## Sponsors



## Integrations

[](https://www.raycast.com/mattiacolombomc/handy)

Control Handy from [Raycast](https://www.raycast.com) — start/stop recording, browse transcript history, manage dictionary, switch models and languages.

[Source](https://github.com/mattiacolombomc/raycast-handy) · by [@mattiacolombomc](https://github.com/mattiacolombomc)

### Debug Mode

Handy includes an advanced debug mode for development and troubleshooting. Access it by pressing:

- **macOS**: `Cmd+Shift+D`
- **Windows/Linux**: `Ctrl+Shift+D`

### CLI Parameters

Handy supports command-line flags for controlling a running instance and customizing startup behavior. These work on all platforms (macOS, Windows, Linux). Largely this is a beta feature.

**Remote control flags** (sent to an already-running instance via the single-instance plugin):

```bash
handy --toggle-transcription    # Toggle recording on/off
handy --toggle-post-process     # Toggle recording with post-processing on/off
handy --cancel                  # Cancel the current operation
```

**Startup flags:**

```bash
handy --start-hidden            # Start without showing the main window
handy --no-tray                 # Start without the system tray icon
handy --debug                   # Enable debug mode with verbose logging
handy --help                    # Show all available flags
```

Flags can be combined for autostart scenarios:

```bash
handy --start-hidden --no-tray
```

> **macOS tip:** When Handy is installed as an app bundle, invoke the binary directly:
>
> ```bash
> /Applications/Handy.app/Contents/MacOS/Handy --toggle-transcription
> ```

## Known Issues & Current Limitations

This project is actively being developed and has some [known issues](https://github.com/cjpais/Handy/issues). We believe in transparency about the current state:

### Bluetooth Headset Microphones (macOS)

Using a Bluetooth headset microphone on macOS may temporarily reduce playback quality or volume while recording because Bluetooth switches to bidirectional audio. Keep your headphones as the output device and select your Mac's built-in or an external microphone in Handy to avoid this.

### fn and Globe Key Shortcuts (macOS)

Shortcuts that include the `fn` (Globe) key **only work on Apple keyboards** — your Mac's built-in keyboard or an Apple external keyboard. They will never trigger on a third-party keyboard, even while it is connected to the same Mac.

This is a hardware limitation rather than a Handy bug. `fn` is not part of the standard USB HID keyboard specification: Apple reports it through a vendor-specific usage that macOS honors only from Apple devices, while third-party keyboards handle their `Fn` key entirely in firmware and send nothing to the computer. There is no event for Handy to listen for.

If you switch between a MacBook keyboard and an external one, pick a shortcut built from standard modifiers (`ctrl`, `option`, `shift`, `command`) or a regular key instead.

### Linux Notes

**Text Input Tools:**

For reliable text input on Linux, install the appropriate tool for your display server:

| Display Server | Recommended Tool | Install Command                                    |
| -------------- | ---------------- | -------------------------------------------------- |
| X11            | `xdotool`        | `sudo apt install xdotool`                         |
| Wayland        | `wtype`          | `sudo apt install wtype`                           |
| Both           | `dotool`         | `sudo apt install dotool` (requires `input` group) |

- **X11**: Install `xdotool` for both direct typing and clipboard paste shortcuts
- **Ubuntu 26.04**: Has Wayland display server by default. `wtype` does not work, you need to install `ydotool` and configure systemd as described [here](https://github.com/cjpais/Handy/pull/557#issuecomment-3781249267).
- **Wayland**: Install `wtype` (preferred) or `dotool` for text input to work correctly
- **dotool setup**: Requires adding your user to the `input` group: `sudo usermod -aG input $USER` (then log out and back in)

Without these tools, Handy falls back to enigo which may have limited compatibility, especially on Wayland.

**Wayland Support (Linux):**

- Limited support for Wayland display server
- Requires [`wtype`](https://github.com/atx/wtype) or [`dotool`](https://sr.ht/~geb/dotool/) for text input to work correctly (see [Linux Notes](#linux-notes) below for installation)

**Other Notes:**

- **Runtime library dependency (`libgtk-layer-shell.so.0`)**:
  - Handy links `gtk-layer-shell` on Linux. If startup fails with `error while loading shared libraries: libgtk-layer-shell.so.0`, install the runtime package for your distro:

    | Distro        | Package to install    | Example command                        |
    | ------------- | --------------------- | -------------------------------------- |
    | Ubuntu/Debian | `libgtk-layer-shell0` | `sudo apt install libgtk-layer-shell0` |
    | Fedora/RHEL   | `gtk-layer-shell`     | `sudo dnf install gtk-layer-shell`     |
    | Arch Linux    | `gtk-layer-shell`     | `sudo pacman -S gtk-layer-shell`       |

  - For building from source on Ubuntu/Debian, you may also need `libgtk-layer-shell-dev`.

- The recording overlay is disabled by default on Linux (`Overlay Position: None`) because certain compositors treat it as the active window. When the overlay is visible it can steal focus, which prevents Handy from pasting back into the application that triggered transcription. If you enable the overlay anyway, be aware that clipboard-based pasting might fail or end up in the wrong window.
- Handy disables the WebKit DMA-BUF renderer by default on Linux, but preserves explicit `WEBKIT_DISABLE_DMABUF_RENDERER` values. For rendering workarounds, see [Troubleshooting → Linux Startup Crashes or Instability](#linux-startup-crashes-or-instability).
- If Handy fails to start reliably on Linux, see [Troubleshooting → Linux Startup Crashes or Instability](#linux-startup-crashes-or-instability).
- **Global keyboard shortcuts (Wayland):** On Wayland, system-level shortcuts must be configured through your desktop environment or window manager. Use the [CLI flags](#cli-parameters) as the command for your custom shortcut.

  **GNOME:**
  1. Open **Settings > Keyboard > Keyboard Shortcuts > Custom Shortcuts**
  2. Click the **+** button to add a new shortcut
  3. Set the **Name** to `Toggle Handy Transcription`
  4. Set the **Command** to `handy --toggle-transcription`
  5. Click **Set Shortcut** and press your desired key combination (e.g., `Super+O`)

  **KDE Plasma:**
  1. Open **System Settings > Shortcuts > Custom Shortcuts**
  2. Click **Edit > New > Global Shortcut > Command/URL**
  3. Name it `Toggle Handy Transcription`
  4. In the **Trigger** tab, set your desired key combination
  5. In the **Action** tab, set the command to `handy --toggle-transcription`

  **Sway / i3:**

  Add to your config file (`~/.config/sway/config` or `~/.config/i3/config`):

  ```ini
  bindsym $mod+o exec handy --toggle-transcription
  ```

  **Hyprland:**

  Add to your config file (`~/.config/hypr/hyprland.conf`):

  ```ini
  bind = $mainMod, O, exec, handy --toggle-transcription
  ```

- You can also trigger Handy externally via Unix signals or the CLI flags, which lets Wayland window managers or other hotkey daemons keep ownership of keybindings:

  | Action                                    | Trigger                                                  |
  | ----------------------------------------- | -------------------------------------------------------- |
  | Toggle transcription                      | `pkill -USR2 -n handy` or `handy --toggle-transcription` |
  | Toggle transcription with post-processing | `handy --toggle-post-process`                            |

  Example Sway config:

  ```ini
  bindsym $mod+o exec pkill -USR2 -n handy
  bindsym $mod+p exec handy --toggle-post-process
  ```

  `pkill` here simply delivers the signal—it does not terminate the process.

  > **Behavior change:** older releases also accepted `SIGUSR1` for toggling transcription with post-processing. WebKitGTK — the webview engine embedded in Handy on Linux — uses SIGUSR1 internally to coordinate JavaScript garbage collection, so listening for it caused phantom recordings and interrupted dictations every few minutes ([#1660](https://github.com/cjpais/Handy/issues/1660)). Handy no longer listens for SIGUSR1 on Linux; the post-processing toggle is still available via `handy --toggle-post-process`. **Remove any `pkill -USR1` bindings**: the signal is now delivered straight to WebKit's internal handler and can crash the app.

**Overlay & Pasting Issues (Linux):**

- The recording overlay window can interfere with pasting transcribed text into target applications on Linux (X11)
- **Solution:** Open **Settings > Advanced** and set **"Overlay Position"** to **"None"** to disable the overlay
- Enable **"Audio Feedback"** (also in Advanced) if you still want audible confirmation of recording state
- Users who upgrade from older versions or import settings from other platforms may need to manually apply this change

## Verify Release Signatures

Handy release artifacts are signed with Tauri's updater signature format. The public key is stored in [`src-tauri/tauri.conf.json`](src-tauri/tauri.conf.json) under `plugins.updater.pubkey`.

To verify a release manually, set `ARTIFACT` to the filename you downloaded, save the `pubkey` value from `src-tauri/tauri.conf.json` to `handy.pub.b64`, then decode the public key and matching `.sig` file from base64 and verify the artifact with `minisign`:

```bash
# Replace with the file you downloaded
ARTIFACT="Handy_0.8.1_amd64.AppImage"

python3 - "$ARTIFACT" <<'PY'
import base64, pathlib, sys

artifact = sys.argv[1]

pub = pathlib.Path("handy.pub.b64").read_text().strip()
pathlib.Path("handy.pub").write_bytes(base64.b64decode(pub))

sig = pathlib.Path(f"{artifact}.sig").read_text().strip()
pathlib.Path(f"{artifact}.minisig").write_bytes(base64.b64decode(sig))
PY

minisign -Vm "$ARTIFACT" \
  -p handy.pub \
  -x "$ARTIFACT.minisig"
```

On success, `minisign` prints:

```text
Signature and comment signature verified
```

Do not use `gpg` for these `.sig` files.

## Troubleshooting

### Previous Clipboard Content Is Pasted Instead of the Transcription

If the transcription is correct in **History** but Handy inserts text you copied earlier, see [issue #502](https://github.com/cjpais/Handy/issues/502). With the standard clipboard paste method, Handy restores your previous clipboard after a fixed delay. Under load, the receiving application may read the clipboard only after that restoration.

1. Open Handy's settings window and press `Cmd+Shift+D` (macOS) or `Ctrl+Shift+D` (Windows/Linux) to reveal **Debug**.
2. On **macOS and Windows**, try **Reliable Paste (Beta)** in Debug with a clipboard paste method selected. It uses clipboard read notifications to delay restoration instead of relying on the standard fixed delay. Test it in the application where the problem occurs; it is still experimental.
3. If Reliable Paste is disabled or unavailable, increase **Paste Delay (After)** in Debug and test again. This controls the wait before restoring your previous clipboard. **Paste Delay (Before)** controls the wait before sending the paste keystroke and addresses a different part of the operation. These delay settings apply to the standard paste path, not Reliable Paste.

If the problem persists, add your Handy version, operating system, receiving application, paste method, Reliable Paste setting, and before/after delays to the existing issue. Redact private dictated text before sharing logs.

### Manual Model Installation (For Proxy Users or Network Restrictions)

If you're behind a proxy, firewall, or in a restricted network environment where Handy cannot download models automatically, you can manually download and install them. The URLs are publicly accessible from any browser.

#### Step 1: Find Your App Data Directory

1. Open Handy settings
2. Navigate to the **About** section
3. Copy the "App Data Directory" path shown there, or use the shortcuts:
   - **macOS**: `Cmd+Shift+D` to open debug menu
   - **Windows/Linux**: `Ctrl+Shift+D` to open debug menu

The typical paths are:

- **macOS**: `~/Library/Application Support/com.pais.handy/`
- **Windows**: `C:\Users\{username}\AppData\Roaming\com.pais.handy\`
- **Linux**: `~/.config/com.pais.handy/`

#### Step 2: Create Models Directory

Inside your app data directory, create a `models` folder if it doesn't already exist:

```bash
# macOS/Linux
mkdir -p ~/Library/Application\ Support/com.pais.handy/models

# Windows (PowerShell)
New-Item -ItemType Directory -Force -Path "$env:APPDATA\com.pais.handy\models"
```

#### Step 3: Download Model Files

Download the models you want from below

**Whisper Models (single .bin files):**

- Small (487 MB): `https://blob.handy.computer/ggml-small.bin`
- Medium (492 MB): `https://blob.handy.computer/whisper-medium-q4_1.bin`
- Turbo (1600 MB): `https://blob.handy.computer/ggml-large-v3-turbo.bin`
- Large (1100 MB): `https://blob.handy.computer/ggml-large-v3-q5_0.bin`

**Parakeet Unified EN 0.6B (single `.gguf` file, recommended):**

- Q8_0 (731 MB): `https://huggingface.co/handy-computer/parakeet-unified-en-0.6b-gguf/resolve/main/parakeet-unified-en-0.6b-Q8_0.gguf`

#### Step 4: Install Models

**For Whisper Models (.bin files):**

Simply place the `.bin` file directly into the `models` directory:

```
{app_data_dir}/models/
├── ggml-small.bin
├── whisper-medium-q4_1.bin
├── ggml-large-v3-turbo.bin
└── ggml-large-v3-q5_0.bin
```

**For GGUF Models (.gguf files):**

Place the `.gguf` file directly into the `models` directory, exactly like the Whisper `.bin` files above. Handy also picks up models already present in the shared Hugging Face cache (`~/.cache/huggingface/hub`), so a copy downloaded by another tool works without being moved.

**Important Notes:**

- Do not rename the `.bin` or `.gguf` files—use the exact filenames from the download URLs
- After placing the files, restart Handy to detect the new models

#### Step 5: Verify Installation

1. Restart Handy
2. Open Settings → Models
3. Your manually installed models should now appear as "Downloaded"
4. Select the model you want to use and test transcription

### Custom Whisper Models

Handy can auto-discover custom Whisper GGML models placed in the `models` directory. This is useful for users who want to use fine-tuned or community models not included in the default model list.

**How to use:**

1. Obtain a Whisper model in GGML `.bin` format (e.g., from [Hugging Face](https://huggingface.co/models?search=whisper%20ggml))
2. Place the `.bin` file in your `models` directory (see paths above)
3. Restart Handy to discover the new model
4. The model will appear in the "Custom Models" section of the Models settings page

**Important:**

- Community models are user-provided and may not receive troubleshooting assistance
- The model must be a valid Whisper GGML format (`.bin` file)
- Model name is derived from the filename (e.g., `my-custom-model.bin` → "My Custom Model")

### Linux Startup Crashes or Instability

If Handy fails to start reliably on Linux — for example, it crashes shortly after launch, never shows its window, or reports a Wayland protocol error — try the steps below in order.

**1. Install (or reinstall) `gtk-layer-shell`**

Handy uses `gtk-layer-shell` for its recording overlay and links against it at runtime. A missing or broken installation is the most common cause of startup failures and can manifest as a crash or a hang well before any window is shown. Make sure the runtime package is installed for your distro:

| Distro        | Package to install    | Example command                        |
| ------------- | --------------------- | -------------------------------------- |
| Ubuntu/Debian | `libgtk-layer-shell0` | `sudo apt install libgtk-layer-shell0` |
| Fedora/RHEL   | `gtk-layer-shell`     | `sudo dnf install gtk-layer-shell`     |
| Arch Linux    | `gtk-layer-shell`     | `sudo pacman -S gtk-layer-shell`       |

If it is already installed and you still see startup problems, try reinstalling it (e.g. `sudo pacman -S gtk-layer-shell` again) in case the library files were corrupted by a partial upgrade.

**2. Disable the GTK layer shell overlay (`HANDY_NO_GTK_LAYER_SHELL`)**

If installing the library does not help, you can skip `gtk-layer-shell` initialization entirely as a workaround. On some compositors (notably KDE Plasma under Wayland) it has been reported to interact poorly with the recording overlay. With this variable set, the overlay falls back to a regular always-on-top window:

```bash
HANDY_NO_GTK_LAYER_SHELL=1 handy
```

**3. Configure WebKit DMA-BUF rendering (`WEBKIT_DISABLE_DMABUF_RENDERER`)**

Handy disables the WebKitGTK DMA-BUF renderer by default on Linux because it can cause blank windows or crashes on some GPU/driver combinations. Explicit environment-variable values are preserved, including in the Nix package. To explicitly keep the renderer disabled:

```bash
WEBKIT_DISABLE_DMABUF_RENDERER=1 handy
```

Some users on WebKitGTK 2.54 report a flickering or partially drawn recording overlay with the renderer disabled ([#2166](https://github.com/cjpais/Handy/issues/2166)). If affected, try opting out of the default workaround:

```bash
WEBKIT_DISABLE_DMABUF_RENDERER=0 handy
```

This allows WebKit to use the renderer when supported, but can reintroduce the GPU/driver-specific crashes described above. Remove the override or use `=1` if that happens.

Fully quit Handy, including its tray instance, before testing either value. Running a second instance with `--toggle-transcription` does not change the running process's environment.

**Making a workaround permanent**

Set the variable in the environment of the launcher that actually starts Handy. Shell profile exports may not apply to desktop-menu or autostart launches. If you launch Handy from a `.desktop` file, you can prefix the `Exec=` line, e.g.:

```ini
Exec=env HANDY_NO_GTK_LAYER_SHELL=1 handy
```

Handy's built-in autostart rewrites its generated desktop entry, so editing that entry is not a durable workaround. For autostart, use an appropriate session/service environment, or disable Handy's built-in autostart and manage your own startup entry with the required variable.

If a workaround helps you, please [open an issue](https://github.com/cjpais/Handy/issues) describing your distro, desktop environment, and session type — that information helps us narrow down the underlying bug.

### Empty recording overlay on Hyprland / Omarchy

If the recording overlay is empty or bordered, fully quit Handy and launch a
[native installation](BUILD.md#linux-install-from-source) with Wayland enabled:

```bash
env -u HANDY_NO_GTK_LAYER_SHELL GDK_BACKEND=wayland handy
```

If this works, apply `GDK_BACKEND=wayland` only to Handy's launcher. This
workaround does not work with the 0.9.6 AppImage, which forces X11.

### Vulkan Overlays and Capture Tools on Windows (`HANDY_KEEP_VULKAN_IMPLICIT_LAYERS`)

On Windows, Handy asks the Vulkan loader to skip implicit layers to avoid crashes caused by overlay and capture hooks ([#2049](https://github.com/cjpais/Handy/issues/2049)). GPU acceleration remains enabled; this does not change system-wide settings.

To opt out for GPU selection or debugging tools, fully quit Handy (including the tray icon), then run both commands in the same PowerShell window:

```powershell
$env:HANDY_KEEP_VULKAN_IMPLICIT_LAYERS = "1"
& "$env:ProgramFiles\Handy\handy.exe"
```

Adjust the executable path if needed. This override only applies to apps launched from that PowerShell session, not the Start menu. Handy also preserves any existing `VK_LOADER_LAYERS_DISABLE` value.

### How to Contribute

1. **Check existing issues** at [github.com/cjpais/Handy/issues](https://github.com/cjpais/Handy/issues)
2. **Fork the repository** and create a feature branch
3. **Test thoroughly** on your target platform
4. **Submit a pull request** with clear description of changes
5. **Join the discussion** - reach out at [contact@handy.computer](mailto:contact@handy.computer)

The goal is to create both a useful tool and a foundation for others to build upon—a well-patterned, simple codebase that serves the community.

## Related Projects

- **[Handy CLI](https://github.com/cjpais/handy-cli)** - The original Python command-line version
- **[handy.computer](https://handy.computer)** - Project website with demos and documentation

## License

MIT License - see [LICENSE](LICENSE) file for details.

Handy is open-source software, but the Handy name, logo, icon, and brand assets are not open-source. Unofficial forks, rewrites, and redistributions must use their own branding and must not imply endorsement or affiliation.

## Acknowledgments

- **Whisper** by OpenAI for the speech recognition model
- **ggml** for an amazing cross-platform tensor library
- **Silero** for great lightweight VAD
- **Tauri** team for the excellent Rust-based app framework
- **Community contributors** helping make Handy better

---

## File: AGENTS.md

# AGENTS.md

This file provides guidance to AI coding assistants working with code in this repository.

## Development Commands

**Prerequisites:**

- [Rust](https://rustup.rs/) (latest stable)
- [Bun](https://bun.sh/) package manager

**Core Development:**

```bash
# Install dependencies
bun install

# Run in development mode
bun run tauri dev
# If cmake error on macOS:
CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev

# Build for production
bun run tauri build

# Frontend only development
bun run dev        # Start Vite dev server
bun run build      # Build frontend (TypeScript + Vite)
bun run preview    # Preview built frontend
```

**Linting and Formatting (run before committing):**

```bash
bun run lint              # ESLint for frontend
bun run lint:fix          # ESLint with auto-fix
bun run format            # Prettier + cargo fmt
bun run format:check      # Check formatting without changes
bun run format:frontend   # Prettier only
bun run format:backend    # cargo fmt only
```

**Model Setup (Required for Development):**

```bash
mkdir -p src-tauri/resources/models
curl -L -o src-tauri/resources/models/silero-vad-v6.2-F32.gguf https://huggingface.co/handy-computer/silero-vad-v6.2-gguf/resolve/main/silero-vad-v6.2-F32.gguf
```

For detailed platform-specific build setup, see [BUILD.md](BUILD.md).

## Architecture Overview

Handy is a cross-platform desktop speech-to-text application built with Tauri 2.x (Rust backend + React/TypeScript frontend).

### Backend Structure (src-tauri/src/)

- `lib.rs` - Main entry point, Tauri setup, manager initialization
- `managers/` - Core business logic:
  - `audio.rs` - Audio recording and device management
  - `model.rs` - Model downloading and management
  - `transcription.rs` - Speech-to-text processing pipeline
  - `history.rs` - Transcription history storage
- `audio_toolkit/` - Low-level audio processing:
  - `audio/` - Recording sink (VAD, levels, live streaming feed), output devices, WAV I/O
  - `vad/` - Voice Activity Detection (Silero VAD)
- `commands/` - Tauri command handlers for frontend communication
- `cli.rs` - CLI argument definitions (clap derive)
- `shortcut.rs` - Global keyboard shortcut handling
- `settings.rs` - Application settings management
- `overlay.rs` - Recording overlay window (platform-specific)
- `signal_handle.rs` - `send_transcription_input()` reusable function
- `utils.rs` - Platform detection helpers

### Frontend Structure (src/)

- `App.tsx` - Main component with onboarding flow
- `components/` - React UI components:
  - `settings/` - Settings UI
  - `model-selector/` - Model management interface
  - `onboarding/` - First-run experience
  - `overlay/` - Recording overlay UI
  - `update-checker/` - App update notifications
  - `shared/`, `ui/`, `icons/`, `footer/` - Shared components
- `hooks/useSettings.ts` - Settings state management hook
- `stores/settingsStore.ts` - Zustand store for settings
- `bindings.ts` - Auto-generated Tauri type bindings (via tauri-specta)
- `overlay/` - Recording overlay window entry point
- `lib/types.ts` - Shared TypeScript type definitions

### Key Architecture Patterns

**Manager Pattern:** Core functionality organized into managers (Audio, Model, Transcription) initialized at startup and managed via Tauri state.

**Command-Event Architecture:** Frontend → Backend via Tauri commands; Backend → Frontend via events.

**Pipeline Processing:** Audio → VAD → Whisper/Parakeet → Text output → Clipboard/Paste

**State Flow:** Zustand → Tauri Command → Rust State → Persistence (tauri-plugin-store)

### Technology Stack

**Core Libraries:**

- `transcribe-cpp` - Local speech recognition (Whisper, Parakeet, Moonshine, SenseVoice, etc.; GGML/GGUF) with GPU acceleration
- `handy-recorder` - Microphone capture: device selection, resampling to 16 kHz, failure detection
- `transcribe-cpp` (Silero VAD v6, CPU-only, in-process) / `earshot` - Voice Activity Detection
- `rdev` - Global keyboard shortcuts
- `rodio` - Audio playback for feedback sounds

### Application Flow

1. **Initialization:** App starts minimized to tray, loads settings, initializes managers
2. **Model Setup:** First-run downloads preferred Whisper model (Small/Medium/Turbo/Large)
3. **Recording:** Global shortcut triggers audio recording with VAD filtering
4. **Processing:** Audio sent to Whisper model for transcription
5. **Output:** Text pasted to active application via system clipboard

### Settings System

Settings are stored using Tauri's store plugin with reactive updates:

- Keyboard shortcuts (configurable, supports push-to-talk)
- Audio devices (microphone/output selection)
- Model preferences (Small/Medium/Turbo/Large Whisper variants)
- Audio feedback and translation options

### Single Instance Architecture

The app enforces single instance behavior — launching when already running brings the settings window to front rather than creating a new process. Remote control flags (`--toggle-transcription`, etc.) work by launching a second instance that sends args to the running instance via `tauri_plugin_single_instance`, then exits.

## Internationalization (i18n)

All user-facing strings must use i18next translations. ESLint enforces this (no hardcoded strings in JSX).

**Adding new text:**

1. Add key to `src/i18n/locales/en/translation.json`
2. Use in component: `const { t } = useTranslation(); t('key.path')`

**File structure:**

```
src/i18n/
├── index.ts           # i18n setup
├── languages.ts       # Language metadata
└── locales/
    ├── en/translation.json  # English (source)
    ├── de/, es/, fr/, ja/, ru/, zh/, ...
    └── ...
```

For translation contribution guidelines, see [CONTRIBUTING_TRANSLATIONS.md](CONTRIBUTING_TRANSLATIONS.md).

## Code Style

**Rust:**

- Run `cargo fmt` and `cargo clippy` before committing
- Handle errors explicitly (avoid unwrap in production)
- Use descriptive names, add doc comments for public APIs

**TypeScript/React:**

- Strict TypeScript, avoid `any` types
- Functional components with hooks
- Tailwind CSS for styling
- Path aliases: `@/` → `./src/`

## CLI Parameters

Handy supports command-line parameters on all platforms for integration with scripts, window managers, and autostart configurations.

**Implementation:** `cli.rs` (definitions), `main.rs` (parsing), `lib.rs` (applying), `signal_handle.rs` (shared logic)

| Flag                     | Description                                                |
| ------------------------ | ---------------------------------------------------------- |
| `--toggle-transcription` | Toggle recording on/off on a running instance              |
| `--toggle-post-process`  | Toggle recording with post-processing on/off               |
| `--cancel`               | Cancel the current operation on a running instance         |
| `--start-hidden`         | Launch without showing the main window (tray icon visible) |
| `--no-tray`              | Launch without system tray (closing window quits the app)  |
| `--debug`                | Enable debug mode with verbose (Trace) logging             |

**Key design decisions:**

- CLI flags are runtime-only overrides — they do NOT modify persisted settings
- Remote control flags work via `tauri_plugin_single_instance`: second instance sends args, then exits
- `send_transcription_input()` in `signal_handle.rs` is shared between signal handlers and CLI

## Debug Mode

Access debug features: `Cmd+Shift+D` (macOS) or `Ctrl+Shift+D` (Windows/Linux)

## Platform Notes

- **macOS**: Metal acceleration, accessibility permissions required for keyboard shortcuts
- **Windows**: Vulkan acceleration, code signing. Implicit Vulkan layers (overlays, capture hooks) are disabled for the Handy process via `VK_LOADER_LAYERS_DISABLE=~implicit~` set in `main.rs`; opt out with `HANDY_KEEP_VULKAN_IMPLICIT_LAYERS=1` or by setting `VK_LOADER_LAYERS_DISABLE` yourself
- **Linux**: OpenBLAS + Vulkan, limited Wayland support, overlay uses GTK layer shell (disable with `HANDY_NO_GTK_LAYER_SHELL=1`)
- **Nix/NixOS**: the Nix package sets `HANDY_DISABLE_UPDATER=1` to force-disable the self-updater at runtime without touching the persisted setting (self-update can't work against an immutable `/nix/store`)

## Troubleshooting

See the [Troubleshooting](README.md#troubleshooting) section in README.md.

## GitHub workflow for AI coding assistants

**MANDATORY. Before opening any PR, issue, or discussion in this repo: you MUST read the relevant template file and follow it strictly.** That includes sections that look "ceremonial" — checklists, AI Assistance disclosures, "Human Written Description". A generic Summary/Test-plan layout is not acceptable.

- **Opening a PR:** Read [`.github/PULL_REQUEST_TEMPLATE.md`](.github/PULL_REQUEST_TEMPLATE.md). Every section listed there is mandatory. If a section requires a human-written paragraph (e.g. "Human Written Description"), leave a clear TODO placeholder and ask the human contributor to fill it in — do not invent their voice.
- **Opening an issue:** Read [`.github/ISSUE_TEMPLATE/`](.github/ISSUE_TEMPLATE/). Blank issues are disabled; pick the right template (`bug_report.md` for bugs). Feature requests do not belong in issues — they go to [Discussions](https://github.com/cjpais/Handy/discussions) (see `.github/ISSUE_TEMPLATE/config.yml`).
- **Proposing a feature:** Handy is under a feature freeze. New features require community support gathered in [Discussions](https://github.com/cjpais/Handy/discussions) before any PR is opened — see the PR template's "Community Feedback" section.
- **Translations:** Follow [CONTRIBUTING_TRANSLATIONS.md](CONTRIBUTING_TRANSLATIONS.md).
- **Full contributor workflow:** [CONTRIBUTING.md](CONTRIBUTING.md).

**Commits:** Use conventional commit prefixes (`feat:`, `fix:`, `docs:`, `refactor:`, `chore:`). Focus the message on _why_, not _what_.

---

## File: BUILD.md

# Build Instructions

This guide covers how to set up the development environment and build Handy from source across different platforms.

## Prerequisites

### All Platforms

- [Rust](https://rustup.rs/) (latest stable)
- [Bun](https://bun.sh/) package manager
- [Tauri Prerequisites](https://tauri.app/start/prerequisites/)

### Platform-Specific Requirements

#### macOS

- Xcode Command Line Tools
- Install with: `xcode-select --install`

#### Windows

- Microsoft C++ Build Tools: Visual Studio 2019/2022 with C++ development
  tools, or Visual Studio Build Tools 2019/2022
- [CMake](https://cmake.org/download/) (must be on `PATH`):

  ```powershell
  winget install Kitware.CMake
  ```

- [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) from LunarG — required to
  build the Vulkan GPU backend (`vulkan-shaders-gen` needs the SDK's headers
  and `glslc`):

  ```powershell
  winget install KhronosGroup.VulkanSDK
  ```

  Open a new terminal afterward so `VULKAN_SDK` is set.

> [!NOTE]
> Windows' 260-character path limit used to break the native Vulkan build in
> most checkouts. Since `transcribe-cpp` 0.1.3 the build works around it
> automatically (it compiles through a short NTFS junction — no admin rights
> or setup needed), so a normal checkout just builds. If you still hit
> path-limit errors, see
> [Windows build fails with path-limit errors](#windows-build-fails-with-path-limit-errors-msb3491--ftk1011--msb6003)
> in Troubleshooting.

#### Linux

- Build essentials
- ALSA development libraries
- Install with:

  ```bash
  # Ubuntu/Debian
  sudo apt update
  sudo apt install build-essential clang libclang-dev libevdev-dev libasound2-dev pkg-config libssl-dev libvulkan-dev vulkan-tools glslc spirv-headers glslang-tools libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev libgtk-layer-shell0 libgtk-layer-shell-dev patchelf cmake

  # Fedora/RHEL
  sudo dnf groupinstall "Development Tools"
  sudo dnf install alsa-lib-devel pkgconf openssl-devel vulkan-devel glslc \
    clang clang-devel libevdev-devel \
    spirv-headers-devel spirv-tools-devel glslang \
    gtk3-devel webkit2gtk4.1-devel libappindicator-gtk3-devel librsvg2-devel \
    gtk-layer-shell gtk-layer-shell-devel \
    cmake

  # Arch Linux
  sudo pacman -S base-devel clang libevdev shaderc spirv-headers glslang alsa-lib pkgconf openssl vulkan-devel \
    gtk3 webkit2gtk-4.1 libappindicator-gtk3 librsvg gtk-layer-shell \
    cmake
  ```

## Setup Instructions

### 1. Clone the Repository

```bash
git clone git@github.com:cjpais/Handy.git
cd Handy
```

### 2. Install Dependencies

```bash
bun install
```

### 3. Start Dev Server

```bash
bun tauri dev
```

### 4. Build for Production

```bash
bun run tauri build
```

This compiles a release binary and generates platform-specific bundles (deb, rpm, AppImage on Linux; dmg on macOS; msi on Windows).

## Linux Install (from source)

The raw binary (`src-tauri/target/release/handy`) cannot run standalone — it needs Tauri resource files (tray icons, sounds, VAD model) to be co-located at the expected path.

**Install from the deb bundle** (works on any Linux distro):

```bash
cd /tmp
ar x /path/to/Handy/src-tauri/target/release/bundle/deb/Handy_*_amd64.deb data.tar.gz
tar xzf data.tar.gz
sudo cp usr/bin/handy /usr/bin/
sudo cp -a usr/lib/. /usr/lib/
sudo cp -r usr/share/icons/hicolor/* /usr/share/icons/hicolor/
sudo cp usr/share/applications/Handy.desktop /usr/share/applications/
```

The runtime libraries live in the app-private `/usr/lib/Handy/` (on the binary's rpath), so no `ldconfig` step is needed.

After subsequent rebuilds, copy the binary and any refreshed runtime libraries:

```bash
sudo cp src-tauri/target/release/handy /usr/bin/
sudo mkdir -p /usr/lib/Handy
sudo cp -a src-tauri/transcribe-libs/. /usr/lib/Handy/
```

Resources only need re-copying if they change upstream (new icons, sounds, models, etc.).

## Troubleshooting

### macOS Accessibility remains enabled after a local rebuild

Local builds use the ad-hoc `signingIdentity: "-"`. A rebuild can have a new macOS code
identity while the old **System Settings > Privacy & Security > Accessibility** entry
remains visibly enabled, leaving Handy on `Waiting...`.

After installing the final bundle at `/Applications/Handy.app`, quit Handy, clear only its
stale Accessibility record, then reopen it:

```bash
osascript -e 'tell application id "com.pais.handy" to quit' || true
tccutil reset Accessibility com.pais.handy
open /Applications/Handy.app
```

Grant Accessibility again when prompted. This does not reset Microphone or other TCC
services, and official releases normally do not need it.

For optional diagnosis, compare the designated requirements of the previous and rebuilt
bundles:

```bash
codesign -dr - /path/to/previous/Handy.app 2>&1
codesign -dr - /Applications/Handy.app 2>&1
```

An ad-hoc requirement contains a `cdhash`; a changed requirement confirms the rebuild is
not covered by the old grant. The reset procedure does not require this check.

See [issue #1618](https://github.com/cjpais/Handy/issues/1618) for the related onboarding
and stale-permission report.

### AppImage build fails on Arch / rolling-release distros

`linuxdeploy` bundles its own `strip` binary which is too old to process system libraries built with newer toolchains on rolling-release distros (Arch, CachyOS, Manjaro, EndeavourOS).

The error from Tauri:

```
Bundling Handy_*_amd64.AppImage
failed to bundle project `failed to run linuxdeploy`
```

Tauri swallows the real linuxdeploy error. To see it, run linuxdeploy manually:

```bash
cd src-tauri/target/release/bundle/appimage
~/.cache/tauri/linuxdeploy-x86_64.AppImage --appimage-extract-and-run \
  --appdir Handy.AppDir --plugin gtk --output appimage
```

**Workaround:** The binary, deb, and rpm bundles all build fine — only the AppImage step fails. To skip it:

```bash
bun run tauri build -- --bundles deb
```

Then install using the deb extraction method above.

### Windows build fails with path-limit errors (`MSB3491` / `FTK1011` / `MSB6003`)

On Windows the native build can fail partway through `transcribe-cpp-sys` with
any of these (all the same root cause):

```
error MSB3491: Could not write lines to file "...VCTargetsPath.tlog\VCTargetsPath.lastbuildstate".
Path: ... exceeds the OS max path limit. The fully qualified file name must be less than 260 characters.
```

```
FileTracker : error FTK1011: could not create the new file tracking log file:
...\vulkan-shaders-gen-build\...\cmTC_xxxxx.tlog\link.write.1.tlog.
The system cannot find the path specified.
```

```
error MSB6003: The specified task executable "CL.exe" could not be run.
System.IO.DirectoryNotFoundException: Could not find a part of the path ...
```

This is **not** a code or toolchain problem — it's Windows' legacy 260-character
path limit (`MAX_PATH`), overflowed by the Vulkan shader generator's nested
CMake build tree on top of Cargo's already-deep
`target\release\build\<crate>-<hash>\out\build\...` directory.

Since `transcribe-cpp` 0.1.3 this is mitigated automatically: the native build
compiles through a short NTFS junction under `%LOCALAPPDATA%\tcs` (created
without admin rights), so a normal checkout builds with no setup. Enabling
Windows long paths does **not** reliably help here — MSBuild's native
`FileTracker` (`tracker.exe`) ignores the long-paths flag — which is why the
junction, not the registry flag, is the fix.

If you still see the errors above, junction creation was likely blocked
(filesystem or corporate policy) — the failing build's log then contains a
`transcribe-cpp-sys: could not create short build junction ...` warning — or
your checkout is deep enough to overflow even the shortened layout. Work
around either case with a short Cargo target directory:

```powershell
# Per-shell:
$env:CARGO_TARGET_DIR = "C:\h"

# Or persist it for all future terminals (note: redirects ALL your
# Rust projects' build output, not just Handy):
[Environment]::SetEnvironmentVariable('CARGO_TARGET_DIR', 'C:\h', 'User')
```

Artifacts then land in `C:\h\release\...` instead of the repo's
`src-tauri\target\`. Open a **new terminal** if you persisted the variable —
it is only picked up by freshly started processes. Then `bun run tauri dev`
and `bun run tauri build` work normally.

### Windows `tauri build` fails at bundling with `program not found`

If the build compiles all the way to `Built application at: ...\handy.exe` and
then fails with:

```
Signing C:\...\handy.exe with a custom signing command
failed to bundle project `program not found`
```

that's the code-signing step: `tauri.conf.json` configures a custom
`signCommand` (`trusted-signing-cli`, Azure Trusted Signing) that only exists
in the release CI environment. Local development doesn't need it:

```powershell
# Development (no bundling/signing at all):
bun run tauri dev

# Or compile a release binary without the installer/signing step:
bun run tauri build --no-bundle
```

---

## File: CLAUDE.md

Read @AGENTS.md

---

## File: CONTRIBUTING.md

# Contributing to Handy

Thank you for your interest in contributing to Handy! This guide will help you get started with contributing to this open source speech-to-text application.

## ⚠️ Feature Freeze

**Handy is currently undergoing a feature freeze.** If you are submitting a PR which is a new feature that the community has not asked for, it will be rejected. If the community has asked for it, or you have explicitly gathered support, it may still be considered.

**Bug fixes are the top priority.** There are 60+ issues to fix. Please focus your contributions on fixing bugs and improving stability.

## 📖 Philosophy

Handy aims to be the most forkable speech-to-text app. The goal is to create both a useful tool and a foundation for others to build upon—a well-patterned, simple codebase that serves the community. We prioritize:

- **Simplicity**: Clear, maintainable code over clever solutions
- **Extensibility**: Make it easy for others to fork and customize
- **Privacy**: Keep everything local and offline
- **Accessibility**: Free tooling that belongs in everyone's hands

## 🚀 Getting Started

### Prerequisites

Before you begin, ensure you have the following installed:

- [Rust](https://rustup.rs/) (latest stable)
- [Bun](https://bun.sh/) package manager
- Platform-specific build tools (see [BUILD.md](BUILD.md))

### Setting Up Your Development Environment

1. **Fork the repository** on GitHub

2. **Clone your fork**:

   ```bash
   git clone git@github.com:YOUR_USERNAME/Handy.git
   cd Handy
   ```

3. **Add upstream remote**:

   ```bash
   git remote add upstream git@github.com:cjpais/Handy.git
   ```

4. **Install dependencies**:

   ```bash
   bun install
   ```

5. **Download required models**:

   ```bash
   mkdir -p src-tauri/resources/models
   curl -L -o src-tauri/resources/models/silero-vad-v6.2-F32.gguf https://huggingface.co/handy-computer/silero-vad-v6.2-gguf/resolve/main/silero-vad-v6.2-F32.gguf
   ```

6. **Run in development mode**:
   ```bash
   bun run tauri dev
   # On macOS if you encounter cmake errors:
   CMAKE_POLICY_VERSION_MINIMUM=3.5 bun run tauri dev
   ```

For detailed platform-specific setup instructions, see [BUILD.md](BUILD.md).

### Understanding the Codebase

Handy follows a clean architecture pattern:

**Backend (Rust - `src-tauri/src/`):**

- `lib.rs` - Main application entry point with Tauri setup
- `managers/` - Core business logic (audio, model, transcription)
- `audio_toolkit/` - Low-level audio processing (recording, VAD)
- `commands/` - Tauri command handlers for frontend communication
- `shortcut.rs` - Global keyboard shortcut handling
- `settings.rs` - Application settings management

**Frontend (React/TypeScript - `src/`):**

- `App.tsx` - Main application component
- `components/` - React UI components
- `hooks/` - Reusable React hooks
- `lib/types.ts` - Shared TypeScript types

For more details, see the Architecture section in [README.md](README.md) or [AGENTS.md](AGENTS.md).

## 🐛 Reporting Bugs

### Before Submitting a Bug Report

1. **Search existing issues** at [github.com/cjpais/Handy/issues](https://github.com/cjpais/Handy/issues)
2. **Check discussions** at [github.com/cjpais/Handy/discussions](https://github.com/cjpais/Handy/discussions)
3. **Try the latest release** to see if the issue has been fixed
4. **Enable debug mode** (`Cmd/Ctrl+Shift+D`) to gather diagnostic information

### Submitting a Bug Report

When creating a bug report, please include:

**System Information:**

- App version (found in settings or about section)
- Operating System (e.g., macOS 14.1, Windows 11, Ubuntu 22.04)
- CPU (e.g., Apple M2, Intel i7-12700K, AMD Ryzen 7 5800X)
- GPU (e.g., Apple M2 GPU, NVIDIA RTX 4080, Intel UHD Graphics)

**Bug Details:**

- Clear description of the bug
- Steps to reproduce
- Expected behavior
- Actual behavior
- Screenshots or logs if applicable
- Information from debug mode if relevant

Use the [Bug Report template](.github/ISSUE_TEMPLATE/bug_report.md) when creating an issue.

## 💡 Suggesting Features

We use GitHub Discussions for feature requests rather than issues. This keeps issues focused on bugs and actionable tasks while allowing more open-ended conversations about features.

### Before Suggesting a Feature

1. **Search existing discussions** at [github.com/cjpais/Handy/discussions](https://github.com/cjpais/Handy/discussions)
2. **Check common feature requests**:
   - [Post-processing / Editing Transcripts](https://github.com/cjpais/Handy/discussions/168)
   - [Keyboard Shortcuts / Hotkeys](https://github.com/cjpais/Handy/discussions/211)

### Submitting a Feature Request

1. Go to [Discussions](https://github.com/cjpais/Handy/discussions)
2. Click "New discussion"
3. Choose the appropriate category (Ideas, Feature Requests, etc.)
4. Describe your feature idea including:
   - The problem you're trying to solve
   - Your proposed solution
   - Any alternatives you've considered
   - How it fits with Handy's philosophy

## 🔧 Making Code Contributions

### Before You Start

**This is critical:** Before writing any code, please do the following:

1. **Search existing issues and PRs** - Check both open AND closed issues and pull requests. Someone may have already addressed this, or there may be a reason it was closed.
   - [Open issues](https://github.com/cjpais/Handy/issues)
   - [Closed issues](https://github.com/cjpais/Handy/issues?q=is%3Aissue+is%3Aclosed)
   - [Open PRs](https://github.com/cjpais/Handy/pulls)
   - [Closed PRs](https://github.com/cjpais/Handy/pulls?q=is%3Apr+is%3Aclosed)

2. **If something was previously closed** - If you want to revisit a closed issue or PR, you need to:
   - Provide a strong argument for why it should be reconsidered
   - Gather community feedback first via [Discussions](https://github.com/cjpais/Handy/discussions)
   - Link to that discussion in your PR

3. **Get community feedback for features** - PRs with demonstrated community interest are **much more likely to be merged**. Start a discussion, get feedback, and link to it in your PR. This helps ensure Handy stays focused and useful for the most people without becoming bloated.

Community feedback is essential to keeping Handy the best it can be for everyone. It helps prioritize what matters most and prevents feature creep.

### Development Workflow

1. **Create a feature branch**:

   ```bash
   git checkout -b feature/your-feature-name
   # or
   git checkout -b fix/your-bug-fix
   ```

2. **Make your changes**:
   - Write clean, maintainable code
   - Follow existing code style and patterns
   - Add comments for complex logic
   - Keep commits focused and atomic

3. **Test thoroughly**:
   - Test on your target platform(s)
   - Verify existing functionality still works
   - Test edge cases and error conditions
   - Use debug mode to verify audio/transcription behavior

4. **Commit your changes**:

   ```bash
   git add .
   git commit -m "feat: add your feature description"
   # or
   git commit -m "fix: describe the bug fix"
   ```

   Use conventional commit messages:
   - `feat:` for new features
   - `fix:` for bug fixes
   - `docs:` for documentation changes
   - `refactor:` for code refactoring
   - `test:` for test additions/changes
   - `chore:` for maintenance tasks

5. **Keep your fork updated**:

   ```bash
   git fetch upstream
   git rebase upstream/main
   ```

6. **Push to your fork**:

   ```bash
   git push origin feature/your-feature-name
   ```

7. **Create a Pull Request**:
   - Go to the [Handy repository](https://github.com/cjpais/Handy)
   - Click "New Pull Request"
   - Select your fork and branch
   - Fill out the PR template completely, including:
     - Clear description of changes
     - Links to related issues or discussions
     - **Community feedback** (especially important for features)
     - How you tested the changes
     - Screenshots/videos if applicable
     - Breaking changes (if any)

   **Remember:** PRs with community support are prioritized. If you haven't already, start a [discussion](https://github.com/cjpais/Handy/discussions) to gather feedback before or alongside your PR. It is not explicitly required to gather feedback, but it certainly helps your PR get merged faster.

### AI Assistance Disclosure

**AI-assisted PRs are welcome!** Use whatever tools help you contribute, just be upfront about it.

In your PR description, please include:

- Whether AI was used (yes/no)
- Which tools were used (e.g., "Claude Code", "GitHub Copilot", "ChatGPT")
- How extensively it was used (e.g., "generated boilerplate", "helped debug", "wrote most of the code")

### Code Style Guidelines

**Rust:**

- Follow standard Rust formatting (`cargo fmt`)
- Run `cargo clippy` and address warnings
- Use descriptive variable and function names
- Add doc comments for public APIs
- Handle errors explicitly (avoid unwrap in production code)

**TypeScript/React:**

- Use TypeScript strictly, avoid `any` types
- Follow React hooks best practices
- Use functional components
- Keep components small and focused
- Use Tailwind CSS for styling

**General:**

- Write self-documenting code
- Add comments for non-obvious logic
- Keep functions small and single-purpose
- Prioritize readability over cleverness

### Testing Your Changes

**Manual Testing:**

- Run the app in development mode: `bun run tauri dev`
- Test your changes with debug mode enabled
- Verify on multiple platforms if possible
- Test with different audio devices
- Try various transcription scenarios

**Building for Production:**

```bash
bun run tauri build
```

Test the production build to ensure it works as expected.

## 📝 Documentation Contributions

Documentation improvements are highly valued! You can contribute by:

- Improving README.md, BUILD.md, or this CONTRIBUTING.md
- Adding code comments and doc comments
- Creating tutorials or guides
- Improving error messages
- Updating the project website content

## 🤝 Community Guidelines

- **Be respectful and inclusive** - We welcome contributors of all skill levels
- **Be patient** - This is maintained by a small team, responses may take time
- **Be constructive** - Focus on solutions and improvements
- **Be collaborative** - Help others and share knowledge
- **Search first** - Check existing issues/discussions before creating new ones

## 🎯 Good First Issues

Look for issues labeled `good first issue` or `help wanted` if you're new to the project. These are typically:

- Well-defined and scoped
- Good for learning the codebase
- Mentor support available

## 📞 Getting Help

- **Discord**: Join our [Discord community](https://discord.com/invite/WVBeWsNXK4)
- **Discussions**: Ask questions in [GitHub Discussions](https://github.com/cjpais/Handy/discussions)
- **Email**: Reach out at [contact@handy.computer](mailto:contact@handy.computer)

## 📜 License

By contributing to Handy, you agree that your contributions will be licensed under the MIT License. See [LICENSE](LICENSE) for details.

---

**Thank you for contributing to Handy!** Your efforts help make speech-to-text technology more accessible, private, and extensible for everyone.

---

## File: CONTRIBUTING_TRANSLATIONS.md

# Contributing Translations to Handy

Thank you for helping translate Handy! This guide explains how to add or improve translations.

## Quick Start

1. Fork the repository
2. Check the [currently supported languages](#currently-supported-languages). For an existing language, edit its translation file; for a new language, copy the English translation file to a new language folder
3. Translate the values (not the keys!)
4. Submit a pull request

## File Structure

Translation files are located in:

```
src/i18n/locales/
├── en/
│   └── translation.json    # English (source)
├── vi/
│   └── translation.json    # Vietnamese
├── fr/
│   └── translation.json    # French
└── [your-language]/
    └── translation.json    # Your contribution!
```

## Adding a New Language

### Step 1: Create the Language Folder

Create a new folder using the [ISO 639-1 language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes):

```bash
mkdir src/i18n/locales/[language-code]
```

Examples:

- `de` for German
- `es` for Spanish
- `ja` for Japanese
- `zh` for Chinese
- `ko` for Korean
- `pt` for Portuguese

### Step 2: Copy the English File

```bash
cp src/i18n/locales/en/translation.json src/i18n/locales/[language-code]/translation.json
```

### Step 3: Translate the Values

Open the file and translate only the **values** (right side), not the keys (left side):

```json
{
  "sidebar": {
    "general": "General",      // ← Translate this value
    "advanced": "Advanced",    // ← Translate this value
    ...
  }
}
```

**Important:**

- Keep all keys exactly the same
- Preserve any `{{variables}}` in the text (e.g., `{{error}}`, `{{model}}`)
- Keep the JSON structure and formatting intact

### Step 4: Register Your Language

Edit `src/i18n/languages.ts` and add your language metadata:

```typescript
export const LANGUAGE_METADATA: Record<
  string,
  { name: string; nativeName: string }
> = {
  en: { name: "English", nativeName: "English" },
  es: { name: "Spanish", nativeName: "Español" },
  fr: { name: "French", nativeName: "Français" },
  vi: { name: "Vietnamese", nativeName: "Tiếng Việt" },
  de: { name: "German", nativeName: "Deutsch" }, // ← Add your language
};
```

### Step 5: Test Your Translation

1. Run the app: `bun run tauri dev`
2. Go to Settings → General → App Language
3. Select your language
4. Verify all text displays correctly

### Step 6: Submit a Pull Request

1. Commit your changes
2. Push to your fork
3. Open a pull request with:
   - Language name in the title (e.g., "Add German translation")
   - Any notes about the translation

## Improving Existing Translations

Found a typo or better translation?

1. Edit the relevant `translation.json` file
2. Submit a PR with a brief description of the change

## Translation Guidelines

### Do:

- Use natural, native-sounding language
- Keep translations concise (UI space is limited)
- Match the tone of the English text (friendly, clear)
- Preserve technical terms when appropriate (e.g., "API", "GPU")

### Don't:

- Translate brand names (Handy, transcribe.cpp, ggml, OpenAI)
- Change or remove `{{variables}}`
- Modify JSON keys
- Add extra spaces or formatting

### Handling Variables

Some strings contain variables like `{{error}}` or `{{model}}`. Keep these exactly as-is:

```json
// English
"downloadModel": "Failed to download model: {{error}}"

// French (correct)
"downloadModel": "Échec du téléchargement du modèle : {{error}}"

// French (incorrect - don't translate the variable!)
"downloadModel": "Échec du téléchargement du modèle : {{erreur}}"
```

### Handling Plurals

Some languages have complex plural rules. For now, use a general form that works for all cases. We may add proper plural support in the future.

## Questions?

- Open an issue on GitHub
- Join the discussion in existing translation PRs

## Currently Supported Languages

See [language metadata](src/i18n/languages.ts) for the current language names and codes, and [locale folders](src/i18n/locales) for their translation files. The app discovers supported languages from these folders.

[English](src/i18n/locales/en/translation.json) is the source language. Existing translations may still need corrections or updates.

## Requested Languages

We welcome additional languages that are not yet in the [locale folders](src/i18n/locales), as well as [improvements to existing translations](#improving-existing-translations).

---

Thank you for making Handy accessible to more people around the world!

---

## File: package.json

{
  "name": "handy-app",
  "private": true,
  "version": "0.9.8",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "tauri": "tauri",
    "lint": "eslint src",
    "lint:fix": "eslint src --fix",
    "format": "prettier --write . && cd src-tauri && cargo fmt",
    "format:check": "prettier --check . && cd src-tauri && cargo fmt -- --check",
    "format:frontend": "prettier --write .",
    "format:backend": "cd src-tauri && cargo fmt",
    "test:keyboard": "bun src/lib/utils/keyboard.test.ts",
    "test:playwright": "playwright test",
    "test:playwright:ui": "playwright test --ui",
    "check:translations": "bun scripts/check-translations.ts",
    "check:model-languages": "bun scripts/check-model-language-coverage.ts",
    "sync:catalog": "bun scripts/sync-catalog.ts",
    "check:catalog": "bun scripts/sync-catalog.ts --check",
    "postinstall": "bun scripts/check-nix-deps.ts"
  },
  "dependencies": {
    "@handy-computer/models": "0.2.2",
    "@tailwindcss/vite": "^4.1.16",
    "@tauri-apps/api": "^2.11.0",
    "@tauri-apps/plugin-autostart": "~2.5.1",
    "@tauri-apps/plugin-clipboard-manager": "~2.3.2",
    "@tauri-apps/plugin-dialog": "~2.6",
    "@tauri-apps/plugin-fs": "~2.4.4",
    "@tauri-apps/plugin-global-shortcut": "~2.3.2",
    "@tauri-apps/plugin-opener": "^2.5.2",
    "@tauri-apps/plugin-os": "~2.3.2",
    "@tauri-apps/plugin-process": "~2.3.1",
    "@tauri-apps/plugin-sql": "~2.3.1",
    "@tauri-apps/plugin-store": "~2.4.1",
    "@tauri-apps/plugin-updater": "~2.10.1",
    "i18next": "^25.7.2",
    "immer": "^11.1.3",
    "lucide-react": "^0.542.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-i18next": "^16.4.1",
    "react-markdown": "^10.1.0",
    "react-select": "^5.8.0",
    "sonner": "^2.0.7",
    "tailwindcss": "^4.1.16",
    "tauri-plugin-macos-permissions-api": "2.3.0",
    "zod": "^3.25.76",
    "zustand": "^5.0.8"
  },
  "devDependencies": {
    "@playwright/test": "^1.58.0",
    "@tauri-apps/cli": "^2.11.0",
    "@types/node": "^24.9.1",
    "@types/react": "^18.3.26",
    "@types/react-dom": "^18.3.7",
    "@types/react-select": "^5.0.1",
    "@typescript-eslint/eslint-plugin": "^8.49.0",
    "@typescript-eslint/parser": "^8.49.0",
    "@vitejs/plugin-react": "^4.7.0",
    "eslint": "^9.39.1",
    "eslint-plugin-i18next": "^6.1.3",
    "prettier": "^3.6.2",
    "typescript": "~5.6.3",
    "vite": "^6.4.1"
  }
}

---

## File: tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "types": ["node"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Path Aliases */
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/bindings": ["./src/bindings.ts"]
    },

    /* Linting */
    "strict": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

---

## File: tsconfig.node.json

{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

---

## File: docs/troubleshooting/ubuntu-26-04-gnome-wayland/README.md

# Handy on Ubuntu 26.04 GNOME Wayland

Tested on Ubuntu 26.04.1 LTS, GNOME Wayland, Handy 0.9.8.

The fix is to use `ydotool` for typing and `handy_keys` for shortcuts.

_Note: Run commands one by one in terminal_

## 1. Check Wayland and uinput

```bash
echo "$XDG_SESSION_TYPE"
id
ls -l /dev/uinput
grep -R 'uinput' /etc/udev/rules.d /usr/lib/udev/rules.d 2>/dev/null
```

You should be using `Wayland`, be in the `input` group, and have `/dev/uinput` owned by `root:input` with mode `0660`. Ubuntu provides the required udev rule in `80-uinput.rules`.

If you are not in `input`:

```bash
sudo usermod -aG input "$USER"
```

Log out and back in after adding the group.

## 2. Install and test ydotool

```bash
sudo apt install ydotool
systemctl --user start ydotool
systemctl --user status ydotool --no-pager
ydotool type "HELLO FROM YDOTOOL"
```

The service should show `active (running)` and the test should type into the focused application.

## 3. Configure Handy

Edit Handy's settings with:

```bash
sed -i 's/"typing_tool": "auto"/"typing_tool": "ydotool"/' ~/.local/share/com.pais.handy/settings_store.json
sed -i 's/"keyboard_implementation": "tauri"/"keyboard_implementation": "handy_keys"/' ~/.local/share/com.pais.handy/settings_store.json
```

Restart Handy:

```bash
pkill handy
handy --start-hidden &
```

Check the Handy log for:

```text
handy-keys manager thread started
handy-keys shortcuts initialized
```

Then use your existing Handy shortcut and test dictation.

## 4. Hide the overlay

On GNOME, the overlay is a regular window that can take the focus, so nothing is pasted. Set **Overlay** to **None**.

## 5. Install wl-clipboard

Handy uses `wl-copy` on Wayland when it is installed:

```bash
sudo apt install wl-clipboard
```

## 6. Non-QWERTY keyboard layouts

`ydotool` sends physical keys, so the built-in paste methods fail on layouts such as bépo or Dvorak: Ctrl+V presses the QWERTY `V` key, and Shift+Insert breaks while a modifier of your shortcut is still held.

Use an external script instead: it presses Ctrl and the key that types `v` on your layout, `KEY_U` (22) on bépo or `KEY_DOT` (52) on Dvorak (codes in `/usr/include/linux/input-event-codes.h`).

Create `~/.local/bin/handy-paste`, here for bépo:

```sh
#!/bin/sh
printf '%s' "$1" | wl-copy
sleep 0.1
ydotool key 29:1 22:1 22:0 29:0
```

```bash
chmod +x ~/.local/bin/handy-paste
```

Set **Paste Method** to **External Script** with this path.

---

## File: src/content/release-notes/README.md

# Release Notes

Add user-facing release notes as Markdown files named by app version:

```text
src/content/release-notes/0.8.4.md
```

The update modal shows the highest bundled release note newer than the
persisted `whats_new_last_seen_version` and not newer than the running app
version.

Keep these files focused on headline user-facing changes. Release notes support
paragraphs, headings, lists, links, code, quotes, separators, hard line breaks,
and local images under `/release-notes/...`. Raw HTML is ignored before
rendering.

Place image assets in `public/release-notes/{version}/` and reference them from
Markdown with absolute paths:

```md
![Streaming transcription preview](/release-notes/0.9.0/streaming-transcription.webp)
```

---

## File: src/content/release-notes/0.9.0.md

## transcribe.cpp

[transcribe.cpp](https://github.com/handy-computer/transcribe.cpp)
is now the primary transcription engine across the app.

transcribe.cpp enables new features in Handy, and also should speed
up transcriptions for most users. The most notable new feature is
the support for streaming models. You will be able to see the
transcribed text as you are speaking. When using streaming models,
typically the transcriptions will also complete much quicker.

## Streaming Support

Handy now supports native streaming models. We encourage you to try
them out. Especially:

- Parakeet Unified for English Speakers
- Nemotron Streaming 3.5 for Multilingual Speakers

A new streaming overlay lets you see what is being written or transcribed as you are speaking.



The new default is to use this overlay when using a compatible streaming model.
If you do not like it you can change to the minimal style in the Advanced Settings.

## New Model Support

There are a ton of new models that Handy supports as a result of the upgrade to transcribe.cpp.
If you're interested in trying a variety of speech-to-text models, go take a look at the Models page.

Some of the families are:

- IBM's Granite Family
- Mistral's Voxtral Family
- Google's MedASR
- Alibaba's Qwen3 ASR

## Notes

This is a big upgrade and theoretically everything will work out of the box.
However there likely will be issues, so please don't hesitate
to report issues on the [GitHub Issues Tracker](https://github.com/cjpais/Handy/issues).

In the future we plan to deprecate the "Legacy" models. We will provide
an upgrade path for any people who are still using those models before
we remove support for them. All this means is that you will need to
re-download the model into a format which is supported by transcribe.cpp.

---

## File: src/content/release-notes/0.9.7.md

## New Shortcut Behavior

Handy now has an **Auto** shortcut mode, so the same shortcut can work as both push-to-talk and a toggle. It is the default for new installs.

- **Auto** — Hold to record and release to stop, or tap once to start and tap again to stop.
- **Hold** — Records only while you hold the shortcut. This is the old push-to-talk behavior.
- **Toggle** — Tap once to start recording and tap again to stop.

Existing users will keep their current shortcut behavior after updating. To try Auto, go to **General → Shortcut Behavior**.

---

## File: src/i18n/locales/ar/translation.json

{
  "tray": {
    "settings": "الإعدادات...",
    "checkUpdates": "التحقق من وجود تحديثات...",
    "copyLastTranscript": "نسخ آخر نص تم تفريغه",
    "unloadModel": "تفريغ النموذج",
    "model": "النموذج",
    "quit": "إنهاء",
    "cancel": "إلغاء",
    "secureInputWarning": "⚠ الاختصارات محجوبة بواسطة Secure Input"
  },
  "sidebar": {
    "general": "عام",
    "advanced": "متقدم",
    "postProcessing": "معالجة لاحقة",
    "history": "السجل",
    "debug": "تصحيح الأخطاء",
    "about": "حول",
    "models": "النماذج"
  },
  "onboarding": {
    "subtitle": "للبدء، اختر نموذج التفريغ الصوتي",
    "existingModelsTitle": "النماذج المتوافقة",
    "downloadModelsTitle": "متاح للتنزيل",
    "showAllModels": "عرض كل النماذج ({{total}})",
    "showFewerModels": "عرض عدد أقل من النماذج",
    "recommended": "موصى به",
    "customModelDescription": "غير مدعوم رسميًا",
    "migration": {
      "title": "نماذجك بحاجة إلى تحديث",
      "description": "يستخدم Handy الآن نوعًا أحدث من النماذج. نزّل الإصدار الجديد لمواصلة التفريغ الصوتي. ستتم إزالة الإصدارات القديمة.",
      "replaces": "يحل محل {{name}}",
      "oldFormat": "تنسيق قديم",
      "failed": "فشل التنزيل",
      "retry": "حاول مرة أخرى",
      "chooseAnother": "اختر نموذجًا مختلفًا",
      "download": "تنزيل النماذج ({{size}})",
      "continue": "متابعة",
      "timeRemaining": "متبقٍ {{time}}"
    },
    "modelCard": {
      "accuracy": "الدقة",
      "speed": "السرعة"
    },
    "models": {
      "small": {
        "name": "Whisper Small",
        "description": ".سريع ودقيق إلى حد ما"
      },
      "medium": {
        "name": "Whisper Medium",
        "description": ".دقة جيدة، سرعة متوسطة"
      },
      "turbo": {
        "name": "Whisper Turbo",
        "description": ".توازن بين الدقة والسرعة"
      },
      "large": {
        "name": "Whisper Large",
        "description": ".دقة جيدة، لكنه بطيء"
      },
      "breeze-asr": {
        "name": "Breeze ASR",
        "description": "محسّن للماندرين التايوانية. دعم التبديل بين اللغات."
      }
    },
    "errors": {
      "selectModel": "فشل تحديد النموذج"
    },
    "permissions": {
      "title": "الأذونات المطلوبة",
      "description": ".يحتاج Handy إلى بعض الأذونات ليعمل بشكل صحيح",
      "microphone": {
        "title": "الوصول إلى الميكروفون",
        "description": ".مطلوب لسماع صوتك من أجل التفريغ الصوتي"
      },
      "accessibility": {
        "title": "إمكانية الوصول",
        "description": ".مطلوب لكتابة النص المفرغ في تطبيقاتك"
      },
      "grant": "منح الإذن",
      "granted": "تم المنح",
      "waiting": "في الانتظار...",
      "allGranted": "كل شيء جاهز!",
      "errors": {
        "checkFailed": "فشل التحقق من الأذونات. يرجير المحاولة مرة أخرى.",
        "requestFailed": "فشل طلب الإذن. يرجى المحاولة مرة أخرى."
      }
    }
  },
  "modelSelector": {
    "custom": "مخصص",
    "streaming": "البث",
    "active": "نشط",
    "noModelsAvailable": "لا توجد نماذج متاحة",
    "verifying": "جارٍ التحقق من {{modelName}}...",
    "verifyingGeneric": "جارٍ التحقق...",
    "downloading": "جاري التنزيل {{percentage}}%",
    "downloadingMultiple": "جاري تنزيل {{count}} من النماذج...",
    "modelReady": "النموذج جاهز",
    "loading": "جاري تحميل {{modelName}}...",
    "loadingGeneric": "جاري التحميل...",
    "modelError": "خطأ في النموذج",
    "modelUnloaded": "تم إلغاء تحميل النموذج",
    "noModelDownloadRequired": "لا يوجد نموذج - التنزيل مطلوب",
    "deleteModel": "حذف {{modelName}}",
    "switching": "جارٍ التبديل...",
    "downloadSpeed": "{{speed}} ميجابايت/ث",
    "cancel": "إلغاء",
    "cancelDownload": "إلغاء التنزيل",
    "capabilities": {
      "languageSelection": "يدعم اختيار اللغة",
      "singleLanguage": "يدعم هذه اللغة فقط",
      "languageCount": "{{total}} لغة",
      "languageOnly": "{{language}} فقط",
      "translation": "يدعم الترجمة",
      "translate": "ترجمة",
      "streaming": "يعرض النسخ المباشر أثناء التحدث"
    },
    "legacy": "قديم"
  },
  "settings": {
    "general": {
      "title": "عام",
      "shortcut": {
        "title": "اختصارات Handy",
        "description": "إعداد اختصارات لوحة المفاتيح لبدء تسجيل التفريغ الصوتي",
        "loading": "جاري تحميل الاختصارات...",
        "none": "لا توجد اختصارات مجهزة",
        "notFound": "الاختصار غير موجود",
        "pressKeys": "اضغط على المفاتيح...",
        "bindings": {
          "transcribe": {
            "name": "اختصار التفريغ الصوتي",
            "description": ".اختصار لوحة المفاتيح لتسجيل وتفريغ صوتك"
          },
          "cancel": {
            "name": "اختصار الإلغاء",
            "description": ".اختصار لوحة المفاتيح لإلغاء التسجيل الحالي"
          },
          "transcribe_with_post_process": {
            "name": "مفتاح المعالجة اللاحقة",
            "description": "اختياري: مفتاح اختصار مخصص يطبق دائماً المعالجة اللاحقة بالذكاء الاصطناعي على التفريغ الصوتي."
          }
        },
        "errors": {
          "restore": "فشل استعادة الاختصار الأصلي",
          "set": "فشل تعيين الاختصار: {{error}}",
          "reset": "فشل إعادة تعيين الاختصار إلى قيمته الأصلية"
        }
      },
      "language": {
        "title": "اللغة",
        "description": ".اختر لغة التعرف على الكلام. سيحدد 'تلقائي' اللغة تلقائياً، بينما يؤدي اختيار لغة معينة إلى تحسين الدقة لتلك اللغة",
        "searchPlaceholder": "البحث عن اللغات...",
        "noResults": "لم يتم العثور على لغات",
        "auto": "تلقائي"
      },
      "shortcutActivation": {
        "title": "سلوك الاختصار",
        "description": "اختر كيفية بدء التسجيل وإيقافه باستخدام اختصار التفريغ الصوتي.",
        "options": {
          "holdOrToggle": "تلقائي",
          "pushToTalk": "ضغط مطوّل",
          "toggle": "تبديل"
        },
        "descriptions": {
          "hold_or_toggle": "اضغط مطوّلًا للتسجيل أو انقر للتبديل",
          "push_to_talk": "يسجّل أثناء الضغط على الاختصار",
          "toggle": "انقر على الاختصار لبدء التسجيل، وانقر مرة أخرى للإيقاف"
        }
      }
    },
    "sound": {
      "title": "الصوت",
      "microphone": {
        "title": "الميكروفون",
        "description": "اختر جهاز الميكروفون المفضل لديك",
        "placeholder": "اختر الميكروفون...",
        "loading": "جاري التحميل..."
      },
      "channel": {
        "title": "قناة الإدخال",
        "description": "اختر قناة الإدخال التي تريد التسجيل منها. استخدم هذا الخيار إذا كانت واجهة الصوت لديك تحتوي على عدة مداخل.",
        "average": "متوسط جميع القنوات",
        "channel": "القناة {{n}}"
      },
      "audioFeedback": {
        "label": "تنبيهات صوتية",
        "description": "تشغيل صوت عند بدء التسجيل وتوقفه"
      },
      "outputDevice": {
        "title": "جهاز الإخراج",
        "description": "اختر جهاز إخراج الصوت المفضل لتنبيهات الصوت",
        "placeholder": "اختر جهاز الإخراج...",
        "loading": "جاري التحميل..."
      },
      "volume": {
        "title": "مستوى الصوت",
        "description": "ضبط مستوى صوت تنبيهات الصوت"
      }
    },
    "advanced": {
      "groups": {
        "app": "التطبيق",
        "output": "الإخراج",
        "transcription": "التفريغ الصوتي",
        "history": "السجل",
        "experimental": "تجريبي"
      },
      "experimentalToggle": {
        "label": "الميزات التجريبية",
        "description": ".تمكين الميزات التجريبية التي لا تزال قيد التطوير"
      },
      "lazyStreamClose": {
        "label": "إبقاء الميكروفون مفتوحًا بين عمليات النسخ",
        "description": "يبقي بث الميكروفون مفتوحًا لمدة 30 ثانية بعد توقف التسجيل، مما يقلل التأخير عند النسخ المتتالي. قد يؤثر على جودة صوت البلوتوث أثناء التفعيل."
      },
      "acceleration": {
        "transcribe": {
          "title": "تسريع الأجهزة",
          "description": "اختر الأجهزة المستخدمة للتفريغ الصوتي. الوضع التلقائي يستخدم GPU إن توفر، وإلا فيستخدم CPU."
        },
        "gpuDevice": {
          "auto": "تلقائي"
        }
      },
      "startHidden": {
        "label": "بدء مخفي",
        "description": ".التشغيل في صينية النظام دون فتح النافذة"
      },
      "autostart": {
        "label": "التشغيل عند بدء التشغيل",
        "description": ".بدء تشغيل Handy تلقائياً عند تسجيل الدخول إلى جهاز الكمبيوتر الخاص بك"
      },
      "showTrayIcon": {
        "label": "إظهار أيقونة شريط النظام",
        "description": ".عرض أيقونة Handy في شريط النظام"
      },
      "overlay": {
        "style": {
          "title": "التراكب",
          "description": "اختر تراكب التسجيل: «بلا» يخفيه، و«مبسط» يعرض شارة صغيرة، و«مباشر» يعرض النص المكتوب فوريًا أثناء التحدث (النماذج التي تدعم البث فقط — ابحث عن شارة «البث» في منتقي النماذج). على Linux يُنصح باختيار «بلا».",
          "options": {
            "none": "بلا",
            "minimal": "مبسط",
            "live": "مباشر"
          }
        },
        "position": {
          "title": "موقع التراكب",
          "description": "عرض تراكب الملاحظات المرئية أثناء التسجيل والتفريغ. على نظام Linux يوصى بـ 'بلا'.",
          "options": {
            "bottom": "أسفل",
            "top": "أعلى"
          }
        }
      },
      "pasteMethod": {
        "title": "طريقة اللصق",
        "description": ".اختر كيفية إدراج النص. مباشر: يحاكي الكتابة عبر إدخال النظام. بلا: يتخطى اللصق، ويحدث السجل/الحافظة فقط",
        "options": {
          "clipboard": "الحافظة ({{modifier}}+V)",
          "clipboardCtrlShiftV": "الحافظة (Ctrl+Shift+V)",
          "clipboardShiftInsert": "الحافظة (Shift+Insert)",
          "direct": "مباشر",
          "none": "بلا",
          "externalScript": "نص برمجي خارجي"
        },
        "externalScriptPlaceholder": "/path/to/your/script.sh"
      },
      "typingTool": {
        "title": "أداة الكتابة",
        "description": ".اختر أداة الكتابة في Linux لاستخدامها مع طريقة اللصق المباشر. سيكتشف \"تلقائي\" تلقائياً أفضل أداة متاحة لنظامك ويستخدمها",
        "options": {
          "auto": "تلقائي (موصى به)"
        }
      },
      "clipboardHandling": {
        "title": "التعامل مع الحافظة",
        "description": ".'عدم تعديل الحافظة' يحافظ على محتويات حافظتك الحالية بعد التفريغ. 'نسخ إلى الحافظة' يترك نتيجة التفريغ في حافظتك بعد اللصق",
        "options": {
          "dontModify": "عدم تعديل الحافظة",
          "copyToClipboard": "نسخ إلى الحافظة"
        }
      },
      "autoSubmit": {
        "title": "إرسال تلقائي",
        "description": "إرسال مجموعة المفاتيح المحددة تلقائياً بعد إدراج النص. Cmd+Enter ينطبق على macOS، بينما يستخدم Windows/Linux مفتاح Super+Enter.",
        "options": {
          "off": "إيقاف",
          "enter": "Enter",
          "cmdEnter": "Cmd+Enter",
          "superEnter": "Super+Enter",
          "ctrlEnter": "Ctrl+Enter"
        }
      },
      "translateToEnglish": {
        "label": "الترجمة إلى الإنجليزية",
        "description": ".ترجمة الكلام من اللغات الأخرى تلقائياً إلى الإنجليزية أثناء التفريغ"
      },
      "modelUnload": {
        "title": "إلغاء تحميل النموذج",
        "description": "تحرير ذاكرة GPU/CPU تلقائياً عندما لا يتم استخدام النموذج للوقت المحدد",
        "options": {
          "never": "أبداً",
          "immediately": "فوراً",
          "min2": "بعد دقيقتين",
          "min5": "بعد 5 دقائق",
          "min10": "بعد 10 دقائق",
          "min15": "بعد 15 دقيقة",
          "hour1": "بعد ساعة واحدة",
          "sec15": "بعد 15 ثوانٍ (تصحيح أخطاء)"
        }
      },
      "customWords": {
        "title": "كلمات مخصصة",
        "description": "ساعد النماذج المدعومة على التعرّف على الأسماء والمصطلحات المتخصصة. يقتصر التصحيح التقريبي حاليًا على الكلمات التي تستخدم الأحرف A–Z والأرقام.",
        "placeholder": "أضف كلمة",
        "add": "إضافة",
        "remove": "إزالة {{word}}",
        "duplicate": "\"{{word}}\" موجود بالفعل"
      },
      "voiceActivityDetection": {
        "title": "كشف النشاط الصوتي",
        "description": "تصفية الصمت من التسجيلات. النماذج التي تدعم البث تستخدم فترة VAD أطول؛ وتعطيل VAD يسجّل الصوت الخام."
      },
      "longFormChunking": {
        "title": "تقسيم الصوت الطويل",
        "description": "يقسّم التسجيلات الطويلة عند فترات التوقف قبل نسخها، مما يحسّن أداء النسخ عادةً."
      },
      "vadBackend": {
        "title": "محرك كشف النشاط الصوتي",
        "description": "اختر الكاشف المستخدم لتصفية الصمت. Earshot تجريبي؛ ارجع إلى Silero إذا تم اقتطاع الكلام أو الاحتفاظ بالضوضاء.",
        "options": {
          "silero": "Silero",
          "earshot": "Earshot"
        }
      },
      "fillerWordRemoval": {
        "title": "إزالة كلمات الحشو",
        "description": "يزيل كلمات التردد الشائعة من النصوص. عطّل هذا الخيار للاحتفاظ بها."
      },
      "chineseScript": {
        "title": "الكتابة الصينية",
        "description": "يكتب نصوص الماندرين والكانتونية بالأحرف المبسطة أو التقليدية. لا تتغير اللغات الأخرى.",
        "options": {
          "asTranscribed": "كما نُسخ",
          "simplified": "مبسطة",
          "traditional": "تقليدية"
        }
      }
    },
    "postProcessing": {
      "hotkey": {
        "title": "مفتاح الاختصار"
      },
      "api": {
        "title": "API",
        "provider": {
          "title": "المزود",
          "description": "اختر مزوداً متوافقاً مع OpenAI أو Anthropic."
        },
        "apiFormat": {
          "title": "تنسيق API",
          "description": "تنسيق الطلب الذي تتوقعه نقطة النهاية المخصصة الخاصة بك.",
          "options": {
            "openai": "متوافق مع OpenAI (Chat Completions)",
            "anthropic": "Anthropic Messages"
          }
        },
        "appleIntelligence": {
          "unavailable": "Apple Intelligence غير متاح على هذا الجهاز. يتطلب جهاز Mac بمعالج Apple Silicon يعمل بنظام macOS Tahoe (26.0) أو أحدث مع تمكين Apple Intelligence في إعدادات النظام."
        },
        "baseUrl": {
          "title": "URL الأساسي",
          "description": "URL الأساسي لـ API للمزود المختار. يمكن تعديل المزود المخصص فقط.",
          "placeholder": "https://api.openai.com/v1",
          "placeholderAnthropic": "https://api.anthropic.com/v1"
        },
        "apiKey": {
          "title": "مفتاح API",
          "description": "مفتاح API للمزود المختار.",
          "placeholder": "sk-..."
        },
        "model": {
          "title": "النموذج",
          "descriptionCustom": "قدم معرف النموذج المتوقع من قبل نقطة النهاية المخصصة الخاصة بك.",
          "descriptionDefault": "اختر نموذجاً يعرضه المزود المختار.",
          "placeholderWithOptions": "ابحث عن نموذج أو اختره",
          "placeholderNoOptions": "اكتب اسم النموذج",
          "refreshModels": "تحديث النماذج"
        }
      },
      "prompts": {
        "title": "المطالبة",
        "selectedPrompt": {
          "title": "المطالبة المختارة",
          "description": ".اختر قالباً لتحسين التفريغ الصوتي أو أنشئ قالباً جديداً. استخدم ${output} داخل نص المطالبة للإشارة إلى النص الملتقط"
        },
        "noPrompts": "لا توجد مطالبات متاحة",
        "selectPrompt": "اختر مطالبة",
        "createNew": "إنشاء مطالبة جديدة",
        "promptLabel": "تسمية المطالبة",
        "promptLabelPlaceholder": "أدخل اسم المطالبة",
        "promptInstructions": "تعليمات المطالبة",
        "promptInstructionsPlaceholder": ".اكتب التعليمات المراد تشغيلها بعد التفريغ الصوتي. مثال: قم بتحسين القواعد والوضوح للنص التالي: ${output}",
        "promptTip": "تلميح: استخدم `${output}` لإدراج النص المفرغ في مطالبتك.",
        "updatePrompt": "تحديث المطالبة",
        "deletePrompt": "حذف المطالبة",
        "createPrompt": "إنشاء مطالبة",
        "cancel": "إلغاء",
        "selectToEdit": ".اختر مطالبة أعلاه لعرض وتعديل تفاصيلها",
        "createFirst": ".انقر على 'إنشاء مطالبة جديدة' أعلاه لإنشاء أول مطالبة معالجة لاحقة لك"
      }
    },
    "history": {
      "title": "السجل",
      "openFolder": "فتح مجلد التسجيلات",
      "loading": "جاري تحميل السجل...",
      "empty": "!لا يوجد تفريغ صوتي بعد. ابدأ التسجيل لبناء سجلك",
      "copyToClipboard": "نسخ التفريغ إلى الحافظة",
      "copyError": "تعذر نسخ التفريغ إلى الحافظة.",
      "save": "حفظ التفريغ",
      "unsave": "إزالة من المحفوظات",
      "delete": "حذف الإدخال",
      "deleteError": ".فشل حذف الإدخال. يرجى المحاولة مرة أخرى",
      "retranscribe": "إعادة النسخ",
      "retranscribeError": "فشلت إعادة النسخ. يرجى المحاولة مرة أخرى.",
      "transcribing": "جارٍ النسخ...",
      "transcriptionFailed": "فشل النسخ. يمكنك إعادة النسخ باستخدام أيقونة إعادة المحاولة."
    },
    "debug": {
      "title": "تصحيح الأخطاء",
      "logDirectory": {
        "title": "مجلد السجلات",
        "description": "الموقع الذي يتم فيه تخزين ملفات السجل"
      },
      "logLevel": {
        "title": "مستوى السجل",
        "description": "ضبط درجة تفصيل السجلات"
      },
      "liveLogs": {
        "title": "السجلات المباشرة",
        "description": "اعرض سجلات التطبيق في الوقت الفعلي لتشخيص المشكلات دون فتح ملف السجل. تُعرض فقط السجلات الصادرة أثناء فتح هذه اللوحة، وتراعي إعداد «مستوى السجل» أعلاه.",
        "live": "مباشر",
        "paused": "متوقف مؤقتًا",
        "pause": "إيقاف مؤقت",
        "resume": "استئناف",
        "copied": "تم النسخ",
        "lineCount": "{{count}} سطر",
        "empty": "بانتظار السجلات… ستظهر الإدخالات هنا فور إصدار التطبيق لها."
      },
      "updateChecks": {
        "label": "التحقق من وجود تحديثات",
        "description": "التحقق تلقائياً من وجود إصدارات جديدة من Handy",
        "lockedDescription": "معطّل بواسطة إعدادات النظام (HANDY_DISABLE_UPDATER)"
      },
      "soundTheme": {
        "label": "سمة الصوت",
        "description": "اختر سمة صوت لتنبيهات بدء وتوقف التسجيل"
      },
      "wordCorrectionThreshold": {
        "title": "عتبة تصحيح الكلمات",
        "description": "حساسية تصحيح الكلمات المخصصة"
      },
      "historyLimit": {
        "title": "حد السجل",
        "description": "الحد الأقصى لعدد إدخالات السجل المراد الاحتفاظ بها",
        "entries": "إدخالات"
      },
      "recordingRetention": {
        "title": "حذف التسجيلات تلقائياً",
        "description": "حذف التسجيلات القديمة تلقائياً لتوفير المساحة",
        "never": "أبداً",
        "preserveLimit": "الاحتفاظ بآخر {{count}}",
        "days3": "بعد 3 أيام",
        "weeks2": "بعد أسبوعين",
        "months3": "بعد 3 أشهر",
        "placeholder": "اختر فترة الاحتفاظ..."
      },
      "alwaysOnMicrophone": {
        "label": "ميكروفون يعمل دائماً",
        "description": "إبقاء الميكروفون نشطاً لاستجابة أسرع"
      },
      "clamshellMicrophone": {
        "title": "ميكروفون وضع الإغلاق",
        "description": "الميكروفون المراد استخدامه عندما يكون غطاء المحمول مغلقاً"
      },
      "postProcessingToggle": {
        "label": "معالجة لاحقة",
        "description": "تمكين تحسين النص المدعوم بالذكاء الاصطناعي بعد التفريغ الصوتي"
      },
      "muteWhileRecording": {
        "label": "كتم الصوت أثناء التسجيل",
        "description": "كتم صوت النظام أثناء التسجيل"
      },
      "appendTrailingSpace": {
        "label": "إضافة مسافة تابعة",
        "description": "إضافة مسافة بعد التفريغ الملصق"
      },
      "keyboardImplementation": {
        "title": "تنفيذ لوحة المفاتيح",
        "description": ".اختر الواجهة الخلفية لاختصارات لوحة المفاتيح",
        "bindingsReset": "كانت اختصارات لوحة المفاتيح غير متوافقة وتمت إعادة تعيينها إلى القيم الافتراضية"
      },
      "paths": {
        "appData": "بيانات التطبيق:",
        "models": "النماذج:",
        "settings": "الإعدادات:"
      },
      "pasteDelay": {
        "title": "تأخير اللصق (قبل)",
        "description": "التأخير (بالمللي ثانية) بعد نسخ النص وقبل إرسال ضغطة مفتاح اللصق. قم بزيادته إذا لم يتم لصق أي شيء."
      },
      "pasteDelayAfter": {
        "title": "تأخير اللصق (بعد)",
        "description": "التأخير (بالمللي ثانية) بعد ضغطة مفتاح اللصق وقبل استعادة محتوى الحافظة السابق. قم بزيادته إذا تم لصق محتوى الحافظة القديم بدلاً من النص المُفرَّغ."
      },
      "holdThreshold": {
        "title": "حد الضغط المطوّل",
        "description": "في الوضع التلقائي فقط: المدة (بالمللي ثانية) التي يجب الاستمرار في الضغط على الاختصار خلالها ليُعدّ ضغطًا مطوّلًا بدلًا من نقرة. قلّلها إذا ظلّ التسجيل مستمرًا بعد ضغطات مطوّلة قصيرة؛ وزدها إذا عُوملت النقرات المقصودة على أنها ضغط مطوّل."
      },
      "reliablePaste": {
        "title": "لصق موثوق (Beta)",
        "description": "استعادة الحافظة فقط بعد أن يقرأ التطبيق المستهدف النص المُفرَّغ فعلياً، بدلاً من الاعتماد على تأخير ثابت. يهدف إلى إصلاح لصق محتوى الحافظة القديم عند وجود حمل على النظام. يتطلب طريقة لصق تعتمد على الحافظة؛ على macOS وWindows فقط."
      },
      "recordingBuffer": {
        "title": "مخزن التسجيل الإضافي",
        "description": "وقت إضافي (بالمللي ثانية) للاستمرار في التسجيل بعد تحرير المفتاح، لالتقاط الصوت المتبقي. 0 = لا مخزن إضافي."
      },
      "keyboardDiagnostic": {
        "title": "تشخيص لوحة المفاتيح",
        "description": "يتحقق مما إذا كانت أحداث لوحة المفاتيح تصل إلى Handy. يُسجَّل عدد الأحداث فقط — ولا تُسجَّل أبدًا المفاتيح التي تضغط عليها.",
        "run": "تشغيل تشخيص لمدة 10 ثوانٍ",
        "running": "جارٍ الاستماع… اضغط اختصارك عدة مرات (مثل Option+Space)",
        "secureInputLabel": "Secure Input",
        "enabled": "مُفعَّل",
        "disabled": "مُعطَّل",
        "holder": "يحتجزه {{name}} (pid {{pid}})",
        "holderUnknown": "لا يوجد محتجِز ظاهر",
        "keyDown": "ضغط مفتاح",
        "keyUp": "تحرير مفتاح",
        "flagsChanged": "مفاتيح التعديل",
        "mouse": "الماوس",
        "verdictBlocked": "يحجب Secure Input أحداث المفاتيح — لا يمكن للاختصارات التي تتضمن مفتاحًا عاديًا أن تعمل حتى تُحلّ المشكلة.",
        "verdictSuspicious": "وصلت أحداث مفاتيح التعديل لكن لم يصل أي حدث مفتاح — هناك شيء يمنع المفاتيح رغم أن Secure Input يظهر معطلاً. يُرجى الإبلاغ عن ذلك على GitHub.",
        "verdictOk": "أحداث المفاتيح تصل إلى Handy بشكل طبيعي.",
        "verdictNoEvents": "لم يُسجَّل أي حدث — هل ضغطت أي مفاتيح أثناء الاختبار؟",
        "failed": "فشل التشخيص: {{error}}"
      },
      "whatsNewPreview": {
        "title": "معاينة الجديد",
        "description": "افتح أحدث ملاحظات إصدار مضمنة دون وضع علامة عليها كمقروءة",
        "button": "معاينة",
        "noNotes": "لم يتم العثور على ملاحظات إصدار مضمنة",
        "error": "تعذرت معاينة ملاحظات الإصدار"
      },
      "onboardingPreview": {
        "title": "معاينة الإعداد الأولي",
        "description": "عاين شاشات الإعداد الأولي دون تغيير الأذونات أو النماذج أو حالة الإكمال",
        "permissionsButton": "الأذونات",
        "modelsButton": "اختيار النموذج",
        "exitButton": "إنهاء المعاينة"
      }
    },
    "about": {
      "title": "حول",
      "version": {
        "title": "الإصدار",
        "description": "الإصدار الحالي من Handy"
      },
      "appDataDirectory": {
        "title": "مجلد بيانات التطبيق",
        "description": "الموقع الذي يخزن فيه Handy بياناته"
      },
      "sourceCode": {
        "title": "كود المصدر",
        "description": "عرض كود المصدر والمساهمة",
        "button": "عرض على GitHub"
      },
      "supportDevelopment": {
        "title": "دعم التطوير",
        "description": "ساعدنا في مواصلة بناء Handy",
        "button": "تبرع"
      },
      "acknowledgments": {
        "title": "شكر وتقدير",
        "ggml": {
          "title": "ggml",
          "description": "استنتاج عالي الأداء لنموذج التعرف التلقائي على الكلام Whisper من OpenAI",
          "details": ".يستخدم Handy برنامج ggml لمعالجة سريعة ومحلية لتحويل الكلام إلى نص. شكراً للعمل الرائع الذي قام به Georgi Gerganov والمساهمون"
        }
      },
      "whatsNewUpdates": {
        "label": "إظهار الجديد",
        "description": "إظهار ملاحظات الإصدار بعد تحديثات Handy"
      }
    },
    "modelSettings": {
      "title": "إعدادات {{model}}"
    },
    "models": {
      "title": "نماذج النسخ",
      "description": "اختر نموذج نسخ أو قم بتنزيل نماذج إضافية. تقدم النماذج المختلفة مستويات متفاوتة من الدقة والسرعة.",
      "yourModels": "النماذج المحمّلة",
      "availableModels": "متاح للتنزيل",
      "deleteConfirm": "هل أنت متأكد أنك تريد حذف {{modelName}}؟ ستحتاج إلى تنزيله مرة أخرى لاستخدامه.",
      "deleteActiveConfirm": "{{modelName}} هو النموذج النشط حاليًا. حذفه سيوقف النسخ حتى تختار نموذجًا جديدًا. هل أنت متأكد؟",
      "deleteTitle": "حذف النموذج",
      "retiredNotice": {
        "title": "النماذج القديمة لم تعد تعمل مع Handy",
        "description": "إنها تشغل {{size}} على جهاز الكمبيوتر الخاص بك.",
        "delete": "حذف النماذج",
        "deleted": "تم حذف النماذج القديمة",
        "deleteFailed": "تعذّر حذف بعض النماذج القديمة. تحقق من السجل لمزيد من التفاصيل."
      },
      "filters": {
        "allLanguages": "جميع اللغات",
        "streaming": "تصفية النماذج التي تدعم النسخ المباشر",
        "translation": "تصفية النماذج التي تدعم الترجمة إلى الإنجليزية"
      },
      "noModelsMatch": "لا توجد نماذج مطابقة لهذا الفلتر.",
      "searchPlaceholder": "ابحث عن النماذج بالاسم…",
      "rescan": {
        "label": "إعادة الفحص",
        "tooltip": "إعادة الفحص بحثًا عن النماذج المضافة إلى مجلد النماذج أو ذاكرة Hugging Face التخزينية المؤقتة خارج Handy"
      }
    }
  },
  "footer": {
    "checkingUpdates": "جاري التحقق من وجود تحديثات...",
    "updateAvailableShort": "تحديث متاح",
    "upToDate": "محدث",
    "updateCheckingDisabled": "تم تعطيل التحقق من التحديث",
    "downloading": "جاري التنزيل {{progress}}%...",
    "installing": "جاري التثبيت...",
    "preparing": "جاري التحضير...",
    "checkForUpdates": "التحقق من وجود تحديثات",
    "portableUpdateTitle": "التحديث اليدوي مطلوب",
    "portableUpdateMessage": "لا يمكن تحديث التثبيتات المحمولة تلقائيًا. نزّل المثبّت المناسب لنظامك من الزر أدناه، شغّله، وثبّته في المجلد نفسه — يبقى مجلد Data/ (الإعدادات والنماذج والتسجيلات) في مكانه.",
    "portableUpdateButton": "تنزيل المثبّت",
    "portableUpdateBrowseMessage": "لا يمكن تحديث التثبيتات المحمولة تلقائيًا. نزّل أحدث إصدار مناسب لنظامك من GitHub Releases وثبّته في المجلد نفسه — يبقى مجلد Data/ (الإعدادات والنماذج والتسجيلات) في مكانه.",
    "portableUpdateBrowseButton": "فتح GitHub Releases"
  },
  "common": {
    "loading": "جاري التحميل...",
    "delete": "حذف",
    "back": "رجوع",
    "close": "إغلاق",
    "open": "فتح",
    "copy": "نسخ",
    "clear": "مسح",
    "noOptionsFound": "لم يتم العثور على خيارات"
  },
  "accessibility": {
    "permissionsDescription": ".يحتاج Handy إلى أذونات إمكانية الوصول لكتابة النص المفرغ",
    "openSettings": "فتح إعدادات النظام"
  },
  "errors": {
    "loadDirectory": "خطأ في تحميل المجلد: {{error}}",
    "micPermissionDeniedTitle": "تم رفض الوصول إلى الميكروفون",
    "micPermissionDenied": {
      "generic": "تم رفض الوصول إلى الميكروفون من قبل نظام التشغيل. يرجى منح إذن الميكروفون في إعدادات النظام.",
      "windows": "قم بتمكين الوصول إلى الميكروفون في الإعدادات ← الخصوصية والأمان ← الميكروفون (بما في ذلك وصول تطبيقات سطح المكتب).",
      "macos": "امنح الوصول إلى الميكروفون في إعدادات النظام ← الخصوصية والأمان ← الميكروفون.",
      "linux": "امنح الوصول إلى الميكروفون في إعدادات الصوت أو الخصوصية في نظامك."
    },
    "noInputDeviceTitle": "لم يتم العثور على ميكروفون",
    "noInputDevice": "لم يتم اكتشاف أي جهاز إدخال صوتي. يرجى توصيل ميكروفون أو سماعة رأس والمحاولة مرة أخرى.",
    "recordingFailed": "فشل في بدء التسجيل: {{error}}",
    "modelLoadFailed": "فشل في تحميل النموذج: {{model}}",
    "modelLoadFailedUnknown": "نموذج غير معروف",
    "pasteFailedTitle": "فشل لصق النص",
    "pasteFailed": "تعذر لصق النص في التطبيق النشط.",
    "transcriptionFailedTitle": "فشل التفريغ النصي"
  },
  "appLanguage": {
    "title": "لغة التطبيق",
    "description": "تغيير لغة واجهة Handy"
  },
  "theme": {
    "title": "سمة التطبيق",
    "description": "اختر ما إذا كان Handy يتبع مظهر النظام أو يبقى فاتحًا أو داكنًا",
    "options": {
      "system": "النظام",
      "light": "فاتح",
      "dark": "داكن"
    }
  },
  "overlay": {
    "transcribing": "جاري التفريغ...",
    "processing": "جاري المعالجة...",
    "loadingModel": "جارٍ تحميل النموذج..."
  },
  "whatsNew": {
    "title": "الجديد في Handy v{{version}}"
  },
  "secureInput": {
    "blockedWithCulprit_one": "قد يحظر {{name}} اختصارًا واحدًا",
    "blockedWithCulprit_other": "قد يحظر {{name}} {{count}} اختصارات",
    "blockedNoCulprit_one": "يحظر macOS مؤقتًا اختصارًا واحدًا",
    "blockedNoCulprit_other": "يحظر macOS مؤقتًا {{count}} اختصارات",
    "recorderBlockedWithCulprit": "قد يمنع {{name}} تغيير الاختصارات",
    "recorderBlockedNoCulprit": "يمنع macOS مؤقتًا تغيير الاختصارات",
    "learnMore": "كيفية الإصلاح",
    "recorderBlocked": "لا يمكن تسجيل الاختصارات الآن — يحجب Secure Input في macOS أحداث المفاتيح. عالج تحذير Secure Input أولاً.",
    "dismiss": "تجاهل"
  }
}

---

## File: src/i18n/locales/bg/translation.json

{
  "tray": {
    "settings": "Настройки...",
    "checkUpdates": "Проверка за актуализации...",
    "copyLastTranscript": "Копиране на последния транскрипт",
    "unloadModel": "Разтоварване на модела",
    "model": "Модел",
    "quit": "Изход",
    "cancel": "Отказ",
    "secureInputWarning": "⚠ Бързите клавиши са блокирани от Secure Input"
  },
  "sidebar": {
    "general": "Общи",
    "models": "Модели",
    "advanced": "Разширени",
    "postProcessing": "Постобработка",
    "history": "История",
    "debug": "Отстраняване",
    "about": "За приложението"
  },
  "onboarding": {
    "subtitle": "За да започнете, изберете модел за транскрипция",
    "existingModelsTitle": "Съвместими модели",
    "downloadModelsTitle": "Достъпни за изтегляне",
    "showAllModels": "Показване на всички {{total}} модела",
    "showFewerModels": "Показване на по-малко модели",
    "recommended": "Препоръчан",
    "customModelDescription": "Не се поддържа официално",
    "migration": {
      "title": "Моделите ви се нуждаят от обновяване",
      "description": "Handy вече използва по-нов вид модели. Изтеглете новата версия, за да продължите да транскрибирате. Старите версии ще бъдат премахнати.",
      "replaces": "Заменя {{name}}",
      "oldFormat": "Стар формат",
      "failed": "Изтеглянето е неуспешно",
      "retry": "Опитайте отново",
      "chooseAnother": "Изберете друг модел",
      "download": "Изтегляне на модели ({{size}})",
      "continue": "Продължи",
      "timeRemaining": "Остават {{time}}"
    },
    "modelCard": {
      "accuracy": "точност",
      "speed": "скорост"
    },
    "models": {
      "small": {
        "name": "Whisper Small",
        "description": "Бърз и доста точен."
      },
      "medium": {
        "name": "Whisper Medium",
        "description": "Добра точност, средна скорост."
      },
      "turbo": {
        "name": "Whisper Turbo",
        "description": "Баланс между точност и скорост."
      },
      "large": {
        "name": "Whisper Large",
        "description": "Добра точност, но по-бавен."
      },
      "breeze-asr": {
        "name": "Breeze ASR",
        "description": "Оптимизиран за тайвански мандарин. Поддръжка на смесване на езици."
      }
    },
    "errors": {
      "selectModel": "Неуспешно избиране на модел"
    },
    "permissions": {
      "title": "Необходими разрешения",
      "description": "Handy се нуждае от няколко разрешения, за да работи правилно.",
      "microphone": {
        "title": "Достъп до микрофон",
        "description": "Необходим е, за да чуе гласа ви при транскрипция."
      },
      "accessibility": {
        "title": "Достъп за достъпност",
        "description": "Необходим е, за да въвежда транскрибирания текст във вашите приложения."
      },
      "grant": "Даване на разрешение",
      "granted": "Разрешено",
      "waiting": "Изчакване...",
      "allGranted": "Готово!",
      "errors": {
        "checkFailed": "Проверката на разрешенията не бе успешна. Опитайте отново.",
        "requestFailed": "Заявката за разрешение не бе успешна. Опитайте отново."
      }
    }
  },
  "modelSelector": {
    "custom": "Персонализиран",
    "streaming": "Стрийминг",
    "active": "Активен",
    "switching": "Превключване...",
    "noModelsAvailable": "Няма налични модели",
    "verifying": "Проверка на {{modelName}}...",
    "verifyingGeneric": "Проверка...",
    "downloading": "Изтегляне {{percentage}}%",
    "downloadingMultiple": "Изтегляне на {{count}} модела...",
    "modelReady": "Моделът е готов",
    "loading": "Зареждане на {{modelName}}...",
    "loadingGeneric": "Зареждане...",
    "modelError": "Грешка в модела",
    "modelUnloaded": "Моделът е разтоварен",
    "noModelDownloadRequired": "Няма модел – необходимо е изтегляне",
    "deleteModel": "Изтриване на {{modelName}}",
    "downloadSpeed": "{{speed}} MB/s",
    "cancel": "Отказ",
    "cancelDownload": "Отказ на изтеглянето",
    "capabilities": {
      "languageSelection": "Поддържа няколко входни езика",
      "singleLanguage": "Поддържа само този език",
      "languageCount": "{{total}} езика",
      "languageOnly": "Само {{language}}",
      "translation": "Може да превежда на английски",
      "translate": "Превод на английски",
      "streaming": "Показва транскрипция на живо, докато говорите"
    },
    "legacy": "Наследен"
  },
  "settings": {
    "modelSettings": {
      "title": "Настройки на {{model}}"
    },
    "general": {
      "title": "Общи",
      "shortcut": {
        "title": "Бързи клавиши на Handy",
        "description": "Настройте клавишни комбинации за стартиране на запис за реч към текст",
        "loading": "Зареждане на бързите клавиши...",
        "none": "Няма настроени бързи клавиши",
        "notFound": "Бързият клавиш не е намерен",
        "pressKeys": "Натиснете клавишите...",
        "bindings": {
          "transcribe": {
            "name": "Бърз клавиш за транскрипция",
            "description": "Клавишната комбинация за запис и транскрибиране на гласа ви."
          },
          "cancel": {
            "name": "Бърз клавиш за отказ",
            "description": "Клавишната комбинация за отмяна на текущия запис."
          },
          "transcribe_with_post_process": {
            "name": "Бърз клавиш за постобработка",
            "description": "По избор: отделна комбинация, която винаги прилага ИИ постобработка върху транскрипцията."
          }
        },
        "errors": {
          "restore": "Неуспешно възстановяване на оригиналния бърз клавиш",
          "set": "Неуспешно задаване на бързия клавиш: {{error}}",
          "reset": "Неуспешно нулиране на бързия клавиш към първоначалната стойност"
        }
      },
      "language": {
        "title": "Език",
        "description": "Изберете езика за разпознаване на реч. „Автоматично“ определя езика сам, а изборът на конкретен език може да подобри точността за него.",
        "searchPlaceholder": "Търсене на езици...",
        "noResults": "Няма намерени езици",
        "auto": "Автоматично"
      },
      "shortcutActivation": {
        "title": "Поведение на клавишната комбинация",
        "description": "Изберете как бързият клавиш за транскрипция стартира и спира записа.",
        "options": {
          "holdOrToggle": "Автоматично",
          "pushToTalk": "Задържане",
          "toggle": "Превключване"
        },
        "descriptions": {
          "hold_or_toggle": "Задръжте за запис или натиснете кратко за превключване",
          "push_to_talk": "Записва, докато държите комбинацията натисната",
          "toggle": "Натиснете кратко комбинацията, за да започне записът, и отново, за да спре"
        }
      }
    },
    "models": {
      "title": "Модели за транскрипция",
      "description": "Изберете модел за транскрипция или изтеглете допълнителни. Различните модели предлагат различна точност и скорост.",
      "yourModels": "Изтеглени модели",
      "availableModels": "Налични за изтегляне",
      "deleteConfirm": "Сигурни ли сте, че искате да изтриете {{modelName}}? Ще трябва да го изтеглите отново, за да го използвате.",
      "deleteActiveConfirm": "{{modelName}} е вашият активен модел. Изтриването му ще спре транскрипциите, докато не изберете нов модел. Сигурни ли сте?",
      "deleteTitle": "Изтриване на модел",
      "retiredNotice": {
        "title": "Старите модели вече не работят с Handy",
        "description": "Заемат {{size}} на компютъра ви.",
        "delete": "Изтрий моделите",
        "deleted": "Старите модели са изтрити",
        "deleteFailed": "Някои стари модели не можаха да бъдат изтрити. Проверете журнала за подробности."
      },
      "filters": {
        "allLanguages": "Всички езици",
        "streaming": "Филтриране на моделите, които поддържат транскрипция на живо",
        "translation": "Филтриране на моделите, които поддържат превод на английски"
      },
      "noModelsMatch": "Няма модели, отговарящи на този филтър.",
      "searchPlaceholder": "Търсене на модели по име…",
      "rescan": {
        "label": "Сканиране отново",
        "tooltip": "Сканиране отново за модели, добавени в папката с модели или кеша на Hugging Face извън Handy"
      }
    },
    "sound": {
      "title": "Звук",
      "microphone": {
        "title": "Микрофон",
        "description": "Изберете предпочитано микрофонно устройство",
        "placeholder": "Изберете микрофон...",
        "loading": "Зареждане..."
      },
      "channel": {
        "title": "Входен канал",
        "description": "Изберете входния канал, от който да записвате. Използвайте тази опция, ако аудио интерфейсът ви има няколко входа.",
        "average": "Осредняване на всички канали",
        "channel": "Канал {{n}}"
      },
      "audioFeedback": {
        "label": "Звукова обратна връзка",
        "description": "Възпроизвеждане на звук при старт и край на записа"
      },
      "outputDevice": {
        "title": "Изходно устройство",
        "description": "Изберете предпочитано изходно аудио устройство за звуци за обратна връзка",
        "placeholder": "Изберете изходно устройство...",
        "loading": "Зареждане..."
      },
      "volume": {
        "title": "Сила на звука",
        "description": "Регулирайте силата на звуците за обратна връзка"
      }
    },
    "advanced": {
      "groups": {
        "app": "Приложение",
        "output": "Изход",
        "transcription": "Транскрипция",
        "history": "История",
        "experimental": "Експериментални"
      },
      "experimentalToggle": {
        "label": "Експериментални функции",
        "description": "Включване на експериментални функции, които са още в разработка."
      },
      "lazyStreamClose": {
        "label": "Държане на микрофона отворен между транскрипции",
        "description": "Държи потокът от микрофона отворен 30 секунди след спиране на записа, което намалява забавянето при поредни транскрипции. Може да влоши качеството на Bluetooth аудио, докато е активно."
      },
      "acceleration": {
        "transcribe": {
          "title": "Хардуерно ускорение",
          "description": "Изберете хардуера, използван за транскрипция. „Автоматично“ използва GPU при наличност, а в противен случай преминава към CPU."
        },
        "gpuDevice": {
          "auto": "Автоматично"
        }
      },
      "startHidden": {
        "label": "Стартиране скрито",
        "description": "Стартиране в системната област без отваряне на прозореца."
      },
      "autostart": {
        "label": "Стартиране със системата",
        "description": "Автоматично стартиране на Handy при влизане в профила ви."
      },
      "showTrayIcon": {
        "label": "Икона в системната област",
        "description": "Показване на иконата на Handy в системната област."
      },
      "overlay": {
        "style": {
          "title": "Наслагване",
          "description": "Изберете наслагването при запис: «Няма» го скрива, «Минимално» показва компактен индикатор, «На живо» показва транскрипцията в реално време, докато говорите (само модели с поддръжка на стрийминг — потърсете етикета «Стрийминг» в избора на модел). На Linux се препоръчва «Няма».",
          "options": {
            "none": "Няма",
            "minimal": "Минимално",
            "live": "На живо"
          }
        },
        "position": {
          "title": "Позиция на наслагването",
          "description": "Показване на визуална обратна връзка при запис и транскрипция. На Linux се препоръчва „Няма“.",
          "options": {
            "bottom": "Долу",
            "top": "Горе"
          }
        }
      },
      "pasteMethod": {
        "title": "Начин на поставяне",
        "description": "Изберете как се вмъква текстът. Директно: симулира писане чрез системния вход. Няма: пропуска поставянето, актуализира само историята/клипборда.",
        "options": {
          "clipboard": "Клипборд ({{modifier}}+V)",
          "clipboardCtrlShiftV": "Клипборд (Ctrl+Shift+V)",
          "clipboardShiftInsert": "Клипборд (Shift+Insert)",
          "direct": "Директно",
          "none": "Няма",
          "externalScript": "Външен скрипт"
        },
        "externalScriptPlaceholder": "/път/към/вашия/script.sh"
      },
      "typingTool": {
        "title": "Инструмент за писане",
        "description": "Изберете кой Linux инструмент за писане да се използва при директно поставяне. „Автоматично“ открива и използва най-подходящия за вашата система.",
        "options": {
          "auto": "Автоматично (препоръчително)"
        }
      },
      "clipboardHandling": {
        "title": "Работа с клипборда",
        "description": "„Без промяна на клипборда“ запазва съдържанието на клипборда след транскрипция. „Копиране в клипборда“ оставя резултата в клипборда след поставяне.",
        "options": {
          "dontModify": "Без промяна на клипборда",
          "copyToClipboard": "Копиране в клипборда"
        }
      },
      "autoSubmit": {
        "title": "Автоматично изпращане",
        "description": "Автоматично изпращане на избраната клавишна комбинация след вмъкване на текста. Cmd+Enter важи за macOS, а Windows/Linux използват Super+Enter.",
        "options": {
          "off": "Изкл.",
          "enter": "Enter",
          "cmdEnter": "Cmd+Enter",
          "superEnter": "Super+Enter",
          "ctrlEnter": "Ctrl+Enter"
        }
      },
      "translateToEnglish": {
        "label": "Превод на английски",
        "description": "Автоматичен превод на реч от други езици на английски по време на транскрипция."
      },
      "modelUnload": {
        "title": "Разтоварване на модела",
        "description": "Автоматично освобождаване на памет на GPU/CPU, ако моделът не е използван за зададеното време",
        "options": {
          "never": "Никога",
          "immediately": "Веднага",
          "min2": "След 2 минути",
          "min5": "След 5 минути",
          "min10": "След 10 минути",
          "min15": "След 15 минути",
          "hour1": "След 1 час",
          "sec15": "След 15 секунди (отстраняване)"
        }
      },
      "customWords": {
        "title": "Персонализирани думи",
        "description": "Помогнете на поддържаните модели да разпознават имена и специализирани термини. Приблизителната корекция в момента е ограничена до думи с букви A–Z и цифри.",
        "placeholder": "Добавете дума",
        "add": "Добавяне",
        "remove": "Премахване на {{word}}",
        "duplicate": "„{{word}}“ вече съществува"
      },
      "voiceActivityDetection": {
        "title": "Откриване на гласова активност",
        "description": "Филтрира тишината от записите. Моделите с поддръжка на стрийминг използват по-дълга опашка на VAD; изключването на VAD записва необработен звук."
      },
      "longFormChunking": {
        "title": "Разделяне на дълго аудио",
        "description": "Разделя дългите записи на паузите преди транскрибиране, което обикновено подобрява качеството на транскрипцията."
      },
      "vadBackend": {
        "title": "Бекенд за откриване на глас",
        "description": "Изберете детектора, използван за филтриране на тишината. Earshot е експериментален; върнете се към Silero, ако речта се отрязва или шумът се запазва.",
        "options": {
          "silero": "Silero",
          "earshot": "Earshot"
        }
      },
      "fillerWordRemoval": {
        "title": "Премахване на думи паразити",
        "description": "Премахва често срещаните думи за колебание от транскрипциите. Изключете, за да ги запазите."
      },
      "chineseScript": {
        "title": "Китайска писменост",
        "description": "Записва транскрипциите на мандарин и кантонски с опростени или традиционни йероглифи. Другите езици остават непроменени.",
        "options": {
          "asTranscribed": "Както е транскрибирано",
          "simplified": "Опростени",
          "traditional": "Традиционни"
        }
      }
    },
    "postProcessing": {
      "hotkey": {
        "title": "Бърз клавиш"
      },
      "api": {
        "title": "API",
        "provider": {
          "title": "Доставчик",
          "description": "Изберете доставчик, съвместим с OpenAI или Anthropic."
        },
        "apiFormat": {
          "title": "Формат на API",
          "description": "Формат на заявката, очакван от вашия персонализиран краен адрес.",
          "options": {
            "openai": "Съвместим с OpenAI (Chat Completions)",
            "anthropic": "Anthropic Messages"
          }
        },
        "appleIntelligence": {
          "unavailable": "Apple Intelligence не е налична на това устройство. Необходим е Mac с Apple Silicon и macOS Tahoe (26.0) или по-нова с включена Apple Intelligence в Системните настройки."
        },
        "baseUrl": {
          "title": "Базов URL",
          "description": "Базов API URL за избрания доставчик. Редактируем е само при персонализиран доставчик.",
          "placeholder": "https://api.openai.com/v1",
          "placeholderAnthropic": "https://api.anthropic.com/v1"
        },
        "apiKey": {
          "title": "API ключ",
          "description": "API ключ за избрания доставчик.",
          "placeholder": "sk-..."
        },
        "model": {
          "title": "Модел",
          "descriptionCustom": "Въведете идентификатора на модела, очакван от вашия персонализиран краен адрес.",
          "descriptionDefault": "Изберете модел, предлаган от избрания доставчик.",
          "placeholderWithOptions": "Търсене или избор на модел",
          "placeholderNoOptions": "Въведете име на модел",
          "refreshModels": "Опресняване на моделите"
        }
      },
      "prompts": {
        "title": "Подкана",
        "selectedPrompt": {
          "title": "Избрана подкана",
          "description": "Изберете шаблон за подобряване на транскрипции или създайте нов. Използвайте ${output} в текста на подканата, за да препратите към уловения транскрипт."
        },
        "noPrompts": "Няма налични подкани",
        "selectPrompt": "Изберете подкана",
        "createNew": "Нова подкана",
        "promptLabel": "Етикет на подканата",
        "promptLabelPlaceholder": "Въведете име на подканата",
        "promptInstructions": "Инструкции за подканата",
        "promptInstructionsPlaceholder": "Напишете инструкциите за изпълнение след транскрипция. Пример: Подобрете граматиката и яснотата на следния текст: ${output}",
        "promptTip": "Съвет: Използвайте `${output}`, за да вмъкнете транскрибирания текст в подканата.",
        "updatePrompt": "Обновяване на подканата",
        "deletePrompt": "Изтриване на подканата",
        "createPrompt": "Създаване на подкана",
        "cancel": "Отказ",
        "selectToEdit": "Изберете подкана по-горе, за да видите и редактирате подробностите ѝ.",
        "createFirst": "Натиснете „Нова подкана“ по-горе, за да създадете първата си подкана за постобработка."
      }
    },
    "history": {
      "title": "История",
      "openFolder": "Отваряне на папката с записи",
      "loading": "Зареждане на историята...",
      "empty": "Все още няма транскрипции. Започнете запис, за да изградите история!",
      "copyToClipboard": "Копиране на транскрипцията в клипборда",
      "copyError": "Транскрипцията не бе копирана в клипборда.",
      "save": "Запазване на транскрипцията",
      "unsave": "Премахване от запазените",
      "delete": "Изтриване на записа",
      "deleteError": "Изтриването на записа не бе успешно. Опитайте отново.",
      "retranscribe": "Повторна транскрипция",
      "retranscribeError": "Повторната транскрипция не бе успешна. Опитайте отново.",
      "transcribing": "Транскрибиране...",
      "transcriptionFailed": "Транскрипцията не бе успешна. Можете да опитате отново с иконата за повторен опит."
    },
    "debug": {
      "title": "Отстраняване",
      "logDirectory": {
        "title": "Папка с логове",
        "description": "Мястото, където се съхраняват лог файловете"
      },
      "logLevel": {
        "title": "Ниво на логване",
        "description": "Задаване на подробността на логовете"
      },
      "liveLogs": {
        "title": "Логове на живо",
        "description": "Преглеждайте логовете на приложението в реално време, за да диагностицирате проблеми, без да отваряте лог файла. Показват се само логове, създадени докато този панел е отворен; спазва настройката «Ниво на логване» по-горе.",
        "live": "На живо",
        "paused": "На пауза",
        "pause": "Пауза",
        "resume": "Възобновяване",
        "copied": "Копирано",
        "lineCount": "Редове: {{count}}",
        "empty": "Изчакване на логове… Записите ще се появят тук, щом приложението ги генерира."
      },
      "updateChecks": {
        "label": "Проверка за актуализации",
        "description": "Автоматична проверка за нови версии на Handy",
        "lockedDescription": "Деактивирано от системната конфигурация (HANDY_DISABLE_UPDATER)"
      },
      "soundTheme": {
        "label": "Звукова тема",
        "description": "Избор на звукова тема за сигнал при старт и край на записа"
      },
      "wordCorrectionThreshold": {
        "title": "Праг за корекция на думи",
        "description": "Чувствителност при корекция на персонализирани думи"
      },
      "historyLimit": {
        "title": "Лимит на историята",
        "description": "Максимален брой записи в историята за пазене",
        "entries": "записа"
      },
      "recordingRetention": {
        "title": "Автоматично изтриване на записи",
        "description": "Автоматично изтриване на стари записи за пестене на място",
        "never": "Никога",
        "preserveLimit": "Запазване на последните {{count}}",
        "days3": "След 3 дни",
        "weeks2": "След 2 седмици",
        "months3": "След 3 месеца",
        "placeholder": "Изберете период на съхранение..."
      },
      "alwaysOnMicrophone": {
        "label": "Постоянно активен микрофон",
        "description": "Държане на микрофона активен за по-бърз отговор"
      },
      "clamshellMicrophone": {
        "title": "Микрофон при затворен капак",
        "description": "Микрофон за използване, когато капакът на лаптопа е затворен"
      },
      "postProcessingToggle": {
        "label": "Постобработка",
        "description": "Включване на подобряване на текста с ИИ след транскрипция"
      },
      "muteWhileRecording": {
        "label": "Заглушаване при запис",
        "description": "Заглушаване на системния звук по време на запис"
      },
      "appendTrailingSpace": {
        "label": "Интервал в края",
        "description": "Добавяне на интервал след поставената транскрипция"
      },
      "keyboardImplementation": {
        "title": "Реализация на клавиатурата",
        "description": "Избор на бекенд за глобалните бързи клавиши.",
        "bindingsReset": "Бързите клавиши бяха несъвместими и са нулирани към подразбираните"
      },
      "paths": {
        "appData": "Данни на приложението:",
        "models": "Модели:",
        "settings": "Настройки:"
      },
      "pasteDelay": {
        "title": "Забавяне при поставяне (преди)",
        "description": "Забавяне (в милисекунди) след копиране на текста, преди изпращане на клавиша за поставяне. Увеличете, ако нищо не се поставя."
      },
      "pasteDelayAfter": {
        "title": "Забавяне при поставяне (след)",
        "description": "Забавяне (в милисекунди) след клавиша за поставяне, преди възстановяване на предишния клипборд. Увеличете, ако вместо транскрипцията се поставя старото съдържание на клипборда."
      },
      "holdThreshold": {
        "title": "Праг на задържане",
        "description": "Само в автоматичен режим: колко дълго (в милисекунди) трябва да се задържи комбинацията, за да се брои за задържане вместо за кратко натискане. Намалете, ако след кратки задържания записът продължава; увеличете, ако умишлените кратки натискания се приемат за задържане."
      },
      "reliablePaste": {
        "title": "Надеждно поставяне (Beta)",
        "description": "Възстановява клипборда само след като целевото приложение действително прочете транскрипцията, вместо след фиксирано забавяне. Предназначено да отстрани поставянето на старото съдържание на клипборда при натоварване на системата. Изисква начин на поставяне през клипборда; само за macOS и Windows."
      },
      "recordingBuffer": {
        "title": "Допълнителен буфер на записа",
        "description": "Допълнително време (в милисекунди) за запис след пускане на клавиша, за да се улови краят на аудиото. 0 = без допълнителен буфер."
      },
      "keyboardDiagnostic": {
        "title": "Диагностика на клавиатурата",
        "description": "Проверява дали клавиатурните събития достигат до Handy. Записва се само броят на събитията — никога кои клавиши натискате.",
        "run": "Стартиране на 10-секундна диагностика",
        "running": "Слушане… натиснете бързия си клавиш няколко пъти (напр. Option+Space)",
        "secureInputLabel": "Secure Input",
        "enabled": "включен",
        "disabled": "изключен",
        "holder": "задържан от {{name}} (pid {{pid}})",
        "holderUnknown": "няма видим държател",
        "keyDown": "Натиснат клавиш",
        "keyUp": "Отпуснат клавиш",
        "flagsChanged": "Модификатори",
        "mouse": "Мишка",
        "verdictBlocked": "Secure Input блокира клавишните събития — бързите клавиши с обикновен клавиш не могат да работят, докато това не бъде решено.",
        "verdictSuspicious": "Пристигнаха събития от модификатори, но нито едно клавишно събитие — нещо потиска клавишите, въпреки че Secure Input се отчита като изключен. Моля, съобщете за това в GitHub.",
        "verdictOk": "Клавишните събития достигат до Handy нормално.",
        "verdictNoEvents": "Не са уловени събития — натиснахте ли някакви клавиши по време на теста?",
        "failed": "Диагностиката е неуспешна: {{error}}"
      },
      "whatsNewPreview": {
        "title": "Преглед на новостите",
        "description": "Отваря последните включени бележки към изданието, без да ги маркира като видени",
        "button": "Преглед",
        "noNotes": "Не са намерени включени бележки към изданието",
        "error": "Неуспешен преглед на бележките към изданието"
      },
      "onboardingPreview": {
        "title": "Преглед на първоначалната настройка",
        "description": "Прегледайте екраните за първоначална настройка, без да променяте разрешенията, моделите или състоянието на завършване",
        "permissionsButton": "Разрешения",
        "modelsButton": "Избор на модел",
        "exitButton": "Изход от прегледа"
      }
    },
    "about": {
      "title": "За приложението",
      "version": {
        "title": "Версия",
        "description": "Текуща версия на Handy"
      },
      "appDataDirectory": {
        "title": "Папка с данни на приложението",
        "description": "Мястото, където Handy съхранява данните си"
      },
      "sourceCode": {
        "title": "Изходен код",
        "description": "Преглед на изходния код и принос",
        "button": "Вижте в GitHub"
      },
      "supportDevelopment": {
        "title": "Подкрепете разработката",
        "description": "Помогнете ни да продължим да развиваме Handy",
        "button": "Дарение"
      },
      "acknowledgments": {
        "title": "Благодарности",
        "ggml": {
          "title": "ggml",
          "description": "Високопроизводително изпълнение на автоматичния модел за разпознаване на реч Whisper на OpenAI",
          "details": "Handy използва ggml за бързо локално реч-към-текст. Благодарности на Georgi Gerganov и сътрудниците за отличната работа."
        }
      },
      "whatsNewUpdates": {
        "label": "Показвай новостите",
        "description": "Показвай бележки към изданието след актуализации на Handy"
      }
    }
  },
  "footer": {
    "checkingUpdates": "Проверка за актуализации...",
    "updateAvailableShort": "Налична актуализация",
    "upToDate": "Актуална версия",
    "updateCheckingDisabled": "Проверката за актуализации е изключена",
    "downloading": "Изтегля се... {{progress}}%",
    "installing": "Инсталиране...",
    "preparing": "Подготовка...",
    "checkForUpdates": "Проверка за актуализации",
    "portableUpdateTitle": "Необходима е ръчна актуализация",
    "portableUpdateMessage": "Преносимите инсталации не могат да бъдат актуализирани автоматично. Изтеглете инсталатора за вашата система от бутона по-долу, стартирайте го и инсталирайте в същата папка — папката Data/ (настройки, модели, записи) остава на мястото си.",
    "portableUpdateButton": "Изтегляне на инсталатора",
    "portableUpdateBrowseMessage": "Преносимите инсталации не могат да бъдат актуализирани автоматично. Изтеглете най-новата версия за вашата система от GitHub Releases и я инсталирайте в същата папка — папката Data/ (настройки, модели, записи) остава на мястото си.",
    "portableUpdateBrowseButton": "Отвори GitHub Releases"
  },
  "common": {
    "loading": "Зареждане...",
    "delete": "Изтриване",
    "back": "Назад",
    "close": "Затваряне",
    "open": "Отваряне",
    "copy": "Копиране",
    "clear": "Изчистване",
    "noOptionsFound": "Няма намерени опции"
  },
  "accessibility": {
    "permissionsDescription": "Handy се нуждае от разрешения за достъпност, за да въвежда транскрибирания текст.",
    "openSettings": "Отваряне на системните настройки"
  },
  "errors": {
    "loadDirectory": "Грешка при зареждане на папката: {{error}}",
    "micPermissionDeniedTitle": "Достъпът до микрофона е отказан",
    "micPermissionDenied": {
      "generic": "Операционната система отказа достъп до микрофона. Моля, дайте разрешение за микрофон в системните настройки.",
      "windows": "Включете достъп до микрофона в Настройки → Поверителност и сигурност → Микрофон (включително за настолни приложения).",
      "macos": "Дайте достъп до микрофона в Системни настройки → Поверителност и сигурност → Микрофон.",
      "linux": "Дайте достъп до микрофона в настройките за звук или поверителност на системата."
    },
    "noInputDeviceTitle": "Не е намерен микрофон",
    "noInputDevice": "Не е открито аудио входно устройство. Свържете микрофон или слушалки и опитайте отново.",
    "recordingFailed": "Стартирането на записа не бе успешно: {{error}}",
    "modelLoadFailed": "Зареждането на модела не бе успешно: {{model}}",
    "modelLoadFailedUnknown": "неизвестен модел",
    "pasteFailedTitle": "Неуспешно поставяне на текст",
    "pasteFailed": "Текстът не можа да бъде поставен в активното приложение.",
    "transcriptionFailedTitle": "Неуспешна транскрипция"
  },
  "appLanguage": {
    "title": "Език на приложението",
    "description": "Промяна на езика на интерфейса на Handy"
  },
  "theme": {
    "title": "Тема на приложението",
    "description": "Изберете дали Handy да следва системната тема, или да остане светла или тъмна",
    "options": {
      "system": "Системна",
      "light": "Светла",
      "dark": "Тъмна"
    }
  },
  "overlay": {
    "transcribing": "Транскрибиране...",
    "processing": "Обработка...",
    "loadingModel": "Зареждане на модела..."
  },
  "whatsNew": {
    "title": "Ново в Handy v{{version}}"
  },
  "secureInput": {
    "blockedWithCulprit_one": "{{name}} може да блокира 1 бърз клавиш",
    "blockedWithCulprit_other": "{{name}} може да блокира {{count}} бързи клавиша",
    "blockedNoCulprit_one": "macOS временно блокира 1 бърз клавиш",
    "blockedNoCulprit_other": "macOS временно блокира {{count}} бързи клавиша",
    "recorderBlockedWithCulprit": "{{name}} може да блокира промените на бързите клавиши",
    "recorderBlockedNoCulprit": "macOS временно блокира промените на бързите клавиши",
    "learnMore": "Как да поправите това",
    "recorderBlocked": "В момента не могат да се записват бързи клавиши — Secure Input на macOS блокира клавишните събития. Първо отстранете предупреждението за Secure Input.",
    "dismiss": "Отхвърляне"
  }
}

---

## File: src/i18n/locales/ca/translation.json

{
  "tray": {
    "settings": "Configuració...",
    "checkUpdates": "Comprova si hi ha actualitzacions...",
    "copyLastTranscript": "Copia l'última transcripció",
    "unloadModel": "Allibera el model de la memòria",
    "model": "Model",
    "quit": "Surt",
    "cancel": "Cancel·la",
    "secureInputWarning": "⚠ Dreceres blocades per l'entrada segura"
  },
  "sidebar": {
    "general": "General",
    "models": "Models",
    "advanced": "Avançat",
    "postProcessing": "Postprocessament",
    "history": "Historial",
    "debug": "Depuració",
    "about": "Quant a"
  },
  "onboarding": {
    "subtitle": "Per començar, trieu un model de transcripció",
    "existingModelsTitle": "Models compatibles",
    "downloadModelsTitle": "Disponibles per baixar",
    "showAllModels": "Mostra tots els {{total}} models",
    "showFewerModels": "Mostra menys models",
    "recommended": "Recomanat",
    "customModelDescription": "Sense suport oficial",
    "migration": {
      "title": "Cal actualitzar els vostres models",
      "description": "Handy ara utilitza un tipus de model més nou. Baixeu la versió nova per continuar transcrivint. Les versions antigues s'eliminaran.",
      "replaces": "Substitueix {{name}}",
      "oldFormat": "Format antic",
      "failed": "Ha fallat la baixada",
      "retry": "Torna-ho a provar",
      "chooseAnother": "Trieu un altre model",
      "download": "Baixa els models ({{size}})",
      "continue": "Continua",
      "timeRemaining": "Queden {{time}}"
    },
    "modelCard": {
      "accuracy": "precisió",
      "speed": "velocitat"
    },
    "models": {
      "small": {
        "name": "Whisper Small",
        "description": "Ràpid i força precís."
      },
      "medium": {
        "name": "Whisper Medium",
        "description": "Bona precisió i velocitat mitjana."
      },
      "turbo": {
        "name": "Whisper Turbo",
        "description": "Equilibri entre precisió i velocitat."
      },
      "large": {
        "name": "Whisper Large",
        "description": "Bona precisió, però lent."
      },
      "breeze-asr": {
        "name": "Breeze ASR",
        "description": "Optimitzat per al xinès mandarí de Taiwan. Admet l'alternança de llengües."
      }
    },
    "errors": {
      "selectModel": "No s'ha pogut seleccionar el model"
    },
    "permissions": {
      "title": "Permisos necessaris",
      "description": "El Handy demana alguns permisos per funcionar correctament.",
      "microphone": {
        "title": "Accés al micròfon",
        "description": "Necessari per escoltar la vostra veu i transcriure-la."
      },
      "accessibility": {
        "title": "Accés a l'accessibilitat",
        "description": "Necessari per escriure el text transcrit a les aplicacions."
      },
      "grant": "Concediu el permís",
      "granted": "Concedit",
      "waiting": "S'està esperant...",
      "allGranted": "Tot a punt!",
      "errors": {
        "checkFailed": "No s'han pogut comprovar els permisos. Torneu-ho a provar.",
        "requestFailed": "No s'ha pogut sol·licitar el permís. Torneu-ho a provar."
      }
    }
  },
  "modelSelector": {
    "custom": "Personalitzat",
    "legacy": "Antic",
    "streaming": "En temps real",
    "active": "Actiu",
    "switching": "S'està canviant...",
    "noModelsAvailable": "No hi ha models disponibles",
    "verifying": "S'està verificant {{modelName}}...",
    "verifyingGeneric": "S'està verificant...",
    "downloading": "S'està baixant: {{percentage}}%",
    "downloadingMultiple": "S'estan baixant {{count}} models...",
    "modelReady": "Model a punt",
    "loading": "S'està carregant {{modelName}}...",
    "loadingGeneric": "S'està carregant...",
    "modelError": "Error del model",
    "modelUnloaded": "Model alliberat de la memòria",
    "noModelDownloadRequired": "No hi ha cap model: cal baixar-ne un",
    "deleteModel": "Suprimiu {{modelName}}",
    "downloadSpeed": "{{speed}} MB/s",
    "cancel": "Cancel·la",
    "cancelDownload": "Cancel·la la baixada",
    "capabilities": {
      "languageSelection": "Admet diverses llengües d'entrada",
      "singleLanguage": "Només admet aquesta llengua",
      "languageCount": "{{total}} llengües",
      "languageOnly": "Només {{language}}",
      "translation": "Pot traduir a l'anglès",
      "translate": "Tradueix",
      "streaming": "Mostra la transcripció en temps real mentre parleu"
    }
  },
  "settings": {
    "modelSettings": {
      "title": "Configuració de {{model}}"
    },
    "general": {
      "title": "General",
      "shortcut": {
        "title": "Dreceres del Handy",
        "description": "Configura les dreceres de teclat per iniciar l'enregistrament de veu per a la transcripció",
        "loading": "S'estan carregant les dreceres...",
        "none": "No hi ha cap drecera configurada",
        "notFound": "No s'ha trobat la drecera",
        "pressKeys": "Premeu les tecles...",
        "bindings": {
          "transcribe": {
            "name": "Drecera de transcripció",
            "description": "La drecera de teclat per enregistrar i transcriure la vostra veu."
          },
          "cancel": {
            "name": "Drecera de cancel·lació",
            "description": "La drecera de teclat per cancel·lar l'enregistrament actual."
          },
          "transcribe_with_post_process": {
            "name": "Drecera de postprocessament",
            "description": "Opcional: una drecera específica que sempre aplica postprocessament amb IA a la transcripció."
          }
        },
        "errors": {
          "restore": "No s'ha pogut restaurar la drecera original",
          "set": "No s'ha pogut definir la drecera: {{error}}",
          "reset": "No s'ha pogut restablir la drecera al valor original"
        }
      },
      "language": {
        "title": "Llengua",
        "description": "Seleccioneu la llengua per al reconeixement de la parla. L'opció automàtica detecta la llengua, mentre que seleccionar-ne una d'específica pot millorar-ne la precisió.",
        "searchPlaceholder": "Cerca llengües...",
        "noResults": "No s'ha trobat cap llengua",
        "auto": "Automàtica"
      },
      "shortcutActivation": {
        "title": "Comportament de la drecera",
        "description": "Trieu com la drecera de transcripció inicia i atura l'enregistrament.",
        "options": {
          "holdOrToggle": "Automàtic",
          "pushToTalk": "Mantén premuda",
          "toggle": "Commuta"
        },
        "descriptions": {
          "hold_or_toggle": "Manteniu-la premuda per enregistrar o feu una pulsació breu per iniciar o aturar l'enregistrament",
          "push_to_talk": "Enregistreu mentre manteniu premuda la drecera",
          "toggle": "Premeu la drecera per iniciar l'enregistrament i torneu-la a prémer per aturar-lo"
        }
      }
    },
    "models": {
      "title": "Models de transcripció",
      "description": "Seleccioneu un model de transcripció o baixeu-ne més. Els models ofereixen diferents nivells de precisió i velocitat.",
      "searchPlaceholder": "Cerqueu models pel nom…",
      "yourModels": "Models baixats",
      "availableModels": "Disponibles per baixar",
      "deleteConfirm": "Segur que voleu suprimir {{modelName}}? Haureu de tornar-lo a baixar per utilitzar-lo.",
      "deleteActiveConfirm": "{{modelName}} és el model actiu. Si el suprimiu, no podreu fer transcripcions fins que seleccioneu un altre model. N'esteu segur?",
      "deleteTitle": "Suprimeix el model",
      "retiredNotice": {
        "title": "Els models antics ja no funcionen amb Handy",
        "description": "Ocupen {{size}} a l'ordinador.",
        "delete": "Suprimeix els models",
        "deleted": "S'han suprimit els models antics",
        "deleteFailed": "No s'han pogut suprimir alguns models antics. Consulteu el registre per a més detalls."
      },
      "filters": {
        "allLanguages": "Totes les llengües",
        "streaming": "Filtra els models que admeten la transcripció en temps real",
        "translation": "Filtra els models que admeten la traducció a l'anglès"
      },
      "rescan": {
        "label": "Torna a cercar",
        "tooltip": "Torna a cercar models afegits a la carpeta de models o a la memòria cau de Hugging Face des de fora del Handy"
      },
      "noModelsMatch": "Cap model no coincideix amb aquest filtre."
    },
    "sound": {
      "title": "So",
      "microphone": {
        "title": "Micròfon",
        "description": "Selecciona el micròfon preferit",
        "placeholder": "Seleccionaun micròfon...",
        "loading": "S'està carregant..."
      },
      "channel": {
        "title": "Canal d'entrada",
        "description": "Seleccioneu el canal d'entrada des del qual voleu enregistrar. Feu servir aquesta opció si la interfície d'àudio té diverses entrades.",
        "average": "Fes la mitjana de tots els canals",
        "channel": "Canal {{n}}"
      },
      "audioFeedback": {
        "label": "Avisos sonors",
        "description": "Reprodueix un so quan s'inicia i s'atura l'enregistrament"
      },
      "outputDevice": {
        "title": "Dispositiu de sortida",
        "description": "Selecciona el dispositiu de sortida d'àudio preferit per als avisos sonors",
        "placeholder": "Selecciona un dispositiu de sortida...",
        "loading": "S'està carregant..."
      },
      "volume": {
        "title": "Volum",
        "description": "Ajusta el volum dels avisos sonors"
      }
    },
    "advanced": {
      "groups": {
        "app": "Aplicació",
        "output": "Sortida",
        "transcription": "Transcripció",
        "history": "Historial",
        "experimental": "Experimental"
      },
      "experimentalToggle": {
        "label": "Funcions experimentals",
        "description": "Activa les funcions experimentals que encara estan en desenvolupament."
      },
      "lazyStreamClose": {
        "label": "Mantén el micròfon obert entre transcripcions",
        "description": "Manté el flux d'àudio del micròfon obert durant 30 segons després d'aturar l'enregistrament per reduir la latència entre transcripcions consecutives. Mentre és actiu, pot reduir la qualitat de l'àudio Bluetooth."
      },
      "acceleration": {
        "transcribe": {
          "title": "Acceleració per maquinari",
          "description": "Trieu el maquinari que s'utilitza per a la transcripció. L'opció automàtica utilitza la GPU si està disponible i, si no, recorre a la CPU."
        },
        "gpuDevice": {
          "auto": "Automàtica"
        }
      },
      "startHidden": {
        "label": "Inicia ocult",
        "description": "Inicia a la safata del sistema sense obrir la finestra."
      },
      "autostart": {
        "label": "Inicia en entrar a la sessió",
        "description": "Inicia automàticament el Handy quan entreu a la sessió de l'ordinador."
      },
      "showTrayIcon": {
        "label": "Mostra la icona a la safata",
        "description": "Mostra la icona del Handy a la safata del sistema."
      },
      "overlay": {
        "style": {
          "title": "Finestra superposada",
          "description": "Tria la finestra superposada d'enregistrament: Cap l'amaga, Mínima mostra un indicador compacte i En directe mostra la transcripció en temps real mentre parleu (només amb models compatibles; cerqueu el distintiu En temps real al selector de models). Al Linux es recomana Cap.",
          "options": {
            "none": "Cap",
            "minimal": "Mínima",
            "live": "En directe"
          }
        },
        "position": {
          "title": "Posició de la finestra superposada",
          "description": "On apareix la finestra superposada a la pantalla durant l'enregistrament i la transcripció.",
          "options": {
            "bottom": "A baix",
            "top": "A dalt"
          }
        }
      },
      "pasteMethod": {
        "title": "Mètode d'enganxament",
        "description": "Trieu com s'insereix el text. Directe: simula l'escriptura mitjançant l'entrada del sistema. Cap: no enganxa el text, només actualitza l'historial i el porta-retalls.",
        "options": {
          "clipboard": "Porta-retalls ({{modifier}}+V)",
          "clipboardCtrlShiftV": "Porta-retalls (Ctrl+Maj+V)",
          "clipboardShiftInsert": "Porta-retalls (Maj+Insert)",
          "direct": "Directe",
          "none": "Cap",
          "externalScript": "Script extern"
        },
        "externalScriptPlaceholder": "/camí/al/vostre/script.sh"
      },
      "typingTool": {
        "title": "Eina d'escriptura",
        "description": "Trieu l'eina d'escriptura del Linux que s'utilitzarà amb el mètode d'enganxament directe. L'opció automàtica detecta i utilitza la millor eina disponible per al sistema.",
        "options": {
          "auto": "Automàtica (recomanada)"
        }
      },
      "clipboardHandling": {
        "title": "Gestió del porta-retalls",
        "description": "«No modifiqueu el porta-retalls» conserva el contingut actual del porta-retalls després de la transcripció. «Copieu al porta-retalls» hi deixa el resultat de la transcripció després d'enganxar-lo.",
        "options": {
          "dontModify": "No modifiqueu el porta-retalls",
          "copyToClipboard": "Copia al porta-retalls"
        }
      },
      "autoSubmit": {
        "title": "Enviament automàtic",
        "description": "Envia automàticament la combinació de tecles seleccionada després d'inserir el text. S'utilitza Cmd+Enter al macOS i Super+Enter al Windows i al Linux.",
        "options": {
          "off": "Desactivat",
          "enter": "Enter",
          "cmdEnter": "Cmd+Enter",
          "superEnter": "Super+Enter",
          "ctrlEnter": "Ctrl+Enter"
        }
      },
      "translateToEnglish": {
        "label": "Traduïu a l'anglès",
        "description": "Tradueix automàticament la parla d'altres llengües a l'anglès durant la transcripció."
      },
      "modelUnload": {
        "title": "Allibera el model de la memòria",
        "description": "Allibera automàticament la memòria de la GPU i de la CPU quan el model no s'ha utilitzat durant el temps especificat",
        "options": {
          "never": "Mai",
          "immediately": "Immediatament",
          "min2": "Al cap de 2 minuts",
          "min5": "Al cap de 5 minuts",
          "min10": "Al cap de 10 minuts",
          "min15": "Al cap de 15 minuts",
          "hour1": "Al cap d'1 hora",
          "sec15": "Al cap de 15 segons (depuració)"
        }
      },
      "customWords": {
        "title": "Paraules personalitzades",
        "description": "Ajudeu els models compatibles a reconèixer noms i termes especialitzats. Actualment, la correcció per similitu es limita a paraules amb lletres de la A a la Z i números.",
        "placeholder": "Afegiu una paraula",
        "add": "Afegiu",
        "remove": "Suprimiu {{word}}",
        "duplicate": "«{{word}}» ja existeix"
      },
      "voiceActivityDetection": {
        "title": "Detecció d'activitat de veu",
        "description": "Filtra els silencis dels enregistraments. Els models compatibles amb la transcripció en temps real utilitzen un marge final de detecció de veu més llarg; si desactiveu la detecció, s'enregistra l'àudio sense filtrar."
      },
      "longFormChunking": {
        "title": "Divisió d'àudio llarg",
        "description": "Divideix els enregistraments llargs a les pauses abans de transcriure'ls, cosa que normalment millora el rendiment de la transcripció."
      },
      "vadBackend": {
        "title": "Motor de detecció d'activitat de veu",
        "description": "Trieu el detector utilitzat per filtrar els silencis. L'Earshot és experimental; torneu al Silero si es corta la parla o es conserva el soroll.",
        "options": {
          "silero": "Silero",
          "earshot": "Earshot"
        }
      },
      "fillerWordRemoval": {
        "title": "Elimina les falques",
        "description": "Elimina les expressions de vacil·lació habituals de les transcripcions. Desactiveu aquesta opció per conservar-les."
      },
      "chineseScript": {
        "title": "Escriptura xinesa",
        "description": "Escriu les transcripcions en mandarí i cantonès amb caràcters simplificats o tradicionals. Els altres idiomes no es modifiquen.",
        "options": {
          "asTranscribed": "Tal com s'ha transcrit",
          "simplified": "Simplificat",
          "traditional": "Tradicional"
        }
      }
    },
    "postProcessing": {
      "hotkey": {
        "title": "Drecera de teclat"
      },
      "api": {
        "title": "API",
        "provider": {
          "title": "Proveïdor",
          "description": "Seleccioneu un proveïdor compatible amb OpenAI o Anthropic."
        },
        "apiFormat": {
          "title": "Format de l'API",
          "description": "Format de sol·licitud que espera el punt de connexió personalitzat.",
          "options": {
            "openai": "Compatible amb OpenAI (Chat Completions)",
            "anthropic": "Anthropic Messages"
          }
        },
        "appleIntelligence": {
          "unavailable": "L'Apple Intelligence no està disponible en aquest dispositiu. Cal un Mac amb Apple Silicon i macOS Tahoe (26.0) o posterior, amb l'Apple Intelligence activada a la configuració del sistema."
        },
        "baseUrl": {
          "title": "URL de base",
          "description": "URL de base de l'API del proveïdor seleccionat. Només es pot editar la del proveïdor personalitzat.",
          "placeholder": "https://api.openai.com/v1",
          "placeholderAnthropic": "https://api.anthropic.com/v1"
        },
        "apiKey": {
          "title": "Clau de l'API",
          "description": "Clau de l'API del proveïdor seleccionat.",
          "placeholder": "sk-..."
        },
        "model": {
          "title": "Model",
          "descriptionCustom": "Introduïu l'identificador de model que espera el punt de connexió personalitzat.",
          "descriptionDefault": "Trieu un model ofert pel proveïdor seleccionat.",
          "placeholderWithOptions": "Cerqueu o seleccioneu un model",
          "placeholderNoOptions": "Escriviu el nom d'un model",
          "refreshModels": "Actualitzeu els models"
        }
      },
      "prompts": {
        "title": "Indicació",
        "selectedPrompt": {
          "title": "Indicació seleccionada",
          "description": "Seleccioneu una plantilla per millorar les transcripcions o creeu-ne una de nova. Utilitzeu ${output} dins del text de la indicació per fer referència a la transcripció enregistrada."
        },
        "noPrompts": "No hi ha indicacions disponibles",
        "selectPrompt": "Seleccioneu una indicació",
        "createNew": "Creeu una indicació nova",
        "promptLabel": "Nom de la indicació",
        "promptLabelPlaceholder": "Introduïu el nom de la indicació",
        "promptInstructions": "Instruccions de la indicació",
        "promptInstructionsPlaceholder": "Escriviu les instruccions que s'executaran després de la transcripció. Exemple: Millora la gramàtica i la claredat del text següol: ${output}",
        "promptTip": "Consell: utilitzeu `${output}` per inserir el text transcrit a la indicació.",
        "updatePrompt": "Actualitzeu la indicació",
        "deletePrompt": "Suprimiu la indicació",
        "createPrompt": "Creeu la indicació",
        "cancel": "Cancel·leu",
        "selectToEdit": "Seleccioneu una indicació more amunt per veure'n i editar-ne els detalls.",
        "createFirst": "Feu clic a «Creeu una indicació nova» més amunt per crear la primera indicació de postprocessament."
      }
    },
    "history": {
      "title": "Historial",
      "openFolder": "Obriu la carpeta d'enregistraments",
      "loading": "S'està carregant l'historial...",
      "empty": "Encara no hi ha transcripcions. Comenceu a enregistrar per crear l'historial!",
      "copyToClipboard": "Copia la transcripció al porta-retalls",
      "copyError": "No s'ha pogut copiar la transcripció al porta-retalls.",
      "save": "Desa la transcripció",
      "unsave": "Treu de les desades",
      "delete": "Suprimeix l'entrada",
      "deleteError": "No s'ha pogut suprimir l'entrada. Torneu-ho a provar.",
      "retranscribe": "Torneu a transcriure",
      "retranscribeError": "No s'ha pogut tornar a transcriure. Torneu-ho a provar.",
      "transcribing": "S'està transcrivint...",
      "transcriptionFailed": "La transcripció ha fallat. Podeu tornar a transcriure amb la icona de reintent."
    },
    "debug": {
      "title": "Depuració",
      "logDirectory": {
        "title": "Directori de registres",
        "description": "Ubicació on es desen els fitxers de registre"
      },
      "logLevel": {
        "title": "Nivell de registre",
        "description": "Establiu el nivell de detall dels registres"
      },
      "liveLogs": {
        "title": "Registres en directe",
        "description": "Mostra els registres de l'aplicació en temps real per diagnosticar problemes sense obrir el fitxer de registre. Només es mostren els registres emesos mentre aquest tauler és obert; es respecta el nivell de registre establert més amunt.",
        "live": "En directe",
        "paused": "En pausa",
        "pause": "Pauseu",
        "resume": "Repreneu",
        "copied": "Copiat",
        "lineCount": "{{count}} línies",
        "empty": "S'estan esperant registres… Les entrades apareixeran aquí a mesura que l'aplicació les emeti."
      },
      "updateChecks": {
        "label": "Comproveu si hi ha actualitzacions",
        "description": "Comprova automàticament si hi ha versions noves del Handy",
        "lockedDescription": "Desactivat per la configuració del sistema (HANDY_DISABLE_UPDATER)"
      },
      "whatsNewPreview": {
        "title": "Previsualització de les novetats",
        "description": "Obre les últimes notes de la versió incloses sense marcar-les com a llegides",
        "button": "Previsualitzeu",
        "noNotes": "No s'han trobat notes de la versió incloses",
        "error": "No s'han pogut previsualitzar les notes de la versió"
      },
      "onboardingPreview": {
        "title": "Previsualització de la configuració inicial",
        "description": "Previsualitza les pantalles de configuració inicial sense canviar els permisos, os models ni l'estat de compleció",
        "permissionsButton": "Permisos",
        "modelsButton": "Selecció del model",
        "exitButton": "Sortiu de la previsualització"
      },
      "soundTheme": {
        "label": "Tema de so",
        "description": "Trieu un tema de so per als avisos d'inici i d'aturada de l'enregistrament"
      },
      "wordCorrectionThreshold": {
        "title": "Llindar de correcció de paraules",
        "description": "Sensibilitat de les correccions de paraules personalitzades"
      },
      "historyLimit": {
        "title": "Límit de l'historial",
        "description": "Nombre màxim d'entrades de l'historial que es conservaran",
        "entries": "entrades"
      },
      "recordingRetention": {
        "title": "Supressió automàtica d'enregistraments",
        "description": "Suprimeix automàticament els enregistraments antics per estalviar espai",
        "never": "Mai",
        "preserveLimit": "Conserva els últims {{count}}",
        "days3": "Al cap de 3 dies",
        "weeks2": "Al cap de 2 setmanes",
        "months3": "Al cap de 3 mesos",
        "placeholder": "Selecciona el període de conservació..."
      },
      "alwaysOnMicrophone": {
        "label": "Micròfon sempre actiu",
        "description": "Mantén el micròfon actiu per respondre més de pressa"
      },
      "clamshellMicrophone": {
        "title": "Micròfon amb la tapa tancada",
        "description": "Micròfon que s'utilitzarà quan la tapa del portàtil estigui tancada"
      },
      "postProcessingToggle": {
        "label": "Postprocessament",
        "description": "Activa la millora del text amb IA després de la transcripció"
      },
      "muteWhileRecording": {
        "label": "Silencia durant l'enregistrament",
        "description": "Silencia l'àudio del sistema durant l'enregistrament"
      },
      "appendTrailingSpace": {
        "label": "Afegiu un espai al final",
        "description": "Afegeix un espai après de la transcripció enganxada"
      },
      "keyboardImplementation": {
        "title": "Implementació del teclat",
        "description": "Tria el motor de dreceres de teclat.",
        "bindingsReset": "Les dreceres de teclat eren incompatibles i s'han restablert als valors per defecte"
      },
      "paths": {
        "appData": "Dades de l'aplicació:",
        "models": "Models:",
        "settings": "Configuració:"
      },
      "pasteDelay": {
        "title": "Retard abans d'enganxar",
        "description": "Retard (en mil·lisegons) després de copiar el text i abans d'enviar la combinació de tecles per enganxar-lo. Augmenteu-lo si no s'enganxa res."
      },
      "pasteDelayAfter": {
        "title": "Retard après d'enganxar",
        "description": "Retard (en mil·lisegons) després d'enviar la combinació de tecles per enganxar i abans de restaurar el porta-retalls anterior. Augmenteu-lo si s'enganxa el contingut antic del porta-retalls en lloc de la transcripció."
      },
      "holdThreshold": {
        "title": "Llindar de pulsació llarga",
        "description": "Només en mode automàtic: temps (en mil·lisegons) que cal mantenir premuda la drecera perquè es consideri una pulsació llarga en lloc d'una de breu. Reduïu-lo si les pulsacions llargues curtes deixen l'enregistrament actiu; augmenteu-lo si les pulsacions breus intencionades es tracten com a llargues."
      },
      "reliablePaste": {
        "title": "Enganxament fiable (beta)",
        "description": "Restaura el porta-retalls només quan l'aplicació de destinació ha llegit la transcripció, en lloc de fer-ho després d'un retard fix. Pensat per evitar que s'enganxi el contingut antic del porta-retalls quan el sistema està carregat. Requereix un mètode d'enganxament amb el porta-retalls; només al macOS i al Windows."
      },
      "recordingBuffer": {
        "title": "Marge addicional d'enregistrament",
        "description": "Temps addicional (en mil·lisegons) durant el qual es continua enregistrant després de deixar anar la tecla per capturar l'àudio final. 0 = sense marge addicional."
      },
      "keyboardDiagnostic": {
        "title": "Diagnòstic del teclat",
        "description": "Comprova si els esdeveniments del teclat arriben al Handy. Només se'n registra el recompte, mai les tecles que premeu.",
        "run": "Executa un diagnòstic de 10 s",
        "running": "S'està escoltant… prema la drecera unes quantes vegades (p. ex., Option+Space)",
        "secureInputLabel": "Entrada segura",
        "enabled": "activada",
        "disabled": "desactivada",
        "holder": "en ús per {{name}} (pid {{pid}})",
        "holderUnknown": "cap procés responsable visible",
        "keyDown": "Tecla premuda",
        "keyUp": "Tecla alliberada",
        "flagsChanged": "Modificadors",
        "mouse": "Ratolí",
        "verdictBlocked": "L'entrada segura bloca els esdeveniments de tecla. Les dreceres amb tecles no poden funcionar fins que es resolgui.",
        "verdictSuspicious": "Han arribat esdeveniments de modificadors, però no de tecles. Alguna cosa suprimeix les tecles tot i que l'entrada segura consta com a desactivada. Informeu-ne al GitHub.",
        "verdictOk": "Els esdeveniments de tecla arriben al Handy amb normalitat.",
        "verdictNoEvents": "No s'ha capturat cap esdeveniment. Heu premut alguna tecla durant la prova?",
        "failed": "El diagnòstic ha fallat: {{error}}"
      }
    },
    "about": {
      "title": "Quant a",
      "version": {
        "title": "Versió",
        "description": "Versió actual del Handy"
      },
      "whatsNewUpdates": {
        "label": "Mostra les novetats",
        "description": "Mostra les notes de la versió després d'actualitzar el Handy"
      },
      "appDataDirectory": {
        "title": "Directori de dades de l'aplicació",
        "description": "Ubicació on el Handy desa les dades"
      },
      "sourceCode": {
        "title": "Codi font",
        "description": "Consulta el codi font i col·laboreu-hi",
        "button": "Mostra al GitHub"
      },
      "supportDevelopment": {
        "title": "Dona suport al desenvolupament",
        "description": "Ajudeu-nos a continuar desenvolupant el Handy",
        "button": "Feu una donació"
      },
      "acknowledgments": {
        "title": "Agraïments",
        "ggml": {
          "title": "ggml",
          "description": "Biblioteca de tensors d'alt rendiment per a la inferència d'aprenentatge automàtic al dispositiu",
          "details": "La transcripció local de veu a text del Handy es basa en el transcribe.cpp i el ggml. Gràcies a la gran feina de Georgi Gerganov i dels col·laboradors."
        }
      }
    }
  },
  "footer": {
    "checkingUpdates": "S'està comprovant si hi ha actualitzacions...",
    "updateAvailableShort": "Hi ha una actualització disponible",
    "upToDate": "Actualitzat",
    "updateCheckingDisabled": "Comprovació d'actualitzacions desactivada",
    "downloading": "S'està baixant... {{progress}}%",
    "installing": "S'està instal·lant...",
    "preparing": "S'està preparant...",
    "checkForUpdates": "Comprova si hi ha actualitzacions",
    "portableUpdateTitle": "Cal actualitzar manualment",
    "portableUpdateMessage": "Les instal·lacions portables no es poden actualitzar automàticament. Baixa l'instal·lador per al vostre sistema a continuació, executeu-lo i instal·leu-lo a la mateixa carpeta. La carpeta Data/ (configuració, models i enregistraments) es conserva.",
    "portableUpdateButton": "Baixa l'instal·lador",
    "portableUpdateBrowseMessage": "Les instal·lacions portables no es poden actualitzar automàticament. Baixa l'última versió per al vostre sistema des de GitHub Releases i instal·leu-la a la mateixa carpeta. La carpeta Data/ (configuració, models i enregistraments) es conserva.",
    "portableUpdateBrowseButton": "Obriu GitHub Releases"
  },
  "whatsNew": {
    "title": "Novetats del Handy v{{version}}"
  },
  "common": {
    "loading": "S'està carregant...",
    "delete": "Suprimeix",
    "back": "Enrere",
    "close": "Tanca",
    "open": "Obre",
    "copy": "Copia",
    "clear": "Neteja",
    "noOptionsFound": "No s'ha trobat cap opció"
  },
  "accessibility": {
    "permissionsDescription": "El Handy necessita permisos d'accessibilitat per escriure el text transcrit.",
    "openSettings": "Obriu la configuració del sistema"
  },
  "errors": {
    "loadDirectory": "Error en carregar el directori: {{error}}",
    "micPermissionDeniedTitle": "Accés al micròfon denegat",
    "micPermissionDenied": {
      "generic": "El sistema operatiu ha denegat l'accés al micròfon. Concediu el permís del micròfon a la configuració del sistema.",
      "windows": "Activa l'accés al micròfon a Configuració → Privadesa i seguretat → Micròfon (inclòs l'accés de les aplicacions d'escriptori).",
      "macos": "Concediu l'accés al micròfon a Configuració del sistema → Privadesa i seguretat → Micròfon.",
      "linux": "Concediu l'accés al micròfon a la configuració de so o de privadesa del sistema."
    },
    "noInputDeviceTitle": "No s'ha trobat cap micròfon",
    "noInputDevice": "No s'ha detectat cap dispositiu d'entrada d'àudio. Connecteu un micròfon o uns auriculars amb micròfon i torneu-ho a provar.",
    "recordingFailed": "No s'ha pogut iniciar l'enregistrament: {{error}}",
    "modelLoadFailed": "No s'ha pogut carregar el model: {{model}}",
    "modelLoadFailedUnknown": "model desconegut",
    "pasteFailedTitle": "No s'ha pogut enganxar el text",
    "pasteFailed": "No s'ha pogut enganxar el text a l'aplicació activa.",
    "transcriptionFailedTitle": "La transcripció ha fallat"
  },
  "appLanguage": {
    "title": "Llengua de l'aplicació",
    "description": "Canvia la llengua de la interfície del Handy"
  },
  "theme": {
    "title": "Tema de l'aplicació",
    "description": "Tria si el Handy segueix el tema del sistema o es manté en mode clar o fosc",
    "options": {
      "system": "Sistema",
      "light": "Clar",
      "dark": "Fosc"
    }
  },
  "overlay": {
    "transcribing": "S'està transcrivint...",
    "processing": "S'està processant...",
    "loadingModel": "S'està carregant el model..."
  },
  "secureInput": {
    "blockedWithCulprit_one": "{{name}} pot estar blocant 1 drecera",
    "blockedWithCulprit_other": "{{name}} pot estar blocant {{count}} dreceres",
    "blockedNoCulprit_one": "El macOS està blocant temporalment 1 drecera",
    "blockedNoCulprit_other": "El macOS està blocant temporalment {{count}} dreceres",
    "recorderBlockedWithCulprit": "{{name}} pot estar blocant els canvis de dreceres",
    "recorderBlockedNoCulprit": "El macOS està blocant temporalment els canvis de dreceres",
    "learnMore": "Com es resol",
    "recorderBlocked": "Ara no se'n poden enregistrar dreceres: l'entrada segura del macOS bloca els esdeveniments de tecla. Resol primer l'avís d'entrada segura.",
    "dismiss": "Descarta"
  }
}

---

## File: src/i18n/locales/cs/translation.json

{
  "tray": {
    "settings": "Nastavení...",
    "checkUpdates": "Zkontrolovat aktualizace...",
    "copyLastTranscript": "Zkopírovat poslední přepis",
    "unloadModel": "Uvolnit model",
    "model": "Model",
    "quit": "Ukončit",
    "cancel": "Zrušit",
    "secureInputWarning": "⚠ Zkratky blokuje Secure Input"
  },
  "sidebar": {
    "general": "Obecné",
    "models": "Modely",
    "advanced": "Pokročilé",
    "postProcessing": "Následné zpracování",
    "history": "Historie",
    "debug": "Ladění",
    "about": "O aplikaci"
  },
  "onboarding": {
    "subtitle": "Pro začátek vyberte model pro přepis",
    "existingModelsTitle": "Kompatibilní modely",
    "downloadModelsTitle": "Dostupné ke stažení",
    "showAllModels": "Zobrazit všech {{total}} modelů",
    "showFewerModels": "Zobrazit méně modelů",
    "recommended": "Doporučeno",
    "customModelDescription": "Oficiálně nepodporováno",
    "migration": {
      "title": "Vaše modely potřebují aktualizaci",
      "description": "Handy nyní používá novější typ modelů. Stáhněte si novou verzi, abyste mohli dál přepisovat. Staré verze budou odstraněny.",
      "replaces": "Nahrazuje {{name}}",
      "oldFormat": "Starý formát",
      "failed": "Stahování selhalo",
      "retry": "Zkusit znovu",
      "chooseAnother": "Vybrat jiný model",
      "download": "Stáhnout modely ({{size}})",
      "continue": "Pokračovat",
      "timeRemaining": "Zbývá {{time}}"
    },
    "modelCard": {
      "accuracy": "přesnost",
      "speed": "rychlost"
    },
    "models": {
      "small": {
        "name": "Whisper Small",
        "description": "Rychlý a poměrně přesný."
      },
      "medium": {
        "name": "Whisper Medium",
        "description": "Dobrá přesnost, střední rychlost"
      },
      "turbo": {
        "name": "Whisper Turbo",
        "description": "Vyvážená přesnost a rychlost."
      },
      "large": {
        "name": "Whisper Large",
        "description": "Dobrá přesnost, ale pomalý."
      },
      "breeze-asr": {
        "name": "Breeze ASR",
        "description": "Optimalizováno pro tchajwanskou mandarínštinu. Podpora přepínání jazyků."
      }
    },
    "errors": {
      "selectModel": "Nepodařilo se vybrat model"
    },
    "permissions": {
      "title": "Vyžadována oprávnění",
      "description": "Handy potřebuje několik oprávnění pro správné fungování.",
      "microphone": {
        "title": "Přístup k mikrofonu",
        "description": "Potřebný pro zachycení vašeho hlasu k přepisu."
      },
      "accessibility": {
        "title": "Přístup k usnadnění",
        "description": "Potřebný pro psaní přepsaného textu do vašich aplikací."
      },
      "grant": "Udělit oprávnění",
      "granted": "Uděleno",
      "waiting": "Čekání...",
      "allGranted": "Vše připraveno!",
      "errors": {
        "checkFailed": "Nepodařilo se zkontrolovat oprávnění. Zkuste to prosím znovu.",
        "requestFailed": "Nepodařilo se požádat o oprávnění. Zkuste to prosím znovu."
      }
    }
  },
  "modelSelector": {
    "custom": "Vlastní",
    "streaming": "Streaming",
    "active": "Aktivní",
    "noModelsAvailable": "Nejsou dostupné žádné modely",
    "verifying": "Ověřování {{modelName}}...",
    "verifyingGeneric": "Ověřování...",
    "downloading": "Stahování {{percentage}}%",
    "downloadingMultiple": "Stahování {{count}} modelů...",
    "modelReady": "Model připraven",
    "loading": "Načítám {{modelName}}...",
    "loadingGeneric": "Načítání...",
    "modelError": "Chyba modelu",
    "modelUnloaded": "Model uvolněn",
    "noModelDownloadRequired": "Žádný model - je nutné stáhnout",
    "deleteModel": "Smazat {{modelName}}",
    "switching": "Přepínání...",
    "downloadSpeed": "{{speed}} MB/s",
    "capabilities": {
      "languageSelection": "Podporuje více vstupních jazyků",
      "languageCount": "{{total}} jazyků",
      "translation": "Umí překládat do angličtiny",
      "translate": "Přeložit do angličtiny",
      "singleLanguage": "Podporuje pouze tento jazyk",
      "languageOnly": "Pouze {{language}}",
      "streaming": "Zobrazuje přepis živě, jak mluvíte"
    },
    "cancel": "Zrušit",
    "cancelDownload": "Zrušit stahování",
    "legacy": "Zastaralé"
  },
  "settings": {
    "modelSettings": {
      "title": "Nastavení {{model}}"
    },
    "models": {
      "title": "Modely přepisu",
      "description": "Vyberte model přepisu nebo stáhněte další modely. Různé modely nabízejí různé úrovně přesnosti a rychlosti.",
      "deleteConfirm": "Opravdu chcete smazat {{modelName}}? Pro opětovné použití jej budete muset znovu stáhnout.",
      "deleteActiveConfirm": "{{modelName}} je váš aktivní model. Jeho smazáním se zastaví přepisy, dokud nevyberete nový model. Opravdu chcete pokračovat?",
      "deleteTitle": "Smazat model",
      "retiredNotice": {
        "title": "Staré modely už s Handy nefungují",
        "description": "Zabírají {{size}} na vašem počítači.",
        "delete": "Smazat modely",
        "deleted": "Staré modely byly smazány",
        "deleteFailed": "Některé staré modely se nepodařilo smazat. Podrobnosti najdete v protokolu."
      },
      "filters": {
        "allLanguages": "Všechny jazyky",
        "streaming": "Filtrovat modely podporující živý přepis",
        "translation": "Filtrovat modely podporující překlad do angličtiny"
      },
      "noModelsMatch": "Tomuto filtru neodpovídají žádné modely.",
      "yourModels": "Stažené modely",
      "availableModels": "Dostupné ke stažení",
      "searchPlaceholder": "Hledat modely podle názvu…",
      "rescan": {
        "label": "Znovu prohledat",
        "tooltip": "Znovu prohledat modely přidané do složky modelů nebo mezipaměti Hugging Face mimo Handy"
      }
    },
    "general": {
      "title": "Obecné",
      "shortcut": {
        "title": "Zkratky Handy",
        "description": "Nastavte klávesové zkratky pro spuštění nahrávání řeči na text",
        "loading": "Načítám zkratky...",
        "none": "Žádné zkratky nejsou nastavené",
        "notFound": "Zkratka nenalezena",
        "pressKeys": "Stiskněte klávesy...",
        "bindings": {
          "transcribe": {
            "name": "Zkratka přepisu",
            "description": "Klávesová zkratka pro nahrávání a přepis vašeho hlasu."
          },
          "cancel": {
            "name": "Zkratka zrušení",
            "description": "Klávesová zkratka pro zrušení aktuálního nahrávání."
          },
          "transcribe_with_post_process": {
            "name": "Klávesa pro následné zpracování",
            "description": "Volitelné: Vyhrazená klávesová zkratka, která vždy použije AI následné zpracování na váš přepis."
          }
        },
        "errors": {
          "restore": "Nepodařilo se obnovit původní zkratku",
          "set": "Nepodařilo se nastavit zkratku: {{error}}",
          "reset": "Nepodařilo se vrátit zkratku na původní hodnotu"
        }
      },
      "language": {
        "title": "Jazyk",
        "description": "Vyberte jazyk rozpoznávání řeči. Auto jazyk určí automaticky, zatímco výběr konkrétního jazyka může zlepšit přesnost.",
        "searchPlaceholder": "Hledat jazyky...",
        "noResults": "Žádné jazyky nenalezeny",
        "auto": "Automaticky"
      },
      "shortcutActivation": {
        "title": "Chování zkratky",
        "description": "Zvolte, jak má zkratka přepisu spouštět a zastavovat nahrávání.",
        "options": {
          "holdOrToggle": "Automaticky",
          "pushToTalk": "Podržet",
          "toggle": "Přepínat"
        },
        "descriptions": {
          "hold_or_toggle": "Podržte pro nahrávání nebo klepněte pro přepnutí",
          "push_to_talk": "Nahrává, dokud držíte zkratku",
          "toggle": "Klepněte na zkratku pro zahájení nahrávání, klepněte znovu pro zastavení"
        }
      }
    },
    "sound": {
      "title": "Zvuk",
      "microphone": {
        "title": "Mikrofon",
        "description": "Vyberte preferované zařízení mikrofonu",
        "placeholder": "Vyberte mikrofon...",
        "loading": "Načítání..."
      },
      "channel": {
        "title": "Vstupní kanál",
        "description": "Vyberte vstupní kanál, ze kterého se má nahrávat. Tuto možnost použijte, pokud má vaše zvukové rozhraní více vstupů.",
        "average": "Průměr všech kanálů",
        "channel": "Kanál {{n}}"
      },
      "audioFeedback": {
        "label": "Zvuková odezva",
        "description": "Přehrát zvuk při zahájení a ukončení nahrávání"
      },
      "outputDevice": {
        "title": "Výstupní zařízení",
        "description": "Vyberte preferované výstupní zařízení pro zvuky odezvy",
        "placeholder": "Vyberte výstupní zařízení...",
        "loading": "Načítání..."
      },
      "volume": {
        "title": "Hlasitost",
        "description": "Upravte hlasitost zvukové odezvy"
      }
    },
    "advanced": {
      "groups": {
        "app": "Aplikace",
        "output": "Výstup",
        "transcription": "Přepis",
        "history": "Historie",
        "experimental": "Experimentální"
      },
      "experimentalToggle": {
        "label": "Experimentální funkce",
        "description": "Povolit experimentální funkce, které jsou stále ve vývoji."
      },
      "lazyStreamClose": {
        "label": "Ponechat mikrofon zapnutý mezi přepisy",
        "description": "Ponechá stream mikrofonu otevřený po dobu 30 sekund po zastavení nahrávání, čímž se sníží latence při opakovaném přepisu. Může zhoršit kvalitu zvuku Bluetooth."
      },
      "acceleration": {
        "transcribe": {
          "title": "Hardwarová akcelerace",
          "description": "Vyberte hardware, který se použije pro přepis. Automatický režim používá GPU, pokud je k dispozici, jinak přejde na CPU."
        },
        "gpuDevice": {
          "auto": "Automaticky"
        }
      },
      "startHidden": {
        "label": "Spouštět skrytě",
        "description": "Spustit do systémové lišty bez otevření okna."
      },
      "autostart": {
        "label": "Spouštět při startu",
        "description": "Automaticky spustit Handy po přihlášení do počítače."
      },
      "showTrayIcon": {
        "label": "Zobrazit ikonu v systémové liště",
        "description": "Zobrazit ikonu Handy v systémové liště."
      },
      "overlay": {
        "style": {
          "title": "Překryv",
          "description": "Vyberte překryv při nahrávání: „Žádné“ jej skryje, „Minimální“ zobrazí kompaktní indikátor, „Živě“ zobrazuje přepis v reálném čase, když mluvíte (pouze modely podporující streaming — hledejte odznak „Streaming“ ve výběru modelů). Na Linuxu se doporučuje „Žádné“.",
          "options": {
            "none": "Žádné",
            "minimal": "Minimální",
            "live": "Živě"
          }
        },
        "position": {
          "title": "Pozice překryvu",
          "description": "Zobrazovat vizuální překryv během nahrávání a přepisu. Na Linuxu je doporučeno 'Žádné'.",
          "options": {
            "bottom": "Dole",
            "top": "Nahoře"
          }
        }
      },
      "pasteMethod": {
        "title": "Způsob vložení",
        "description": "Vyberte, jak se text vkládá. Přímé: simuluje psaní přes systémový vstup. Žádné: přeskočí vložení a aktualizuje pouze historii/schránku.",
        "options": {
          "clipboard": "Schránka ({{modifier}}+V)",
          "clipboardCtrlShiftV": "Schránka (Ctrl+Shift+V)",
          "clipboardShiftInsert": "Schránka (Shift+Insert)",
          "direct": "Přímé",
          "none": "Žádné",
          "externalScript": "Externí skript"
        },
        "externalScriptPlaceholder": "/cesta/k/vasemu/skriptu.sh"
      },
      "typingTool": {
        "title": "Nástroj pro psaní",
        "description": "Vyberte, který linuxový nástroj pro psaní použít pro metodu přímého vložení. Auto automaticky zjistí a použije nejlepší dostupný nástroj pro váš systém.",
        "options": {
          "auto": "Auto (Doporučeno)"
        }
      },
      "clipboardHandling": {
        "title": "Práce se schránkou",
        "description": "'Neměnit schránku' zachová po přepisu aktuální obsah schránky. 'Kopírovat do schránky' ponechá po vložení výsledek přepisu ve schránce.",
        "options": {
          "dontModify": "Neměnit schránku",
          "copyToClipboard": "Kopírovat do schránky"
        }
      },
      "autoSubmit": {
        "title": "Automatické odeslání",
        "description": "Automaticky odešle vybranou kombinaci kláves po vložení textu. Cmd+Enter platí pro macOS, zatímco Windows/Linux používají Super+Enter.",
        "options": {
          "off": "Vypnuto",
          "enter": "Enter",
          "cmdEnter": "Cmd+Enter",
          "superEnter": "Super+Enter",
          "ctrlEnter": "Ctrl+Enter"
        }
      },
      "translateToEnglish": {
        "label": "Překládat do angličtiny",
        "description": "Během přepisu automaticky překládat řeč z jiných jazyků do angličtiny."
      },
      "modelUnload": {
        "title": "Uvolnění modelu",
        "description": "Automaticky uvolnit paměť GPU/CPU, když model nebyl použit po zadanou dobu",
        "options": {
          "never": "Nikdy",
          "immediately": "Okamžitě",
          "min2": "Po 2 minutách",
          "min5": "Po 5 minutách",
          "min10": "Po 10 minutách",
          "min15": "Po 15 minutách",
          "hour1": "Po 1 hodině",
          "sec15": "Po 15 sekundách (Debug)"
        }
      },
      "customWords": {
        "title": "Vlastní slova",
        "description": "Pomozte podporovaným modelům rozpoznávat jména a odborné termíny. Přibližné opravy jsou v současnosti omezeny na slova obsahující písmena A–Z a číslice.",
        "placeholder": "Přidat slovo",
        "add": "Přidat",
        "remove": "Odebrat {{word}}",
        "duplicate": "\"{{word}}\" již existuje"
      },
      "voiceActivityDetection": {
        "title": "Detekce hlasové aktivity",
        "description": "Odfiltruje ticho z nahrávek. Modely podporující streaming používají delší doběh VAD; vypnutí VAD nahrává surový zvuk."
      },
      "longFormChunking": {
        "title": "Dělení dlouhého zvuku",
        "description": "Před přepisem rozdělí dlouhé nahrávky v pauzách, což obvykle zlepšuje kvalitu přepisu."
      },
      "vadBackend": {
        "title": "Backend detekce hlasu",
        "description": "Vyberte detektor používaný k filtrování ticha. Earshot je experimentální; pokud je řeč ořezávána nebo zůstává šum, přepněte zpět na Silero.",
        "options": {
          "silero": "Silero",
          "earshot": "Earshot"
        }
      },
      "fillerWordRemoval": {
        "title": "Odstranit výplňová slova",
        "description": "Odstraňuje běžná slova vyjadřující váhání z přepisů. Vypnutím je zachováte."
      },
      "chineseScript": {
        "title": "Čínské písmo",
        "description": "Zapisuje přepisy v mandarínštině a kantonštině zjednodušenými nebo tradičními znaky. Ostatní jazyky zůstávají beze změny.",
        "options": {
          "asTranscribed": "Jak bylo přepsáno",
          "simplified": "Zjednodušené",
          "traditional": "Tradiční"
        }
      }
    },
    "postProcessing": {
      "hotkey": {
        "title": "Klávesová zkratka"
      },
      "api": {
        "title": "API",
        "provider": {
          "title": "Poskytovatel",
          "description": "Vyberte poskytovatele kompatibilního s OpenAI nebo Anthropic."
        },
        "apiFormat": {
          "title": "Formát API",
          "description": "Formát požadavku očekávaný vaším vlastním endpointem.",
          "options": {
            "openai": "Kompatibilní s OpenAI (Chat Completions)",
            "anthropic": "Anthropic Messages"
          }
        },
        "appleIntelligence": {
          "unavailable": "Apple Intelligence není na tomto zařízení k dispozici. Vyžaduje Mac s Apple Silicon a macOS Tahoe (26.0) nebo novější a povolené Apple Intelligence v Nastavení systému."
        },
        "baseUrl": {
          "title": "Základní URL",
          "description": "Základní URL API pro vybraného poskytovatele. Upravitelné je pouze u vlastního poskytovatele.",
          "placeholder": "https://api.openai.com/v1",
          "placeholderAnthropic": "https://api.anthropic.com/v1"
        },
        "apiKey": {
          "title": "API klíč",
          "description": "API klíč pro vybraného poskytovatele.",
          "placeholder": "sk-..."
        },
        "model": {
          "title": "Model",
          "descriptionCustom": "Zadejte identifikátor modelu očekávaný vaším vlastním endpointem.",
          "descriptionDefault": "Vyberte model dostupný u vybraného poskytovatele.",
          "placeholderWithOptions": "Vyhledejte nebo vyberte model",
          "placeholderNoOptions": "Zadejte název modelu",
          "refreshModels": "Obnovit modely"
        }
      },
      "prompts": {
        "title": "Prompt",
        "selectedPrompt": {
          "title": "Vybraný prompt",
          "description": "Vyberte šablonu pro zpřesnění přepisu nebo vytvořte novou. V textu promptu použijte ${output} pro vložení zachyceného přepisu."
        },
        "noPrompts": "Nejsou k dispozici žádné prompty",
        "selectPrompt": "Vyberte prompt",
        "createNew": "Vytvořit nový prompt",
        "promptLabel": "Název promptu",
        "promptLabelPlaceholder": "Zadejte název promptu",
        "promptInstructions": "Instrukce promptu",
        "promptInstructionsPlaceholder": "Napište instrukce, které se mají spustit po přepisu. Příklad: Zlepšete gramatiku a srozumitelnost následujícího textu: ${output}",
        "promptTip": "Tip: Použijte `${output}` pro vložení přepsaného textu do promptu.",
        "updatePrompt": "Aktualizovat prompt",
        "deletePrompt": "Smazat prompt",
        "createPrompt": "Vytvořit prompt",
        "cancel": "Zrušit",
        "selectToEdit": "Vyberte výše prompt, abyste zobrazili a upravili jeho podrobnosti.",
        "createFirst": "Klikněte nahoře na 'Vytvořit nový prompt' a vytvořte svůj první prompt pro následné zpracování."
      }
    },
    "history": {
      "title": "Historie",
      "openFolder": "Otevřít složku nahrávek",
      "loading": "Načítám historii...",
      "empty": "Zatím žádné přepisy. Začněte nahrávat a vytvořte si historii!",
      "copyToClipboard": "Kopírovat přepis do schránky",
      "copyError": "Přepis se nepodařilo zkopírovat do schránky.",
      "save": "Uložit přepis",
      "unsave": "Odebrat z uložených",
      "delete": "Smazat záznam",
      "deleteError": "Nepodařilo se smazat záznam. Zkuste to prosím znovu.",
      "retranscribe": "Přepsat znovu",
      "retranscribeError": "Opětovný přepis se nezdařil. Zkuste to prosím znovu.",
      "transcribing": "Přepisuji...",
      "transcriptionFailed": "Přepis selhal. Můžete zkusit přepis znovu pomocí ikony opakování."
    },
    "debug": {
      "title": "Ladění",
      "logDirectory": {
        "title": "Složka protokolů",
        "description": "Umístění, kde jsou uloženy soubory protokolu"
      },
      "logLevel": {
        "title": "Úroveň logování",
        "description": "Nastavte úroveň podrobnosti logování"
      },
      "liveLogs": {
        "title": "Živé logy",
        "description": "Sledujte logy aplikace v reálném čase a diagnostikujte problémy bez otevírání souboru logu. Zobrazují se pouze logy vytvořené, když je tento panel otevřený; respektuje nastavení Úroveň logování výše.",
        "live": "Živě",
        "paused": "Pozastaveno",
        "pause": "Pozastavit",
        "resume": "Pokračovat",
        "copied": "Zkopírováno",
        "lineCount": "Řádků: {{count}}",
        "empty": "Čekání na logy… Záznamy se zde zobrazí, jakmile je aplikace vygeneruje."
      },
      "updateChecks": {
        "label": "Kontrolovat aktualizace",
        "description": "Automaticky kontrolovat nové verze Handy",
        "lockedDescription": "Zakázáno systémovou konfigurací (HANDY_DISABLE_UPDATER)"
      },
      "soundTheme": {
        "label": "Zvukový motiv",
        "description": "Vyberte zvukový motiv pro odezvu při startu a ukončení nahrávání"
      },
      "wordCorrectionThreshold": {
        "title": "Práh korekce slov",
        "description": "Citlivost pro opravy vlastních slov"
      },
      "historyLimit": {
        "title": "Limit historie",
        "description": "Maximální počet záznamů historie k uchování",
        "entries": "záznamů"
      },
      "recordingRetention": {
        "title": "Automatické mazání nahrávek",
        "description": "Automaticky mazat staré nahrávky pro úsporu místa",
        "never": "Nikdy",
        "preserveLimit": "Ponechat posledních {{count}}",
        "days3": "Po 3 dnech",
        "weeks2": "Po 2 týdnech",
        "months3": "Po 3 měsících",
        "placeholder": "Vyberte dobu uchování..."
      },
      "alwaysOnMicrophone": {
        "label": "Vždy zapnutý mikrofon",
        "description": "Udržovat mikrofon aktivní pro rychlejší odezvu"
      },
      "clamshellMicrophone": {
        "title": "Mikrofon při zavřeném víku",
        "description": "Mikrofon, který se použije při zavřeném víku notebooku"
      },
      "postProcessingToggle": {
        "label": "Následné zpracování",
        "description": "Povolit AI vylepšení textu po přepisu"
      },
      "muteWhileRecording": {
        "label": "Ztlumit při nahrávání",
        "description": "Ztlumit systémový zvuk během nahrávání"
      },
      "appendTrailingSpace": {
        "label": "Přidat koncovou mezeru",
        "description": "Přidat mezeru po vloženém přepisu"
      },
      "keyboardImplementation": {
        "title": "Implementace klávesnice",
        "description": "Zvolte backend pro klávesové zkratky.",
        "bindingsReset": "Klávesové zkratky byly nekompatibilní a byly obnoveny na výchozí hodnoty"
      },
      "paths": {
        "appData": "Data aplikace:",
        "models": "Modely:",
        "settings": "Nastavení:"
      },
      "pasteDelay": {
        "title": "Zpoždění vložení (před)",
        "description": "Zpoždění (v milisekundách) po zkopírování textu, před odesláním klávesy pro vložení. Zvyšte, pokud se nic nevloží."
      },
      "pasteDelayAfter": {
        "title": "Zpoždění vložení (po)",
        "description": "Zpoždění (v milisekundách) po klávese pro vložení, před obnovením předchozí schránky. Zvyšte, pokud se místo přepisu vkládá starý obsah schránky."
      },
      "holdThreshold": {
        "title": "Práh podržení",
        "description": "Pouze v automatickém režimu: jak dlouho (v milisekundách) musí být zkratka držena, aby se počítala jako podržení místo klepnutí. Snižte, pokud po krátkém podržení nahrávání zůstává zapnuté; zvyšte, pokud jsou záměrná klepnutí vyhodnocena jako podržení."
      },
      "reliablePaste": {
        "title": "Spolehlivé vložení (Beta)",
        "description": "Obnoví schránku teprve poté, co cílová aplikace skutečně přečte přepis, místo po pevně daném zpoždění. Má odstranit vkládání starého obsahu schránky při zatížení systému. Vyžaduje způsob vložení přes schránku; pouze macOS a Windows."
      },
      "recordingBuffer": {
        "title": "Extra vyrovnávací paměť nahrávání",
        "description": "Extra čas (v milisekundách) pro pokračování nahrávání po uvolnění klávesy, pro zachycení zbývajícího zvuku. 0 = žádná extra vyrovnávací paměť."
      },
      "keyboardDiagnostic": {
        "title": "Diagnostika klávesnice",
        "description": "Ověří, zda události klávesnice dorazí do Handy. Zaznamenává se pouze počet událostí — nikdy to, které klávesy stisknete.",
        "run": "Spustit 10s diagnostiku",
        "running": "Naslouchání… několikrát stiskněte svou zkratku (např. Option+Space)",
        "secureInputLabel": "Secure Input",
        "enabled": "zapnuto",
        "disabled": "vypnuto",
        "holder": "drží ji {{name}} (pid {{pid}})",
        "holderUnknown": "žádný viditelný držitel",
        "keyDown": "Stisk klávesy",
        "keyUp": "Uvolnění klávesy",
        "flagsChanged": "Modifikátory",
        "mouse": "Myš",
        "verdictBlocked": "Secure Input blokuje události kláves — zkratky obsahující běžnou klávesu nemohou fungovat, dokud se to nevyřeší.",
        "verdictSuspicious": "Dorazily události modifikátorů, ale žádné události kláves — něco klávesy potlačuje, přestože Secure Input hlásí, že je vypnutý. Nahlaste to prosím na GitHubu.",
        "verdictOk": "Události kláves dorazí do Handy normálně.",
        "verdictNoEvents": "Nebyly zachyceny žádné události — stiskli jste během testu nějaké klávesy?",
        "failed": "Diagnostika selhala: {{error}}"
      },
      "whatsNewPreview": {
        "title": "Náhled novinek",
        "description": "Otevřít nejnovější přibalené poznámky k vydání bez označení jako zobrazené",
        "button": "Náhled",
        "noNotes": "Nebyly nalezeny žádné přibalené poznámky k vydání",
        "error": "Nepodařilo se zobrazit náhled poznámek k vydání"
      },
      "onboardingPreview": {
        "title": "Náhled úvodního nastavení",
        "description": "Prohlédněte si obrazovky úvodního nastavení beze změny oprávnění, modelů nebo stavu dokončení",
        "permissionsButton": "Oprávnění",
        "modelsButton": "Výběr modelu",
        "exitButton": "Ukončit náhled"
      }
    },
    "about": {
      "title": "O aplikaci",
      "version": {
        "title": "Verze",
        "description": "Aktuální verze Handy"
      },
      "appDataDirectory": {
        "title": "Adresář dat aplikace",
        "description": "Umístění, kde Handy ukládá data"
      },
      "sourceCode": {
        "title": "Zdrojový kód",
        "description": "Zobrazit zdrojový kód a přispět",
        "button": "Zobrazit na GitHubu"
      },
      "supportDevelopment": {
        "title": "Podpořte vývoj",
        "description": "Pomozte nám pokračovat ve vývoji Handy",
        "button": "Přispět"
      },
      "acknowledgments": {
        "title": "Poděkování",
        "ggml": {
          "title": "ggml",
          "description": "Vysoce výkonné inferenční zpracování modelu automatického rozpoznávání řeči Whisper od OpenAI",
          "details": "Handy používá ggml pro rychlé lokální zpracování řeči na text. Díky skvělé práci Georgiho Gerganova a přispěvatelů."
        }
      },
      "whatsNewUpdates": {
        "label": "Zobrazovat novinky",
        "description": "Zobrazovat poznámky k vydání po aktualizacích Handy"
      }
    }
  },
  "footer": {
    "checkingUpdates": "Kontroluji aktualizace...",
    "updateAvailableShort": "Dostupná aktualizace",
    "upToDate": "Vše aktuální",
    "updateCheckingDisabled": "Kontrola aktualizací vypnuta",
    "downloading": "Stahování... {{progress}}%",
    "installing": "Instalace...",
    "preparing": "Příprava...",
    "checkForUpdates": "Zkontrolovat aktualizace",
    "portableUpdateTitle": "Vyžadována ruční aktualizace",
    "portableUpdateMessage": "Přenosné instalace nelze aktualizovat automaticky. Níže stáhněte instalátor pro svůj systém, spusťte jej a nainstalujte do stejné složky — vaše složka Data/ (nastavení, modely, nahrávky) zůstane na místě.",
    "portableUpdateButton": "Stáhnout instalátor",
    "portableUpdateBrowseMessage": "Přenosné instalace nelze aktualizovat automaticky. Stáhněte nejnovější verzi pro svůj systém z GitHub Releases a nainstalujte ji do stejné složky — vaše složka Data/ (nastavení, modely, nahrávky) zůstane na místě.",
    "portableUpdateBrowseButton": "Otevřít GitHub Releases"
  },
  "common": {
    "loading": "Načítání...",
    "delete": "Smazat",
    "back": "Zpět",
    "close": "Zavřít",
    "open": "Otevřít",
    "copy": "Kopírovat",
    "clear": "Vymazat",
    "noOptionsFound": "Nenalezeny žádné možnosti"
  },
  "accessibility": {
    "permissionsDescription": "Handy potřebuje oprávnění usnadnění přístupu, aby mohlo psát přepsaný text.",
    "openSettings": "Otevřít Nastavení systému"
  },
  "errors": {
    "loadDirectory": "Chyba při načítání adresáře: {{error}}",
    "micPermissionDeniedTitle": "Přístup k mikrofonu byl odepřen",
    "micPermissionDenied": {
      "generic": "Přístup k mikrofonu byl odepřen operačním systémem. Povolte prosím přístup k mikrofonu v nastavení systému.",
      "windows": "Povolte přístup k mikrofonu v Nastavení → Soukromí a zabezpečení → Mikrofon (včetně přístupu desktopových aplikací).",
      "macos": "Povolte přístup k mikrofonu v Nastavení systému → Soukromí a zabezpečení → Mikrofon.",
      "linux": "Povolte přístup k mikrofonu v nastavení zvuku nebo soukromí vašeho systému."
    },
    "noInputDeviceTitle": "Mikrofon nebyl nalezen",
    "noInputDevice": "Nebylo detekováno žádné zvukové vstupní zařízení. Připojte mikrofon nebo sluchátka a zkuste to znovu.",
    "recordingFailed": "Nepodařilo se spustit nahrávání: {{error}}",
    "modelLoadFailed": "Nepodařilo se načíst model: {{model}}",
    "modelLoadFailedUnknown": "neznámý model",
    "pasteFailedTitle": "Vložení textu se nezdařilo",
    "pasteFailed": "Text nebylo možné vložit do aktivní aplikace.",
    "transcriptionFailedTitle": "Přepis selhal"
  },
  "appLanguage": {
    "title": "Jazyk aplikace",
    "description": "Změňte jazyk rozhraní Handy"
  },
  "theme": {
    "title": "Motiv aplikace",
    "description": "Zvolte, zda má Handy sledovat motiv systému, nebo zůstat světlý či tmavý",
    "options": {
      "system": "Systém",
      "light": "Světlý",
      "dark": "Tmavý"
    }
  },
  "overlay": {
    "transcribing": "Přepisuji...",
    "processing": "Zpracovávám...",
    "loadingModel": "Načítání modelu..."
  },
  "whatsNew": {
    "title": "Novinky v Handy v{{version}}"
  },
  "secureInput": {
    "blockedWithCulprit_one": "{{name}} může blokovat 1 zkratku",
    "blockedWithCulprit_other": "{{name}} může blokovat {{count}} zkratek",
    "blockedNoCulprit_one": "macOS dočasně blokuje 1 zkratku",
    "blockedNoCulprit_other": "macOS dočasně blokuje {{count}} zkratek",
    "recorderBlockedWithCulprit": "{{name}} může blokovat změny zkratek",
    "recorderBlockedNoCulprit": "macOS dočasně blokuje změny zkratek",
    "learnMore": "Jak to vyřešit",
    "recorderBlocked": "Zkratky teď nelze nahrát — Secure Input v macOS blokuje události kláves. Nejprve vyřešte upozornění na Secure Input.",
    "dismiss": "Zavřít"
  }
}

---

## File: src/i18n/locales/da/translation.json

{
  "tray": {
    "settings": "Indstillinger...",
    "checkUpdates": "Tjek for opdateringer...",
    "copyLastTranscript": "Kopiér seneste transskription",
    "unloadModel": "Frigør model",
    "model": "Model",
    "quit": "Afslut",
    "cancel": "Annuller",
    "secureInputWarning": "⚠ Genveje blokeret af Secure Input"
  },
  "sidebar": {
    "general": "Generelt",
    "models": "Modeller",
    "advanced": "Avanceret",
    "postProcessing": "Efterbehandling",
    "history": "Historik",
    "debug": "Fejlfinding",
    "about": "Om"
  },
  "onboarding": {
    "subtitle": "For at komme i gang skal du vælge en transskriptionsmodel",
    "existingModelsTitle": "Kompatible modeller",
    "downloadModelsTitle": "Tilgængelig til download",
    "showAllModels": "Vis alle {{total}} modeller",
    "showFewerModels": "Vis færre modeller",
    "recommended": "Anbefalet",
    "customModelDescription": "Ikke officielt understøttet",
    "migration": {
      "title": "Dine modeller skal opdateres",
      "description": "Handy bruger nu en nyere type model. Download den nye version for at fortsætte med at transskribere. Gamle versioner vil blive fjernet.",
      "replaces": "Erstatter {{name}}",
      "oldFormat": "Gammelt format",
      "failed": "Download mislykkedes",
      "retry": "Prøv igen",
      "chooseAnother": "Vælg en anden model",
      "download": "Download modeller ({{size}})",
      "continue": "Fortsæt",
      "timeRemaining": "{{time}} tilbage"
    },
    "modelCard": {
      "accuracy": "nøjagtighed",
      "speed": "hastighed"
    },
    "models": {
      "small": {
        "name": "Whisper Small",
        "description": "Hurtig og ret nøjagtig."
      },
      "medium": {
        "name": "Whisper Medium",
        "description": "God nøjagtighed, middel hastighed"
      },
      "turbo": {
        "name": "Whisper Turbo",
        "description": "Afbalanceret nøjagtighed og hastighed."
      },
      "large": {
        "name": "Whisper Large",
        "description": "God nøjagtighed, men langsom."
      },
      "breeze-asr": {
        "name": "Breeze ASR",
        "description": "Optimeret til taiwansk mandarin. Understøtter kodeskift."
      }
    },
    "errors": {
      "selectModel": "Kunne ikke vælge model"
    },
    "permissions": {
      "title": "Tilladelser påkrævet",
      "description": "Handy har brug for et par tilladelser for at fungere korrekt.",
      "microphone": {
        "title": "Mikrofonadgang",
        "description": "Påkrævet for at høre din stemme til transskription."
      },
      "accessibility": {
        "title": "Tilgængelighedsadgang",
        "description": "Påkrævet for at skrive den transskriberede tekst ind i dine programmer."
      },
      "grant": "Giv tilladelse",
      "granted": "Givet",
      "waiting": "Venter...",
      "allGranted": "Alt er klar!",
      "errors": {
        "checkFailed": "Kunne ikke tjekke tilladelser. Prøv igen.",
        "requestFailed": "Kunne ikke anmode om tilladelse. Prøv igen."
      }
    }
  },
  "modelSelector": {
    "custom": "Brugerdefineret",
    "legacy": "Forældet",
    "streaming": "Streaming",
    "active": "Aktiv",
    "switching": "Skifter...",
    "noModelsAvailable": "Ingen modeller tilgængelige",
    "verifying": "Verificerer {{modelName}}...",
    "verifyingGeneric": "Verificerer...",
    "downloading": "Downloader {{percentage}}%",
    "downloadingMultiple": "Downloader {{count}} modeller...",
    "modelReady": "Model klar",
    "loading": "Indlæser {{modelName}}...",
    "loadingGeneric": "Indlæser...",
    "modelError": "Modelfejl",
    "modelUnloaded": "Model frigjort",
    "noModelDownloadRequired": "Ingen model - download påkrævet",
    "deleteModel": "Slet {{modelName}}",
    "downloadSpeed": "{{speed}} MB/s",
    "cancel": "Annuller",
    "cancelDownload": "Annuller download",
    "capabilities": {
      "languageSelection": "Understøtter flere inputsprog",
      "singleLanguage": "Understøtter kun dette sprog",
      "languageCount": "{{total}} sprog",
      "languageOnly": "Kun {{language}}",
      "translation": "Kan oversætte til engelsk",
      "translate": "Oversæt",
      "streaming": "Viser live transskription, mens du taler"
    }
  },
  "settings": {
    "modelSettings": {
      "title": "{{model}}-indstillinger"
    },
    "general": {
      "title": "Generelt",
      "shortcut": {
        "title": "Handy-genveje",
        "description": "Konfigurer tastaturgenveje til at udløse tale-til-tekst-optagelse",
        "loading": "Indlæser genveje...",
        "none": "Ingen genveje konfigureret",
        "notFound": "Genvej ikke fundet",
        "pressKeys": "Tryk på ønskede taster...",
        "bindings": {
          "transcribe": {
            "name": "Transskriptionsgenvej",
            "description": "Tastaturgenvejen til at optage og transskribere din stemme."
          },
          "cancel": {
            "name": "Annulleringsgenvej",
            "description": "Tastaturgenvejen til at annullere den aktuelle optagelse."
          },
          "transcribe_with_post_process": {
            "name": "Genvej til efterbehandling",
            "description": "Valgfrit: En dedikeret genvej, der altid anvender AI-efterbehandling på din transskription."
          }
        },
        "errors": {
          "restore": "Kunne ikke gendanne den oprindelige genvej",
          "set": "Kunne ikke indstille genvej: {{error}}",
          "reset": "Kunne ikke nulstille genvej til oprindelig indstilling"
        }
      },
      "language": {
        "title": "Sprog",
        "description": "Vælg sproget til talegenkendelse. Auto tyder automatisk sproget, mens valg af et specifikt sprog kan forbedre nøjagtigheden for det pågældende sprog.",
        "searchPlaceholder": "Søg efter sprog...",
        "noResults": "Ingen sprog fundet",
        "auto": "Automatisk"
      },
      "shortcutActivation": {
        "title": "Genvejens adfærd",
        "description": "Vælg, hvordan transskriptionsgenvejen starter og stopper optagelsen.",
        "options": {
          "holdOrToggle": "Automatisk",
          "pushToTalk": "Hold",
          "toggle": "Skift"
        },
        "descriptions": {
          "hold_or_toggle": "Hold nede for at optage, eller tryk kort for at slå til/fra",
          "push_to_talk": "Optager, mens du holder genvejen nede",
          "toggle": "Tryk på genvejen for at starte optagelsen, tryk igen for at stoppe"
        }
      }
    },
    "models": {
      "title": "Transskriptionsmodeller",
      "description": "Vælg en transskriptionsmodel, eller download flere modeller. Forskellige modeller tilbyder varierende niveauer af nøjagtighed og hastighed.",
      "searchPlaceholder": "Søg efter modeller ved navn…",
      "yourModels": "Downloadede modeller",
      "availableModels": "Tilgængelig til download",
      "deleteConfirm": "Er du sikker på, at du vil slette {{modelName}}? Du skal downloade den igen for at bruge den.",
      "deleteActiveConfirm": "{{modelName}} er din aktive model. Sletning af den vil stoppe transskriptioner, indtil du vælger en ny model. Er du sikker?",
      "deleteTitle": "Slet model",
      "retiredNotice": {
        "title": "Gamle modeller virker ikke længere med Handy",
        "description": "De fylder {{size}} på din computer.",
        "delete": "Slet modeller",
        "deleted": "Gamle modeller er slettet",
        "deleteFailed": "Nogle gamle modeller kunne ikke slettes. Se loggen for detaljer."
      },
      "filters": {
        "allLanguages": "Alle sprog",
        "streaming": "Filtrer modeller, der understøtter live transskription",
        "translation": "Filtrer modeller, der understøtter oversættelse til engelsk"
      },
      "rescan": {
        "label": "Søg igen",
        "tooltip": "Søg igen efter modeller, der er tilføjet til modelmappen eller Hugging Face-cachen uden for Handy"
      },
      "noModelsMatch": "Ingen modeller matcher dette filter."
    },
    "sound": {
      "title": "Lyd",
      "microphone": {
        "title": "Mikrofon",
        "description": "Vælg din foretrukne mikrofonenhed",
        "placeholder": "Vælg mikrofon...",
        "loading": "Indlæser..."
      },
      "channel": {
        "title": "Inputkanal",
        "description": "Vælg, hvilken inputkanal der skal optages fra. Brug dette, hvis din lydgrænseflade har flere indgange.",
        "average": "Gennemsnit af alle kanaler",
        "channel": "Kanal {{n}}"
      },
      "audioFeedback": {
        "label": "Lydfeedback",
        "description": "Afspil lyd, når optagelse starter og stopper"
      },
      "outputDevice": {
        "title": "Outputenhed",
        "description": "Vælg din foretrukne lydoutputenhed til feedbacklyde",
        "placeholder": "Vælg outputenhed...",
        "loading": "Indlæser..."
      },
      "volume": {
        "title": "Lydstyrke",
        "description": "Juster lydstyrken for lydfeedback"
      }
    },
    "advanced": {
      "groups": {
        "app": "App",
        "output": "Output",
        "transcription": "Transskription",
        "history": "Historik",
        "experimental": "Eksperimentel"
      },
      "experimentalToggle": {
        "label": "Eksperimentelle funktioner",
        "description": "Aktivér eksperimentelle funktioner, der stadig er under udvikling."
      },
      "lazyStreamClose": {
        "label": "Hold mikrofon åben mellem transskriptioner",
        "description": "Holder mikrofonstrømmen åben i 30 sekunder, efter at optagelsen stopper, hvilket reducerer ventetid ved transskriptioner lige efter hinanden. Kan forringe Bluetooth-lydkvaliteten, mens den er aktiv."
      },
      "acceleration": {
        "transcribe": {
          "title": "Hardwareacceleration",
          "description": "Vælg den hardware, der bruges til transskription. Automatisk bruger GPU'en, når den er tilgængelig, og falder ellers tilbage til CPU'en."
        },
        "gpuDevice": {
          "auto": "Automatisk"
        }
      },
      "startHidden": {
        "label": "Start skjult",
        "description": "Vis på meddelelsesområdet uden at åbne vinduet."
      },
      "autostart": {
        "label": "Start ved opstart",
        "description": "Start Handy automatisk, når du logger ind på din computer."
      },
      "showTrayIcon": {
        "label": "Vis proceslinjeikon",
        "description": "Vis Handy-ikonet i meddelelsesområdet."
      },
      "overlay": {
        "style": {
          "title": "Overlay",
          "description": "Vælg optagelses-overlay: Ingen skjuler det, Minimal viser en kompakt pille, Live viser transskription i realtid, mens du taler (kun modeller med streaming-understøttelse — se efter Streaming-mærket i modelvælgeren). På Linux anbefales 'Ingen'.",
          "options": {
            "none": "Ingen",
            "minimal": "Minimal",
            "live": "Live"
          }
        },
        "position": {
          "title": "Overlay-placering",
          "description": "Hvor overlayet vises på skærmen under optagelse og transskription.",
          "options": {
            "bottom": "Bund",
            "top": "Top"
          }
        }
      },
      "pasteMethod": {
        "title": "Indsætningsmetode",
        "description": "Vælg, hvordan tekst indsættes. Direkte: simulerer tastetryk via systeminput. Ingen: springer indsætning over, opdaterer kun historik/udklipsholder.",
        "options": {
          "clipboard": "Udklipsholder ({{modifier}}+V)",
          "clipboardCtrlShiftV": "Udklipsholder (Ctrl+Shift+V)",
          "clipboardShiftInsert": "Udklipsholder (Shift+Insert)",
          "direct": "Direkte",
          "none": "Ingen",
          "externalScript": "Eksternt script"
        },
        "externalScriptPlaceholder": "/sti/til/dit/script.sh"
      },
      "typingTool": {
        "title": "Skriveværktøj",
        "description": "Vælg hvilket Linux-skriveværktøj der skal bruges til Direkte indsætningsmetode. Auto registrerer og bruger automatisk det bedste tilgængelige værktøj til dit system.",
        "options": {
          "auto": "Auto (anbefalet)"
        }
      },
      "clipboardHandling": {
        "title": "Håndtering af udklipsholder",
        "description": "Rør ikke ved udklipsholder bevarer dit nuværende udklipsholderindhold efter transskription. Kopiér til udklipsholder efterlader transskriptionsresultatet i din udklipsholder efter indsætning.",
        "options": {
          "dontModify": "Rør ikke ved udklipsholder",
          "copyToClipboard": "Kopiér til udklipsholder"
        }
      },
      "autoSubmit": {
        "title": "Automatisk indsendelse",
        "description": "Send automatisk den valgte tastekombination efter tekstindsættelse. Cmd+Enter gælder på macOS, mens Windows/Linux bruger Super+Enter.",
        "options": {
          "off": "Fra",
          "enter": "Enter",
          "cmdEnter": "Cmd+Enter",
          "superEnter": "Super+Enter",
          "ctrlEnter": "Ctrl+Enter"
        }
      },
      "translateToEnglish": {
        "label": "Oversæt til engelsk",
        "description": "Oversæt automatisk tale fra andre sprog til engelsk under transskription."
      },
      "modelUnload": {
        "title": "Frigør model",
        "description": "Frigør automatisk GPU-/CPU-hukommelse, når modellen ikke har været brugt i den angivne tid",
        "options": {
          "never": "Aldrig",
          "immediately": "Med det samme",
          "min2": "Efter 2 minutter",
          "min5": "Efter 5 minutter",
          "min10": "Efter 10 minutter",
          "min15": "Efter 15 minutter",
          "hour1": "Efter 1 time",
          "sec15": "Efter 15 sekunder (fejlfinding)"
        }
      },
      "customWords": {
        "title": "Brugerdefinerede ord",
        "description": "Hjælp understøttede modeller med at genkende navne og specialiserede udtryk. Fuzzy-korrektion er i øjeblikket begrænset til ord, der bruger A-Z og tal.",
        "placeholder": "Tilføj et ord",
        "add": "Tilføj",
        "remove": "Fjern {{word}}",
        "duplicate": "\"{{word}}\" findes allerede"
      },
      "voiceActivityDetection": {
        "title": "Stemmeaktivitetsdetektion",
        "description": "Filtrer stilhed fra optagelser. Streaming-modeller bruger en længere VAD-hale; deaktivering af VAD optager rå lyd."
      },
      "longFormChunking": {
        "title": "Opdeling af lang lyd",
        "description": "Deler lange optagelser ved pauser før transskribering, hvilket typisk forbedrer transskriptionen."
      },
      "vadBackend": {
        "title": "Stemmedetektions-backend",
        "description": "Vælg den detektor, der bruges til at filtrere stilhed. Earshot er eksperimentel; skift tilbage til Silero, hvis tale klippes eller støj bevares.",
        "options": {
          "silero": "Silero",
          "earshot": "Earshot"
        }
      },
      "fillerWordRemoval": {
        "title": "Fjern fyldord",
        "description": "Fjerner almindelige tøveord fra transskriptioner. Slå fra for at beholde dem."
      },
      "chineseScript": {
        "title": "Kinesisk skrift",
        "description": "Skriver mandarin- og kantonesisk-transskriptioner med forenklede eller traditionelle tegn. Andre sprog ændres ikke.",
        "options": {
          "asTranscribed": "Som transskriberet",
          "simplified": "Forenklet",
          "traditional": "Traditionel"
        }
      }
    },
    "postProcessing": {
      "hotkey": {
        "title": "Genvejstast"
      },
      "api": {
        "title": "API",
        "provider": {
          "title": "Udbyder",
          "description": "Vælg en OpenAI- eller Anthropic-kompatibel udbyder."
        },
        "apiFormat": {
          "title": "API-format",
          "description": "Det anmodningsformat, som det brugerdefinerede endpoint forventer.",
          "options": {
            "openai": "OpenAI-kompatibel (Chat Completions)",
            "anthropic": "Anthropic Messages"
          }
        },
        "appleIntelligence": {
          "unavailable": "Apple Intelligence er ikke tilgængelig på denne enhed. Kræver en Apple Silicon Mac med macOS Tahoe (26.0) eller nyere med Apple Intelligence aktiveret i Systemindstillinger."
        },
        "baseUrl": {
          "title": "Basis-URL",
          "description": "API-basis-URL for den valgte udbyder. Kun den brugerdefinerede udbyder kan redigeres.",
          "placeholder": "https://api.openai.com/v1",
          "placeholderAnthropic": "https://api.anthropic.com/v1"
        },
        "apiKey": {
          "title": "API-nøgle",
          "description": "API-nøgle til den valgte udbyder.",
          "placeholder": "sk-..."
        },
        "model": {
          "title": "Model",
          "descriptionCustom": "Angiv den modelidentifikator, som det brugerdefinerede endpoint forventer.",
          "descriptionDefault": "Vælg en model, der udbydes af den valgte udbyder.",
          "placeholderWithOptions": "Søg eller vælg en model",
          "placeholderNoOptions": "Skriv et modelnavn",
          "refreshModels": "Opdater modeller"
        }
      },
      "prompts": {
        "title": "Prompt",
        "selectedPrompt": {
          "title": "Valgt prompt",
          "description": "Vælg en skabelon til at forfine transskriptioner, eller opret en ny. Brug ${output} i promptteksten for at referere til den fangede transskription."
        },
        "noPrompts": "Ingen prompts tilgængelige",
        "selectPrompt": "Vælg en prompt",
        "createNew": "Opret ny prompt",
        "promptLabel": "Promptnavn",
        "promptLabelPlaceholder": "Indtast promptnavn",
        "promptInstructions": "Promptinstruktioner",
        "promptInstructionsPlaceholder": "Skriv de instruktioner, der skal køres efter transskription. Eksempel: Forbedr grammatik og klarhed for følgende tekst: ${output}",
        "promptTip": "Tip: Brug `${output}` til at indsætte den transskriberede tekst i din prompt.",
        "updatePrompt": "Opdater prompt",
        "deletePrompt": "Slet prompt",
        "createPrompt": "Opret prompt",
        "cancel": "Annuller",
        "selectToEdit": "Vælg en prompt ovenfor for at se og redigere dens detaljer.",
        "createFirst": "Klik på 'Opret ny prompt' ovenfor for at oprette din første efterbehandlingsprompt."
      }
    },
    "history": {
      "title": "Historik",
      "openFolder": "Åbn optagelsesmappe",
      "loading": "Indlæser historik...",
      "empty": "Ingen transskriptioner endnu. Start optagelse for at opbygge din historik!",
      "copyToClipboard": "Kopiér transskription til udklipsholder",
      "copyError": "Kunne ikke kopiere transskriptionen til udklipsholderen.",
      "save": "Gem transskription",
      "unsave": "Fjern fra gemte",
      "delete": "Slet post",
      "deleteError": "Kunne ikke slette posten. Prøv igen.",
      "retranscribe": "Transskribér igen",
      "retranscribeError": "Kunne ikke transskribere igen. Prøv igen.",
      "transcribing": "Transskriberer...",
      "transcriptionFailed": "Transskription mislykkedes. Du kan transskribere igen ved hjælp af genforsøgsikonet."
    },
    "debug": {
      "title": "Fejlfinding",
      "logDirectory": {
        "title": "Logmappe",
        "description": "Placering af logfiler"
      },
      "logLevel": {
        "title": "Logniveau",
        "description": "Detaljeringsgrad for logning"
      },
      "liveLogs": {
        "title": "Live logs",
        "description": "Stream applikationslogs i realtid for at diagnosticere problemer uden at åbne logfilen. Kun logs udsendt, mens dette panel er åbent, vises; respekterer indstillingen Logniveau ovenfor.",
        "live": "Live",
        "paused": "På pause",
        "pause": "Pause",
        "resume": "Genoptag",
        "copied": "Kopieret",
        "lineCount": "{{count}} linjer",
        "empty": "Venter på logs… Poster vises her, når appen udsender dem."
      },
      "updateChecks": {
        "label": "Tjek for opdateringer",
        "description": "Tjek automatisk for nye versioner af Handy",
        "lockedDescription": "Deaktiveret af systemkonfiguration (HANDY_DISABLE_UPDATER)"
      },
      "whatsNewPreview": {
        "title": "Forhåndsvisning af nyheder",
        "description": "Åbn de seneste medfølgende udgivelsesnoter uden at markere dem som set",
        "button": "Forhåndsvisning",
        "noNotes": "Ingen medfølgende udgivelsesnoter fundet",
        "error": "Kunne ikke forhåndsvise udgivelsesnoter"
      },
      "onboardingPreview": {
        "title": "Forhåndsvis introduktion",
        "description": "Forhåndsvis introduktionsskærmene uden at ændre tilladelser, modeller eller fuldførelsesstatus",
        "permissionsButton": "Tilladelser",
        "modelsButton": "Modelvalg",
        "exitButton": "Afslut forhåndsvisning"
      },
      "soundTheme": {
        "label": "Lydtema",
        "description": "Vælg et lydtema til feedback ved start og stop af optagelse"
      },
      "wordCorrectionThreshold": {
        "title": "Tærskel for ordkorrektion",
        "description": "Følsomhed for brugerdefinerede ordkorrektioner"
      },
      "historyLimit": {
        "title": "Historikgrænse",
        "description": "Maksimalt antal historikposter, der skal beholdes",
        "entries": "poster"
      },
      "recordingRetention": {
        "title": "Automatisk sletning af optagelser",
        "description": "Slet automatisk gamle optagelser for at spare plads",
        "never": "Aldrig",
        "preserveLimit": "Behold de seneste {{count}}",
        "days3": "Efter 3 dage",
        "weeks2": "Efter 2 uger",
        "months3": "Efter 3 måneder",
        "placeholder": "Vælg opbevaringsperiode..."
      },
      "alwaysOnMicrophone": {
        "label": "Altid aktiv mikrofon",
        "description": "Hold mikrofonen aktiv for hurtigere reaktion"
      },
      "clamshellMicrophone": {
        "title": "Clamshell-mikrofon",
        "description": "Mikrofon, der skal bruges, når den bærbares låg er lukket"
      },
      "postProcessingToggle": {
        "label": "Efterbehandling",
        "description": "Aktivér AI-drevet tekstforbedring efter transskription"
      },
      "muteWhileRecording": {
        "label": "Mute under optagelse",
        "description": "Sluk for systemlyd under optagelse"
      },
      "appendTrailingSpace": {
        "label": "Tilføj efterfølgende mellemrum",
        "description": "Tilføj et mellemrum efter indsat transskription"
      },
      "keyboardImplementation": {
        "title": "Tastaturimplementering",
        "description": "Vælg backend til tastaturgenveje.",
        "bindingsReset": "Tastaturgenveje var inkompatible og blev nulstillet til standard"
      },
      "paths": {
        "appData": "Appdata:",
        "models": "Modeller:",
        "settings": "Indstillinger:"
      },
      "pasteDelay": {
        "title": "Forsinkelse af indsættelse (før)",
        "description": "Forsinkelse (i millisekunder) efter kopiering af tekst, før tastetrykket for indsætning sendes. Øg, hvis intet bliver indsat."
      },
      "pasteDelayAfter": {
        "title": "Forsinkelse af indsættelse (efter)",
        "description": "Forsinkelse (i millisekunder) efter tastetrykket for indsætning, før din tidligere udklipsholder gendannes. Øg, hvis dit gamle udklipsholderindhold bliver indsat i stedet for transskriptionen."
      },
      "holdThreshold": {
        "title": "Holdetærskel",
        "description": "Kun i automatisk tilstand: hvor længe (i millisekunder) genvejen skal holdes nede, før det tæller som et hold i stedet for et tryk. Sænk værdien, hvis korte hold lader optagelsen køre videre; hæv den, hvis bevidste tryk behandles som hold."
      },
      "reliablePaste": {
        "title": "Pålidelig indsættelse (Beta)",
        "description": "Gendan først din udklipsholder, når målappen faktisk har læst transskriptionen, i stedet for efter en fast forsinkelse. Skal afhjælpe, at gammelt udklipsholderindhold indsættes, når systemet er belastet. Kræver en indsætningsmetode via udklipsholderen; kun macOS og Windows."
      },
      "recordingBuffer": {
        "title": "Ekstra optagelsesbuffer",
        "description": "Ekstra tid (i millisekunder) til at fortsætte optagelsen, efter du slipper tasten, for at fange efterfølgende lyd. 0 = ingen ekstra buffer."
      },
      "keyboardDiagnostic": {
        "title": "Tastaturdiagnostik",
        "description": "Kontrollerer, om tastaturhændelser når frem til Handy. Kun antallet af hændelser registreres — aldrig hvilke taster du trykker på.",
        "run": "Kør 10 s diagnostik",
        "running": "Lytter… tryk på din genvej et par gange (f.eks. Option+Space)",
        "secureInputLabel": "Secure Input",
        "enabled": "aktiveret",
        "disabled": "deaktiveret",
        "holder": "holdes af {{name}} (pid {{pid}})",
        "holderUnknown": "ingen synlig indehaver",
        "keyDown": "Tast ned",
        "keyUp": "Tast op",
        "flagsChanged": "Modifikatorer",
        "mouse": "Mus",
        "verdictBlocked": "Secure Input blokerer tastehændelser — genveje med en almindelig tast kan ikke fungere, før det er løst.",
        "verdictSuspicious": "Der kom modifikatorhændelser, men ingen tastehændelser — noget undertrykker taster, selvom Secure Input rapporteres som deaktiveret. Rapportér det gerne på GitHub.",
        "verdictOk": "Tastehændelser når frem til Handy som normalt.",
        "verdictNoEvents": "Ingen hændelser registreret — trykkede du på nogen taster under testen?",
        "failed": "Diagnostik mislykkedes: {{error}}"
      }
    },
    "about": {
      "title": "Om",
      "version": {
        "title": "Version",
        "description": "Nuværende version af Handy"
      },
      "whatsNewUpdates": {
        "label": "Vis nyheder",
        "description": "Vis udgivelsesnoter, efter Handy opdateres"
      },
      "appDataDirectory": {
        "title": "Appdatamappe",
        "description": "Placering, hvor Handy gemmer sine data"
      },
      "sourceCode": {
        "title": "Kildekode",
        "description": "Se kildekode og bidrag",
        "button": "Vis på GitHub"
      },
      "supportDevelopment": {
        "title": "Støt udviklingen",
        "description": "Hjælp os med at fortsætte udviklingen af Handy",
        "button": "Donér"
      },
      "acknowledgments": {
        "title": "Anerkendelser",
        "ggml": {
          "title": "ggml",
          "description": "Højtydende tensor-bibliotek til maskinlæringsinferens på enheden",
          "details": "Handys lokale tale-til-tekst funktionalitet er bygget på transcribe.cpp og ggml. Tak for det fantastiske arbejde af Georgi Gerganov og bidragyderne."
        }
      }
    }
  },
  "footer": {
    "checkingUpdates": "Tjekker for opdateringer...",
    "updateAvailableShort": "Opdatering tilgængelig",
    "upToDate": "Opdateret",
    "updateCheckingDisabled": "Tjek for opdateringer deaktiveret",
    "downloading": "Downloader... {{progress}}%",
    "installing": "Installerer...",
    "preparing": "Forbereder...",
    "checkForUpdates": "Tjek for opdateringer",
    "portableUpdateTitle": "Manuel opdatering påkrævet",
    "portableUpdateMessage": "Flytbare installationer kan ikke opdateres automatisk. Download installeren til dit system nedenfor, kør den, og installer i den samme mappe — din Data/-mappe (indstillinger, modeller, optagelser) bliver bevaret.",
    "portableUpdateButton": "Download installer",
    "portableUpdateBrowseMessage": "Flytbare installationer kan ikke opdateres automatisk. Download den nyeste version til dit system fra GitHub Releases, og installer den i den samme mappe — din Data/-mappe (indstillinger, modeller, optagelser) bliver bevaret.",
    "portableUpdateBrowseButton": "Åbn GitHub Releases"
  },
  "whatsNew": {
    "title": "Nyt i Handy v{{version}}"
  },
  "common": {
    "loading": "Indlæser...",
    "delete": "Slet",
    "back": "Tilbage",
    "close": "Luk",
    "open": "Åbn",
    "copy": "Kopiér",
    "clear": "Ryd",
    "noOptionsFound": "Ingen muligheder fundet"
  },
  "accessibility": {
    "permissionsDescription": "Handy har brug for tilgængelighedstilladelser for at skrive den transskriberede tekst.",
    "openSettings": "Åbn systemindstillinger"
  },
  "errors": {
    "loadDirectory": "Fejl ved indlæsning af mappe: {{error}}",
    "micPermissionDeniedTitle": "Mikrofonadgang nægtet",
    "micPermissionDenied": {
      "generic": "Mikrofonadgang blev nægtet af operativsystemet. Giv venligst mikrofontilladelse i dine systemindstillinger.",
      "windows": "Aktivér mikrofonadgang under Indstillinger → Privatliv og sikkerhed → Mikrofon (inklusive adgang for skrivebordsapps).",
      "macos": "Giv mikrofonadgang under Systemindstillinger → Privatliv og sikkerhed → Mikrofon.",
      "linux": "Giv mikrofonadgang i dit systems lyd- eller privatlivsindstillinger."
    },
    "noInputDeviceTitle": "Ingen mikrofon fundet",
    "noInputDevice": "Ingen lydinputenhed blev registreret. Tilslut en mikrofon eller et headset, og prøv igen.",
    "recordingFailed": "Kunne ikke starte optagelse: {{error}}",
    "modelLoadFailed": "Kunne ikke indlæse model: {{model}}",
    "modelLoadFailedUnknown": "ukendt model",
    "pasteFailedTitle": "Kunne ikke indsætte tekst",
    "pasteFailed": "Teksten kunne ikke indsættes i det aktive program.",
    "transcriptionFailedTitle": "Transskription mislykkedes"
  },
  "appLanguage": {
    "title": "Applikationssprog",
    "description": "Skift sproget for Handy-grænsefladen"
  },
  "theme": {
    "title": "Applikationstema",
    "description": "Vælg om Handy skal følge dit systemtema eller forblive lyst eller mørkt",
    "options": {
      "system": "System",
      "light": "Lys",
      "dark": "Mørk"
    }
  },
  "overlay": {
    "transcribing": "Transskriberer...",
    "processing": "Behandler...",
    "loadingModel": "Indlæser model..."
  },
  "secureInput": {
    "blockedWithCulprit_one": "{{name}} blokerer muligvis 1 genvej",
    "blockedWithCulprit_other": "{{name}} blokerer muligvis {{count}} genveje",
    "blockedNoCulprit_one": "macOS blokerer midlertidigt 1 genvej",
    "blockedNoCulprit_other": "macOS blokerer midlertidigt {{count}} genveje",
    "recorderBlockedWithCulprit": "{{name}} blokerer muligvis ændringer af genveje",
    "recorderBlockedNoCulprit": "macOS blokerer midlertidigt ændringer af genveje",
    "learnMore": "Sådan løser du det",
    "recorderBlocked": "Genveje kan ikke optages lige nu — macOS Secure Input blokerer tastehændelser. Løs advarslen om Secure Input først.",
    "dismiss": "Afvis"
  }
}

## 2. Official Technical Reference & Guides (cjpais/cjpais.github.io)

## File: README.md

# cjpais.github.io
Personal blog + scripts to make it work

## Automaded workflow from iPad -> Blog

1. Write something on Notability
2. Sync to Dropbox
3. After file updated on server execute process.py via auto_update.sh. This does the following
  4. Convert .pdf to SVG
  5. Process SVG
  6. From the processed guy generate HTML pages from SVG (index + page)
  7. Commit to git repo and see

## Dependencies
  * inkscape (for process.py)
  * inotify-tools (for running process.py automagically)
  * requirements.txt (Jinja2, static site generation) 
  
## Temp Notes on Pages:
There are no hyperlinks as of yet. This will be added in a later revision of process.py that inserts links into the SVG's themselves. CSS style will come with this. For now other pages can be found manually by going [here](https://github.com/cjpais/cjpais.github.io/tree/master/page), then navigating to cjpais.com/page/

Needs directory structure in Notability. I will have to rearrange my Dropbox & process.py to support. This will be more natural but still not ideal. Needs a flag tagged (graph like) structure, not heirarchy.

---

## File: last_updated.txt

2019-09-03 00:36:12.144071

---

## File: requirements.txt

Jinja2

---

## File: structure.md

# Directory Structure Overview

  page - where all rendered pages go (except index.html)
  res - resources. where svg's go :)
  src - source files (js, css, and html templates)