Polingu
A Polish language learning app using spaced repetition (FSRS) to drill vocabulary, full sentences, and declensions.
Technical Deep Dive
Tech Stack
Frontend: React 19 + TypeScript, Material UI, ViteBackend: Firebase (Firestore, Auth, Cloud Functions)APIs: DeepL for translation, OpenAI GPT-4 for content generationAlgorithm: ts-fsrs (Free Spaced Repetition Scheduler)
Architecture Decisions
1. Domain-Specific Schedulers
Rather than a single generic scheduler, each learning module (declension, vocabulary, sentences, conjugation) has its own scheduler implementation. Why? Each module has unique filtering requirements (cases/genders for declension, CEFR levels for sentences, verb aspects for conjugation). Separate schedulers let me handle these differences without polluting a generic abstraction with module-specific logic.
2. Factory Pattern for Custom Content Storage
Custom content (user-created cards) uses a generic factory that creates type-safe CRUD operations. Each custom content type (vocabulary, declension, sentences) instantiates this factory with its specific type, eliminating duplicated Firestore logic.
3. Server-Side Rate Limiting
Translation calls go through Firebase Cloud Functions with dual-layer rate limiting:
- Per-minute limit: 30 requests (prevents abuse)
- Daily character limit: 1,500 characters (controls API costs)
The rate limit state is stored in Firestore per-user and automatically resets at UTC midnight.
Interesting Challenges
Interactive Word Translation with Phrase Selection
The hardest UX challenge was making text both tappable (single word) and draggable (phrase selection) without conflicts. Solution: A context provider (TranslatableTextProvider) tracks drag state across all words in a sentence:
- Each <TranslatableWord> registers itself with an index
- On mousedown, dragging begins; mousemove over other words expands the selection
- On mouseup, the selected phrase is extracted and translated
- Clicking anywhere dismisses the phrase tooltip
This creates a natural UX where tapping shows a single word tooltip, but drag-selecting shows a phrase translation.
Translation Caching Strategy
To minimize API calls, translations are cached directly on the card documents in Firestore. The next time any user views that card, the translation is pre-populated. This creates a crowdsourced translation cache that improves over time.
Verb Conjugation Validation
Polish verbs have complex rules:
- Perfective verbs have no present tense
- Modal verbs (móc, musieć) have no imperative
- Aspect pairs must cross-reference each other
- Imperfective futures have alternative forms
I built a validation pipeline that enforces all these rules before importing verb data, catching errors like "perfective verb has present tense" or "aspect pair doesn't cross-reference."
AI-Powered Content Generation
The app uses GPT-4 via Cloud Functions for three admin-only features:
- Example sentence generation - Given a vocabulary word, generates 2-3 natural Polish sentences at A2-B1 level
- Sentence generation - Creates practice sentences filtered by CEFR level and topic tags
- Curriculum discovery - Analyzes existing content tags and suggests missing grammar concepts (particularly Polish-specific features like verbal aspect, motion verbs, impersonal constructions)
All responses are validated server-side to ensure proper structure before returning.
Four Learning Modules
- Declension — Master noun and pronoun endings across all 7 Polish cases (Nominative, Genitive, Dative, Accusative, Instrumental, Locative, Vocative) with fill-in-the-blank flashcards and grammar hints explaining why each case is used.
- Conjugation — Drill verb forms across present, past, future, conditional, and imperative tenses. Handles both imperfective and perfective aspects, including irregular verbs and modal verbs with their quirks.
- Vocabulary — Learn common Polish words with example sentences, part of speech labels, and gender information. Cards show the word in context so you understand usage, not just definitions.
- Sentences — Practice translating complete Polish sentences, organized by CEFR level (A1 through C2). Each sentence includes detailed word-by-word annotations with lemmas and grammar notes.
FSRS Spaced Repetition
Uses the Free Spaced Repetition Scheduler—the same algorithm powering Anki 23+. After revealing an answer, rate your recall:
- Again — Forgot (card repeats in current session)
- Hard — Struggled to remember
- Good — Remembered with effort
- Easy — Instantly recalled
The algorithm calculates optimal review intervals based on your history, maximizing retention while minimizing study time.
Tap-to-Translate
Tap any Polish word to see its English translation instantly. The translation appears in a tooltip with contextual meaning based on the surrounding sentence.
Drag-select phrases for multi-word translations—useful for idioms and expressions that don't translate word-by-word.
Translations are cached to Firestore, so repeated lookups are instant and don't consume API quota.
Bidirectional Practice
Vocabulary and sentences support two practice directions:
- Recognition (Polish → English) — See Polish, produce the English meaning
- Production (English → Polish) — See English, produce the Polish translation
Production mode is harder but builds active recall—essential for actually speaking Polish, not just understanding it.
Smart Filtering
Customize what you study without breaking your review schedule:
- Filter declension cards by case, gender, and number
- Filter sentences by CEFR level (A1–C2)
- Filter conjugation by tense, aspect, and verb class
Filters only affect new cards. Due reviews always appear regardless of filters—you can't skip cards you've already started learning.
Custom Content
Add your own learning material alongside system content:
- Create custom vocabulary with part of speech, gender, and notes
- Create custom declension fill-in-the-blank cards
- Create custom sentences with Polish/English pairs
Custom cards integrate seamlessly with the SRS system and are prioritized in session order so you see your own content first.
Flexible Study Modes
Normal mode shows due reviews plus new cards up to your daily limit. But when you need more flexibility:
- Practice mode — Drill cards without affecting your SRS progress (great for pre-exam cramming)
- Practice Ahead — Review future cards before they're officially due
- Learn Extra — Add more new cards beyond your daily limit when you're feeling ambitious
Reference Cheat Sheets
Quick-access reference materials available from the bottom menu:
- Declension endings — Complete tables for masculine, feminine, and neuter nouns
- Consonants — Soft, hard, and hardened consonant categories (crucial for spelling)
- Y/I rules — When to use Y vs I in Polish spelling
- Conjugation patterns — Regular verb endings organized by class
Cloud Sync
Sign in with Google to sync progress across devices. All review data is stored in Firebase Firestore and works offline with automatic sync when you're back online.
AI-Powered Tools (Admin)
- Example generation — GPT-4 creates natural Polish sentences for vocabulary words
- Sentence generation — Generate practice sentences by CEFR level and topic tags
- Curriculum discovery — AI analyzes existing content and suggests missing grammar concepts for comprehensive coverage
Building for Yourself is Different
This was the first project where I was genuinely the primary user. Every feature decision came from real frustration—not hypothetical user stories. When I added the "tap any word to translate" feature, it wasn't because it seemed cool; it was because I kept alt-tabbing to Google Translate mid-study session and losing focus.The lesson: Building for yourself removes the guesswork. You know immediately when something doesn't work because you feel the friction. The flip side is you have to be careful not to over-engineer for your specific edge cases.
Spaced Repetition is Fascinating (and Humbling)
I went deep on spaced repetition research while building this. The FSRS algorithm models memory as an exponential decay curve, and the math behind optimal review intervals is genuinely beautiful. But it also made me realize how much of traditional studying is wasted effort—cramming the night before is statistically terrible for long-term retention.The lesson: Sometimes building a tool teaches you more about the domain than the code. I understand how memory works now in a way I never did before.
AI as a Power Tool, Not a Crutch
I used GPT-4 for content generation (example sentences, curriculum suggestions), but every piece of AI output goes through validation before hitting the database. Early on, I trusted the AI too much and ended up with grammatically incorrect Polish sentences in my deck.The lesson: AI is great for generating candidates, but you need guardrails. Server-side validation, structured output parsing, and human review for anything user-facing.
State Management for Interactive UX is Hard
The tap-to-translate feature sounds simple: click a word, show a tooltip. But then I wanted drag-to-select for phrases. Now I needed to track: which word started the drag, which words are currently selected, whether we're in "phrase mode" vs "word mode," and how to dismiss tooltips properly. The TranslatableTextContext provider went through three rewrites.The lesson: Interactive features that feel simple to use are often complex to build. Budget extra time for anything involving drag, selection, or multiple interaction modes.
Content is Harder Than Code
I spent more time curating and validating Polish verb conjugations than writing the conjugation driller itself. Each verb has ~50 forms across tenses/persons/genders. Modal verbs don't have imperatives. Perfective verbs don't have present tense. Aspect pairs need to cross-reference each other.I ended up building CLI tools (verbs:validate, verbs:import) just to manage the content pipeline.The lesson: For content-heavy apps, the tooling around the content is as important as the app itself. Invest early in validation and import workflows.
Ship the Thing You'll Actually Use
I've started and abandoned many side projects. This one survived because I use it every day. The secret wasn't motivation—it was choosing a project where shipping meant I got something I genuinely wanted.The lesson: Personal projects succeed when the reward is intrinsic. If you wouldn't use your own app, you probably won't finish it.