Lvim Linguistics is a plugin for controlling spelling and keyboard language in insert mode (useful when writing documents in a foreign language).
Supports per-project local config (
.lvim_linguistics.jsonin the project root)
Current version - 1.2.00 (2026-03-20)
Keyboard switching depends on your display server. The plugin auto-detects the session and picks the right tool:
| Session | Tool |
|---|---|
| X11 | xkb-switch |
| Wayland — Hyprland | hyprctl (bundled with Hyprland) |
| Wayland — niri | niri (bundled) |
| Wayland — sway | swaymsg (bundled) |
| Wayland — mango | mmsg |
| Wayland — GNOME | gdbus (bundled with GLib) |
You can always override
kbrd_cmdmanually — see configuration below.
{
"lvim-tech/lvim-linguistics",
dependencies = { "lvim-tech/lvim-utils" },
config = function()
require("lvim-linguistics").setup({ ... })
end,
}-- In your init.lua, after the plugin is on the runtimepath:
vim.pack.add({
{ src = "https://github.com/lvim-tech/lvim-utils" },
{ src = "https://github.com/lvim-tech/lvim-linguistics" },
})
require("lvim-linguistics").setup({ ... })use({
"lvim-tech/lvim-linguistics",
requires = { "lvim-tech/lvim-utils" },
config = function()
require("lvim-linguistics").setup({ ... })
end,
})plugin_config = {
-- Keyboard switch command. Auto-detected from XDG_SESSION_TYPE /
-- XDG_CURRENT_DESKTOP / WAYLAND_DISPLAY at startup.
-- Can be a string prefix ("xkb-switch -s ") or a function:
--
-- kbrd_cmd = function(lang)
-- local idx = { en = 0, bg = 1 }
-- return "hyprctl switchxkblayout all " .. (idx[lang] or 0)
-- end
--
kbrd_cmd = "<auto-detected>",
-- Folder where spell files (.spl) are stored.
-- Files are downloaded automatically on first use (async, no blocking).
spell_files_folder = os.getenv("HOME") .. "/.config/nvim/spell/",
},base_config = {
mode_language = {
active = false,
file_types = {
black_list = { ... }, -- filetypes where switching is disabled
white_list = {},
},
normal_mode_language = nil, -- layout name used in Normal mode
insert_mode_language = nil, -- layout name used in Insert mode
insert_mode_languages = {}, -- selectable layouts in the UI
},
spell = {
active = false,
file_types = {
black_list = { ... },
white_list = {},
},
language = nil, -- active language key (must exist in languages)
languages = {
en = {
spelllang = "en",
spellfile = "en.add",
},
},
},
}require("lvim-linguistics").setup({
base_config = {
mode_language = {
active = true,
file_types = {
white_list = { "tex", "markdown" },
},
normal_mode_language = "us",
insert_mode_language = "bg",
insert_mode_languages = { "bg", "de", "fr" },
},
spell = {
active = true,
file_types = {
white_list = { "tex", "markdown" },
},
language = "en",
languages = {
en = { spelllang = "en", spellfile = "en.add" },
bg = { spelllang = "bg", spellfile = "bg.add" },
en_bg = { spelllang = "en,bg", spellfile = "en_bg.add" },
},
},
},
})For Hyprland / mango (index-based layout switching), use a function for
kbrd_cmd:plugin_config = { kbrd_cmd = function(lang) local idx = { en = 0, bg = 1 } return "hyprctl switchxkblayout all " .. (idx[lang] or 0) end, },
All functionality is exposed through a single command with subcommands:
:LvimLinguistics [subcommand]
| Subcommand | Description |
|---|---|
| (none) | Open the main UI panel |
spelling |
Open the UI on the Spelling tab |
insert-mode |
Open the UI on the Insert Mode tab |
config |
Open the UI on the Config tab |
toggle-spelling |
Toggle spell checking on/off |
toggle-insert-mode |
Toggle insert-mode language switching on/off |
vim.keymap.set(
"n",
"<C-c>s",
"<cmd>LvimLinguistics toggle-spelling<cr>",
{ noremap = true, silent = true, desc = "Toggle spelling" }
)
vim.keymap.set(
"n",
"<C-c>l",
"<cmd>LvimLinguistics toggle-insert-mode<cr>",
{ noremap = true, silent = true, desc = "Toggle insert mode language" }
)Spell files (.spl) are downloaded automatically from the Vim FTP mirror the first time a language is activated. The download is non-blocking — Neovim stays responsive and a notification appears when the file is ready.
The plugin defines the following highlight groups via lvim-utils.highlight. They adapt automatically to palette changes (e.g. when switching colorschemes via lvim-colorscheme).
| Group | Default color | Purpose |
|---|---|---|
LvimLinguisticsIcon |
teal | General icons |
LvimLinguisticsSpellActive |
green | Active spell indicator |
LvimLinguisticsSpellLang |
blue | Language name |
LvimLinguisticsSpellSep |
blue (blended) | Separator |
LvimLinguisticsLangActive |
purple bold | Active language in mode switching |
LvimLinguisticsLangNormal |
teal | Normal mode language |
LvimLinguisticsLangInsert |
yellow | Insert mode language |
Override in your colorscheme or config:
vim.api.nvim_set_hl(0, "LvimLinguisticsSpellActive", { fg = "#your_color" })-- Heirline example
local spell = {
condition = function()
return require("lvim-linguistics.status").spell_has()
end,
provider = function()
return require("lvim-linguistics.status").spell_get()
end,
hl = { fg = "#YOUR_COLOR", bold = true },
}