acidsugarx/babel.nvim

github github
language
stars 13
issues 3
subscribers 0
forks 2
CREATED

UPDATED


๐ŸŒ babel.nvim

Translate text without leaving Neovim

Neovim Lua License


โœจ Features

  • ๐Ÿ”ค Translate selected text or word under cursor
  • ๐ŸชŸ Multiple display modes (float, picker)
  • ๐Ÿ” Auto-detect installed picker
  • ๐Ÿ“‹ Copy translation to clipboard with y
  • โšก Async translation (non-blocking)

Supported Pickers

Picker Status
Native float โœ…
snacks.nvim โœ…
telescope.nvim โœ…
fzf-lua โœ…
mini.pick โœ…

โšก Requirements

  • Neovim >= 0.9.0
  • curl

Optional (for picker display):

  • snacks.nvim, telescope.nvim, fzf-lua, or mini.pick

๐Ÿ“ฆ Installation

lazy.nvim

{
  "acidsugarx/babel.nvim",
  version = "*", -- recomended for the latest tag, not main
  opts = {
    target = "ru",  -- target language
  },
  keys = {
    { "<leader>tr", mode = "v", desc = "Translate selection" },
    { "<leader>tw", desc = "Translate word" },
  },
}

โš™๏ธ Configuration

Minimal Setup

require("babel").setup({
  target = "ru",
})

Full Options

require("babel").setup({
  source = "auto",        -- source language (auto-detect)
  target = "ru",          -- target language
  provider = "google",    -- translation provider: "google", "deepl"
  network = {
    connect_timeout = 5,   -- curl connect timeout in seconds
    request_timeout = 15,  -- max request time in seconds
  },
  cache = {
    enabled = false,       -- enable in-memory translation cache
    limit = 200,           -- max cache entries
  },
  history = {
    enabled = false,       -- keep in-memory translation history
    limit = 20,            -- max saved entries
  },
  fallback_chain = {
    deepl = { "google" }, -- if DeepL fails, try Google
    google = {},
  },
  display = "float",      -- "float" or "picker"
  picker = "auto",        -- "auto", "telescope", "fzf", "snacks", "mini"
  float = {
    border = "rounded",
    mode = "center", -- "center" or "cursor"
    max_width = 80,
    max_height = 20,
    auto_close_ms = 0, -- auto-close delay, 0 = disabled
    pin = true, -- allow pin toggle with `p` when auto-close is enabled
    copy_original = false, -- allow copying original text with `Y`
    nvim_open_win = {}, -- extra nvim_open_win() options (overrides defaults)
  },
  keymaps = {
    translate = "<leader>tr",
    translate_word = "<leader>tw",
  },
  -- DeepL provider settings (optional)
  deepl = {
    api_key = nil,        -- or use DEEPL_API_KEY env variable
    pro = nil,            -- nil = auto-detect, true = Pro, false = Free
    formality = "default", -- "default", "more", "less", "prefer_more", "prefer_less"
  },
})

Options

Option Type Default Description
source string "auto" Source language (auto-detect)
target string "ru" Target language code
provider string "google" Translation provider: "google", "deepl"
network.connect_timeout number 5 Network connect timeout (seconds)
network.request_timeout number 15 Network request timeout (seconds)
cache.enabled boolean false Enable in-memory translation cache
cache.limit number 200 Maximum cache entries
history.enabled boolean false Enable in-memory translation history
history.limit number 20 Maximum stored history entries
fallback_chain table { deepl = {"google"}, google = {} } Per-provider fallback chain
display string "float" Display mode: "float" or "picker"
picker string "auto" Picker: "auto", "telescope", "fzf", "snacks", "mini"
float.mode string "center" Float preset: "center" or "cursor"
float.auto_close_ms number 0 Auto-close timeout in milliseconds (0 disables)
float.pin boolean true Enable pin toggle key (p) when auto-close is enabled
float.copy_original boolean false Enable copying original text with Y
float.nvim_open_win table {} Extra nvim_open_win() options for float window
deepl.api_key string nil DeepL API key (or use DEEPL_API_KEY env)
deepl.pro boolean nil Force Pro/Free endpoint (nil = auto-detect by key)
deepl.formality string "default" Formality: "default", "more", "less", "prefer_more", "prefer_less"

