Home

Awesome

SilverDict – Web-Based Alternative to GoldenDict

Crowdin

Documentation and Guides (Please read at least the general notes before using. There are some important notices.)

This project is intended to be a modern, from-the-ground-up, maintainable alternative to GoldenDict(-ng), developed with Flask and React.

You can access the live demo here (library management and settings are disabled). It is hosted by Python Anywhere on a free plan, so it might be quite slow. Demo last updated on 16th August 2024.

Screenshots

Light 1 Light 2 Dark Mobile

The dark theme is not built in, but rendered with the Dark Reader Firefox extension.

Features

Roadmap

Server-side

Client-side

Note: If all you need is this key combination, then perhaps xbindkeys suffices, which is quite huge though. Or you can use the following script:

import pyperclip
import webbrowser
from pynput import keyboard
from urllib.parse import urlencode

server_address = 'http://localhost:2628/'
group = 'English'

class KeyListener:
	def __init__(self):
		self.ctrl_pressed = False
		self.c_pressed_once = False

	def on_press(self, key):
		try:
			if key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:
				self.ctrl_pressed = True
			elif key.char == 'c' and self.ctrl_pressed:
				if self.c_pressed_once:
					selection = pyperclip.paste()
					# Uncomment (and comment the line below) to use the full web UI
					# params = urlencode({'group': group, 'key': selection})
					# webbrowser.open_new_tab(server_address + '?' + params)

					webbrowser.open_new_tab(f'{server_address}api/query/{group}/{selection}')
					self.reset()
				else:
					self.c_pressed_once = True
			else:
				self.reset()
		except AttributeError:
			self.reset()

	def on_release(self, key):
		if key == keyboard.Key.esc:
			return False

	def reset(self):
		self.ctrl_pressed = False
		self.c_pressed_once = False

if __name__ == '__main__':
	listener = KeyListener()
	with keyboard.Listener(
			on_press=listener.on_press,
			on_release=listener.on_release
		) as listener:
		listener.join()

Usage

Dependencies

This project utilises some Python 3.10 features, such as the match syntax, and a minimal set of dependencies:

PyYAML # configuration files
Flask # the web framework
Flask-Cors
waitress # the WSGI server, note: other servers like Gunicorn and uWSGI work but you have to adjust the code
python-idzip # for dictzip
python-lzo # for v1/v2 MDict
xxhash # for v3 MDict
dsl2html # for DSL
xdxf2html # for XDXF-formatted StarDicts
requests # for auto-update

The packages dsl2html and xdxf2html are mine, and could potentially be used by other projects.

In order to enable morphology analysis, you need to place the Hunspell dictionaries into ~/.silverdict/hunspell, and install the Python package sibel or hunspell. I developed sibel as a faster alternative to PyHunspell. But it might be difficult to install (see its Readme).

In order to enable Simplified/Traditional Chinese conversion, you need to install the Python package opencc.

To use full-text search, please install xapian and the Python bindings, optionally also lxml.

Note about the non pure Python dependencies

python-lzo, xxhash, dsl2html, xdxf2html all have pure Python alternatives, but they are either much slower or not very robust. If you are unable to install python-lzo or dsl2html, no action is needed. For xxhash, please install the pure Python implementation ppxxh instead. For xdxf2html, install lxml, which is not pure Python either, but its binary wheels are available for most platforms.

Local Deployment

The simplest method to use this app is to run it locally.

cd client
yarn install
yarn build
mv build ../server/

And then:

cd ../server
pip install -r requirements.txt
python server.py # working-directory-agnostic

Then access it at localhost:2628.

Or, if you do not wish to build the web app yourself or clone the whole repository, you can download from release a zip archive, which contains everything you need to run SilverDict.

For Windows users: A zip archive complete with a Python interpreter and all the dependencies (except for Xapian) is available in release. Download the archive, unzip it, and double-click setup.bat to generate a shortcut. Then you can move it wherever you wish and click on it to run SilverDict. After launching the server, you can access it at localhost:2628.

For Termux users: run the bash script termux_setup.sh in the top-level directory, which will install all the dependencies, including PyHunspell. The script assumes you have enabled external storage access and will create a default source directory at /sdcard/Documents/Dictionaries.

Alternatively, you could use dedicated HTTP servers such as nginx to serve the static files and proxy API requests. Check out the sample config for more information.

Server Deployment

I recommend nginx if you plan to deploy SilverDict to a server. Run yarn build to generate the static files of the web app, or download a prebuilt one from release (inside server/build), and then place them into whatever directory where nginx looks for static files. Remember to reverse-proxy all API requests and permit methods other than GET and POST.

Assuming your distribution uses systemd, you can refer to the provided sample systemd config and run the script as a service.

Docker Deployment

Build Docker Image from Source

docker build --tag silverdict https://github.com/Crissium/SilverDict.git
docker run --rm --publish 2628:2628 --volume ${PATH_TO_DICTIONARIES}:/root/.silverdict/ --name silverdict silverdict

${PATH_TO_DICTIONARIES} is the path where settings and dictionaries are stored. The default path to scan is ${PATH_TO_DICTIONARIES}/source for dictionaries and ${PATH_TO_DICTIONARIES}/hunspell for spellchecking libraries (if enabled).

When building the Docker image, optional features can be enabled by passing --build-arg. For example, if you want to enable full text search:

docker build --tag silverdict --build-arg ENABLE_FULL_TEXT_SEARCH=true https://github.com/Crissium/SilverDict.git

Available arguments are:

Argumentvaluedefault
ENABLE_FULL_TEXT_SEARCHtrue or emptyempty
ENABLE_MORPHOLOGY_ANALYSIStrue or emptyempty
ENABLE_CHINESE_CONVERSIONtrue or emptyempty

Or use Docker Compose: Edit docker-compose.yml and

docker compose up -d

Note that if the paths to be mounted do not exist, Docker will create them with root ownership.

Use Ready-Built Docker Image

docker pull mathdodger/silverdict

Then run the container as above. This image is built with all three optional features enabled.

Contributing

Credits

The favicon is the icon for 'Dictionary' from the Papirus icon theme, licensed under GPLv3.

The Dockerfile is contributed by simonmysun.

This project uses or has adapted code from the following projects:

NameDeveloperLicence
GoldenDictKonstantin IsakovGPLv3
mdict-analysisXiaoqiang WangGPLv3
mdict-querymmjangNo licence
python-stardictSu XingGPLv3
dictionary-db (together with the n-gram method)Jean-François DockesGPL 2.1
pyglossarySaeed RasooliGPLv3

I would also express my gratitude to my long-time 'alpha-tester' Jiang Qian, without whom this project could never become what it is today.

A word about the licence

This project is licensed under GPLv3. But it's said that programs designed to run on a server should be licensed under AGPL instead. One thing I don't understand is that, for example, if you were to write a custom transformation script for a dictionary and run it on your server, then under AGPL you'd have to release the script's source code. That sounds unreasonable to me.

Similar projects


Footnotes

  1. GoldenDict stores the decoded entries and full-text definitions in its custom index. I see no reason why I should follow suit when one can always convert dictionaries in this obnoxious format into HTML-formatted StarDict with the excellent pyglossary.

  2. The original use case is to support an ancient Greek lexicon that uses mixed Greek/Beta Code headwords. Normal dictionaries should not have this problem. Besides, implementing, say, a QWERTY-JCUKEN mapping is too trivial, whereas real transliteration is overcomplicated.