简体中文

Table of Contents
Introduction
Yet another simple dictionary application. Support multiple sources, including Longman online dictionary, and user-loaded MDX/MDD dictionary files.
视频介绍(简中)
Disclaimer
It is trying to be just a dictionary, which you may need during your English study or writing some English posts.
It is NOT a "translator", in which scenario an LLM model based tool is more suitable in modern days.
Web mode is RECOMMENDED, because both the online engine and MDX engine are based on HTML/CSS stuff. So when you need its output to be rendered as markdown, a independent parser and renderer need to written for each source, that's quite a lot of work and almost impossible.
I just write a simple markdown renderer for Longman Dictionary of Contemporary English MDX dictionary, which I uploaded in some releases, so that you can roughly use its markdown rendered output in some cases, such as working as a TUI(which has no "web core") editor plugin, or just using it in the terminal.
Other choices
There are some similar products. They are all mature products, but may not suit me(or you) in some cases. Compared to these, this application is not yet so well polished, but it has its own advantages.
- It can serve as a CLI application, conforming to the UNIX stdout convention. So you can further process the output, or embed it into any UNIX-like CLI tools, then integrate it into your CLI/TUI based workflow very easily.
- The output can be plain text(such as Markdown format), so feel free to render it with any renderer. You can also embed it into your extensible editor. I provide neovim integration out of the box.
- The raw output(for MDX dictionaries especially) are just HTML/CSS/JavaScript, so your can just use it to build a server, which you can access anywhere by your browser, without having to install any other apps. E.g. Demo Website
- Easy to cross platform. The core is written in Go.
- It's free and open source, just fork/PR/issue it if you like!
Prerequisites
- Go version >=1.16, and add $GOBIN in your $PATH
- Neovim version >= 0.9.1 (recommended, because I developed it on this version, but previous versions may also use it, only some "lsp" utils (not lsp feature itself) are required. So it can also be ported to Vim with little effort theoretically, but I am not quite familiar with vim's popup feature yet)
Features
- You can deploy it on your own server in Web/HTTP mode, no telemetry.
- Online query support based on Longman online dictionary
- Integrated with (n)vim, feel free to use it in whatever editor you are using!
- In the offline mode(do not need extra online queries itself), MDX engine is supported. The online engine may be more comprehensive and updated, but they are slow since an HTTP request is made for the first time. The offline mode, however, can work without internet connection, but pre-loaded dictionary files are needed.
- When
vocab.db is built, offline definitions can also be searched by keyword through SQLite FTS.
- Android app — a native APK that runs the full dictionary server on-device. Import your MDX files directly on the phone; the app auto-builds a SQLite cache so subsequent launches are fast.
Installation
Build from source(Recommended)
go install github.com/ChaosNyaruko/ondict@latest
or
git clone https://github.com/ChaosNyaruko/ondict.git
make install
Initialization
After installation, you can run the initialization command to set up the configuration and download the default dictionary automatically.
ondict -init
It will guide you through:
- Creating the configuration directory and file.
- Downloading the default Longman Dictionary (MDX).
- Downloading the pronunciation/image data (MDD).
- Converting the dictionary to SQLite format for faster startup and definition search (Optional but recommended).
Android
Download the latest APK from the Releases page and install it on your phone.
Requirements: Android 11+ (API 36 target). Enable "Install from unknown sources" in your phone settings if installing outside the Play Store.
How it works
The Android app embeds the full Go dictionary server via gomobile. On launch it starts a local HTTP server on 127.0.0.1:1345 and displays the web UI in a WebView — the same interface as the desktop web mode.
Dictionary setup
- Open the app and tap Import Dict (or the menu button)
- Select your
.mdx file from local storage
- Set the dictionary type (e.g.
LONGMAN/Easy, MDX)
- Restart the app — on first launch with a new dictionary the app builds a SQLite cache in the background; subsequent launches load from SQLite and start in under a second
Data storage
All user data (imported dictionaries, word bank, query history) is stored in the app's private storage and survives app updates. Only uninstalling the app removes the data.
Using Docker and serving as a HTTP server in the containerFor your convenience, the config directory in the container is remapped/mounted to your host config directory, so all generated content(such as query history) will be dumped into this directory. No other pollution.
Local
docker build . -t ondict
docker run --rm --name ondict-app --publish 1345:1345 --mount type=bind,source={your $HOME/.config/ondict},target=/root/.config/ondict ondict
Remote
docker run --rm --name ondict-app --publish 1345:1345 --mount type=bind,source={your $HOME/.config/ondict},target=/root/.config/ondict chaosnyaruko/ondict:latest
go install github.com/ChaosNyaruko/ondict/cmd/dumpdict@latest
It is used to install a "standalone" CLI tool that can decode MDX files, and dump them into a sqlite3 datebase file. See schema for more (the "vocab" table).
If you just want a decoder to parse your MDX files, this would be enough!
You can also choose the tokenizer used by the definition-search index:
dumpdict -f path/to/dict.mdx -fts-tokenizer trigram
Usage
Help
ondict
ondict -h
Examples
Work as an HTTP server
A.K.A. Web mode, recommended.
This app can also serve as a HTTP server, allowing remote fetch and query, with cache and acceleration.
ondict -serve -listen=localhost:1345 -e=mdx
Launch a http request
curl "http://localhost:1345/?query=apple&engine=mdx&format=x"
Or just open your browser, visit localhost:1345 and you'll see!
Web Mode
If you are visiting the URL with a web browser, setting format to "html" is recommended. The browser will automatically render a more beautiful page than it is in the "CLI" interface.
The HTML server home page now has two modes:
- Headword mode: jump directly to the entry page
- Definition mode: search the SQLite FTS index and browse result cards
The HTML server also includes a dedicated Word Bank page at /words.
Use the buttons on entry pages or definition search result cards to save words you want to learn, then open /words to review, revisit, or remove them.
Word Bank is stored separately from normal query history in the Ondict config directory.
Merging databases (offline)
If you have multiple wordbank.db / history.db files from different machines (laptop + desktop, old phone + new phone), the ondict merge subcommand folds them into one with conflict resolution by update_time (last-writer-wins) and a per-row tombstone for deletions:
ondict merge wordbank --dst merged.db src1.db src2.db src3.db
ondict merge history --dst merged.db src1.db src2.db [--dry-run]
For history, the merged count is MAX across sources (idempotent: re-merging the same source twice is a no-op).
See docs/adr/0001-wordbank-history-sync.md for the full design rationale.
Cloud sync (self-hosted)
A long-running ondict instance (your home box, NAS, VPS) can act as a sync server so a phone, laptop, or browser-only setup can converge on the same Word Bank and history. Endpoints live under /sync/v1 and use HTTP Basic Auth.
Server side (terminate TLS at your reverse proxy, e.g. Caddy / nginx):
export ONDICT_SYNC_USER=alice
export ONDICT_SYNC_PASSWORD='something-strong'
ondict -serve -listen=:1345 -sync-server -sync-data-dir=/var/lib/ondict/sync
Desktop client (one-shot or daemon):
export ONDICT_SYNC_USER=alice
export ONDICT_SYNC_PASSWORD='something-strong'
ondict sync --base-url https://sync.example.com # one-shot
ondict sync --base-url https://sync.example.com --loop 10m \
--gc-tombstones-after 2160h # daemon, 90-day tombstone GC
Mobile (Android): mobile.ConfigureSync(baseURL, user, pass) followed by mobile.Sync() from your Activity. The Android app records its own query history starting with this release, so it propagates upstream too.
For the full deployment / migration guide (including how to convert an existing
~/.config/ondict/{wordbank,history}.db into a sync user library), see
docs/sync-deployment.md.
You can also deploy it on your server, as an upstream of Nginx/, or just exposing it with a suitable ip/port.
You can run make serve locally for an easy example. My front-end skill is poor, so the page is ugly and rough, don't hate it :(.
There are still a lot of TODOs, feel free to give me PRs and contribute to the immature project, thanks in advance.
One-shot query
A one-shot query, it will take some time when you call it the first time, it needs some loading work.
It will launch an local server using unix domain socket.
online engine (you don't have to specify the -e option):
ondict -q <word> [-e anything]

mdx engine (ldoce5):
ondict -q <word> -e mdx

definition search (SQLite-backed MDX):
ondict -q "kidney problem" -search-def -e mdx
One-shot query, but from remote server
ondict -q <word> -remote localhost:1345

A "repl" querier
ondict -i -e mdx
input .help for commands that can be used.

Work with Neovim
See Integrated with Neovim


KNOWN BUGS:
If you use hammerspoon's "task" feature, i.e. "hs.task.new" and then "xx::start", some word queries will block the process, and can't see the result(because it hasn't returned yet), such as "test". But no such problems in real web mode, it only happens with hammerspoon.
Don't know why yet, the same word queries also works normally in Neovim integration, which also uses Lua as its async runtime. So I guess maybe it has something to do with the implementation, and it might be a bug of hammerspoon.
WORKAROUND
Using hs.execute instead of hs.task(Be careful with the shell-escaping), which is a "synchronous" method of executing a task. Normal query is fast enough and you won't notice the difference and will see the result "immediately". See for example
FZF-like completion
The web completion endpoint supports two modes:
- Prefix mode (default):
GET /complete?prefix=app
- FZF-like fuzzy mode:
GET /complete?prefix=apd&mode=fzf
The fuzzy mode is implemented in-process and does not require the external fzf binary.
The definition results page is served at:
GET /search?query=heart+attack&mode=definition&format=html
Integrated with FZF (experimental and MacOS only)
ondict -fzf
You should have FZF installed and have your ondict server listening on localhost:1345 (for now, developing)

Configuration
config.json can include tokenizer settings for the definition-search index:
{
"dicts": [
{
"name": "Longman Dictionary of Contemporary English",
"type": "LONGMAN/Easy"
}
],
"search": {
"definition_index": {
"tokenizer": "unicode61"
}
}
}
Supported tokenizer values:
unicode61: default, best for English/token and phrase search
trigram: useful for substring-style matching and some CJK scenarios, but queries shorter than 3 characters are limited
How to use it in Neovim
Installation
require("lazy").setup({
spec = {
-- add your plugins here
{
"ChaosNyaruko/ondict",
event = "VeryLazy",
build = function(plugin)
require("ondict").install(plugin.dir)
end,
dev = false,
config = function()
require("ondict").setup("localhost:1345") -- If you already have a running ondict server, you can just specify the address.
end
},
},
-- Configure any other settings here. See the documentation for more details.
-- colorscheme that will be used when installing plugins.
-- automatically check for plugin updates
checker= { enabled = false },
})
Manually
cd ~/.local/share/nvim/site/pack/packer/start/
git clone https://github.com/ChaosNyaruko/ondict.git
cd ondict
go install .
Mapping examples
nnoremap <leader>d <cmd>lua require("ondict").query()<cr>
vnoremap <leader>d <cmd>lua require("ondict").query()<cr>
vim.keymap.set("n", "<leader>d", require("ondict").query)
vim.keymap.set("v", "<leader>d", require("ondict").query)
Offline dictionary files
Put dictionary files in $HOME/.config/ondict/dicts, support formats are:
- "key-value" organized pairs JSON files.
- MDX files, refer to mdict or pdawiki.
Configuration
XDG_CONFIG_HOME convention
// cd ~/.config/ondict
.
├── config.json
├── dicts
│ ├── LDOCE5++ V 1-35.mdd
│ ├── LDOCE5++ V 1-35.mdx
│ ├── LM5style.css
│ ├── LM5style_vanilla.css
│ ├── Longman Dictionary of Contemporary English.css
│ ├── Longman Dictionary of Contemporary English.mdx
│ ├── ODE_Zh.css
│ ├── ahd3af.css
│ ├── oald9.css
│ ├── oald9.mddx
│ └── oald9.mdx
├── history.db # SQLite query history (synced)
├── history.table # human-readable append-only log (desktop only, not synced)
├── wordbank.db # SQLite saved-word bank (synced)
└── sync/ # only present on the sync server
└── <user>/
├── wordbank.db
└── history.db
An example of config.json
{
"dicts": [
{
"name": "LDOCE5++ V 1-35",
"type": "LONGMAN5/Online"
},
{
"name": "Longman Dictionary of Contemporary English",
"type": "LONGMAN/Easy",
"css": "Longman Dictionary of Contemporary English"
},
{
"name": "oald9",
"css": "oald9"
}
]
}
CSS loading
Each dictionary entry in config.json may carry an optional "css" field whose
value is the stem (no .css extension) of a CSS file located in the dicts/
directory. When the server renders a dictionary entry as HTML it injects the
matching CSS inside a <style> block so that MDX-specific formatting is applied.
CSS is resolved for every dictionary in this priority order:
- Explicit
"css" field – e.g. "css": "oald9" loads dicts/oald9.css.
- Known-prefix auto-detect – dict names containing
LDOCE5 automatically
use LM5style.css without any config change.
- Global default – the top-level
"default_css" key in config.json is
tried as a last resort, e.g. "default_css": "LM5style".
- Nothing – if no CSS is resolved, or the file does not exist, the entry is
rendered without extra styles.
Example with a global default:
{
"default_css": "LM5style",
"dicts": [
{ "name": "LDOCE5++ V 1-35", "type": "LONGMAN5/Online" },
{ "name": "oald9", "css": "oald9" }
]
}
TODOs
There are still a lot of TODOs, feel free to give me PRs and contribute to the immature project, thanks in advance.
LICENSE
LICENSE