Create vocabulary drill flashcards using sdcv and org-drill

This post shows a way to automatically convert word look-ups in a custom desktop dictionary into flashcards of org-drill.

Whats’ this about?

In a previous post I talked about a spaced repetition (SR) system in Emacs org-mode. I use it to keep track of my learning progress. But, such SR systems are more typically used as flashcards, for vocabulary acquisition tasks.

In Emacs, there is an org-drill package that is designed to do just that: it allows one to create flashcards from notes taken in org-mode, and offers several different algorithms to compute the "optimal" (in a sense of maximizing memory retention with minimum repetition efforts) review intervals, taking into account your recall performance feedback.

In another previous post, I showed a desktop dictionary created from sdcv and rofi. It uses the sdcv dictionary as backend and the rofi application runner as frontend.

So, a natural step ahead might be to combine these two sets of tools, and this is the topic of this post: I show how to modify the bash script in the desktop dictionary tool such that the words we look up are converted into flashcards compatible with the org-drill format, so that one can drill himself on these new words using the SR system of org-drill.

How does it work

Any time we need to look up a word, we can press a custom key-binding to open the desktop dictionary interface (which is the rofi runner interface), enter the word and hit Enter. See Figure below for a screenshot.

img

It sends the query to the sdcv dictionary, whose return value is then directed back to the rofi interface. See Figure below.

img

These above 2 steps are exactly the same as explained in the post desktop dictionary created from sdcv and rofi.

If the dictionary finds the target word, we also take the looked-up word together with the dictionary translation, and format those into a flashcard compatible with the org-drill format, and append the new flashcard to a dedicated drill file, which is an org file. This step is done by adding some extra stuff to the bash script that we used to build the desktop dictionary.

When one has collected some number of flashcards, he could perform a "drill" on those new vocabulary by calling the org-drill command in that dedicated org file inside Emacs.

In each flashcard, the question is the looked-up word, and the answer the dictionary translation, which is hidden at first, giving you some time to recall that yourself. See screenshot below.

img

Hitting any key then reveals the answer. Based on how well you recalled the answer, you give a rating on a scale of 0-5, with 0 meaning "Wrong, and the answer is unfamiliar when you see it", and 5 being "Correct answer, effortless". See Figure below for a screenshot. More details about the ratings are given in org-drill’s documentation.

img

Depending on how well you performed, org-drill computes a review interval, which would be further into the future for words given higher recall ratings, and shorter for poorly memorized words. Future drill sessions will be scheduled use thus computed intervals.

Install and config org-drill

I’m using vanilla Emacs. I don’t think the particular version matters, but I’ve used version 26, 27 and both worked. I manage my Emacs plug-ins using use-package, and below is the relevant snippet that installs org-drill and sets up some configurations. The settings should be self-explanatory, and more customization options are given in org-drill’s documentation.

(use-package org-drill
    :ensure t
    :config
    (setq org-drill-add-random-noise-to-intervals-p t)
    (setq org-drill-adjust-intervals-for-early-and-late-repetitions-p t)
    (setq org-drill-maximum-items-per-session 30)
    (setq org-drill-maximum-duration 15)   ; minutes
)

The org-drill format

The flashcard format of org-drill is:

* question        :drill:

Question again

** Answer

    Detailed answer here.

Some explanations:

  • The heading used as question can be of any level, but needs to be decorated with the :drill: tag.
  • Note that you need at least something under the question heading, otherwise it won’t work. If there is nothing else to say about the question, you can just copy the question again.
  • The answer to the question is given in a sub-heading, under which details of the answer are given.

Example:

** stoicism                               :drill:
    :PROPERTIES:
    :ADDED: 2021-02-27 13:25
    :END:

    stoicism

*** Dictionary lookup:

    Found 1 items, similar to stoicism.
    -->牛津英汉双解美化版
    -->stoicism
    
    /ˈstəʊɪsɪzəm; `stoɪˌsɪzəm/
    n [U] (fml 文) behaving stoically 斯多葛哲学; 自制; 坚忍
    +She showed great stoicism during her husband's final illness. 她在丈夫患病临终期间表现出了坚强的毅力.
    + They reacted to the appalling weather with typical British stoicism. 他们忍受着恶劣天气的煎熬, 表现了英国人典型的坚忍不拔的精神.

Here the question is the word stoicism, and the answer its explanation/translation we obtained from a dictionary.

The property drawer in between :PROPERTIES: and :END: is optional.

The bash script that converts sdcv look-ups into flashcards

Please refer back to the previous post of desktop dictionary created from sdcv and rofi on how to set up the sdcv+rofi desktop dictionary.

We only need to add some small changes to the old bash script. Below is the complete code:

VOCABULARY_FILE=$HOME/Notebooks/org/vocabulary.org

function lookup() {
	short=${2:-y}
	if [[ $short = y ]]; then
		ans=$(sdcv -u '牛津英汉双解美化版' -n "$1")
	else
		ans=$(sdcv -n "$1")
	fi
	echo "$ans"
}

function createEntry() {

	trans=$(echo "$2" | sed 's/\*/\+/g' | sed 's/^/    /')

	printf "%-41s %s\n" "** $1" ":drill:"
	printf "    %s\n" ":PROPERTIES:"
	printf "    %s %s\n" ":ADDED:" "$(date +'%Y-%m-%d %H:%M')"
	printf "    %s\n\n" ":END:"
	printf "    %s\n\n" "$1"
	printf "%s\n\n" "*** Dictionary lookup:"
	printf "%s\n\n" "$trans"
}

# show intital lookup interface and save input to QUERY
QUERY=$(rofi -config ~/.config/rofi/config_oneliner -lines 0 -dmenu -p \
	'Dictionary. Type in the word to search:')

while [[ -n $QUERY ]]; do

	# lookup dict and save to trans
	trans=$(lookup "$QUERY" 'y')
	if [[ $trans != "Nothing similar to"* ]]; then
		# format a drill entry and append to VOCABULARY_FILE
		drill_entry=$(createEntry "$QUERY" "$trans")
		echo "$drill_entry" >> "$VOCABULARY_FILE"
	fi

	# get new search
	QUERY=$(echo "$trans" | \
		rofi -dmenu -lines 30 -config ~/.config/rofi/config_recoll -format f)
done

Some explanations:

  • The VOCABULARY_FILE variable points to the org file where we store the flashcards.
  • The lookup function calls the sdcv dictionary to look up a given word, stored in the 1st input argument $1. If the 2nd optional argument is not given, it defaults to use the Oxford dictionary.
  • The createEntry function takes a look-up word and the translation from the dictionary, and formats them into the org-drill format explained above.
  • The main body of the script repeatedly prompts for word look-up in the rofi interface. Every time the dictionary manages to find the word entry, a new flashcard is created and appended to the vocabulary org file.

Some remarks

A simple vocabulary drill workflow is built using the sdcv dictionary as backend, the rofi application runner as frontend, and org-drill as the spaced repetition drilling tool. An advantage of this workflow is that you don’t need to manually create flashcards for the vocabulary drills, and it is often a reasonable assumption that the words you need to lookup in a dictionary are those that you are not quite familiar with, thus automatically qualified to be included in a flashcard collection.

To be honest, I rarely use it to learn new words, mostly because I didn’t manage to foster a habit out from this. Also, as I’m getting older, I found it increasingly difficult for new words to stick into the memory. What usually happens is that I encounter an unfamiliar word when reading, look it up, move on, and with a very high chance that that word is gone from memory very soon. Sometimes even after looking up a same word multiple times I still don’t remember it, and that’s quite frustrating.

Leave a Reply