Show HN: I send myself automated emails to practice Dutch
github.com
github.com
I didn't want to pay for expensive vocabulary apps that often start with beginner words while I am looking for advanced vocabulary, so I built it myself.
I built a comparable system that sends me an email every day that I can respond to, to maintain a journal; it works like described above and has been running for about 5 years now with zero downtime.
Anyway the idea is really good!
Don't need that. You're already paying for (or self host) your primary email address right? That includes sending emails from that email address. Use those same login credentials to send emails to yourself, no need to contract a third party for sending a handful of emails per day, especially to yourself
Although, I appreciate the idea and wish luck with learning the language (as NT2 I assume). Questions welcome.
Are you reinventing a stove-pipe version of Anki, based on cloud services and e-mail?
Are you really going to send yourself 300 e-mails when 300 cards are due that day?
My partner and I do something similar for Korean & English (she’s Korean native and is fluent in English and I’m learning Korean). We actually built it out for ourselves and some friends and just released it yesterday[0].
Still working out some kinks, but it sends a question every weekday via email that you’d respond to. It then sends back feedback on vocab & grammar, all with spaced repetition baked in to keep track of words you learn/use as you continue.
It’s currently tailored towards those that can already read and have basics under their belt.
You've made some cool stuff, inspirational.
OP actually did it more efficiently than most! You should see the AWS suggested architecture. It uses something like 10 different AWS services.
My company actually set out to solve this very problem. We have a cloud cron hosting that's more reliable than the AWS architecture but just requires a few lines of code. Literally this is all you have to do:
@DBOS.scheduled('* * * * *')
@DBOS.workflow()
def example_scheduled_workflow(scheduled_time: datetime, actual_time: datetime):
DBOS.logger.info("I am a workflow scheduled to run once a minute.")
https://github.com/dbos-inc/dbos-demo-apps/blob/main/python/...FWIW you can't really do the same thing on Cloudflare workers -- their crons are "best effort", and you'd still need to get storage somewhere else. With DBOS the storage is built right in.
Cron trigger.
Process.
Gpt API.
Database for persistence.
Email sender.
Which part of that wouldn't you have?
The second one, I'd say either bepalen/beslissen (if you want to make a decision) or uitvinden ("out-finding", find out). The word from the screenshot, vaststellen (literally: "fixed setting", think of it as fixating), is still in common enough use, particularly in formal writing, but more of a word for "good to know" than to use in active vocabulary
No comments on the third one :) That's idiomatic use (though I'd have thought of, just like in English, "not falling over" as opposed to "work-life balance" as the defining meaning of the word)
The readme says the examples are generated using ChatGPT. Why not use an existing dataset instead of generating mediocre examples with lots of energy? Similar to what YouGlish(.com) does, you could get a lot of sentences spoken by native speakers from YouTube transcripts for example, or Wikipedia for written language, or other sources costing virtually no energy at all to find a word in and being better as well
So going by the screenshot in the readme where you have vaststellen; https://hetnederlands.com/dictionary/vaststellen
The things you can do with language learning and LLMs is just incredible :)
Nuenki uses processed wiktionary data. Its definition for that word is this: https://dictionary.nuenki.app/get_definition?language=Dutch&...
(ofc rendered nicely in the client).
For dutch, I would use something like van dale or woordenlijst (het groene boekje), both have free online versions.
I already spend all my time in the inbox and find it hard to ignore an email. Inbox zero habits would kick in and ensure that I do at least some memorization every day. A single Anki card in my inbox is far less daunting than the entire deck staring at me when I open the app.
Unfortunately Anki doesn't have a proper API and isn't easy to reverse engineer. I tried to build something using a scraper that logs in to the Anki web app, but it turned out to be very janky, and couldn't identify overdue cards. Somebody with better desktop app/python skills could probably do it locally, but I gave up.
Tried any of the below?
AnkiConnect (HTTP API): https://git.foosoft.net/alex/anki-connect
Rust: https://github.com/ankitects/anki/tree/main/rslib via Protobuf: https://github.com/ankitects/anki/tree/main/proto/anki
Rough DB Schema (outdated, but sufficient): https://github.com/ankidroid/Anki-Android/wiki/Database-Stru...
“Talk to me in <language> and point out my grammar errors in English”
I imagine it’s risky, learning bad habits. But it seems like it might be very convenient. I believe the biggest issue for me is actually using a language regularly. But I’m way too socially afraid to do one of those “speak to a random person live” things.
Or even some sort of, “translate all my emails to <language>, but show English when I mouse over.”
I bolstered my French by setting almost all my video games to French in university. It helped me a ton, and was accessible because I understood the context.
Translation tech has come a long way. Might not even need LLMs.
Maybe I should feed it bits from Wikipedia and have it censor word classes for me (or is part-of-speech identification by human-made algorithms reliable?), but that's a lot more involved to code up than prompting it "hey just do this task". I'm sure I'm just holding it wrong and it can be a useful language teacher in some way, e.g. I have had good results with 1:1 translations, but don't expect it just does what you ask it when you can't verify the result
Reading the other couple of replies, though, maybe I should rethink doing even that.
I suppose a bank of words on a .CSV, a script which selects words, and a job triggered via a ChronJob which opens an issue does the trick. I had it so when an issue is opened, I got emailed.
The pro of this approach is you don’t have to deploy any infra. The con is that your emails never look as nice as you got it :’)
I was much more lazy, though, and set this up on Zapier.
You could generalize this into all sorts of reminders , notices, affirmations, quotes.
I've had really great success with my children using national TV networks iOS apps with a VPN, e.g. SVT Barn (Swedish), WDR/ZDF/ARD (German), etc
As for spaced repetition I developed an alternative which just has a column for number of times correct answer was given and order by descending order on that field. This gives you new words first followed by words you've barely gotten correct etc
...or use Anki? Set a calendar reminder to open the app, then there's a similar notification area trigger as with emails
If I may ask you, how do you plan on building vocabulary from these e-mails? Do you use anki or some other method?
Seems a bit complex though, compared to doing a shell script showing a notification or sending yourself an email each morning when you open it?
Or just doing a light script on val.town?
For instance this could be an example val.town script that does something similar (just need to bind to a data source for the dictionary)
import { sqlite } from "https://esm.town/v/stevekrouse/sqlite";
import { OpenAI } from "https://esm.town/v/std/openai";
import { email } from "https://esm.town/v/std/email";
// Dutch words database
const dutchWords = [
{ word: "boek", translation: "book" },
{ word: "huis", translation: "house" },
{ word: "boom", translation: "tree" },
{ word: "water", translation: "water" },
{ word: "kat", translation: "cat" },
{ word: "hond", translation: "dog" },
{ word: "appel", translation: "apple" },
{ word: "tafel", translation: "table" },
{ word: "school", translation: "school" },
{ word: "fiets", translation: "bicycle" }
];
export default async function generateDutchWordLearning() {
const KEY = new URL(import.meta.url).pathname.split("/").at(-1);
const openai = new OpenAI();
// Ensure SQLite table exists
await sqlite.execute(`
CREATE TABLE IF NOT EXISTS ${KEY}_dutch_words (
word TEXT PRIMARY KEY,
translation TEXT,
example TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// Fetch words not previously used
const usedWords = await sqlite.execute(`
SELECT word FROM ${KEY}_dutch_words
`);
const availableWords = dutchWords.filter(
w => !usedWords.rows.some(row => row.word === w.word)
);
if (availableWords.length < 3) {
// Reset if we've used all words
await sqlite.execute(`DELETE FROM ${KEY}_dutch_words`);
availableWords = dutchWords;
}
// Randomly select 3 unique words
const selectedWords = [];
for (let i = 0; i < 3; i++) {
const randomIndex = Math.floor(Math.random() * availableWords.length);
selectedWords.push(availableWords.splice(randomIndex, 1)[0]);
}
// Generate example sentences with ChatGPT
const wordDetails = await Promise.all(selectedWords.map(async (wordObj) => {
const exampleResponse = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{
role: "user",
content: `Geef een voorbeeld zin met het woord "${wordObj.word}" in het Nederlands.`
}]
});
const example = exampleResponse.choices[0].message.content || "Geen voorbeeld gevonden";
// Store in database
await sqlite.execute(`
INSERT INTO ${KEY}_dutch_words (word, translation, example)
VALUES (?, ?, ?)
`, [wordObj.word, wordObj.translation, example]);
return { ...wordObj, example };
}));
// Prepare HTML email
const htmlContent = `
<html>
<body>
<h2>Dutch Word Learning </h2>
${wordDetails.map(w => `
<div>
<h3>${w.word} (${w.translation})</h3>
<p><em>Example:</em> ${w.example}</p>
</div>
`).join('')}
</body>
</html>
`;
// Send email
await email({
subject: "Your Daily Dutch Words ",
html: htmlContent,
text: wordDetails.map(w =>
`${w.word} (${w.translation}): ${w.example}`
).join('\n')
});
return wordDetails;
}Kind of weird we have people submitting GPT samples to people that likely have GPT themselves and could ask it for one if that's what they wanted.
But then plenty of people link google searches as though that makes sense.
What's the hardest European language to learn?
Dutch.
Why?
Because every time you speak to them in Dutch they respond to you in English.
It seems this is a way around that :-D
(I don't actually think it's the hardest language but have found that yes, many Dutch speak English very well)
It helps make the rest of us look good.
Hah!
It's not even that they won't speak Dutch, often they can't! Sometimes you'll be hard-pressed to find someone capable of speaking Dutch in Amsterdam in some shops and restaurants. I've had people look sheepish/annoyed for presuming to use and expect Dutch in my own country.
Working an IT job in a company of ~30 employees, someone joined who didn't speak Dutch. They would always excuse themselves and have lunch in their office¹ because it was very obvious that half the people just didn't really interact with the previously lively conversation anymore and were just biding their time to get back to work. Those who did speak, it worked but it's not as jovial as before. Sure, these people can all hold a presentation about their field of work, or order a sandwich with the correct words in England, but a spontaneous conversation about something random? It's a different set of vocabulary that you need every day, and far from everyone has that
¹ yes, we made clear they shouldn't do that and they should feel invited and part of the team. Many people did interact. And many of us made sure they were, at least, not having lunch alone in their office. Situation unfortunately remained as it was until I left
As I’ve learned it, I found it very charming and often surprisingly sweet - as an example idiomatic terms for urination and defecation are very funny: plassen (making a large pond) and klaaivormen (forming clay) - add to that a rather easy to rhyme language with a tendency towards charming and heartfelt emotional range, and the end result is quite nice.
Add lots of domestic and Caribbean regional variation in the home countries, close sister languages: Vlaams (certainly in its higher form a very different register of the language than the Hollands standard form), Afrikaans and West-Frisk, Papiamento etc and you’ve got a very cosy (gezellig!) and dynamic inter-language community!
The aggressive simplification of standard Dutch initially offended my tastes, but later I’ve found that particular discipline improved my English by accident and I’m now a fan of the sparse elegance and surprising nuance of that style …
Edit: spelling, never ran into the NL word for clay in writing as an adult language learner not into geophysics, civil engineering or pottery
But agreed on it being pretty useless outside of a few small regions / couple million speakers. I've been saying we should apply winning team joining and get to something more internationally useful, as everyone here seems to already agree we are small and that trade and cooperation has brought the current prosperity. The area I'm from, though, people clutch to local dialects as cultural heritage that should be continued to be spoken... it doesn't even have a writing system... whatever, I don't mind so long as people are okay with a useful language alongside