From 82d982dea0b5fe40193253fda056bb1bfbea432c Mon Sep 17 00:00:00 2001 From: Ismail Ali Date: Tue, 28 Apr 2026 15:35:11 +0200 Subject: [PATCH] Switch coach experience to English only --- README.md | 76 ++++++++++++++++----------------- src/components/ChatBubble.tsx | 2 +- src/components/ProgressCard.tsx | 4 +- src/hooks/useDailyReminder.ts | 2 +- src/navigation/AppNavigator.tsx | 2 +- src/screens/ChatScreen.tsx | 10 ++--- src/screens/HomeScreen.tsx | 8 ++-- src/screens/LevelScreen.tsx | 4 +- src/services/mockAiCoach.ts | 10 ++--- src/services/ollamaClient.ts | 18 ++++---- src/utils/levels.ts | 8 ++-- 11 files changed, 73 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 65fea4c..0c5186b 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ # English AI Coach -English AI Coach ist ein Starterprojekt fuer eine mobile App zum Englischlernen mit KI. Die App hilft Nutzern, kurze englische Saetze zu schreiben, Fehler zu erkennen und Korrekturen mit deutscher Erklaerung zu erhalten. +English AI Coach is a starter project for a mobile AI-based English learning app. It helps users write short English sentences, find mistakes, and get clear corrections in English. -Das Projekt ist bewusst schlank gehalten: React Native, Expo, TypeScript und React Navigation. Es nutzt keine unnoetigen Expo-only Libraries und ist so vorbereitet, dass spaeter lokale KI ueber Ollama, Speech-to-Text, Text-to-Speech und Push Notifications ergaenzt werden koennen. +The project is intentionally lightweight: React Native, Expo, TypeScript, and React Navigation. It avoids unnecessary Expo-only libraries and is prepared for local AI with Ollama, speech input, text-to-speech, and push notifications. ## Features MVP -- HomeScreen mit Fortschrittskarte und Mock Daily Reminder -- Level-Auswahl fuer A1, A2, B1 und B2 -- ChatScreen mit einfacher User- und AI-Nachrichtenansicht -- AI-Antworten koennen auf dem iPhone vorgelesen werden -- Mock KI-Service fuer Beispielkorrekturen -- TypeScript-typisierte Navigation -- Struktur fuer spaetere Services, Hooks und Utilities -- Web/PWA-Ausbau vorbereitet ueber Expo Web +- HomeScreen with progress card and mock daily reminder +- Level selection for A1, A2, B1, and B2 +- ChatScreen with user and AI messages +- AI replies can be read aloud on iPhone +- Ollama integration with offline fallback examples +- TypeScript-typed navigation +- Structure for future services, hooks, and utilities +- Web/PWA path prepared through Expo Web ## Beispiel @@ -22,7 +22,7 @@ User: I go yesterday to work AI Coach: Good try. Correct: "I went to work yesterday." -"Yesterday" braucht Past Tense. +Use the past tense with "yesterday". ``` ## Tech Stack @@ -31,11 +31,11 @@ Good try. Correct: "I went to work yesterday." - Expo - TypeScript - React Navigation -- Geplant: Ollama API fuer lokale KI -- Geplant: Speech-to-Text und Text-to-Speech -- Text-to-Speech mit `expo-speech` -- Spracheingabe aktuell ueber iPhone-Diktierfunktion in der Tastatur -- Geplant: Push Notifications fuer Daily Reminder +- Ollama API for local AI +- Text-to-speech with `expo-speech` +- Speech input currently through iPhone keyboard dictation +- Planned: in-app speech-to-text +- Planned: push notifications for daily reminders ## Projektstruktur @@ -69,56 +69,56 @@ english-ai-coach/ ## Installation -Voraussetzungen: +Requirements: - Node.js - npm -- Expo ueber `npx expo` +- Expo through `npx expo` -Abhaengigkeiten installieren: +Install dependencies: ```bash npm install ``` -Optionale lokale KI-Konfiguration anlegen: +Create optional local AI configuration: ```bash cp .env.example .env ``` -In `.env` kannst du dein Ollama-Modell eintragen: +Set your Ollama model in `.env`: ```text EXPO_PUBLIC_OLLAMA_BASE_URL=http://localhost:11434 EXPO_PUBLIC_OLLAMA_MODEL=llama3.2 ``` -Wenn du die App auf einem echten Smartphone mit Expo Go testest, ist `localhost` das Smartphone selbst. Nutze dann die IP deines PCs, zum Beispiel: +When testing on a real phone with Expo Go, `localhost` means the phone itself. Use your laptop IP instead, for example: ```text EXPO_PUBLIC_OLLAMA_BASE_URL=http://192.168.10.102:11434 EXPO_PUBLIC_OLLAMA_MODEL=qwen2.5:7b ``` -App starten: +Start the app: ```bash npm run start ``` -## Spracheingabe und Vorlesen +## Speech Input And Playback -Vorlesen funktioniert direkt in Expo Go. AI-Antworten werden automatisch ueber den iPhone-Lautsprecher vorgelesen. Im Chat gibt es ausserdem Buttons fuer `Letzte Antwort vorlesen` und `Stop`. +Playback works directly in Expo Go. AI replies are read aloud through the iPhone speaker. The chat also has buttons for `Read last answer` and `Stop`. -Spracheingabe funktioniert aktuell ueber die iPhone-Tastatur: +Speech input currently works through the iPhone keyboard: -- Chat-Eingabefeld antippen -- Mikrofon-Symbol auf der iPhone-Tastatur druecken -- Englisch sprechen -- Text pruefen und senden +- Tap the chat input field +- Press the microphone icon on the iPhone keyboard +- Speak English +- Review the text and send it -Echtes In-App Speech-to-Text mit eigenem Mikrofon-Button ist fuer spaeter geplant. Dafuer braucht die App entweder ein natives iOS-Modul nach `expo prebuild` oder eine Transkriptions-API wie Whisper. +Real in-app speech-to-text with a custom microphone button is planned for later. That requires either a native iOS module after `expo prebuild` or a transcription API such as Whisper. iOS Preview starten: @@ -132,23 +132,23 @@ Web Preview starten: npm run web ``` -Hinweis: Fuer lokale iOS-Simulator-Builds wird macOS mit Xcode benoetigt. Mit Expo Go kann die App auf einem echten Geraet getestet werden. +Note: local iOS simulator builds require macOS with Xcode. With Expo Go, the app can be tested on a real device. ## Ollama Integration -Der Chat nutzt `src/services/ollamaClient.ts` fuer echte lokale KI-Antworten. Falls Ollama nicht erreichbar ist, faellt `src/services/mockAiCoach.ts` automatisch auf Beispielantworten zurueck. Die Modell-Konfiguration liegt in `src/config/ai.ts` und liest Werte aus `.env`. +The chat uses `src/services/ollamaClient.ts` for real local AI replies. If Ollama is not reachable, `src/services/mockAiCoach.ts` automatically falls back to example replies. Model configuration lives in `src/config/ai.ts` and reads values from `.env`. -Moeglicher lokaler Ablauf: +Local flow: ```text React Native Chat UI -> AI Service --> lokale Ollama API --> Modell wie llama, qwen oder mistral --> Antwort mit Korrektur und Erklaerung +-> local Ollama API +-> model such as llama, qwen, or mistral +-> English-only correction and explanation ``` -Fuer eine PWA oder lokale Web-Version kann die App spaeter im Browser laufen und mit einem lokalen Ollama-Server im Netzwerk verbunden werden. +For a PWA or local web version, the app can later run in the browser and connect to a local Ollama server on the network. ## PWA Idee diff --git a/src/components/ChatBubble.tsx b/src/components/ChatBubble.tsx index 707cd9f..55a51b1 100644 --- a/src/components/ChatBubble.tsx +++ b/src/components/ChatBubble.tsx @@ -18,7 +18,7 @@ export function ChatBubble({ message }: ChatBubbleProps) { return ( - {isUser ? 'Du' : 'AI Coach'} + {isUser ? 'You' : 'AI Coach'} {message.text} diff --git a/src/components/ProgressCard.tsx b/src/components/ProgressCard.tsx index 31ad14c..38e0dc7 100644 --- a/src/components/ProgressCard.tsx +++ b/src/components/ProgressCard.tsx @@ -6,13 +6,13 @@ export function ProgressCard() { return ( - Heute gelernt + Practice today 12 min - Daily Reminder: 18:30 Uhr, 10 Minuten Englisch ueben. + Daily Reminder: 6:30 PM, 10 minutes of English practice. ); } diff --git a/src/hooks/useDailyReminder.ts b/src/hooks/useDailyReminder.ts index e936d19..35c5391 100644 --- a/src/hooks/useDailyReminder.ts +++ b/src/hooks/useDailyReminder.ts @@ -3,6 +3,6 @@ export function useDailyReminder() { return { enabled: true, time: '18:30', - label: 'Taegliche Uebung ist vorbereitet', + label: 'Daily practice is ready', }; } diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 0b8660c..5ae80c5 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -21,7 +21,7 @@ export function AppNavigator() { }} > - + diff --git a/src/screens/ChatScreen.tsx b/src/screens/ChatScreen.tsx index 994319d..bf5a392 100644 --- a/src/screens/ChatScreen.tsx +++ b/src/screens/ChatScreen.tsx @@ -19,7 +19,7 @@ export function ChatScreen({ route }: ChatScreenProps) { { id: 'welcome', role: 'assistant', - text: `Willkommen! Dein Level ist ${level}. Schreib einen englischen Satz und ich korrigiere ihn.`, + text: `Welcome. Your level is ${level}. Write an English sentence and I will correct it.`, }, ]); const [isSending, setIsSending] = useState(false); @@ -34,7 +34,7 @@ export function ChatScreen({ route }: ChatScreenProps) { setInput(''); setIsSending(true); - // This mock can later be replaced by Ollama or another AI API client. + // Uses Ollama when available and falls back to local examples when offline. const reply = await getMockAiReply(userMessage.text, level); setMessages((current) => [...current, { id: `ai-${Date.now()}`, role: 'assistant', text: reply }]); speakText(reply); @@ -59,21 +59,21 @@ export function ChatScreen({ route }: ChatScreenProps) { - + { const lastAiMessage = [...messages].reverse().find((message) => message.role === 'assistant'); diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index e916d1a..45a06e0 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -18,9 +18,9 @@ export function HomeScreen({ navigation }: HomeScreenProps) { Personal English Tutor - Lerne Englisch mit einem KI Coach + Practice English with an AI teacher - Starte mit deinem Level, schreibe kurze Saetze und erhalte Korrekturen mit deutscher Erklaerung. + Choose your level, write short sentences, and get clear corrections in English. @@ -29,10 +29,10 @@ export function HomeScreen({ navigation }: HomeScreenProps) { Daily Reminder {reminder.label} - {reminder.enabled ? reminder.time : 'Aus'} + {reminder.enabled ? reminder.time : 'Off'} - navigation.navigate('Level')} /> + navigation.navigate('Level')} /> ); diff --git a/src/screens/LevelScreen.tsx b/src/screens/LevelScreen.tsx index 2986e1a..3e23007 100644 --- a/src/screens/LevelScreen.tsx +++ b/src/screens/LevelScreen.tsx @@ -12,8 +12,8 @@ export function LevelScreen({ navigation }: LevelScreenProps) { return ( - Welches Englisch-Level passt zu dir? - Du kannst spaeter jederzeit wechseln. Das Level steuert die Mock-Antworten. + Which English level fits you? + You can change it later. The level controls how simple the coach answers. {levels.map((item) => ( = { - A1: 'Fast richtig! Correct: "I am learning English." Warum? Bei "I" nutzt du "am".', - A2: 'Good try. Correct: "I went to work yesterday." "Yesterday" braucht Past Tense.', - B1: 'Nice sentence. Alternative: "I have been practicing every day." Das klingt natuerlicher.', + A1: 'Good try! Correct: "I am learning English." Use "am" with "I" in the present continuous.', + A2: 'Good try. Correct: "I went to work yesterday." Use the past tense with "yesterday".', + B1: 'Nice sentence. Alternative: "I have been practicing every day." This sounds more natural.', B2: 'Strong idea. Improve flow: use linking words like "however", "therefore" or "in addition".', }; export async function getMockAiReply(input: string, level: EnglishLevel) { if (!input.trim()) { - return 'Schreib einen kurzen englischen Satz, dann korrigiere ich ihn fuer dein Level.'; + return 'Write a short English sentence and I will correct it for your level.'; } try { @@ -20,5 +20,5 @@ export async function getMockAiReply(input: string, level: EnglishLevel) { await new Promise((resolve) => setTimeout(resolve, 350)); } - return `${examples[level]}\n\nDein Satz: "${input.trim()}"\n\nKI Backend vorbereitet: ${aiConfig.ollamaModel}`; + return `${examples[level]}\n\nYour sentence: "${input.trim()}"\n\nAI backend: ${aiConfig.ollamaModel}`; } diff --git a/src/services/ollamaClient.ts b/src/services/ollamaClient.ts index a61249b..4df4acb 100644 --- a/src/services/ollamaClient.ts +++ b/src/services/ollamaClient.ts @@ -23,17 +23,19 @@ export async function askOllama(input: string, level: EnglishLevel) { } const data = (await response.json()) as OllamaGenerateResponse; - return data.response?.trim() || 'Ich konnte keine Antwort erzeugen.'; + return data.response?.trim() || 'I could not generate a useful answer.'; } function buildPrompt(input: string, level: EnglishLevel) { - return `Du bist ein freundlicher Englisch-Coach fuer deutsche Lernende. + return `You are a friendly English teacher and conversation coach. Level: ${level} -Aufgabe: -1. Korrigiere den englischen Satz. -2. Erklaere den wichtigsten Fehler kurz auf Deutsch. -3. Gib eine bessere natuerliche Variante. -4. Antworte kurz und motivierend. +Rules: +1. Reply only in English. +2. Correct the user's sentence. +3. Explain the main mistake briefly in simple English. +4. Give one natural alternative sentence. +5. Keep the answer short, clear, and encouraging. +6. Do not use German. -Satz des Nutzers: ${input}`; +User sentence: ${input}`; } diff --git a/src/utils/levels.ts b/src/utils/levels.ts index 6f58304..9071e17 100644 --- a/src/utils/levels.ts +++ b/src/utils/levels.ts @@ -1,8 +1,8 @@ import type { EnglishLevel } from '../navigation/types'; export const levels: Array<{ level: EnglishLevel; title: string; description: string }> = [ - { level: 'A1', title: 'Beginner', description: 'Einfache Saetze, Alltag und Grundlagen.' }, - { level: 'A2', title: 'Elementary', description: 'Vergangenheit, Fragen und kurze Dialoge.' }, - { level: 'B1', title: 'Intermediate', description: 'Freies Schreiben mit klaren Korrekturen.' }, - { level: 'B2', title: 'Upper Intermediate', description: 'Natuerlicher Ausdruck und bessere Struktur.' }, + { level: 'A1', title: 'Beginner', description: 'Simple sentences, daily life, and basics.' }, + { level: 'A2', title: 'Elementary', description: 'Past tense, questions, and short conversations.' }, + { level: 'B1', title: 'Intermediate', description: 'Free writing with clear corrections.' }, + { level: 'B2', title: 'Upper Intermediate', description: 'Natural expression and better structure.' }, ];