I've been learning German for a while and couldn't find a tool that fit my workflow — most apps are either too gamified or locked behind paywalls. So I built my own: AlmancaPratik, a free web app for Turkish speakers learning German.
Here's the stack I used and what I learned along the way.
Stack
Vanilla JS (ES Modules, no React/Vue)
Firebase Auth (Google Sign-In)
Firestore (user data — words, texts)
GitHub Pages (hosting — completely free)
Zero server costs. Zero monthly fees.
Architecture
Every user's data lives under their own Firestore path:
users/{userId}/words/{wordId}
users/{userId}/texts/{textId}
Firestore Security Rules handle authorization — only the authenticated user can read/write their own data:
jsmatch /users/{userId}/{document=**} {
allow read, write: if request.auth.uid == userId;
}
No backend needed. The rules ARE the backend.
The Interesting Part — Example Sentences
When a user saves a German word, they can click it to see real usage examples. I pull these from two public APIs in sequence:
Wiktionary API — parses the "Beispiele" (examples) section from the German Wiktionary entry
Tatoeba API — fallback if Wiktionary returns nothing
jsasync function fetchExampleSentences(word) {
let sentences = await fetchFromWiktionary(word);
if (sentences.length < 2) {
const tatoeba = await fetchFromTatoeba(word);
sentences = [...sentences, ...tatoeba.slice(0, 2 - sentences.length)];
}
return sentences;
}
Both APIs are free, no auth required. The results are cached in a Map() so the same word is never fetched twice in a session.
What the App Does
Text Analysis — paste German text, click any word to look it up
Quiz — multiple choice + typing mode from your saved words
Artikel Finder — der / die / das lookup
Sentence Examples — Wiktionary + Tatoeba
Word List — save, tag, filter, edit your vocabulary
Lighthouse Scores
Performance: 99
Accessibility: 100
Best Practices: 100
SEO: 100
Getting accessibility to 100 required fixing contrast ratios, adding landmarks, and making sure all buttons had proper aria-label attributes.
What I'd Do Differently
Vanilla JS was a deliberate choice — I wanted zero build tooling. But as the app grew, managing state across pages without a framework got messy. If I started today I'd probably use a lightweight option like Preact.
Try it: almancapratik.com
GitHub feedback welcome in the comments.
Top comments (0)