A personal progressive web app for translating text and automatically generating flashcards to build vocabulary over time.
- Translate β Paste text in Spanish, Romanian, Galician, or English and get a natural translation powered by DeepSeek.
- Auto-generate flashcards β The LLM intelligently extracts words, phrases, and grammar concepts worth learning and creates flashcards automatically.
- Practice β Flip through flashcards, self-assess with "Again" or "Good", and track your progress.
- Archive β Cards you master (7 correct in a row) automatically move to your archive. Restore them anytime if you feel rusty.
- Framework: Next.js 15 (App Router)
- Styling: Tailwind CSS + shadcn/ui
- Storage: localStorage by default; optional Firebase (Auth + Firestore) for cloud sync and push notifications
- Translation & Flashcard Generation: DeepSeek Chat API
- PWA: Installable on iOS and Android
npm install
npm run devOpen http://localhost:3000. Everything works immediately β all data is saved to your browser's localStorage. No sign-up, no API keys required (except DeepSeek for translations).
Create a .env.local file with:
# Required for translations
DEEPSEEK_API_KEY=your_deepseek_api_key
# Optional β only needed for cloud sync, multi-device, and push notifications
NEXT_PUBLIC_FIREBASE_API_KEY=your_firebase_api_key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=123456789
NEXT_PUBLIC_FIREBASE_APP_ID=1:123456789:web:abcdef
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-XXXXXXXXXX
# Required for push notifications (generate with: npx web-push generate-vapid-keys)
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your_vapid_public_key
VAPID_PRIVATE_KEY=your_vapid_private_key
# Optional β secures the Vercel cron job that sends daily reminders
CRON_SECRET=any_random_string_at_least_16_charsIf you skip the Firebase variables, the app runs entirely offline with localStorage. All your translations, flashcards, quiz results, and concept progress are saved in your browser. The header shows "Local" with a πΎ icon.
If you add Firebase variables, the app auto-detects them on next restart and switches to cloud mode β Google sign-in appears, data syncs across devices, and push notification reminders become available.
β οΈ Never commit real Firebase keys..env*.localfiles are gitignored.
The app is configured for Vercel.
- Push to GitHub
- Import repo in Vercel
- Add
DEEPSEEK_API_KEYenvironment variable (required) - Optionally add Firebase
NEXT_PUBLIC_*variables for cloud features - If you want push notifications, also add
NEXT_PUBLIC_VAPID_PUBLIC_KEY,VAPID_PRIVATE_KEY, andCRON_SECRET
Open the deployed app in Safari (iOS) or Chrome (Android) and tap Add to Home Screen.
This app was built with an AI coding agent and is designed to be easily customized by giving natural-language instructions. Below is a catalog of every hardcoded decision and the instruction you would give your agent to change it.
The app currently assumes Spanish β English as the primary learning pair. All defaults, concepts, quizzes, and keyword detection are hardcoded for this language direction.
Instruction for your AI agent: "Change this app from a SpanishβEnglish tutor to a [YOUR TARGET LANGUAGE]β[YOUR NATIVE LANGUAGE] tutor. Update the default source and target languages, the language list, all 22 grammar/concept lessons and their quizzes, the auto-suggestion keyword matching system, and the DeepSeek system prompt. Replace every Spanish example with equivalent content in [YOUR TARGET LANGUAGE] and every English translation with [YOUR NATIVE LANGUAGE]."
Files to change:
| File | What's Hardcoded |
|---|---|
types/index.ts |
LANGUAGES array (en, ro, es, gl), DEFAULT_SOURCE_LANG = "es", DEFAULT_TARGET_LANG = "en" |
app/api/translate/route.ts |
SYSTEM_PROMPT instructs DeepSeek about translation direction and flashcard tags ["vocabulary", "phrase", "grammar"] |
app/page.tsx |
CONCEPT_KEYWORDS β 22 concept detectors with Spanish-specific keywords (e.g., "estuve", "ojalΓ‘", "vale"). The suggestConcepts() function matches against these. |
lib/concepts-data.ts |
CONCEPTS array β 22 full lessons with Spanish examples, English translations, and English-language explanations. Covers ser/estar, subjunctive, false friends, colloquialisms, etc. |
lib/quiz-data.ts |
QUIZZES mapping β 3 quiz versions per concept, all in Spanish/English. |
Instruction: "Add [LANGUAGE] to the supported language list with flag [EMOJI]." or "Remove [LANGUAGE] from the language selector."
File: types/index.ts β edit the LANGUAGES array.
Instruction: "Set the default source language to [CODE] and the default target to [CODE]."
File: types/index.ts β change DEFAULT_SOURCE_LANG and DEFAULT_TARGET_LANG.
Currently using DeepSeek Chat API.
Instruction: "Replace DeepSeek with [OPENAI/CLAUDE/GEMINI]. Update the API endpoint, authentication header format, model name, and request body structure. Keep the same JSON response contract."
File: app/api/translate/route.ts β DEEPSEEK_URL, Authorization header format, model: "deepseek-chat", response_format: { type: "json_object" }.
Instruction: "Update the translation system prompt to also include [BEHAVIOR]. Change the flashcard output format to include [FIELD]." or "Change the flashcard tag categories from 'vocabulary/phrase/grammar' to [YOUR TAGS]."
File: app/api/translate/route.ts β SYSTEM_PROMPT constant and tag values.
Firebase is optional. Without it, everything saves to the browser's localStorage and works offline. Add Firebase env vars to get cloud sync, Google sign-in, and push notifications.
Instruction: "Add Firebase to this app. Here are my config values: apiKey=[...], authDomain=[...], projectId=[...]"
File: .env.local β add NEXT_PUBLIC_FIREBASE_* variables. The app detects them automatically on restart. No code changes needed.
Files involved in Firebase integration (for reference):
| File | Role |
|---|---|
lib/firebase.ts |
Reads env vars, initializes Firebase app/auth/firestore, exports isFirebaseConfigured |
lib/db.ts |
Dispatcher β routes to Firestore or localStorage depending on config |
lib/local-db.ts |
Full localStorage mirror of the Firestore API |
components/auth/AuthProvider.tsx |
If Firebase is configured: Google sign-in flow. If not: auto-creates a local user with a stable UUID. |
components/shared/AppHeader.tsx |
Shows user info (or "Local" in offline mode), toggles notifications |
Instruction: "Add email/password authentication alongside Google sign-in." or "Replace Google auth with GitHub auth."
Files: lib/firebase.ts (auth provider setup), components/auth/AuthProvider.tsx (sign-in method).
Instruction: "Remove Firebase and switch to localStorage only." or "Add Firebase config for cloud sync."
File: .env.local β add or remove NEXT_PUBLIC_FIREBASE_* variables. The app auto-detects on startup.
Instruction: "Rename the app to [NAME]. Update the title, description, PWA manifest, and any references in the UI."
Files: app/layout.tsx β metadata.title, metadata.description, appleWebApp.title. Also public/manifest.json.
Instruction: "Change the theme color from #FDF8F0 to [COLOR]." or "Add a dark mode toggle."
Files: app/layout.tsx β themeColor in viewport. app/globals.css β Tailwind CSS variables and base styles. The design uses a warm cream (bg-butter), mint (bg-mint), and blush (bg-blush) palette.
Instruction: "Switch from Arimo to [FONT NAME] from Google Fonts."
File: app/layout.tsx β the Google Fonts <link> in <head> references Arimo.
Instruction: "Add a new concept '[TITLE]' for [LANGUAGE] learners about [TOPIC]. Include 3-4 paragraphs of explanation, 4-6 example sentence pairs, tips, and 3 quiz versions with 6 questions each."
Files: lib/concepts-data.ts β add entry to CONCEPTS array. lib/quiz-data.ts β add entry to QUIZZES object. app/page.tsx β add keyword detector to CONCEPT_KEYWORDS object.
Instruction: "Update the '[CONCEPT TITLE]' concept to also cover [TOPIC]. Fix the example that says '[WRONG EXAMPLE]'."
Files: lib/concepts-data.ts (content) and lib/quiz-data.ts (quiz questions for that concept ID).
Instruction: "Remove the '[CONCEPT TITLE]' concept entirely from the app, including its quizzes and keyword suggestions."
Files: lib/concepts-data.ts, lib/quiz-data.ts, app/page.tsx (CONCEPT_KEYWORDS entry).
Instruction: "When a translation is saved, suggest concepts based on [DIFFERENT CRITERIA] instead of keyword matching. Or increase/reduce the number of suggested concepts."
File: app/page.tsx β the suggestConcepts() function and CONCEPT_KEYWORDS object.
Instruction: "Change the mastery threshold from 7 correct in a row to [NUMBER]."
File: app/flashcards/page.tsx β line if (newStreak >= 7) in handleAnswer.
Instruction: "Show an animation when a card is mastered." or "Add a 'hard' option alongside 'again' and 'good'." or "Shuffle cards differently."
File: app/flashcards/page.tsx β the answer buttons and session logic.
All custom data flows through these files β understanding them is the key to customizing:
| File | Purpose |
|---|---|
types/index.ts |
All TypeScript interfaces and language constants |
lib/db.ts |
Dispatcher β calls Firestore or localStorage based on config |
lib/local-db.ts |
localStorage mirror of the full Firestore API |
lib/firebase.ts |
Firebase initialization from env vars, exports isFirebaseConfigured |
lib/concepts-data.ts |
22 Spanish grammar/conversation concept lessons |
lib/quiz-data.ts |
3 quiz versions per concept |
app/api/translate/route.ts |
DeepSeek API integration and LLM prompt |
app/page.tsx |
Translation page + concept suggestion engine |
app/flashcards/page.tsx |
Spaced-repetition flashcard practice |
app/concepts/page.tsx |
Concept browser, quiz player, self-assessment |
app/mastered/page.tsx |
Archived/mastered cards gallery |
app/layout.tsx |
Root layout, metadata, fonts, color theme |
All Firestore reads/writes require authentication. Security rules are not included in this repo β configure them in the Firebase console.