Linguado

Language learning program for the terminal.

Screenshot of the application
Screenshot of the application

In 2017 I was really interested in learning two different languages: german and ruby. For german, I was using mainly Assimil’s German with Ease books, Anki flashcards and a lot of duolingo. As for ruby I was making little projects like a Tetris clone.

During this period my main laptop broke down and I was left with a really underpowered HP Stream netbook. I really like the little guy since it was pretty cheap, lightweight and had decent battery life but it really didn’t perform all that well on javascript-heavy pages like duolingo was.

Since I wanted to learn more ruby and needed a less resource-intensive way to do duolingo-style lessons I started working on a terminal-based duolingo clone called linguado. It emulated the key parts of the learning method duolingo had on its website: It had lessons that prompted users to transcribe, complete, choose between options or translate sentences both written or spoken (using text-to-speech software) all from the comfort of the command line.

I was also interested in Domain Specific Languages at the time and, harnessing ruby’s flexibility, I decided to create a sort of DSL for the lessons: They are just ruby code like the rest of the app, inheritting from a base Lesson class:

class BasicsI < Lesson
  def initialize
    super course: 'German', language: 'de-DE', name: 'Basics I' 
    
    ask_to write: 'hallo'
    ask_to choose: '? katze', answer: 'die', wrong: ['das', 'der']
    ask_to translate: 'ich bin fröhlich', answers: ['I am happy', 'I am cheerful']
    ask_to select: 'hallo', answers: ['hi', 'hello'], wrong: ['goodbye']
  end
end
Example german lesson

Same thing happens with courses: They all inherit from the Course class with the topic method allowing you to specify lesson progression and dependencies:

class GermanCourse < Course
  def initialize
    topic 'Basics I', lesson: BasicsI.new

    topic 'Greetings', lesson: Greetings.new, depends_upon: 'Basics I'

    topic 'Basics II', lesson: BasicsII.new, depends_upon: 'Greetings'
    topic 'Phrases', lesson: Phrases.new, depends_upon: 'Greetings'
  end
end
Example course definition

To give some leeway for accidental typos, the lesson creator can also specify word policies allowing a certain levenshtein distance between words. Finer tuning of this can also be achieved with conditions and exceptions to allow ‘ ein’, for instance, to have a typo like ‘eni’ but not be written like ‘eine’ or ‘einen’.

Word policies screenshot
Typos can be considered correct if a word policy is configured

Answers, errors and lessons completed are stored in a local sqlite database to generate stats for the user, adjust learning and allow progression between lessons.