Cursor-follow float preset

For a smaller popup that follows your cursor, use:

require("babel").setup({
  float = {
    mode = "cursor",
    max_width = 60,
    max_height = 10,
  },
})

Network timeout customization

You can tune provider request timeouts for slow/unstable networks:

require("babel").setup({
  network = {
    connect_timeout = 3,
    request_timeout = 25,
  },
})

Translation history

You can keep the latest successful translations in memory:

require("babel").setup({
  history = {
    enabled = true,
    limit = 50,
  },
})

Fallback chain and cache

You can control provider fallback behavior and enable in-memory caching:

require("babel").setup({
  provider = "deepl",
  fallback_chain = {
    deepl = { "google" },
    google = {},
  },
  cache = {
    enabled = true,
    limit = 500,
  },
})

Float window customization

You can override Babel's default float settings by passing options directly to nvim_open_win():

require("babel").setup({
  float = {
    max_width = 60,
    max_height = 10,
    nvim_open_win = {
      relative = "cursor",
      row = 1,
      col = 0,
      anchor = "NW",
      border = "single",
      title = " Babel ",
    },
  },
})

Language Codes

Code Language
en English
ru Russian
de German
fr French
es Spanish
it Italian
pt Portuguese
zh Chinese
ja Japanese
ko Korean
ar Arabic
hi Hindi
tr Turkish
pl Polish
uk Ukrainian

๐Ÿš€ Usage

Keymaps

Keymap Mode Description
<leader>tr Visual Translate selection
<leader>tw Normal Translate word under cursor

Commands

Command Description
:Babel [text] Translate provided text
:[range]Babel Translate selected line range (e.g. :10,20Babel)
:BabelWord Translate word under cursor
:BabelRepeat Repeat last translation input

In Translation Window

Key Action
q / <Esc> / <CR> Close window
y Copy translation to clipboard
Y Copy original text (if float.copy_original = true)
p Pin/unpin auto-close timer (if enabled)
j / k Scroll

๐ŸŒ Providers

Provider Status API Key Notes
Google Translate โœ… No Default, unofficial API
DeepL ๐Ÿงช Yes (free tier) Best quality, 500k chars/month free
LibreTranslate ๐Ÿ”œ No Open source, self-hostable
Yandex ๐Ÿ”œ Yes Great for Russian
Lingva ๐Ÿ”œ No Google proxy, no rate limits

Provider capabilities

You can inspect provider capabilities from Lua:

local caps = require("babel").get_provider_capabilities()
-- caps.google.supports_formality == false
-- caps.deepl.supports_formality == true

local deepl = require("babel").get_provider_capabilities("deepl")

๐Ÿงช Testing: DeepL provider is implemented but needs testing. If you have a DeepL API key and want to help test, please open an issue with your feedback!

  1. Get a free API key at deepl.com/pro#developer (500k chars/month free)

  2. Set up the API key (choose one):

    Option A: Environment variable

    export DEEPL_API_KEY="your-api-key-here"
    

    Option B: In config

    require("babel").setup({
      provider = "deepl",
      deepl = {
        api_key = "your-api-key-here",
      },
    })
    
  3. The endpoint (Free/Pro) is auto-detected from the key suffix (:fx = Free). You can override with deepl.pro = true/false.

  4. If no API key is found, babel.nvim will automatically fall back to Google Translate with a warning.

๐Ÿค Contributing

Contributions are welcome! Feel free to:

  • ๐Ÿ› Report bugs
  • ๐Ÿ’ก Suggest features
  • ๐Ÿ”ง Submit pull requests

If you use AI coding assistants, check AGENTS.md for project architecture, quality gates, and safe-change checklist.

๐Ÿ™ Acknowledgments

Thanks to the amazing Neovim plugin ecosystem:

๐Ÿ“ License

MIT ยฉ Ilya Gilev