Highlight text anywhere, hit a keybind, get a translation (Czech by default —
configurable, see Configuration). The source language is
auto-detected by Google Translate by default, or can be pinned to a preset
one. Translation is done via
translate-shell (trans -b, the
free web endpoint — no API key). Reads the primary selection
(middle-click buffer) first and falls back to the regular clipboard if the
primary selection is empty. An empty selection just shows a "Nothing selected"
notification instead of erroring.
Built for Sway on EndeavourOS/Arch. Defaults to kitty as the terminal and notify-send (mako) for notifications, but both are swappable — see Swapping the terminal / notification tool.
| Keybind | Mode | Script |
|---|---|---|
Super+T |
Notification popup, click it to copy | translate-notify.sh |
Super+Shift+T |
Empty floating window, type the text yourself | translate-input.sh |
Super+Ctrl+T |
Floating kitty window, stays open | translate-popup.sh |
translate-notify.sh grabs the selection, translates it, and shows the result
as a corner notification (8 s timeout). Clicking the notification copies the
translation to the clipboard (wl-copy), confirmed by a short "Copied to
clipboard" toast. If you ignore or dismiss it, nothing is copied.
How the click works: notify-send --action default=… (needs libnotify ≥ 0.8)
blocks until the notification is clicked/dismissed/expired and prints the
invoked action's id; mako fires the default action on left-click. This is
daemon-specific — plain fire-and-forget notify-send can't capture clicks,
and on dunst you'd use dunstify --action instead.
translate-popup.sh grabs the selection, snapshots it to a temp file, and
opens kitty --class trans-popup running translate-view.sh, which shows the
original text and the translation. The window floats (via the Sway rule below)
and stays open until you press a key: c copies the translation to the
clipboard and closes, n starts the next translation (prompts you to type
new text), any other key just closes the window.
translate-input.sh opens the same floating kitty window (same trans-popup
app_id, so the same Sway float rule), but empty and waiting for you to type
the text — it never touches the selection or clipboard. Use it when there's
nothing highlighted, or when you want to translate something you're about to
type rather than something already on screen. It runs translate-prompt.sh,
which prompts for a line of text, translates it, and offers the same
c to copy / n for the next translation / any-other-key-to-close
finish as terminal mode. Submitting an empty line just closes the window.
Hard dependencies (required):
translate-shell— thetranscommandwl-clipboard—wl-paste/wl-copy
Swappable (defaults below; see Swapping the terminal / notification tool):
kitty— terminal for the popup/input modes. Overrideopen_terminalintranslator.confto use foot, alacritty, etc.mako— notification daemon. On this machine neither mako nor dunst was installed when this was set up (2026-07-08), so mako was installed (pacman -S mako) and is autostarted from the Sway config (see below). Itsnotify-sendinterface is overridable viaNOTIFY; both mako and dunst ship a compatiblenotify-send, so click-to-copy keeps working on dunst (via itsnotify-send, nodunstifyrewrite needed).libnotify≥ 0.8 —notify-sendwith--actionsupport
pacman -S --needed translate-shell wl-clipboard kitty mako libnotifyThese lines are in the main Sway config (~/.config/sway/config), not in
this repo, so git does not capture them. On a fresh setup you must add
them manually. They must appear after set $mod is defined:
# ═══ TRANSLATOR (~/.config/sway/translator) — managed block, start ═══
# notification daemon (needed for notification mode + click-to-copy)
exec mako
# floating rule for the terminal-mode popup window
for_window [app_id="trans-popup"] floating enable, resize set 800 400, move position center
# notification mode: translate selection (per translator.conf), click notification to copy
bindsym $mod+t exec ~/.config/sway/translator/translate-notify.sh
# input mode: floating kitty window that waits for you to type the text
bindsym $mod+Shift+t exec ~/.config/sway/translator/translate-input.sh
# terminal mode: floating kitty window with original + translation
bindsym $mod+Ctrl+t exec ~/.config/sway/translator/translate-popup.sh
# ═══ TRANSLATOR — managed block, end ══════════════════════════════════
Then reload Sway (Super+Shift+R → 1 on this setup, or swaymsg reload).
Languages are configured in translator.conf (same directory as the
scripts, sourced as shell by both modes via load-config.sh). The file is
optional — without it the defaults below apply. Changes take effect on the
next keypress, no reload needed.
TARGET_LANG="cs" # language to translate into (any `trans -list-codes` code)
AUTO_DETECT="on" # "on" = Google detects the source; "off" = use SOURCE_LANG
SOURCE_LANG="en" # source language, only used when AUTO_DETECT="off"Setting AUTO_DETECT="off" while leaving SOURCE_LANG empty shows a warning
notification and falls back to auto-detection. Full details and examples:
docs/CONFIG.md.
The terminal (kitty) and the notification command (notify-send) are the only
two non-hard dependencies — everything else (trans, wl-clipboard) is
required. Both are overridable in translator.conf; the defaults are kept if
you leave it alone.
# notification command — must accept notify-send's flags (-a -u -t -A);
# mako and dunst both provide a compatible notify-send.
NOTIFY="notify-send"
# terminal for the popup/input windows — a function so it can absorb each
# terminal's flag differences. The window MUST get the app_id/class
# "trans-popup" (so the Sway float rule matches) and run the command in "$@".
open_terminal() { exec kitty --class trans-popup --title "Translator" "$@"; }
# alacritty needs -e before the command:
# open_terminal() { exec alacritty --class trans-popup --title "Translator" -e "$@"; }- Each translation takes about a second. This is not a bug in the
scripts —
trans -bsends the text to Google Translate's free web endpoint over the network and waits for the reply, so every translation costs one internet round-trip (typically ~0.5–1 s, more on a slow connection; repeat translations of the same text come back near-instantly from Google's own caching). There's nothing to speed up locally short of an offline engine, which is an explicit non-goal — expect the brief pause, that's why the terminal/input modes print aTranslating…line while it works.
- No notification at all → is mako running?
pgrep mako; start withswaymsg exec makoor reload Sway. - "Translation failed" → no network, or Google rate-limited the free
endpoint; wait a bit. Test manually:
trans -b :cs "hello"(use your configuredTARGET_LANG). - Wrong language detected / wrong direction → check
translator.conf: either fixTARGET_LANG, or setAUTO_DETECT="off"with an explicitSOURCE_LANG(see docs/CONFIG.md). - Popup window isn't floating → the
for_window [app_id="trans-popup"]rule is missing from the main Sway config (see above). - Wrong text translated → primary selection was empty, so the clipboard fallback kicked in. Re-highlight the text.