diff --git a/backend/scripts/create-german-for-bisaya-course-content.js b/backend/scripts/create-german-for-bisaya-course-content.js index 5e297b4..41151c3 100644 --- a/backend/scripts/create-german-for-bisaya-course-content.js +++ b/backend/scripts/create-german-for-bisaya-course-content.js @@ -29,13 +29,6 @@ const GERMAN_DIDACTICS = { ...GERMAN_FOR_BISAYA_PHASE5_DIDACTICS }; -const GENERIC_DISTRACTOR_PATTERNS = Array.from(new Set( - Object.values(GERMAN_DIDACTICS) - .flatMap((entry) => Array.isArray(entry?.corePatterns) ? entry.corePatterns : []) - .map((pattern) => String(pattern || '').trim()) - .filter(Boolean) -)).slice(0, 300); - function normalizeText(value) { return String(value || '') .trim() @@ -100,6 +93,18 @@ function pickDistractors(pattern, allPatterns, count) { .slice(0, count); } +function getLessonPatternPool(didactics) { + const speakingCues = (Array.isArray(didactics.speakingPrompts) ? didactics.speakingPrompts : []) + .flatMap((entry) => [entry?.cue, entry?.prompt]) + .map((entry) => normalizeText(entry)) + .filter(Boolean); + + return Array.from(new Set([ + ...didactics.corePatterns, + ...speakingCues + ])); +} + function buildChoiceExercise(lesson, didactics, pattern, allPatterns, variant = 0) { const distractors = pickDistractors(pattern, allPatterns, 3); if (distractors.length < 3) return null; @@ -277,7 +282,7 @@ function generateExercisesFromDidactics(lesson) { const patternA = corePatterns[0]; const patternB = corePatterns[1] || corePatterns[0]; - const pool = Array.from(new Set([...corePatterns, ...GENERIC_DISTRACTOR_PATTERNS])); + const pool = getLessonPatternPool(didactics); if (lesson.lessonType === 'conversation') { return [ @@ -561,6 +566,595 @@ const GERMAN_EXERCISES = { }, explanation: 'Im Perfekt braucht man hier das Hilfsverb "habe".' } + ], + 'Wohnung & Nachbarn': [ + { + exerciseTypeId: 2, + title: 'Wohnen passend ausdrücken', + instruction: 'Wähle die natürlichste Aussage zur Wohnsituation.', + questionData: { + type: 'multiple_choice', + question: 'Du willst sagen, dass du hier wohnst und den Nachbarn kennst. Welche Form passt?', + options: ['Ich wohne hier. Das ist mein Nachbar.', 'Ich bin hier wohnen. Das mein Nachbar.', 'Ich wohne dort Nachbar.', 'Hier wohne ich mein Nachbar.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Im Deutschen sind kurze klare Hauptsätze am Anfang am sichersten.' + }, + { + exerciseTypeId: 1, + title: 'Wohnort ergänzen', + instruction: 'Fülle die Lücke im Satz.', + questionData: { + type: 'gap_fill', + text: 'Ich {gap} hier.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['wohne'] + }, + explanation: 'Das Kernmuster lautet "Ich wohne hier."' + }, + withTypeName('situational_response', { + title: 'Nachbarn kurz vorstellen', + instruction: 'Antworte mit zwei kurzen Sätzen auf Deutsch.', + questionData: { + type: 'situational_response', + question: 'Jemand fragt dich: "Wo wohnst du? Und wer ist das?"', + keywords: ['wohne', 'hier', 'nachbar'] + }, + answerData: { + modelAnswer: 'Ich wohne hier. Das ist mein Nachbar.', + keywords: ['wohne', 'hier', 'nachbar'] + }, + explanation: 'Beide Informationen lassen sich mit zwei sehr einfachen Sätzen sauber ausdrücken.' + }) + ], + 'Besuch empfangen': [ + { + exerciseTypeId: 2, + title: 'Gast freundlich begrüßen', + instruction: 'Wähle die passende deutsche Reaktion.', + questionData: { + type: 'multiple_choice', + question: 'Ein Gast steht an der Tür. Welche Formulierung passt am besten?', + options: ['Komm doch rein. Setz dich bitte.', 'Du kommst rein sitzen bitte.', 'Ich rein bitte du.', 'Setz doch komm rein.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: '"Komm doch rein. Setz dich bitte." klingt freundlich und natürlich.' + }, + { + exerciseTypeId: 3, + title: 'Einladung richtig bauen', + instruction: 'Ordne die Wörter zu einer höflichen Einladung.', + questionData: { + type: 'sentence_building', + question: 'Baue eine freundliche Aufforderung für einen Gast.', + tokens: ['Setz', 'dich', 'bitte'] + }, + answerData: { + correct: ['Setz dich bitte'] + }, + explanation: 'Kurze Aufforderungen sind im Alltag sehr häufig.' + }, + withTypeName('speaking_from_memory', { + title: 'Begrüßung frei sprechen', + instruction: 'Sprich eine kurze Begrüßung für einen Gast frei nach.', + questionData: { + type: 'speaking_from_memory', + question: 'Ein Freund besucht dich. Was sagst du an der Tür?', + expectedText: 'Komm doch rein. Setz dich bitte.', + keywords: ['komm', 'rein', 'setz', 'bitte'] + }, + answerData: { + type: 'speaking_from_memory' + }, + explanation: 'Die Übung trainiert einen natürlichen Mini-Dialog beim Empfang von Besuch.' + }) + ], + 'Arzt, Apotheke, Termin': [ + { + exerciseTypeId: 2, + title: 'Arzttermin richtig sagen', + instruction: 'Wähle die passende Aussage.', + questionData: { + type: 'multiple_choice', + question: 'Du bist beim Arzt und brauchst Medikamente. Welche Form passt?', + options: ['Ich habe einen Termin. Ich brauche Medikamente.', 'Ich bin ein Termin. Ich brauche Medizin machen.', 'Ich habe Medikamente Termin.', 'Termin ich brauche.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Beide Aussagen sind typische frühe Standardsätze für Arzt und Apotheke.' + }, + { + exerciseTypeId: 1, + title: 'Apotheke ergänzen', + instruction: 'Setze das passende Wort ein.', + questionData: { + type: 'gap_fill', + text: 'Wo ist die {gap}?', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['Apotheke'] + }, + explanation: 'Das ist eine zentrale Frage für Alltag und Gesundheit.' + }, + withTypeName('situational_response', { + title: 'Beim Arzt knapp reagieren', + instruction: 'Antworte kurz und passend auf Deutsch.', + questionData: { + type: 'situational_response', + question: 'Die Sprechstundenhilfe fragt: "Haben Sie einen Termin?"', + keywords: ['habe', 'termin'] + }, + answerData: { + modelAnswer: 'Ja, ich habe einen Termin.', + keywords: ['habe', 'termin'] + }, + explanation: 'Kurze sichere Antworten helfen besonders in belastenden Situationen.' + }) + ], + 'Amt, Dokumente, Anmeldung': [ + { + exerciseTypeId: 2, + title: 'Anmeldung im Amt', + instruction: 'Wähle die passende deutsche Formulierung.', + questionData: { + type: 'multiple_choice', + question: 'Du willst dich anmelden. Welche Aussage passt?', + options: ['Ich möchte mich anmelden.', 'Ich will Anmeldung machen mich.', 'Ich melde ich.', 'Anmelden ich möchte Formular.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: '"Ich möchte mich anmelden." ist ein zentraler Standardsatz für Ämter.' + }, + { + exerciseTypeId: 1, + title: 'Dokument zeigen', + instruction: 'Fülle die Lücke mit dem passenden Wort.', + questionData: { + type: 'gap_fill', + text: 'Hier ist mein {gap}.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['Formular'] + }, + explanation: 'Das Formular ist ein zentrales Schlüsselwort dieser Lektion.' + }, + { + exerciseTypeId: 3, + title: 'Adressfrage bauen', + instruction: 'Ordne die Wörter zu einer typischen Amtsfrage.', + questionData: { + type: 'sentence_building', + question: 'Baue die Frage nach der Adresse.', + tokens: ['Was', 'ist', 'Ihre', 'Adresse'] + }, + answerData: { + correct: ['Was ist Ihre Adresse'] + }, + explanation: 'Solche festen Fragen tauchen in Formular- und Anmeldungssituationen ständig auf.' + } + ], + 'Nebensätze mit weil - Einstieg': [ + { + exerciseTypeId: 2, + title: 'Weil-Satz erkennen', + instruction: 'Wähle den grammatisch passenden Satz.', + questionData: { + type: 'multiple_choice', + question: 'Welche Form mit "weil" ist richtig?', + options: ['Ich bleibe zu Hause, weil ich krank bin.', 'Ich bleibe zu Hause, weil ich bin krank.', 'Weil ich krank, ich bleibe zu Hause.', 'Ich bleibe weil zu Hause ich krank bin.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Im Nebensatz mit "weil" steht das Verb am Ende.' + }, + { + exerciseTypeId: 1, + title: 'Weil ergänzen', + instruction: 'Setze das passende Bindewort ein.', + questionData: { + type: 'gap_fill', + text: 'Ich komme später, {gap} ich arbeite.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['weil'] + }, + explanation: 'Mit "weil" gibst du im Deutschen einen Grund an.' + }, + { + exerciseTypeId: 3, + title: 'Grund richtig ordnen', + instruction: 'Ordne die Wörter zu einem korrekten weil-Satz.', + questionData: { + type: 'sentence_building', + question: 'Baue den Grundsatz richtig.', + tokens: ['weil', 'ich', 'arbeite'] + }, + answerData: { + correct: ['weil ich arbeite'] + }, + explanation: 'Kleine korrekte Nebensätze sind ein wichtiger nächster Schritt.' + } + ], + 'Arbeitssuche & Termine': [ + { + exerciseTypeId: 2, + title: 'Arbeit suchen', + instruction: 'Wähle die passende Aussage.', + questionData: { + type: 'multiple_choice', + question: 'Welche Formulierung passt zu Arbeitssuche und Termin?', + options: ['Ich suche Arbeit. Ich habe morgen einen Termin.', 'Ich suche arbeiten. Morgen Termin ich.', 'Ich bin Arbeit suchen.', 'Morgen ich suche Termin Arbeit.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Beide Sätze sind typische kurze Aussagen für Bewerbung und Alltag.' + }, + { + exerciseTypeId: 1, + title: 'Termin ergänzen', + instruction: 'Fülle die Lücke aus.', + questionData: { + type: 'gap_fill', + text: 'Ich habe morgen einen {gap}.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['Termin'] + }, + explanation: 'Der Termin gehört zu den wichtigsten Alltagswörtern im Deutschen.' + } + ], + 'Trennbare Verben - Einstieg': [ + { + exerciseTypeId: 2, + title: 'Trennbares Verb erkennen', + instruction: 'Wähle den richtigen Hauptsatz.', + questionData: { + type: 'multiple_choice', + question: 'Welche Form mit trennbarem Verb ist richtig?', + options: ['Ich rufe dich an.', 'Ich anrufe dich.', 'Ich dich rufe an.', 'Ich rufe an dich.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Im Hauptsatz trennt sich das Verb: "Ich rufe dich an."' + }, + { + exerciseTypeId: 1, + title: 'Trennteil ergänzen', + instruction: 'Setze den fehlenden Verbteil ein.', + questionData: { + type: 'gap_fill', + text: 'Ich stehe um sechs Uhr {gap}.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['auf'] + }, + explanation: 'Bei "aufstehen" steht der Teil "auf" im Hauptsatz am Ende.' + } + ], + 'Einladungen & soziale Treffen': [ + { + exerciseTypeId: 2, + title: 'Einladung formulieren', + instruction: 'Wähle die natürlichste Einladung.', + questionData: { + type: 'multiple_choice', + question: 'Du willst jemanden für morgen einladen. Welche Form passt?', + options: ['Hast du Zeit? Kommst du morgen?', 'Du Zeit morgen kommst?', 'Morgen du kommst Zeit?', 'Kommst du Zeit hat morgen?'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Kurze direkte Fragen sind im Deutschen in dieser Situation sehr üblich.' + }, + withTypeName('situational_response', { + title: 'Treffen ausmachen', + instruction: 'Antworte kurz und passend auf Deutsch.', + questionData: { + type: 'situational_response', + question: 'Du willst ein Treffen für morgen bestätigen.', + keywords: ['morgen', 'zeit'] + }, + answerData: { + modelAnswer: 'Ja, morgen habe ich Zeit.', + keywords: ['morgen', 'zeit'] + }, + explanation: 'Die Antwort bestätigt knapp und klar die Verfügbarkeit.' + }) + ], + 'Einkauf, Reklamation, Rückgabe': [ + { + exerciseTypeId: 2, + title: 'Rückgabe im Laden', + instruction: 'Wähle die passende Reklamation.', + questionData: { + type: 'multiple_choice', + question: 'Ein gekaufter Gegenstand ist kaputt. Was sagst du?', + options: ['Das ist kaputt. Ich möchte das zurückgeben.', 'Das kaputt ich zurückgeben.', 'Ich kaputt zurück das.', 'Zurückgeben ich kaputt das.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Das ist die klare und höfliche Standardsituation bei einer Reklamation.' + }, + { + exerciseTypeId: 1, + title: 'Kassenbon ergänzen', + instruction: 'Fülle die Lücke mit dem passenden Wort.', + questionData: { + type: 'gap_fill', + text: 'Haben Sie einen {gap}?', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['Kassenbon'] + }, + explanation: 'Der Kassenbon ist im Rückgabe-Dialog ein sehr typisches Schlüsselwort.' + } + ], + 'würde / hätte gern - Einstieg': [ + { + exerciseTypeId: 2, + title: 'Höflichen Wunsch wählen', + instruction: 'Wähle die höflichste Formulierung.', + questionData: { + type: 'multiple_choice', + question: 'Welche Form klingt in einem Servicegespräch höflich?', + options: ['Ich hätte gern einen Termin.', 'Ich will Termin.', 'Ich nehme Termin.', 'Termin ich gern habe.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: '"Ich hätte gern ..." ist ein sehr nützliches höfliches Muster.' + }, + { + exerciseTypeId: 1, + title: 'Wunschform ergänzen', + instruction: 'Setze den passenden Ausdruck ein.', + questionData: { + type: 'gap_fill', + text: 'Ich {gap} gern Hilfe.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['hätte'] + }, + explanation: 'Mit "hätte gern" formuliert man Wünsche höflicher.' + } + ], + 'Dialogtag - Alltag': [ + { + exerciseTypeId: 2, + title: 'Alltagsschritte verbinden', + instruction: 'Wähle die natürlichste kleine Alltagsfolge.', + questionData: { + type: 'multiple_choice', + question: 'Welche Minifolge klingt als Alltagsbericht am besten?', + options: ['Heute muss ich arbeiten. Danach gehe ich einkaufen.', 'Heute muss ich arbeiten danach ich einkaufen gehe.', 'Ich heute arbeiten muss danach einkaufen.', 'Danach heute ich muss arbeiten.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Die Lektion trainiert verbundene Alltagsschritte in klaren deutschen Hauptsätzen.' + }, + { + exerciseTypeId: 3, + title: 'Alltagsfolge bauen', + instruction: 'Ordne die Wörter zur ersten Aussage.', + questionData: { + type: 'sentence_building', + question: 'Baue einen typischen Alltagsaussagesatz.', + tokens: ['Heute', 'muss', 'ich', 'arbeiten'] + }, + answerData: { + correct: ['Heute muss ich arbeiten'] + }, + explanation: 'So trainierst du gleichzeitig Alltagssprache und Satzstellung.' + } + ], + 'Fehlertraining - Artikel & Kasus I': [ + { + exerciseTypeId: 2, + title: 'Kasusform erkennen', + instruction: 'Wähle die richtige Form im Satz.', + questionData: { + type: 'multiple_choice', + question: 'Welche Form ist richtig?', + options: ['Ich brauche den Termin.', 'Ich brauche dem Termin.', 'Ich brauche der Termin.', 'Ich brauche das Termin.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Bei "brauchen" steht das Objekt hier im Akkusativ: "den Termin".' + }, + { + exerciseTypeId: 2, + title: 'Präposition mit Dativ', + instruction: 'Wähle die korrekte Form mit Präposition.', + questionData: { + type: 'multiple_choice', + question: 'Welche Form ist richtig?', + options: ['Ich fahre mit dem Bus.', 'Ich fahre mit den Bus.', 'Ich fahre mit der Bus.', 'Ich fahre mit das Bus.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Nach "mit" steht hier der Dativ: "mit dem Bus".' + } + ], + 'Rollenspiel - Missverständnisse lösen': [ + { + exerciseTypeId: 2, + title: 'Missverständnis klären', + instruction: 'Wähle die passendste Reaktion.', + questionData: { + type: 'multiple_choice', + question: 'Du hast etwas nicht verstanden. Was sagst du höflich?', + options: ['Ich verstehe das nicht. Können Sie das bitte wiederholen?', 'Ich nicht verstehe. Sie wiederholen.', 'Nicht verstehen ich das.', 'Wiederholen das bitte ich.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Die Kombination aus Klärung und höflicher Bitte ist hier besonders nützlich.' + }, + withTypeName('situational_response', { + title: 'Ruhig nachfragen', + instruction: 'Antworte mit einer höflichen Nachfrage.', + questionData: { + type: 'situational_response', + question: 'Du hast etwas akustisch nicht verstanden.', + keywords: ['verstehe', 'bitte', 'wiederholen'] + }, + answerData: { + modelAnswer: 'Ich verstehe das nicht. Können Sie das bitte wiederholen?', + keywords: ['verstehe', 'bitte', 'wiederholen'] + }, + explanation: 'Das Muster lässt sich in vielen realen Situationen direkt verwenden.' + }) + ], + 'Freies Sprechen - Alltag ohne Stütze': [ + withTypeName('speaking_from_memory', { + title: 'Typischen Tag frei erzählen', + instruction: 'Sprich frei über einen normalen Tag.', + questionData: { + type: 'speaking_from_memory', + question: 'Erzähle frei: Was machst du zuerst, dann und später?', + expectedText: 'Zuerst arbeite ich. Dann gehe ich einkaufen. Später bin ich zu Hause.', + keywords: ['zuerst', 'dann', 'später', 'arbeite', 'zu'] + }, + answerData: { + type: 'speaking_from_memory' + }, + explanation: 'Die Übung trainiert freien Ablauf statt einzelner isolierter Sätze.' + }), + withTypeName('situational_response', { + title: 'Freier Tagesbericht', + instruction: 'Antworte mit einem kurzen freien Mini-Bericht.', + questionData: { + type: 'situational_response', + question: 'Was machst du heute und später?', + keywords: ['heute', 'dann', 'später'] + }, + answerData: { + modelAnswer: 'Heute arbeite ich. Dann gehe ich einkaufen. Später bin ich zu Hause.', + keywords: ['heute', 'dann', 'später'] + }, + explanation: 'Wichtig ist eine klare kleine Struktur, nicht maximale Länge.' + }) + ], + 'Abschlussprüfung - Gesamtpfad': [ + { + exerciseTypeId: 2, + title: 'Gesamtpfad - Alltagssituation', + instruction: 'Wähle die komplett passendste Reaktion.', + questionData: { + type: 'multiple_choice', + question: 'Du bist neu in Deutschland, hast morgen einen Termin und brauchst heute Hilfe. Welche Aussage passt am besten?', + options: ['Ich brauche heute Hilfe. Morgen habe ich einen Termin.', 'Heute Hilfe ich brauche morgen Termin habe.', 'Ich morgen Hilfe heute Termin.', 'Termin morgen Hilfe heute ich.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Die Abschlussprüfung bündelt kurze, funktionale Alltagssprache über mehrere Themen.' + }, + { + exerciseTypeId: 1, + title: 'Abschluss - wichtiges Kernwort', + instruction: 'Fülle das wichtige Alltagswort ein.', + questionData: { + type: 'gap_fill', + text: 'Können Sie mir bitte {gap}?', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['helfen'] + }, + explanation: '"Können Sie mir bitte helfen?" ist ein sehr nützliches Kernmuster für den Gesamtpfad.' + }, + withTypeName('speaking_from_memory', { + title: 'Abschluss - frei reagieren', + instruction: 'Sprich eine kurze funktionale Antwort frei nach.', + questionData: { + type: 'speaking_from_memory', + question: 'Stell dich vor und nenne ein aktuelles Alltagsproblem.', + expectedText: 'Hallo, ich heiße Ana. Ich brauche Hilfe, weil ich morgen einen Termin habe.', + keywords: ['hallo', 'heiße', 'hilfe', 'weil', 'termin'] + }, + answerData: { + type: 'speaking_from_memory' + }, + explanation: 'Die Abschlussübung verbindet Vorstellung, Bedarf und Begründung in einem kurzen freien Beitrag.' + }) + ], + 'Kulturelle Orientierung in Deutschland vertieft': [ + { + exerciseTypeId: 2, + title: 'Kulturelle Alltagserwartung', + instruction: 'Wähle die Aussage, die gut zum kulturellen Fokus passt.', + questionData: { + type: 'multiple_choice', + question: 'Welche Aussage passt besonders gut zum kulturellen Schwerpunkt dieser Lektion?', + options: ['Termine und Pünktlichkeit sind oft sehr wichtig.', 'In Deutschland sind Termine meistens egal.', 'Direktheit ist immer unhöflich.', 'Siezen spielt keine Rolle.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Pünktlichkeit und klare Absprachen sind in vielen Alltagssituationen zentral.' + }, + withTypeName('situational_response', { + title: 'Kulturell angemessen reagieren', + instruction: 'Antworte mit einer kurzen kulturell passenden Aussage.', + questionData: { + type: 'situational_response', + question: 'Warum ist es wichtig, bei einem Termin pünktlich zu sein?', + keywords: ['termin', 'pünktlich', 'wichtig'] + }, + answerData: { + modelAnswer: 'Termine und Pünktlichkeit sind in Deutschland oft sehr wichtig.', + keywords: ['termin', 'pünktlich', 'wichtig'] + }, + explanation: 'Die Übung verbindet Sprache mit kultureller Orientierung.' + }) ] }; diff --git a/docs/BISAYA_SITE_LOCALIZATION_IMPLEMENTATION_SPEC.md b/docs/BISAYA_SITE_LOCALIZATION_IMPLEMENTATION_SPEC.md new file mode 100644 index 0000000..35b81ee --- /dev/null +++ b/docs/BISAYA_SITE_LOCALIZATION_IMPLEMENTATION_SPEC.md @@ -0,0 +1,520 @@ +# Bisaya-Lokalisierung der Gesamtwebseite: Umsetzungsdokument + +## 1. Ziel + +Die gesamte Webseite soll zusätzlich in `Bisaya` verfügbar werden, nicht nur einzelne Sprachkurse. + +Das Ziel ist: + +- die komplette Produktoberfläche für Bisaya-Nutzer verständlich zu machen +- Hauptnavigation, Formulare, Dialoge, Fehlermeldungen und Lernbereiche konsistent zu übersetzen +- vorhandene i18n-Struktur weiterzuverwenden statt einen Sonderpfad zu bauen +- die Qualität der Übersetzungen hoch zu halten, besonders bei Alltagssprache und sprachdidaktischen Inhalten + +Das ist kein reines JSON-Kopieren. Es ist eine Kombination aus: + +- technischer i18n-Erweiterung +- Audit von hartcodierten Strings +- kontrollierter Übersetzungsarbeit +- fachlicher QA + +## 2. Ist-Stand + +Die aktuelle Lokalisierung ist in [frontend/src/i18n/index.js](/mnt/share/torsten/Programs/YourPart3/frontend/src/i18n/index.js) zentral verdrahtet. + +Derzeit existieren drei Web-Locales: + +- `de` +- `en` +- `es` + +Die Struktur ist modulartig aufgebaut: + +- `general.json` +- `header.json` +- `navigation.json` +- `home.json` +- `chat.json` +- `settings.json` +- `socialnetwork.json` +- `falukant.json` +- weitere Funktionsbereiche + +Die bestehende Struktur ist grundsätzlich gut genug für eine vierte Sprache. + +Der Haken ist: + +- es gibt weiterhin viele hartcodierte Texte in Vue-Komponenten +- manche Bereiche nutzen bereits sauber `$t(...)` +- andere mischen i18n und direkte Strings +- einige `tr:`-Muster umgehen die normale Komponentensprache + +Für Bisaya reicht es deshalb nicht, nur neue Locale-Dateien zu erzeugen. + +## 3. Sprachentscheidung + +### 3.1 Locale-Code + +Empfehlung: + +- Web-Locale-Code: `ceb` + +Begründung: + +- `ceb` ist der etablierte technische Code für Cebuano/Bisaya +- besser anschlussfähig an Standards und spätere Integrationen +- klarer als ein frei erfundener Key wie `bis` + +### 3.2 Sichtbarer Sprachname + +Empfehlung im UI: + +- `Bisaya` + +Optional im Admin-/Technik-Kontext: + +- `Bisaya (Cebuano)` + +### 3.3 Sprachstil + +Der Stil darf nicht zu formal oder zu schulbuchartig sein. + +Empfohlen: + +- alltagsnah +- warm +- direkt verständlich +- kurze Sätze +- neutrale, breit verständliche Bisaya-Formulierungen + +Nicht empfohlen: + +- künstlich hochsprachliche Formulierungen +- unnötig spanisch geprägte oder regionale Spezialformen, wenn einfachere Alternativen reichen +- gemischtes Englisch-Bisaya ohne klare Regel + +## 4. Produktentscheidung + +Die Bisaya-Lokalisierung wird als vollwertige vierte Oberfläche behandelt, nicht als Teil des Sprachkurs-Features. + +Das heißt: + +- Bisaya ist globale UI-Sprache +- dieselbe Sprache kann zusätzlich als Lernsprache in Kursen vorkommen +- Website-Locale und Sprachkursinhalt sind fachlich getrennt + +Wichtig: + +- `Website-Bisaya` ist Produkt-Lokalisierung +- `Bisaya-Sprachkurs` ist Lerninhalt + +Das muss technisch und redaktionell getrennt bleiben. + +## 5. Hauptprobleme, die vor der Übersetzung geklärt werden müssen + +### 5.1 Hartcodierte Strings + +Die größte technische Altlast sind direkte Texte in Komponenten. + +Beispiele aus dem aktuellen Frontend: + +- Abschnittslabels in Komponenten wie [AppSectionBar.vue](/mnt/share/torsten/Programs/YourPart3/frontend/src/components/AppSectionBar.vue) +- direkte Buttontexte und Statusmeldungen in Views +- Fehlermeldungen, die als deutscher Klartext im Code stehen +- teils auch Titel, Platzhalter und Hilfetexte + +Konsequenz: + +- vor oder während der Bisaya-Einführung müssen diese Texte systematisch in i18n überführt werden + +### 5.2 Fallback-Logik + +Aktuell ist `fallbackLocale: 'de'` gesetzt. + +Für Bisaya ist das kurzfristig okay, aber fachlich nicht ideal. + +Empfehlung: + +- während der Einführung: globaler Fallback kann vorerst `de` bleiben +- mittelfristig: für `ceb` bevorzugt auf `en` oder spezifisches Ketten-Fallback umstellen + +Zielbild: + +```js +fallbackLocale: { + ceb: ['en', 'de'], + default: ['de'] +} +``` + +So landet ein Bisaya-Nutzer bei fehlenden Schlüsseln nicht sofort in Deutsch. + +### 5.3 Übersetzungstiefe + +Nicht alles muss in einem Schritt übersetzt werden. + +Wir brauchen klare Ebenen: + +- Shell und Navigation zuerst +- Hauptnutzungspfade danach +- Spezialbereiche später + +## 6. Übersetzungsprinzipien + +### 6.1 Quelle + +Primäre Übersetzungsquelle sollte `de` sein. + +Begründung: + +- das Produkt wird aktuell stark deutsch geführt +- viele Texte sind fachlich auf Deutsch zuerst gepflegt +- so vermeiden wir doppelte Drift zwischen `de` und `en` + +### 6.2 Glossar + +Vor dem Massenübersetzen braucht es ein verbindliches Glossar. + +Pflichtbegriffe: + +- Anmeldung +- Einstellungen +- Freunde +- Nachrichten +- Chat +- Kurs +- Lektion +- Übung +- Wiederholung +- Abschluss +- Senden +- Löschen +- Speichern +- Suche +- Zurück +- Weiter + +Zusätzlich fachliche Glossare für: + +- Vokabeltrainer +- Falukant +- Socialnetwork +- Moderation +- Erotik-Bereich + +### 6.3 Nicht alles wörtlich + +Die beste Bisaya-UI ist nicht immer die wörtlichste. + +Beispiel: + +- kurze, handlungsnahe Buttons sind wichtiger als exakte 1:1-Übertragung +- Hilfetexte dürfen freier formuliert werden, solange der Zweck gleich bleibt + +### 6.4 Konsistenzregeln + +Es muss pro Begriff genau eine Standardform geben, außer es gibt einen klaren Kontextgrund. + +Beispiel: + +- `Weiter` nicht an einer Stelle frei übersetzen und an anderer nur halb Englisch lassen +- `Lektion` und `Kurs` nicht ständig unterschiedlich umschreiben + +## 7. Rollout-Phasen + +## 7.1 Phase A: Technische Basis + +Ziel: + +- `ceb` als viertes Locale technisch einführen + +Umfang: + +- neue Locale-Dateien unter `frontend/src/i18n/locales/ceb/` +- Ergänzung in [index.js](/mnt/share/torsten/Programs/YourPart3/frontend/src/i18n/index.js) +- Sprachumschalter auf `ceb` erweitern +- Store- und Default-Sprachlogik prüfen + +Ergebnis: + +- die App kann Bisaya als UI-Sprache laden +- zunächst mit vielen Fallbacks, aber technisch sauber + +## 7.2 Phase B: Shell und globale Oberfläche + +Ziel: + +- die Seite fühlt sich sofort als Bisaya-Oberfläche an + +Umfang: + +- `general.json` +- `header.json` +- `navigation.json` +- `message.json` +- `error.json` + +Zusätzlich: + +- hartcodierte Hauptnavigationen in Komponenten in i18n überführen + +Ergebnis: + +- Header, Footer, Navigation, Standarddialoge, Basisaktionen und Fehlermeldungen sind in Bisaya + +## 7.3 Phase C: Einstiegspfade + +Ziel: + +- zentrale Nutzungspfade vollständig lokalisieren + +Umfang: + +- `home.json` +- `register.json` +- `activate.json` +- `passwordReset.json` +- `settings.json` +- allgemeine No-Login-/Login-Flows + +Ergebnis: + +- neue Nutzer können die Plattform in Bisaya betreten und bedienen + +## 7.4 Phase D: Socialnetwork und Sprachlernen + +Ziel: + +- Hauptanwendungsbereich für Community und Lernen abdecken + +Umfang: + +- `socialnetwork.json` +- `friends.json` +- Vokabeltrainer-Views +- Sprachkurs-Views +- Such- und Dialogflächen im Social-Bereich + +Wichtig: + +- Sprachlerntexte im Kurs selbst nicht blind mitsynchronisieren +- UI-Texte ja, Lerninhalte separat prüfen + +## 7.5 Phase E: Falukant und Spezialbereiche + +Ziel: + +- komplexe Produktmodule sauber nachziehen + +Umfang: + +- `falukant.json` +- `blog.json` +- `chat.json` +- `minigames.json` +- Admin-Bereiche optional später + +Hinweis: + +- Falukant ist textlich sehr groß und fachlich komplex +- dafür braucht es gesonderte QA + +## 8. Technische Umsetzung + +### 8.1 Neue Locale-Dateien + +Geplante Struktur: + +```text +frontend/src/i18n/locales/ceb/ + activate.json + admin.json + blog.json + chat.json + error.json + falukant.json + friends.json + general.json + header.json + home.json + message.json + minigames.json + navigation.json + passwordReset.json + personal.json + register.json + settings.json + socialnetwork.json +``` + +### 8.2 Import in i18n + +In [frontend/src/i18n/index.js](/mnt/share/torsten/Programs/YourPart3/frontend/src/i18n/index.js): + +- alle `ceb`-Dateien importieren +- vierten `messages.ceb`-Block ergänzen +- Fallback-Regel ggf. modernisieren + +### 8.3 Sprachwahl im Store + +In [frontend/src/store/index.js](/mnt/share/torsten/Programs/YourPart3/frontend/src/store/index.js): + +- `setLanguage` muss `ceb` akzeptieren +- Browser-Spracherkennung bleibt vorerst `de/en`, außer wir wollen `ceb` aktiv auch automatisch setzen + +Empfehlung: + +- Auto-Default nicht auf `ceb` +- Bisaya bewusst auswählbar machen + +### 8.4 UI-Auswahl erweitern + +Komponenten wie: + +- [SettingsWidget.vue](/mnt/share/torsten/Programs/YourPart3/frontend/src/components/SettingsWidget.vue) + +müssen `ceb` als auswählbare Sprache bekommen. + +## 9. Audit von Nicht-i18n-Texten + +Vor der Vollübersetzung braucht es einen String-Audit. + +Kategorien: + +1. komplett hartcodierte Texte +2. gemischte Texte mit Teil-i18n +3. technische Fehlermeldungen +4. Admin-/Debugtexte, die bewusst vorerst unübersetzt bleiben dürfen + +Besonders kritisch: + +- [AppSectionBar.vue](/mnt/share/torsten/Programs/YourPart3/frontend/src/components/AppSectionBar.vue) +- Views mit hero-texten und Buttons +- direkte Dialogtitel +- direkte Success-/Error-Meldungen in JS-Dateien + +Empfehlung: + +- zuerst Audit-Backlog erzeugen +- dann Bereich für Bereich in i18n ziehen + +## 10. Übersetzungsworkflow + +### 10.1 Empfohlener Ablauf pro Datei + +1. deutsche Quelldatei nehmen +2. Schlüsselstruktur unverändert lassen +3. Bisaya-Datei anlegen +4. problematische Begriffe mit Glossar abgleichen +5. UI-Stichprobe im echten Produkt machen + +### 10.2 Qualitätssicherung + +Jede größere Datei braucht: + +- formale Prüfung: alle Keys vorhanden +- Produktprüfung: Text passt im Layout +- Sprachprüfung: natürliches Bisaya +- Kontextprüfung: Button- und Fehlermeldungstexte klingen im UI sinnvoll + +### 10.3 Platzhalter und Variablen + +Besondere Aufmerksamkeit für: + +- `{count}` +- `{minutes}` +- `{title}` +- `{name}` +- plural-/choice-nahe Muster + +Bisaya-Übersetzungen müssen Variablen und Platzhalter exakt beibehalten. + +## 11. Inhaltliche Risiken + +### 11.1 Falukant + +Falukant hat viele produktinterne Begriffe. + +Risiko: + +- zu direkte Übersetzung zerstört die interne Fachsprache + +Empfehlung: + +- einige Begriffe bewusst nicht übersetzen +- stattdessen mit konsistenten Lehnformen arbeiten + +### 11.2 Sprachkurs-Bereich + +Risiko: + +- UI wird lokalisiert, Lerninhalt aber versehentlich mitübersetzt, obwohl er als Kursmaterial bestehen bleiben soll + +Empfehlung: + +- klare Trennung zwischen: + - UI-Text + - Kursinhalt + - Vokabel-/Satzmaterial + +### 11.3 Mischnutzung Deutsch/Bisaya + +Viele Nutzer könnten Deutsch lernen und gleichzeitig die Website in Bisaya benutzen. + +Das ist erlaubt, aber nur wenn: + +- UI-Lokalisierung stabil ist +- Kursinhalt nicht durcheinandergerät + +## 12. Priorisierte Umsetzung + +Empfohlene Reihenfolge: + +1. `ceb` technisch einführen +2. Sprachumschalter erweitern +3. `general/header/navigation/message/error` +4. hartcodierte Shell-Texte bereinigen +5. `settings/home/register/passwordReset` +6. `socialnetwork/friends` +7. Vokabeltrainer- und Kurs-UI +8. Chat/Blog/Minigames +9. Falukant +10. Admin-Bereich optional zuletzt + +## 13. Definition von "fertig" + +Die Bisaya-Weblokalisierung ist erst dann wirklich fertig, wenn: + +- `ceb` als Sprache global auswählbar ist +- Hauptnavigation und Standardsystemtexte lokalisiert sind +- zentrale Nutzerflüsse ohne Deutsch funktionieren +- Sprachkurs-UI sauber lokalisiert ist +- Falukant und Spezialmodule zumindest konsistent oder bewusst ausgeklammert sind +- fehlende Keys systematisch kontrolliert werden + +## 14. Konkrete nächste technische Phase + +Die sinnvolle erste echte Bauphase ist: + +### Phase 1 + +- `frontend/src/i18n/locales/ceb/` anlegen +- `index.js` für `ceb` erweitern +- `SettingsWidget.vue` Sprachwahl erweitern +- `AppSectionBar.vue` und vergleichbare Shell-Komponenten in i18n ziehen +- `general/header/navigation/message/error` auf Bisaya anlegen + +Erst danach lohnt es sich, größere Produktbereiche wie Socialnetwork oder Falukant vollständig zu übersetzen. + +## 15. Ergebnis + +Die Website in Bisaya zu lokalisieren ist machbar und passt gut zur bestehenden Struktur, aber nur, wenn wir es als echtes Produktprojekt behandeln. + +Die richtige Reihenfolge ist: + +- erst technische Sprachbasis +- dann Shell und Kernpfade +- dann die großen Module +- parallel dazu Audit und Bereinigung harter Strings + +So entsteht keine halbfertige vierte Sprache, sondern eine wirklich benutzbare Bisaya-Oberfläche. diff --git a/frontend/src/components/AppSectionBar.vue b/frontend/src/components/AppSectionBar.vue index afe9b0d..3bf66cd 100644 --- a/frontend/src/components/AppSectionBar.vue +++ b/frontend/src/components/AppSectionBar.vue @@ -10,76 +10,76 @@ class="app-section-bar__back" @click="navigateBack" > - Zurück + {{ $t('general.general.back') }}