macOS app

bloop — translate anything you can select, with a double-tap

Select text in any app. Double-tap ⌥. The translation appears in a popover under the selection, and the app you were in stays in front. Apple's on-device translation where it can; Google, opt-in and labelled, where it cannot.

swift, swiftui, appkitbloopit.app

Translating a line used to be a detour: copy it, switch to a translator, paste, read, switch back — and find the place in whatever I was doing. Bloop turns that into a gesture that never leaves the app.

Select text anywhere — Safari, Notes, Slack, a terminal — and double-tap ⌥. The translation appears under the selection, the selection stays where it was, ⌘C copies the result and Esc puts it away. With nothing selected, the same double-tap opens a field: type in Hebrew, press ↩, and paste the English into the reply.

Around that: any modifier key as the trigger, at your own speed; pinned languages that ⇥ steps through; a searchable history on ⌥⌘Y; light, dark or follow the Mac. No account, no analytics. Built with Claude. Available now at bloopit.app via Lemon Squeezy. Requires macOS 26.

fig.1— select, double-tap ⌥, read it; ⌘C takes it with you.
fig.2— nothing selected: type in Hebrew, ↩, paste the English into the reply.

How a double-tap becomes a popover

A bare modifier is not a hotkey — Carbon's RegisterEventHotKey cannot see one — so Bloop listens through a listen-only event tap, which leaves every key working exactly as it did. A tap only counts if nothing else happens during it: ⌥-tabbing between windows or typing an ⌥-accented letter never fires. The tap also hears key-downs, purely so that typing between two taps cancels the gesture, and that handler never looks at which key it was. A translation app with a keystroke tap owes you that sentence.

  bloop — two taps, and the app you were in stays in front

   ⌥ ⌥ ──▶ event tap ──▶ ┌────────────┐
          listen-only    │  detector  │  clean tap · clean tap · in the window
                         └─────┬──────┘
                               │ fire
                               ▼
                         ┌────────────┐ ──▶ AX: selected text + its bounds
                         │   reader   │
                         └─────┬──────┘ ──▶ else ⌘C, pasteboard put back
                               │
                               ▼
                         ┌────────────┐ ──┬──▶ Apple, on-device
                         │ translator │   │
                         └─────┬──────┘   └──▶ Google, opt-in, labelled
                               ▼
              popover under the selection · key window, app not activated
 ⌥ ⌥ ─▶ event tap, listen-only
             │
             ▼
       ┌────────────┐
       │  detector  │
       └─────┬──────┘
             │ fire
             ▼
       ┌────────────┐
       │   reader   │ ─▶ AX, else ⌘C
       └─────┬──────┘
             ▼
       ┌────────────┐
       │ translator │ ─▶ Apple · Google
       └─────┬──────┘
             └─▶ popover · app stays front
fig.3— detect, read, translate. Focus never leaves the app you were in.

Reading the selection is Accessibility first: the selected text and the on-screen bounds of its first and last character, so the popover lands 8pt under the last line, aligned with the first. Apps that expose no text — Electron, parts of Chrome — get a synthesized ⌘C, with the pasteboard snapshotted and put back. The popover then becomes key without activating Bloop, the way Spotlight does, so Esc and ⌘C reach it while the app behind it keeps its selection.

Where apple stops

TranslationSession(installedSource:target:), new in macOS 26, is the reason Bloop can be a menu-bar app at all: it translates headless, with no SwiftUI view on screen. But Apple's framework has no Hebrew — the language both demos are in. So there is a second engine, off by default: Google Translate, used only for the pairs Apple cannot do, and every result it returns is labelled via Google.

It is not an official API, so it is built to fail politely. A burst of about 150 requests while testing earned a 429 on Google's gtx hosts that lingered for a long time — while clients5.google.com, the endpoint Chrome's own translation uses, kept answering. Requests made during a block extend it. So there are two front-ends with failover, exponential back-off per front-end, and a minimum spacing between requests so a burst can never start here. One person translating a line every few seconds is nowhere near any of it.

The app you were in should never notice that you asked.
macosswiftswiftuiappkitaccessibilitytranslationclaude
← All projects