diff --git a/css/games.css b/css/games.css index 68ef0ae..4d67482 100644 --- a/css/games.css +++ b/css/games.css @@ -437,9 +437,11 @@ max-width: 1000px; margin: 0 auto; user-select: none; + padding-top: 20px; /* Add space to prevent overlap with top navigation */ } -.game-hud { +.adventure-reader-wrapper .game-hud { + position: relative; /* Override the absolute positioning */ display: flex; justify-content: space-between; align-items: center; @@ -448,6 +450,11 @@ border-radius: var(--border-radius); box-shadow: var(--shadow-light); border: 2px solid var(--primary-color); + top: auto; /* Reset top positioning */ + left: auto; /* Reset left positioning */ + right: auto; /* Reset right positioning */ + height: auto; /* Allow natural height */ + z-index: auto; /* Reset z-index */ } .hud-left { @@ -1734,6 +1741,7 @@ max-width: 1400px; flex-direction: row; gap: 30px; + padding-top: 20px; /* Maintain top padding on large screens */ } .game-map { diff --git a/js/content/SBS-level-1.js b/js/content/SBS-level-1.js new file mode 100644 index 0000000..c37b4d3 --- /dev/null +++ b/js/content/SBS-level-1.js @@ -0,0 +1,479 @@ +// === ENGLISH LEARNING MODULE === +// Complete English learning module with Chinese translation and pronunciation + +window.ContentModules = window.ContentModules || {}; + +window.ContentModules.SBSLevel1 = { + id: "sbs-level-1", + name: "SBS-1", + description: "English introduction lessons with Chinese translation and pronunciation", + difficulty: "beginner", + language: "en-US", + userLanguage: "zh-CN", + totalWords: 150, + + // === GRAMMAR LESSONS SYSTEM === + grammar: { + "to-be-verb": { + title: "The Verb 'To Be' - 动词Be", + explanation: "The verb 'be' is one of the most important verbs in English, used to describe states, identity, and location.", + rules: [ + "I am - 我是 (first person singular)", + "You are - 你是/你们是 (second person)", + "He/She/It is - 他/她/它是 (third person singular)", + "We are - 我们是 (first person plural)", + "They are - 他们是 (third person plural)" + ], + examples: [ + { + english: "My name is Maria.", + chinese: "我的名字是玛丽亚。", + explanation: "Use 'is' because 'name' is third person singular", + pronunciation: "/maɪ neɪm ɪz məˈriːə/" + }, + { + english: "I am from Mexico City.", + chinese: "我来自墨西哥城。", + explanation: "Use 'am' because the subject is 'I'", + pronunciation: "/aɪ æm frʌm ˈmeksɪkoʊ ˈsɪti/" + }, + { + english: "Where are you from?", + chinese: "你来自哪里?", + explanation: "Use 'are' because the subject is 'you'", + pronunciation: "/wer ɑr ju frʌm/" + } + ], + exercises: [ + { + type: "fill_blank", + sentence: "My address _____ 235 Main Street.", + options: ["am", "is", "are"], + correct: "is", + explanation: "Use 'is' because 'address' is third person singular" + }, + { + type: "translation", + english: "What's your phone number?", + chinese: "你的电话号码是多少?", + focus: "Contraction What's = What is" + } + ] + }, + + "contractions": { + title: "Contractions - 缩写形式", + explanation: "English often uses contractions to make conversation more natural and fluent.", + rules: [ + "What's = What is - 什么是", + "I'm = I am - 我是", + "You're = You are - 你是", + "He's/She's/It's = He/She/It is - 他/她/它是" + ], + examples: [ + { + english: "What's your name?", + chinese: "你叫什么名字?", + explanation: "What's is the contraction of What is", + pronunciation: "/wʌts jʊr neɪm/" + }, + { + english: "I'm Nancy Lee.", + chinese: "我是南希·李。", + explanation: "I'm is the contraction of I am", + pronunciation: "/aɪm ˈnænsi li/" + } + ], + exercises: [ + { + type: "contraction_match", + full_form: "What is your address?", + contracted: "What's your address?", + chinese: "你的地址是什么?" + } + ] + }, + + "personal-information": { + title: "Personal Information - 个人信息", + explanation: "Learn how to ask for and provide basic personal information in English.", + rules: [ + "Name - 姓名: What's your name? My name is...", + "Address - 地址: What's your address? My address is...", + "Phone - 电话: What's your phone number? My phone number is...", + "Origin - 来源: Where are you from? I'm from..." + ], + examples: [ + { + english: "My name is David Carter.", + chinese: "我的名字是大卫·卡特。", + explanation: "Standard expression for introducing name", + pronunciation: "/maɪ neɪm ɪz ˈdeɪvɪd ˈkɑrtər/" + }, + { + english: "I'm from San Francisco.", + chinese: "我来自旧金山。", + explanation: "Expression for stating origin", + pronunciation: "/aɪm frʌm sæn frænˈsɪskoʊ/" + } + ], + exercises: [ + { + type: "dialogue_completion", + prompt: "A: What's your name? B: _____", + answer: "My name is [your name].", + chinese: "A: 你叫什么名字? B: 我的名字是[你的名字]。" + } + ] + }, + + "meeting-people": { + title: "Meeting People - 与人见面", + explanation: "Common phrases and expressions used when meeting new people.", + rules: [ + "Hello - 你好 (formal greeting)", + "Hi - 嗨 (informal greeting)", + "Nice to meet you - 很高兴认识你", + "Nice to meet you, too - 我也很高兴认识你" + ], + examples: [ + { + english: "Hello. My name is Peter Lewis.", + chinese: "你好。我的名字是彼得·刘易斯。", + explanation: "Formal introduction", + pronunciation: "/həˈloʊ maɪ neɪm ɪz ˈpitər ˈluɪs/" + }, + { + english: "Hi. I'm Nancy Lee. Nice to meet you.", + chinese: "嗨。我是南希·李。很高兴认识你。", + explanation: "Informal introduction with greeting", + pronunciation: "/haɪ aɪm ˈnænsi li naɪs tu mit ju/" + } + ], + exercises: [ + { + type: "role_play", + scenario: "Meeting someone new", + dialogue: "A: Hello. B: Hi. A: What's your name? B: My name is ____." + } + ] + } + }, + + vocabulary: { + "name": { + "user_language": "名字", + "type": "noun", + "pronunciation": "/neɪm/" + }, + "address": { + "user_language": "地址", + "type": "noun", + "pronunciation": "/əˈdres/" + }, + "phone number": { + "user_language": "电话号码", + "type": "noun", + "pronunciation": "/foʊn ˈnʌmbər/" + }, + "telephone number": { + "user_language": "电话号码", + "type": "noun", + "pronunciation": "/ˈteləfoʊn ˈnʌmbər/" + }, + "apartment number": { + "user_language": "公寓号码", + "type": "noun", + "pronunciation": "/əˈpɑrtmənt ˈnʌmbər/" + }, + "e-mail address": { + "user_language": "电子邮件地址", + "type": "noun", + "pronunciation": "/ˈiːmeɪl əˈdres/" + }, + "first name": { + "user_language": "名", + "type": "noun", + "pronunciation": "/fɜrst neɪm/" + }, + "last name": { + "user_language": "姓", + "type": "noun", + "pronunciation": "/læst neɪm/" + }, + "hello": { + "user_language": "你好", + "type": "interjection", + "pronunciation": "/həˈloʊ/" + }, + "hi": { + "user_language": "嗨", + "type": "interjection", + "pronunciation": "/haɪ/" + }, + "nice to meet you": { + "user_language": "很高兴认识你", + "type": "phrase", + "pronunciation": "/naɪs tu mit ju/" + }, + "where": { + "user_language": "哪里", + "type": "adverb", + "pronunciation": "/wer/" + }, + "from": { + "user_language": "来自", + "type": "preposition", + "pronunciation": "/frʌm/" + }, + "alphabet": { + "user_language": "字母表", + "type": "noun", + "pronunciation": "/ˈælfəbet/" + }, + "numbers": { + "user_language": "数字", + "type": "noun", + "pronunciation": "/ˈnʌmbərz/" + } + }, + + story: { + title: "To Be: Introduction - 动词Be的介绍", + totalSentences: 50, + chapters: [ + { + title: "Chapter 1: Vocabulary Preview - 第一章:词汇预览", + sentences: [ + { + id: 1, + original: "Learn the alphabet: Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz", + translation: "学习字母表:Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz", + words: [ + {word: "Learn", translation: "学习", type: "verb", pronunciation: "/lɜrn/"}, + {word: "alphabet", translation: "字母表", type: "noun", pronunciation: "/ˈælfəbet/"} + ] + }, + { + id: 2, + original: "Practice numbers: 0 1 2 3 4 5 6 7 8 9 10", + translation: "练习数字:0 1 2 3 4 5 6 7 8 9 10", + words: [ + {word: "Practice", translation: "练习", type: "verb", pronunciation: "/ˈpræktɪs/"}, + {word: "numbers", translation: "数字", type: "noun", pronunciation: "/ˈnʌmbərz/"} + ] + }, + { + id: 3, + original: "This is Maria's name tag.", + translation: "这是玛丽亚的姓名牌。", + words: [ + {word: "This", translation: "这", type: "pronoun", pronunciation: "/ðɪs/"}, + {word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"}, + {word: "Maria's", translation: "玛丽亚的", type: "possessive", pronunciation: "/məˈriːəz/"}, + {word: "name", translation: "姓名", type: "noun", pronunciation: "/neɪm/"}, + {word: "tag", translation: "牌", type: "noun", pronunciation: "/tæg/"} + ] + }, + { + id: 4, + original: "235 Main Street is an address.", + translation: "主街235号是一个地址。", + words: [ + {word: "235", translation: "235", type: "number", pronunciation: "/tu ˈθɜrti faɪv/"}, + {word: "Main", translation: "主要的", type: "adjective", pronunciation: "/meɪn/"}, + {word: "Street", translation: "街", type: "noun", pronunciation: "/strit/"}, + {word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"}, + {word: "an", translation: "一个", type: "article", pronunciation: "/æn/"}, + {word: "address", translation: "地址", type: "noun", pronunciation: "/əˈdres/"} + ] + }, + { + id: 5, + original: "741-8906 is a telephone number.", + translation: "741-8906是一个电话号码。", + words: [ + {word: "741-8906", translation: "741-8906", type: "number", pronunciation: "/ˈsevən fɔr wʌn eɪt naɪn oʊ sɪks/"}, + {word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"}, + {word: "a", translation: "一个", type: "article", pronunciation: "/ə/"}, + {word: "telephone", translation: "电话", type: "noun", pronunciation: "/ˈteləfoʊn/"}, + {word: "number", translation: "号码", type: "noun", pronunciation: "/ˈnʌmbər/"} + ] + } + ] + }, + { + title: "Chapter 2: What's Your Name? - 第二章:你叫什么名字?", + sentences: [ + { + id: 6, + original: "What's your name?", + translation: "你叫什么名字?", + words: [ + {word: "What's", translation: "什么是", type: "contraction", pronunciation: "/wʌts/"}, + {word: "your", translation: "你的", type: "possessive", pronunciation: "/jʊr/"}, + {word: "name", translation: "名字", type: "noun", pronunciation: "/neɪm/"} + ] + }, + { + id: 7, + original: "My name is Maria.", + translation: "我的名字是玛丽亚。", + words: [ + {word: "My", translation: "我的", type: "possessive", pronunciation: "/maɪ/"}, + {word: "name", translation: "名字", type: "noun", pronunciation: "/neɪm/"}, + {word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"}, + {word: "Maria", translation: "玛丽亚", type: "name", pronunciation: "/məˈriːə/"} + ] + }, + { + id: 8, + original: "What's your address?", + translation: "你的地址是什么?", + words: [ + {word: "What's", translation: "什么是", type: "contraction", pronunciation: "/wʌts/"}, + {word: "your", translation: "你的", type: "possessive", pronunciation: "/jʊr/"}, + {word: "address", translation: "地址", type: "noun", pronunciation: "/əˈdres/"} + ] + }, + { + id: 9, + original: "My address is 235 Main Street.", + translation: "我的地址是主街235号。", + words: [ + {word: "My", translation: "我的", type: "possessive", pronunciation: "/maɪ/"}, + {word: "address", translation: "地址", type: "noun", pronunciation: "/əˈdres/"}, + {word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"}, + {word: "235", translation: "235", type: "number", pronunciation: "/tu ˈθɜrti faɪv/"}, + {word: "Main", translation: "主要的", type: "adjective", pronunciation: "/meɪn/"}, + {word: "Street", translation: "街", type: "noun", pronunciation: "/strit/"} + ] + }, + { + id: 10, + original: "I'm from Mexico City.", + translation: "我来自墨西哥城。", + words: [ + {word: "I'm", translation: "我是", type: "contraction", pronunciation: "/aɪm/"}, + {word: "from", translation: "来自", type: "preposition", pronunciation: "/frʌm/"}, + {word: "Mexico", translation: "墨西哥", type: "place", pronunciation: "/ˈmeksɪkoʊ/"}, + {word: "City", translation: "城", type: "noun", pronunciation: "/ˈsɪti/"} + ] + } + ] + }, + { + title: "Chapter 3: Meeting People - 第三章:与人见面", + sentences: [ + { + id: 11, + original: "Hello. My name is Peter Lewis.", + translation: "你好。我的名字是彼得·刘易斯。", + words: [ + {word: "Hello", translation: "你好", type: "interjection", pronunciation: "/həˈloʊ/"}, + {word: "My", translation: "我的", type: "possessive", pronunciation: "/maɪ/"}, + {word: "name", translation: "名字", type: "noun", pronunciation: "/neɪm/"}, + {word: "is", translation: "是", type: "verb", pronunciation: "/ɪz/"}, + {word: "Peter", translation: "彼得", type: "name", pronunciation: "/ˈpitər/"}, + {word: "Lewis", translation: "刘易斯", type: "name", pronunciation: "/ˈluɪs/"} + ] + }, + { + id: 12, + original: "Hi. I'm Nancy Lee. Nice to meet you.", + translation: "嗨。我是南希·李。很高兴认识你。", + words: [ + {word: "Hi", translation: "嗨", type: "interjection", pronunciation: "/haɪ/"}, + {word: "I'm", translation: "我是", type: "contraction", pronunciation: "/aɪm/"}, + {word: "Nancy", translation: "南希", type: "name", pronunciation: "/ˈnænsi/"}, + {word: "Lee", translation: "李", type: "name", pronunciation: "/li/"}, + {word: "Nice", translation: "很好的", type: "adjective", pronunciation: "/naɪs/"}, + {word: "to", translation: "到", type: "preposition", pronunciation: "/tu/"}, + {word: "meet", translation: "遇见", type: "verb", pronunciation: "/mit/"}, + {word: "you", translation: "你", type: "pronoun", pronunciation: "/ju/"} + ] + }, + { + id: 13, + original: "Nice to meet you, too.", + translation: "我也很高兴认识你。", + words: [ + {word: "Nice", translation: "很好的", type: "adjective", pronunciation: "/naɪs/"}, + {word: "to", translation: "到", type: "preposition", pronunciation: "/tu/"}, + {word: "meet", translation: "遇见", type: "verb", pronunciation: "/mit/"}, + {word: "you", translation: "你", type: "pronoun", pronunciation: "/ju/"}, + {word: "too", translation: "也", type: "adverb", pronunciation: "/tu/"} + ] + } + ] + } + ] + }, + + // === GRAMMAR-BASED FILL IN THE BLANKS === + fillInBlanks: [ + { + sentence: "My name _____ David.", + options: ["am", "is", "are"], + correctAnswer: "is", + explanation: "Use 'is' because 'name' is third person singular", + grammarFocus: "to-be-verb" + }, + { + sentence: "I _____ from China.", + options: ["am", "is", "are"], + correctAnswer: "am", + explanation: "Use 'am' because the subject is 'I'", + grammarFocus: "to-be-verb" + }, + { + sentence: "_____ your phone number?", + options: ["What", "What's", "Where"], + correctAnswer: "What's", + explanation: "What's = What is, used to ask for phone number", + grammarFocus: "contractions" + }, + { + sentence: "Where _____ you from?", + options: ["am", "is", "are"], + correctAnswer: "are", + explanation: "Use 'are' because the subject is 'you'", + grammarFocus: "to-be-verb" + }, + { + sentence: "_____ to meet you.", + options: ["Nice", "Good", "Fine"], + correctAnswer: "Nice", + explanation: "Standard expression for meeting people", + grammarFocus: "meeting-people" + } + ], + + // === GRAMMAR CORRECTION EXERCISES === + corrections: [ + { + incorrect: "My name are John.", + correct: "My name is John.", + explanation: "'Name' is third person singular, so use 'is'", + grammarFocus: "to-be-verb" + }, + { + incorrect: "Where you are from?", + correct: "Where are you from?", + explanation: "In questions, the be verb comes before the subject", + grammarFocus: "to-be-verb" + }, + { + incorrect: "What is you name?", + correct: "What is your name?", + explanation: "Use possessive 'your' not subject pronoun 'you'", + grammarFocus: "personal-information" + }, + { + incorrect: "I are from Mexico.", + correct: "I am from Mexico.", + explanation: "Use 'am' with subject 'I'", + grammarFocus: "to-be-verb" + } + ] +}; \ No newline at end of file diff --git a/js/content/WTA1B1.js b/js/content/WTA1B1.js new file mode 100644 index 0000000..4c5b3d3 --- /dev/null +++ b/js/content/WTA1B1.js @@ -0,0 +1,1094 @@ +// === ENGLISH LETTERS AND PETS STORY === +// Complete English story with Chinese translation and pronunciation + +window.ContentModules = window.ContentModules || {}; + +window.ContentModules.WTA1B1 = { + id: "wta1b1", + name: "WTA1B-1", + description: "English learning story with letters U, V, T and pet vocabulary", + difficulty: "beginner", + language: "en-US", + userLanguage: "zh-CN", + totalWords: 150, + + // === GRAMMAR LESSONS SYSTEM === + grammar: { + "demonstrative-pronouns": { + title: "Demonstrative Pronouns - 指示代词", + explanation: "English uses specific words to point to things that are near or far, singular or plural.", + rules: [ + "this - for one thing that is close", + "that - for one thing that is far", + "these - for multiple things that are close", + "those - for multiple things that are far" + ], + examples: [ + { + english: "What is this?", + chinese: "这是什么?", + explanation: "Use 'this' for one thing close to you", + pronunciation: "wʌt ɪz ðɪs" + }, + { + english: "What are those?", + chinese: "那些是什么?", + explanation: "Use 'those' for multiple things far from you", + pronunciation: "wʌt ɑːr ðoʊz" + }, + { + english: "These are rabbits.", + chinese: "这些是兔子。", + explanation: "Use 'these' for multiple things close to you", + pronunciation: "ðiːz ɑːr ræbɪts" + }, + { + english: "That is my pet bird.", + chinese: "那是我的宠物鸟。", + explanation: "Use 'that' for one thing far from you", + pronunciation: "ðæt ɪz maɪ pet bɜːrd" + }, + { + english: "This cat is very cute.", + chinese: "这只猫很可爱。", + explanation: "Use 'this' when pointing to something nearby", + pronunciation: "ðɪs kæt ɪz veri kjuːt" + }, + { + english: "Are these your turtles?", + chinese: "这些是你的乌龟吗?", + explanation: "Use 'these' in questions about nearby plural things", + pronunciation: "ɑːr ðiːz jʊr tɜːrtəlz" + }, + { + english: "Those dogs are playing.", + chinese: "那些狗在玩耍。", + explanation: "Use 'those' for distant plural animals or things", + pronunciation: "ðoʊz dɔːgz ɑːr pleɪɪŋ" + }, + { + english: "This is my favorite hamster.", + chinese: "这是我最喜欢的仓鼠。", + explanation: "Use 'this' to introduce something specific and close", + pronunciation: "ðɪs ɪz maɪ feɪvərɪt hæmstər" + } + ], + exercises: [ + { + type: "fill_blank", + sentence: "_____ is a dog.", + options: ["This", "These", "That", "Those"], + correct: "This", + explanation: "Use 'This' for one thing close to you" + }, + { + type: "translation", + english: "Those are turtles.", + chinese: "那些是海龟。", + focus: "Demonstrative pronoun for plural distant objects" + } + ] + }, + + "be-verb-usage": { + title: "Be Verb Usage - Be动词使用", + explanation: "English 'be' verbs change based on whether you're talking about one thing or many things.", + rules: [ + "is - used with singular nouns: It is a cat", + "are - used with plural nouns: They are dogs", + "Pattern: This/That + is, These/Those + are" + ], + examples: [ + { + english: "It is a bird.", + chinese: "它是一只鸟。", + explanation: "Use 'is' with singular nouns", + pronunciation: "ɪt ɪz ə bɜːrd" + }, + { + english: "They are birds.", + chinese: "它们是鸟。", + explanation: "Use 'are' with plural nouns", + pronunciation: "ðeɪ ɑːr bɜːrdz" + }, + { + english: "Where is the cat?", + chinese: "猫在哪里?", + explanation: "Use 'is' when asking about one thing", + pronunciation: "wer ɪz ðə kæt" + }, + { + english: "The dog is happy.", + chinese: "狗很高兴。", + explanation: "Use 'is' with singular subjects and adjectives", + pronunciation: "ðə dɔːg ɪz hæpi" + }, + { + english: "My pets are cute.", + chinese: "我的宠物很可爱。", + explanation: "Use 'are' with plural nouns like 'pets'", + pronunciation: "maɪ pets ɑːr kjuːt" + }, + { + english: "Where are the rabbits?", + chinese: "兔子在哪里?", + explanation: "Use 'are' when asking about multiple things", + pronunciation: "wer ɑːr ðə ræbɪts" + }, + { + english: "This is my turtle.", + chinese: "这是我的乌龟。", + explanation: "Use 'is' with demonstrative 'this'", + pronunciation: "ðɪs ɪz maɪ tɜːrtəl" + }, + { + english: "These are my friends.", + chinese: "这些是我的朋友。", + explanation: "Use 'are' with demonstrative 'these'", + pronunciation: "ðiːz ɑːr maɪ frends" + } + ], + exercises: [ + { + type: "fill_blank", + sentence: "They ____ turtles.", + options: ["is", "are", "am", "be"], + correct: "are", + explanation: "Use 'are' with plural subjects like 'they'" + } + ] + }, + + "prepositions-of-place": { + title: "Prepositions of Place - 地点介词", + explanation: "English uses specific words to show where things are located.", + rules: [ + "on - things touching the top of something: on the chair", + "in - things inside something: in the box", + "under - things below something: under the table" + ], + examples: [ + { + english: "The cat is on the chair.", + chinese: "猫在椅子上。", + explanation: "Use 'on' when something is touching the top", + pronunciation: "ðə kæt ɪz ɑːn ðə tʃer" + }, + { + english: "The turtle is in the water.", + chinese: "海龟在水里。", + explanation: "Use 'in' when something is inside or surrounded", + pronunciation: "ðə tɜːrtəl ɪz ɪn ðə wɔːtər" + }, + { + english: "The dog is under the table.", + chinese: "狗在桌子下面。", + explanation: "Use 'under' when something is below another thing", + pronunciation: "ðə dɔːg ɪz ʌndər ðə teɪbəl" + }, + { + english: "The bird is on the tree.", + chinese: "鸟在树上。", + explanation: "Use 'on' for things resting on surfaces", + pronunciation: "ðə bɜːrd ɪz ɑːn ðə triː" + }, + { + english: "The rabbit is in the garden.", + chinese: "兔子在花园里。", + explanation: "Use 'in' for enclosed or surrounded spaces", + pronunciation: "ðə ræbɪt ɪz ɪn ðə gɑːrdən" + }, + { + english: "The hamster is under the bed.", + chinese: "仓鼠在床下面。", + explanation: "Use 'under' for things below furniture", + pronunciation: "ðə hæmstər ɪz ʌndər ðə bed" + }, + { + english: "My pet is on the sofa.", + chinese: "我的宠物在沙发上。", + explanation: "Use 'on' when pets sit on furniture", + pronunciation: "maɪ pet ɪz ɑːn ðə soʊfə" + } + ], + exercises: [ + { + type: "fill_blank", + sentence: "The rabbit is ____ the box.", + options: ["on", "in", "under", "at"], + correct: "in", + explanation: "Use 'in' when something is inside a container" + } + ] + }, + + "modal-can": { + title: "Modal Verb 'Can' - 情态动词can", + explanation: "English uses 'can' to talk about abilities - things someone is able to do.", + rules: [ + "can + verb (base form) - expresses ability: can sing, can swim", + "can't = cannot - negative form: can't fly", + "Can + subject + verb? - question form: Can birds fly?" + ], + examples: [ + { + english: "She can sing.", + chinese: "她会唱歌。", + explanation: "Use 'can' + base verb to show ability", + pronunciation: "ʃi kæn sɪŋ" + }, + { + english: "I can't find Ding Ding!", + chinese: "我找不到丁丁!", + explanation: "Use 'can't' for negative ability", + pronunciation: "aɪ kænt faɪnd dɪŋ dɪŋ" + }, + { + english: "What can Ding Ding do?", + chinese: "丁丁能做什么?", + explanation: "Use 'can' in questions about ability", + pronunciation: "wʌt kæn dɪŋ dɪŋ du" + }, + { + english: "Dogs can run very fast.", + chinese: "狗跑得很快。", + explanation: "Use 'can' to describe general abilities", + pronunciation: "dɔːgz kæn rʌn veri fæst" + }, + { + english: "Fish can't walk on land.", + chinese: "鱼不能在陆地上走路。", + explanation: "Use 'can't' for impossible abilities", + pronunciation: "fɪʃ kænt wɔːk ɑːn lænd" + }, + { + english: "Can cats climb trees?", + chinese: "猫能爬树吗?", + explanation: "Use 'Can' at the start of yes/no questions", + pronunciation: "kæn kæts klaɪm triːz" + }, + { + english: "Birds can fly in the sky.", + chinese: "鸟能在天空中飞行。", + explanation: "Use 'can' for natural abilities", + pronunciation: "bɜːrdz kæn flaɪ ɪn ðə skaɪ" + }, + { + english: "I can take care of pets.", + chinese: "我能照顾宠物。", + explanation: "Use 'can' for learned skills", + pronunciation: "aɪ kæn teɪk ker ʌv pets" + }, + { + english: "Turtles can't run quickly.", + chinese: "乌龟不能跑得很快。", + explanation: "Use 'can't' for limited abilities", + pronunciation: "tɜːrtəlz kænt rʌn kwɪkli" + } + ], + exercises: [ + { + type: "fill_blank", + sentence: "Turtles ____ swim.", + options: ["can", "can't", "is", "are"], + correct: "can", + explanation: "Use 'can' to show natural ability" + } + ] + } + }, + + vocabulary: { + "unhappy": { + "user_language": "不开心的", + "type": "adjective", + "pronunciation": "ʌnhæpi" + }, + "umbrella": { + "user_language": "雨伞", + "type": "noun", + "pronunciation": "ʌmbrɛlə" + }, + "up": { + "user_language": "向上", + "type": "adverb", + "pronunciation": "ʌp" + }, + "under": { + "user_language": "在...下面", + "type": "preposition", + "pronunciation": "ʌndər" + }, + "uncle": { + "user_language": "叔叔", + "type": "noun", + "pronunciation": "ʌŋkəl" + }, + "violet": { + "user_language": "紫色的", + "type": "adjective", + "pronunciation": "vaɪələt" + }, + "van": { + "user_language": "面包车", + "type": "noun", + "pronunciation": "væn" + }, + "vet": { + "user_language": "兽医", + "type": "noun", + "pronunciation": "vɛt" + }, + "vest": { + "user_language": "背心", + "type": "noun", + "pronunciation": "vɛst" + }, + "violin": { + "user_language": "小提琴", + "type": "noun", + "pronunciation": "vaɪəlɪn" + }, + "tall": { + "user_language": "高的", + "type": "adjective", + "pronunciation": "tɔl" + }, + "turtle": { + "user_language": "海龟", + "type": "noun", + "pronunciation": "tɜrtəl" + }, + "tent": { + "user_language": "帐篷", + "type": "noun", + "pronunciation": "tɛnt" + }, + "tiger": { + "user_language": "老虎", + "type": "noun", + "pronunciation": "taɪgər" + }, + "teacher": { + "user_language": "老师", + "type": "noun", + "pronunciation": "titʃər" + }, + "dog": { + "user_language": "狗", + "type": "noun", + "pronunciation": "dɔg" + }, + "cat": { + "user_language": "猫", + "type": "noun", + "pronunciation": "kæt" + }, + "bird": { + "user_language": "鸟", + "type": "noun", + "pronunciation": "bɜrd" + }, + "rabbit": { + "user_language": "兔子", + "type": "noun", + "pronunciation": "ræbɪt" + }, + "hamster": { + "user_language": "仓鼠", + "type": "noun", + "pronunciation": "hæmstər" + }, + "sofa": { + "user_language": "沙发", + "type": "noun", + "pronunciation": "soʊfə" + }, + "table": { + "user_language": "桌子", + "type": "noun", + "pronunciation": "teɪbəl" + }, + "chair": { + "user_language": "椅子", + "type": "noun", + "pronunciation": "tʃɛr" + }, + "box": { + "user_language": "盒子", + "type": "noun", + "pronunciation": "bɑks" + }, + "cupboard": { + "user_language": "橱柜", + "type": "noun", + "pronunciation": "kʌbərd" + }, + "shelf": { + "user_language": "架子", + "type": "noun", + "pronunciation": "ʃɛlf" + } + }, + + story: { + title: "The Pet Adventure - 宠物历险记", + totalSentences: 25, + chapters: [ + { + title: "Chapter 1: Choosing a Pet - 第一章:选择宠物", + sentences: [ + { + id: 1, + original: "What is this?", + translation: "这是什么?", + words: [ + {word: "What", translation: "什么", type: "pronoun", pronunciation: "wʌt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "this", translation: "这个", type: "pronoun", pronunciation: "ðɪs"} + ] + }, + { + id: 2, + original: "It is a dog.", + translation: "这是一只狗。", + words: [ + {word: "It", translation: "它", type: "pronoun", pronunciation: "ɪt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一只", type: "article", pronunciation: "ə"}, + {word: "dog", translation: "狗", type: "noun", pronunciation: "dɔg"} + ] + }, + { + id: 3, + original: "What is that?", + translation: "那是什么?", + words: [ + {word: "What", translation: "什么", type: "pronoun", pronunciation: "wʌt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "that", translation: "那个", type: "pronoun", pronunciation: "ðæt"} + ] + }, + { + id: 4, + original: "It is a hamster.", + translation: "它是一只仓鼠。", + words: [ + {word: "It", translation: "它", type: "pronoun", pronunciation: "ɪt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一只", type: "article", pronunciation: "ə"}, + {word: "hamster", translation: "仓鼠", type: "noun", pronunciation: "hæmstər"} + ] + }, + { + id: 5, + original: "What are these?", + translation: "这些是什么?", + words: [ + {word: "What", translation: "什么", type: "pronoun", pronunciation: "wʌt"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "these", translation: "这些", type: "pronoun", pronunciation: "ðiz"} + ] + }, + { + id: 6, + original: "They are rabbits.", + translation: "它们是兔子。", + words: [ + {word: "They", translation: "它们", type: "pronoun", pronunciation: "ðeɪ"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "rabbits", translation: "兔子", type: "noun", pronunciation: "ræbɪts"} + ] + }, + { + id: 7, + original: "What are those?", + translation: "那些是什么?", + words: [ + {word: "What", translation: "什么", type: "pronoun", pronunciation: "wʌt"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "those", translation: "那些", type: "pronoun", pronunciation: "ðoʊz"} + ] + }, + { + id: 8, + original: "They are turtles.", + translation: "它们是海龟。", + words: [ + {word: "They", translation: "它们", type: "pronoun", pronunciation: "ðeɪ"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "turtles", translation: "海龟", type: "noun", pronunciation: "tɜrtəlz"} + ] + } + ] + }, + { + title: "Chapter 2: Dora's Pet Ding Ding - 第二章:多拉的宠物丁丁", + sentences: [ + { + id: 9, + original: "It is a new pet for you, Dora.", + translation: "多拉,这是给你的新宠物。", + words: [ + {word: "It", translation: "它", type: "pronoun", pronunciation: "ɪt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一个", type: "article", pronunciation: "ə"}, + {word: "new", translation: "新的", type: "adjective", pronunciation: "nu"}, + {word: "pet", translation: "宠物", type: "noun", pronunciation: "pɛt"}, + {word: "for", translation: "给", type: "preposition", pronunciation: "fɔr"}, + {word: "you", translation: "你", type: "pronoun", pronunciation: "ju"}, + {word: "Dora", translation: "多拉", type: "noun", pronunciation: "dɔrə"} + ] + }, + { + id: 10, + original: "Oh! It is a bird. Thank you very much.", + translation: "哦!是一只鸟。非常感谢。", + words: [ + {word: "Oh", translation: "哦", type: "interjection", pronunciation: "oʊ"}, + {word: "It", translation: "它", type: "pronoun", pronunciation: "ɪt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一只", type: "article", pronunciation: "ə"}, + {word: "bird", translation: "鸟", type: "noun", pronunciation: "bɜrd"}, + {word: "Thank", translation: "感谢", type: "verb", pronunciation: "θæŋk"}, + {word: "you", translation: "你", type: "pronoun", pronunciation: "ju"}, + {word: "very", translation: "非常", type: "adverb", pronunciation: "vɛri"}, + {word: "much", translation: "多", type: "adverb", pronunciation: "mʌtʃ"} + ] + }, + { + id: 11, + original: "This is my pet Ding Ding. She is a yellow bird. She can sing.", + translation: "这是我的宠物丁丁。她是一只黄色的鸟。她会唱歌。", + words: [ + {word: "This", translation: "这", type: "pronoun", pronunciation: "ðɪs"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "my", translation: "我的", type: "pronoun", pronunciation: "maɪ"}, + {word: "pet", translation: "宠物", type: "noun", pronunciation: "pɛt"}, + {word: "She", translation: "她", type: "pronoun", pronunciation: "ʃi"}, + {word: "yellow", translation: "黄色的", type: "adjective", pronunciation: "jɛloʊ"}, + {word: "can", translation: "能", type: "modal", pronunciation: "kæn"}, + {word: "sing", translation: "唱歌", type: "verb", pronunciation: "sɪŋ"} + ] + }, + { + id: 12, + original: "I can't find Ding Ding!", + translation: "我找不到丁丁了!", + words: [ + {word: "I", translation: "我", type: "pronoun", pronunciation: "aɪ"}, + {word: "can't", translation: "不能", type: "modal", pronunciation: "kænt"}, + {word: "find", translation: "找到", type: "verb", pronunciation: "faɪnd"} + ] + }, + { + id: 13, + original: "What are those? They are birds. One is yellow. One is blue.", + translation: "那些是什么?它们是鸟。一只是黄色的。一只是蓝色的。", + words: [ + {word: "What", translation: "什么", type: "pronoun", pronunciation: "wʌt"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "those", translation: "那些", type: "pronoun", pronunciation: "ðoʊz"}, + {word: "They", translation: "它们", type: "pronoun", pronunciation: "ðeɪ"}, + {word: "birds", translation: "鸟", type: "noun", pronunciation: "bɜrdz"}, + {word: "One", translation: "一只", type: "number", pronunciation: "wʌn"}, + {word: "yellow", translation: "黄色的", type: "adjective", pronunciation: "jɛloʊ"}, + {word: "blue", translation: "蓝色的", type: "adjective", pronunciation: "blu"} + ] + }, + { + id: 14, + original: "Now I have two pets!", + translation: "现在我有两只宠物了!", + words: [ + {word: "Now", translation: "现在", type: "adverb", pronunciation: "naʊ"}, + {word: "I", translation: "我", type: "pronoun", pronunciation: "aɪ"}, + {word: "have", translation: "有", type: "verb", pronunciation: "hæv"}, + {word: "two", translation: "两", type: "number", pronunciation: "tu"}, + {word: "pets", translation: "宠物", type: "noun", pronunciation: "pɛts"} + ] + } + ] + }, + { + title: "Chapter 3: Where Are the Pets? - 第三章:宠物在哪里?", + sentences: [ + { + id: 15, + original: "Where is the cat?", + translation: "猫在哪里?", + words: [ + {word: "Where", translation: "哪里", type: "adverb", pronunciation: "wɛr"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "the", translation: "这只", type: "article", pronunciation: "ðə"}, + {word: "cat", translation: "猫", type: "noun", pronunciation: "kæt"} + ] + }, + { + id: 16, + original: "It is on the chair.", + translation: "它在椅子上。", + words: [ + {word: "It", translation: "它", type: "pronoun", pronunciation: "ɪt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "on", translation: "在...上", type: "preposition", pronunciation: "ɑn"}, + {word: "the", translation: "这个", type: "article", pronunciation: "ðə"}, + {word: "chair", translation: "椅子", type: "noun", pronunciation: "tʃɛr"} + ] + }, + { + id: 17, + original: "Where are the turtles?", + translation: "海龟在哪里?", + words: [ + {word: "Where", translation: "哪里", type: "adverb", pronunciation: "wɛr"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "the", translation: "这些", type: "article", pronunciation: "ðə"}, + {word: "turtles", translation: "海龟", type: "noun", pronunciation: "tɜrtəlz"} + ] + } + ] + } + ] + }, + + // === GRAMMAR-BASED FILL IN THE BLANKS === + fillInBlanks: [ + { + sentence: "_____ is a dog.", + options: ["This", "These", "That", "Those"], + correctAnswer: "This", + explanation: "Use 'This' for one thing close to you", + grammarFocus: "demonstrative-pronouns" + }, + { + sentence: "_____ are turtles.", + options: ["This", "These", "That", "Those"], + correctAnswer: "These", + explanation: "Use 'These' for multiple things close to you", + grammarFocus: "demonstrative-pronouns" + }, + { + sentence: "They _____ birds.", + options: ["is", "are", "am", "be"], + correctAnswer: "are", + explanation: "Use 'are' with plural subjects", + grammarFocus: "be-verb-usage" + }, + { + sentence: "The cat is _____ the chair.", + options: ["on", "in", "under", "at"], + correctAnswer: "on", + explanation: "Use 'on' when something is on top of something else", + grammarFocus: "prepositions-of-place" + }, + { + sentence: "She _____ sing.", + options: ["can", "can't", "is", "are"], + correctAnswer: "can", + explanation: "Use 'can' to show ability", + grammarFocus: "modal-can" + }, + { + sentence: "I _____ find my pet.", + options: ["can", "can't", "is", "are"], + correctAnswer: "can't", + explanation: "Use 'can't' for negative ability", + grammarFocus: "modal-can" + } + ], + + // === GRAMMAR CORRECTION EXERCISES === + corrections: [ + { + incorrect: "This are dogs.", + correct: "These are dogs.", + explanation: "Use 'These' for multiple things, not 'This'", + grammarFocus: "demonstrative-pronouns" + }, + { + incorrect: "They is cats.", + correct: "They are cats.", + explanation: "Use 'are' with plural subjects like 'they'", + grammarFocus: "be-verb-usage" + }, + { + incorrect: "The bird is in the chair.", + correct: "The bird is on the chair.", + explanation: "Use 'on' when something is on top of furniture", + grammarFocus: "prepositions-of-place" + }, + { + incorrect: "She can sings.", + correct: "She can sing.", + explanation: "After 'can', use the base form of the verb", + grammarFocus: "modal-can" + } + ], + + // === ADDITIONAL READING STORIES === + additionalStories: [ + { + title: "My Uncle's Pets - 我叔叔的宠物", + totalSentences: 13, + chapters: [ + { + title: "Chapter 1: The Vet Uncle - 兽医叔叔", + sentences: [ + { + id: 1, + original: "My uncle is tall.", + translation: "我的叔叔很高。", + words: [ + {word: "My", translation: "我的", type: "pronoun", pronunciation: "maɪ"}, + {word: "uncle", translation: "叔叔", type: "noun", pronunciation: "ʌŋkəl"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "tall", translation: "高的", type: "adjective", pronunciation: "tɔl"} + ] + }, + { + id: 2, + original: "He is a vet.", + translation: "他是一名兽医。", + words: [ + {word: "He", translation: "他", type: "pronoun", pronunciation: "hi"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一名", type: "article", pronunciation: "ə"}, + {word: "vet", translation: "兽医", type: "noun", pronunciation: "vɛt"} + ] + }, + { + id: 3, + original: "He can take care of pets.", + translation: "他会照顾宠物。", + words: [ + {word: "He", translation: "他", type: "pronoun", pronunciation: "hi"}, + {word: "can", translation: "会", type: "modal", pronunciation: "kæn"}, + {word: "take care", translation: "照顾", type: "verb", pronunciation: "teɪk ker"}, + {word: "of", translation: "的", type: "preposition", pronunciation: "ʌv"}, + {word: "pets", translation: "宠物", type: "noun", pronunciation: "pets"} + ] + }, + { + id: 4, + original: "This is his house.", + translation: "这是他的房子。", + words: [ + {word: "This", translation: "这", type: "pronoun", pronunciation: "ðɪs"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "his", translation: "他的", type: "pronoun", pronunciation: "hɪz"}, + {word: "house", translation: "房子", type: "noun", pronunciation: "haʊs"} + ] + }, + { + id: 5, + original: "What are these?", + translation: "这些是什么?", + words: [ + {word: "What", translation: "什么", type: "pronoun", pronunciation: "wʌt"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "these", translation: "这些", type: "pronoun", pronunciation: "ðiːz"} + ] + }, + { + id: 6, + original: "These are his pets.", + translation: "这些是他的宠物。", + words: [ + {word: "These", translation: "这些", type: "pronoun", pronunciation: "ðiːz"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "his", translation: "他的", type: "pronoun", pronunciation: "hɪz"}, + {word: "pets", translation: "宠物", type: "noun", pronunciation: "pets"} + ] + }, + { + id: 7, + original: "That is a dog under the table.", + translation: "那是桌子下面的一只狗。", + words: [ + {word: "That", translation: "那", type: "pronoun", pronunciation: "ðæt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一只", type: "article", pronunciation: "ə"}, + {word: "dog", translation: "狗", type: "noun", pronunciation: "dɔg"}, + {word: "under", translation: "在...下面", type: "preposition", pronunciation: "ʌndər"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "table", translation: "桌子", type: "noun", pronunciation: "teɪbəl"} + ] + }, + { + id: 8, + original: "This cat is on the chair.", + translation: "这只猫在椅子上。", + words: [ + {word: "This", translation: "这", type: "pronoun", pronunciation: "ðɪs"}, + {word: "cat", translation: "猫", type: "noun", pronunciation: "kæt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "on", translation: "在...上面", type: "preposition", pronunciation: "ɑn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "chair", translation: "椅子", type: "noun", pronunciation: "tʃɛr"} + ] + }, + { + id: 9, + original: "Those rabbits are in the box.", + translation: "那些兔子在盒子里。", + words: [ + {word: "Those", translation: "那些", type: "pronoun", pronunciation: "ðoʊz"}, + {word: "rabbits", translation: "兔子", type: "noun", pronunciation: "ræbɪts"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "in", translation: "在...里面", type: "preposition", pronunciation: "ɪn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "box", translation: "盒子", type: "noun", pronunciation: "bɑks"} + ] + }, + { + id: 10, + original: "The turtle is in the cupboard.", + translation: "乌龟在橱柜里。", + words: [ + {word: "The", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "turtle", translation: "乌龟", type: "noun", pronunciation: "tɜrtəl"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "in", translation: "在...里面", type: "preposition", pronunciation: "ɪn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "cupboard", translation: "橱柜", type: "noun", pronunciation: "kʌbərd"} + ] + }, + { + id: 11, + original: "Where is the bird?", + translation: "鸟在哪里?", + words: [ + {word: "Where", translation: "哪里", type: "adverb", pronunciation: "wer"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "bird", translation: "鸟", type: "noun", pronunciation: "bɜrd"} + ] + }, + { + id: 12, + original: "The bird is up on the shelf.", + translation: "鸟在架子上面。", + words: [ + {word: "The", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "bird", translation: "鸟", type: "noun", pronunciation: "bɜrd"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "up", translation: "向上", type: "adverb", pronunciation: "ʌp"}, + {word: "on", translation: "在...上面", type: "preposition", pronunciation: "ɑn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "shelf", translation: "架子", type: "noun", pronunciation: "ʃɛlf"} + ] + }, + { + id: 13, + original: "My uncle can help unhappy pets.", + translation: "我叔叔能帮助不开心的宠物。", + words: [ + {word: "My", translation: "我的", type: "pronoun", pronunciation: "maɪ"}, + {word: "uncle", translation: "叔叔", type: "noun", pronunciation: "ʌŋkəl"}, + {word: "can", translation: "能", type: "modal", pronunciation: "kæn"}, + {word: "help", translation: "帮助", type: "verb", pronunciation: "hɛlp"}, + {word: "unhappy", translation: "不开心的", type: "adjective", pronunciation: "ʌnhæpi"}, + {word: "pets", translation: "宠物", type: "noun", pronunciation: "pets"} + ] + } + ] + } + ] + }, + { + title: "The Violet Van Adventure - 紫色面包车冒险记", + totalSentences: 15, + chapters: [ + { + title: "Chapter 1: The Magic Van - 神奇的面包车", + sentences: [ + { + id: 1, + original: "This is a violet van.", + translation: "这是一辆紫色的面包车。", + words: [ + {word: "This", translation: "这", type: "pronoun", pronunciation: "ðɪs"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一辆", type: "article", pronunciation: "ə"}, + {word: "violet", translation: "紫色的", type: "adjective", pronunciation: "vaɪələt"}, + {word: "van", translation: "面包车", type: "noun", pronunciation: "væn"} + ] + }, + { + id: 2, + original: "My teacher can drive this van.", + translation: "我的老师会开这辆面包车。", + words: [ + {word: "My", translation: "我的", type: "pronoun", pronunciation: "maɪ"}, + {word: "teacher", translation: "老师", type: "noun", pronunciation: "titʃər"}, + {word: "can", translation: "会", type: "modal", pronunciation: "kæn"}, + {word: "drive", translation: "开", type: "verb", pronunciation: "draɪv"}, + {word: "this", translation: "这辆", type: "pronoun", pronunciation: "ðɪs"}, + {word: "van", translation: "面包车", type: "noun", pronunciation: "væn"} + ] + }, + { + id: 3, + original: "What are those in the van?", + translation: "面包车里的那些是什么?", + words: [ + {word: "What", translation: "什么", type: "pronoun", pronunciation: "wʌt"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "those", translation: "那些", type: "pronoun", pronunciation: "ðoʊz"}, + {word: "in", translation: "在...里面", type: "preposition", pronunciation: "ɪn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "van", translation: "面包车", type: "noun", pronunciation: "væn"} + ] + }, + { + id: 4, + original: "Those are pets!", + translation: "那些是宠物!", + words: [ + {word: "Those", translation: "那些", type: "pronoun", pronunciation: "ðoʊz"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "pets", translation: "宠物", type: "noun", pronunciation: "pets"} + ] + }, + { + id: 5, + original: "There is a hamster in a tent.", + translation: "有一只仓鼠在帐篷里。", + words: [ + {word: "There", translation: "有", type: "adverb", pronunciation: "ðer"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "a", translation: "一只", type: "article", pronunciation: "ə"}, + {word: "hamster", translation: "仓鼠", type: "noun", pronunciation: "hæmstər"}, + {word: "in", translation: "在...里面", type: "preposition", pronunciation: "ɪn"}, + {word: "a", translation: "一个", type: "article", pronunciation: "ə"}, + {word: "tent", translation: "帐篷", type: "noun", pronunciation: "tɛnt"} + ] + }, + { + id: 6, + original: "That tiger is tall.", + translation: "那只老虎很高。", + words: [ + {word: "That", translation: "那只", type: "pronoun", pronunciation: "ðæt"}, + {word: "tiger", translation: "老虎", type: "noun", pronunciation: "taɪgər"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "tall", translation: "高的", type: "adjective", pronunciation: "tɔl"} + ] + }, + { + id: 7, + original: "These turtles are under the umbrella.", + translation: "这些乌龟在雨伞下面。", + words: [ + {word: "These", translation: "这些", type: "pronoun", pronunciation: "ðiːz"}, + {word: "turtles", translation: "乌龟", type: "noun", pronunciation: "tɜrtəlz"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "under", translation: "在...下面", type: "preposition", pronunciation: "ʌndər"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "umbrella", translation: "雨伞", type: "noun", pronunciation: "ʌmbrɛlə"} + ] + }, + { + id: 8, + original: "The bird is on the violin.", + translation: "鸟在小提琴上。", + words: [ + {word: "The", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "bird", translation: "鸟", type: "noun", pronunciation: "bɜrd"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "on", translation: "在...上面", type: "preposition", pronunciation: "ɑn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "violin", translation: "小提琴", type: "noun", pronunciation: "vaɪəlɪn"} + ] + }, + { + id: 9, + original: "Where are the rabbits?", + translation: "兔子在哪里?", + words: [ + {word: "Where", translation: "哪里", type: "adverb", pronunciation: "wer"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "rabbits", translation: "兔子", type: "noun", pronunciation: "ræbɪts"} + ] + }, + { + id: 10, + original: "They are in the violet vest.", + translation: "它们在紫色背心里。", + words: [ + {word: "They", translation: "它们", type: "pronoun", pronunciation: "ðeɪ"}, + {word: "are", translation: "是", type: "verb", pronunciation: "ɑr"}, + {word: "in", translation: "在...里面", type: "preposition", pronunciation: "ɪn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "violet", translation: "紫色的", type: "adjective", pronunciation: "vaɪələt"}, + {word: "vest", translation: "背心", type: "noun", pronunciation: "vɛst"} + ] + }, + { + id: 11, + original: "My teacher is unhappy.", + translation: "我的老师不开心。", + words: [ + {word: "My", translation: "我的", type: "pronoun", pronunciation: "maɪ"}, + {word: "teacher", translation: "老师", type: "noun", pronunciation: "titʃər"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "unhappy", translation: "不开心的", type: "adjective", pronunciation: "ʌnhæpi"} + ] + }, + { + id: 12, + original: "She can't find her cat.", + translation: "她找不到她的猫。", + words: [ + {word: "She", translation: "她", type: "pronoun", pronunciation: "ʃi"}, + {word: "can't", translation: "不能", type: "modal", pronunciation: "kænt"}, + {word: "find", translation: "找到", type: "verb", pronunciation: "faɪnd"}, + {word: "her", translation: "她的", type: "pronoun", pronunciation: "hər"}, + {word: "cat", translation: "猫", type: "noun", pronunciation: "kæt"} + ] + }, + { + id: 13, + original: "Where is my cat?", + translation: "我的猫在哪里?", + words: [ + {word: "Where", translation: "哪里", type: "adverb", pronunciation: "wer"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "my", translation: "我的", type: "pronoun", pronunciation: "maɪ"}, + {word: "cat", translation: "猫", type: "noun", pronunciation: "kæt"} + ] + }, + { + id: 14, + original: "The cat is up on the van!", + translation: "猫在面包车上面!", + words: [ + {word: "The", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "cat", translation: "猫", type: "noun", pronunciation: "kæt"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "up", translation: "向上", type: "adverb", pronunciation: "ʌp"}, + {word: "on", translation: "在...上面", type: "preposition", pronunciation: "ɑn"}, + {word: "the", translation: "这", type: "article", pronunciation: "ðə"}, + {word: "van", translation: "面包车", type: "noun", pronunciation: "væn"} + ] + }, + { + id: 15, + original: "Now my teacher is happy.", + translation: "现在我的老师开心了。", + words: [ + {word: "Now", translation: "现在", type: "adverb", pronunciation: "naʊ"}, + {word: "my", translation: "我的", type: "pronoun", pronunciation: "maɪ"}, + {word: "teacher", translation: "老师", type: "noun", pronunciation: "titʃər"}, + {word: "is", translation: "是", type: "verb", pronunciation: "ɪz"}, + {word: "happy", translation: "开心的", type: "adjective", pronunciation: "hæpi"} + ] + } + ] + } + ] + } + ] +}; \ No newline at end of file diff --git a/js/content/chinese-long-story.js b/js/content/chinese-long-story.js index 496b276..f9c25a9 100644 --- a/js/content/chinese-long-story.js +++ b/js/content/chinese-long-story.js @@ -10,6 +10,173 @@ window.ContentModules.ChineseLongStory = { language: "zh-CN", totalWords: 1200, + // === GRAMMAR LESSONS SYSTEM === + grammar: { + "chinese-particles": { + title: "Chinese Grammar Particles - 语法助词", + explanation: "Chinese particles are essential grammatical markers that show relationships between words and add meaning to sentences.", + rules: [ + "的 (de) - Possessive marker and adjective connector", + "在 (zài) - Location and time marker 'at/in/on'", + "里 (lǐ) - Inside/within location marker", + "中 (zhōng) - In/among/middle position marker" + ], + examples: [ + { + chinese: "老人的故事", + english: "the old man's story", + explanation: "的 shows possession - 'old man's'", + pronunciation: "lǎo rén de gù shì" + }, + { + chinese: "在山里", + english: "in the mountains", + explanation: "在...里 shows location 'in/inside'", + pronunciation: "zài shān lǐ" + }, + { + chinese: "村庄中的龙", + english: "the dragon in the village", + explanation: "中 shows position 'in/among'", + pronunciation: "cūn zhuāng zhōng de lóng" + } + ], + exercises: [ + { + type: "fill_blank", + sentence: "这是小明___书包", + options: ["的", "在", "里", "中"], + correct: "的", + explanation: "Use 的 for possession - 'Xiaoming's backpack'" + }, + { + type: "translation", + chinese: "龙在水里", + english: "The dragon is in the water", + focus: "Location marker 在...里" + } + ] + }, + + "chinese-word-order": { + title: "Chinese Word Order - 中文语序", + explanation: "Chinese follows Subject-Verb-Object order like English, but with important differences for time, place, and manner.", + rules: [ + "Basic pattern: Subject + Time + Place + Verb + Object", + "Time comes before place: '昨天在家' (yesterday at home)", + "Manner often comes before verb: '慢慢地走' (slowly walk)", + "Place words use specific markers: 在 (at), 里 (in), 上 (on)" + ], + examples: [ + { + chinese: "老人昨天在村庄里讲故事", + english: "The old man told stories in the village yesterday", + breakdown: "老人(S) + 昨天(Time) + 在村庄里(Place) + 讲(V) + 故事(O)", + pronunciation: "lǎo rén zuó tiān zài cūn zhuāng lǐ jiǎng gù shì" + }, + { + chinese: "龙慢慢地飞向山顶", + english: "The dragon slowly flew toward the mountain peak", + breakdown: "龙(S) + 慢慢地(Manner) + 飞向(V) + 山顶(O)", + pronunciation: "lóng màn màn de fēi xiàng shān dǐng" + } + ], + exercises: [ + { + type: "word_order", + scrambled: ["在", "昨天", "老人", "家里", "休息"], + correct: ["老人", "昨天", "在", "家里", "休息"], + english: "The old man rested at home yesterday" + } + ] + }, + + "measure-words": { + title: "Chinese Measure Words - 量词", + explanation: "Chinese uses specific measure words (classifiers) between numbers and nouns, similar to 'a piece of paper' in English.", + rules: [ + "Pattern: Number + Measure Word + Noun", + "个 (gè) - Most common, used for people and general objects", + "条 (tiáo) - For long, thin things like dragons, rivers, roads", + "座 (zuò) - For mountains, buildings, bridges", + "本 (běn) - For books, magazines" + ], + examples: [ + { + chinese: "一条龙", + english: "one dragon", + explanation: "条 is used for long creatures like dragons", + pronunciation: "yì tiáo lóng" + }, + { + chinese: "三座山", + english: "three mountains", + explanation: "座 is used for large structures like mountains", + pronunciation: "sān zuò shān" + }, + { + chinese: "两个老人", + english: "two old people", + explanation: "个 is the general classifier for people", + pronunciation: "liǎng gè lǎo rén" + } + ], + exercises: [ + { + type: "classifier_choice", + chinese: "五___珠子", + options: ["个", "条", "座", "本"], + correct: "个", + explanation: "珠子 (pearls) use 个 as the general classifier" + } + ] + }, + + "chinese-tones": { + title: "Chinese Tones - 声调", + explanation: "Mandarin Chinese has 4 main tones that change word meaning. Tone is crucial for communication.", + rules: [ + "First tone (ā) - High, flat tone", + "Second tone (á) - Rising tone, like asking a question", + "Third tone (ǎ) - Falling then rising, dip tone", + "Fourth tone (à) - Sharp falling tone", + "Neutral tone (a) - Light, quick, no specific pitch" + ], + examples: [ + { + chinese: "妈 (mā) - mother", + tone: "First tone - high and flat", + pronunciation: "mā" + }, + { + chinese: "麻 (má) - hemp/numb", + tone: "Second tone - rising", + pronunciation: "má" + }, + { + chinese: "马 (mǎ) - horse", + tone: "Third tone - dip", + pronunciation: "mǎ" + }, + { + chinese: "骂 (mà) - to scold", + tone: "Fourth tone - falling", + pronunciation: "mà" + } + ], + exercises: [ + { + type: "tone_identification", + word: "山", + pronunciation: "shān", + tone_options: ["First", "Second", "Third", "Fourth"], + correct: "First", + explanation: "山 (shān) uses first tone - high and flat" + } + ] + } + }, + vocabulary: { "龙": { "user_language": "dragon", @@ -336,5 +503,73 @@ window.ContentModules.ChineseLongStory = { ] } ] - } + }, + + // === GRAMMAR-BASED FILL IN THE BLANKS === + fillInBlanks: [ + { + sentence: "这是老人___故事", + options: ["的", "在", "里", "中"], + correctAnswer: "的", + explanation: "Use 的 to show possession - 'the old man's story'", + grammarFocus: "chinese-particles" + }, + { + sentence: "龙___水里游泳", + options: ["的", "在", "里", "中"], + correctAnswer: "在", + explanation: "Use 在 to show location - 'the dragon is swimming in the water'", + grammarFocus: "chinese-particles" + }, + { + sentence: "一___龙飞向天空", + options: ["个", "条", "座", "本"], + correctAnswer: "条", + explanation: "Use 条 for long creatures like dragons", + grammarFocus: "measure-words" + }, + { + sentence: "三___山很高", + options: ["个", "条", "座", "本"], + correctAnswer: "座", + explanation: "Use 座 for large structures like mountains", + grammarFocus: "measure-words" + }, + { + sentence: "老人昨天___村庄里讲故事", + options: ["在", "的", "里", "中"], + correctAnswer: "在", + explanation: "Word order: Subject + Time + Place (在 + location) + Verb + Object", + grammarFocus: "chinese-word-order" + }, + { + sentence: "山___读音是第几声?", + options: ["第一声", "第二声", "第三声", "第四声"], + correctAnswer: "第一声", + explanation: "山 (shān) uses first tone - high and flat", + grammarFocus: "chinese-tones" + } + ], + + // === GRAMMAR CORRECTION EXERCISES === + corrections: [ + { + incorrect: "龙里水游泳", + correct: "龙在水里游泳", + explanation: "Need 在 (at/in) before location marker 里", + grammarFocus: "chinese-particles" + }, + { + incorrect: "老人在村庄昨天讲故事", + correct: "老人昨天在村庄里讲故事", + explanation: "Time (昨天) must come before place (在村庄里)", + grammarFocus: "chinese-word-order" + }, + { + incorrect: "五龙飞在天空", + correct: "五条龙飞在天空中", + explanation: "Need measure word 条 for dragons and location marker 中", + grammarFocus: "measure-words" + } + ] }; \ No newline at end of file diff --git a/js/content/french-beginner-story.js b/js/content/french-beginner-story.js new file mode 100644 index 0000000..3805de5 --- /dev/null +++ b/js/content/french-beginner-story.js @@ -0,0 +1,524 @@ +// === CHINESE BEGINNER STORY === +// Histoire chinoise pour débutants+ avec traduction française et prononciation pinyin + +window.ContentModules = window.ContentModules || {}; + +window.ContentModules.FrenchBeginnerStory = { + id: "french-beginner-story", + name: "Le Jardin Magique - The Magic Garden", + description: "Simple French story for English speakers", + difficulty: "beginner-plus", + language: "fr-FR", // Target language = français + userLanguage: "en-US", // User language = anglais + totalWords: 15, + type: "story_course", + + // === GRAMMAIRE DE BASE === + grammar: { + "basic-sentence-structure": { + title: "French Basic Sentence Structure - Structure de Phrase Française", + explanation: "French follows Subject-Verb-Object order like English, but with some important differences.", + mainRules: [ + "Subject + Verb + Object (Je mange une pomme - I eat an apple)", + "French verbs conjugate according to the subject (je mange, tu manges, il mange)", + "Adjectives usually come after the noun (une fleur rouge - a red flower)", + "French nouns have gender (masculine/feminine)" + ], + examples: [ + { + french: "J'aime les fleurs", + english: "I love flowers", + pronunciation: "ʒɛm le flœʁ", + explanation: "Basic structure: Je(I) + aime(love) + les fleurs(flowers)" + }, + { + french: "Le jardin est très beau", + english: "The garden is very beautiful", + pronunciation: "lə ʒaʁdɛ̃ ɛ tʁɛ bo", + explanation: "être(to be) + adjective structure" + } + ], + detailedExplanation: { + "subject-verb-object": { + title: "Ordre Sujet-Verbe-Objet", + explanation: "Le chinois suit la même logique que le français pour l'ordre des mots de base.", + pattern: "Sujet + Verbe + Objet", + examples: [ + { + chinese: "小猫吃鱼", + english: "Le petit chat mange du poisson", + pronunciation: "xiǎo māo chī yú", + breakdown: "小猫(petit chat) + 吃(manger) + 鱼(poisson)" + }, + { + chinese: "我看书", + english: "Je lis un livre", + pronunciation: "wǒ kàn shū", + breakdown: "我(je) + 看(regarder/lire) + 书(livre)" + } + ] + }, + "adjectives": { + title: "Utilisation des Adjectifs", + explanation: "Les adjectifs peuvent être utilisés directement après 很 (très) sans verbe 'être'.", + pattern: "Sujet + 很 + Adjectif", + examples: [ + { + chinese: "花很红", + english: "La fleur est très rouge", + pronunciation: "huā hěn hóng", + breakdown: "花(fleur) + 很(très) + 红(rouge)" + } + ] + } + }, + commonMistakes: [ + { + mistake: "Conjuguer les verbes", + wrong: "我吃了,你吃着,他吃的", + correct: "我吃,你吃,他吃", + explanation: "Les verbes chinois ne se conjuguent pas selon la personne" + }, + { + mistake: "Oublier 很 avec les adjectifs", + wrong: "花园美", + correct: "花园很美", + explanation: "Utiliser 很 devant les adjectifs pour une phrase complète" + } + ], + practicePoints: [ + "Commencez par des phrases simples : Sujet + Verbe + Objet", + "Utilisez 很 + adjectif pour décrire", + "Pas de conjugaison = plus simple !", + "Écoutez la mélodie de la langue chinoise" + ] + } + }, + + // === VOCABULAIRE FRANÇAIS POUR APPRENANTS CHINOIS (15+ mots) === + vocabulary: { + "fleur": { + user_language: "flower", + type: "noun", + pronunciation: "flœʁ", + gender: "feminine" + }, + "jardin": { + user_language: "garden", + type: "noun", + pronunciation: "ʒaʁdɛ̃", + gender: "masculine" + }, + "arbre": { + user_language: "tree", + type: "noun", + pronunciation: "aʁbʁ", + gender: "masculine" + }, + "petit": { + user_language: "small/little", + type: "adjective", + pronunciation: "pəti" + }, + "grand": { + user_language: "big/large", + type: "adjective", + pronunciation: "ɡʁɑ̃" + }, + "beau": { + user_language: "beautiful/handsome", + type: "adjective", + pronunciation: "bo" + }, + "rouge": { + user_language: "red", + type: "adjective", + pronunciation: "ʁuʒ" + }, + "vert": { + user_language: "green", + type: "adjective", + pronunciation: "vɛʁ" + }, + "je": { + user_language: "I", + type: "pronoun", + pronunciation: "ʒə" + }, + "tu": { + user_language: "you", + type: "pronoun", + pronunciation: "ty" + }, + "regarder": { + user_language: "to look/watch", + type: "verb", + pronunciation: "ʁəɡaʁde" + }, + "aimer": { + user_language: "to love/like", + type: "verb", + pronunciation: "ɛme" + }, + "très": { + user_language: "very", + type: "adverb", + pronunciation: "tʁɛ" + }, + "avoir": { + user_language: "to have", + type: "verb", + pronunciation: "avwaʁ" + }, + "chat": { + user_language: "cat", + type: "noun", + pronunciation: "ʃa", + gender: "masculine" + }, + "mignon": { + user_language: "cute/adorable", + type: "adjective", + pronunciation: "miɲɔ̃" + }, + "maintenant": { + user_language: "now", + type: "adverb", + pronunciation: "mɛ̃tnɑ̃" + } + }, + + // === STRUCTURE PAR LETTRES POUR LETTER DISCOVERY === + letters: { + "A": [ + { + word: "arbre", + translation: "tree", + pronunciation: "aʁbʁ", + type: "noun", + gender: "masculine" + }, + { + word: "aimer", + translation: "to love/like", + pronunciation: "ɛme", + type: "verb" + }, + { + word: "avoir", + translation: "to have", + pronunciation: "avwaʁ", + type: "verb" + } + ], + "B": [ + { + word: "beau", + translation: "beautiful/handsome", + pronunciation: "bo", + type: "adjective" + }, + { + word: "beaucoup", + translation: "a lot/much", + pronunciation: "boku", + type: "adverb" + } + ], + "C": [ + { + word: "chat", + translation: "cat", + pronunciation: "ʃa", + type: "noun", + gender: "masculine" + } + ], + "F": [ + { + word: "fleur", + translation: "flower", + pronunciation: "flœʁ", + type: "noun", + gender: "feminine" + } + ], + "G": [ + { + word: "grand", + translation: "big/large", + pronunciation: "ɡʁɑ̃", + type: "adjective" + } + ], + "J": [ + { + word: "jardin", + translation: "garden", + pronunciation: "ʒaʁdɛ̃", + type: "noun", + gender: "masculine" + }, + { + word: "je", + translation: "I", + pronunciation: "ʒə", + type: "pronoun" + } + ], + "M": [ + { + word: "mignon", + translation: "cute/adorable", + pronunciation: "miɲɔ̃", + type: "adjective" + }, + { + word: "maintenant", + translation: "now", + pronunciation: "mɛ̃tnɑ̃", + type: "adverb" + } + ], + "P": [ + { + word: "petit", + translation: "small/little", + pronunciation: "pəti", + type: "adjective" + } + ], + "R": [ + { + word: "rouge", + translation: "red", + pronunciation: "ʁuʒ", + type: "adjective" + }, + { + word: "regarder", + translation: "to look/watch", + pronunciation: "ʁəɡaʁde", + type: "verb" + } + ], + "T": [ + { + word: "tu", + translation: "you", + pronunciation: "ty", + type: "pronoun" + }, + { + word: "très", + translation: "very", + pronunciation: "tʁɛ", + type: "adverb" + } + ], + "V": [ + { + word: "vert", + translation: "green", + pronunciation: "vɛʁ", + type: "adjective" + } + ] + }, + + // === HISTOIRE SIMPLE === + story: { + title: "Le Jardin Magique - 魔法花园", + totalSentences: 8, + chapters: [ + { + title: "第一章:美丽的花园 (Chapitre 1: Le Beau Jardin)", + sentences: [ + { + id: 1, + original: "J'ai un petit jardin.", + translation: "I have a small garden.", + words: [ + {word: "J'", translation: "I", type: "pronoun", pronunciation: "ʒ"}, + {word: "ai", translation: "have", type: "verb", pronunciation: "e"}, + {word: "un", translation: "a", type: "article", pronunciation: "œ̃"}, + {word: "petit", translation: "small", type: "adjective", pronunciation: "pəti"}, + {word: "jardin", translation: "garden", type: "noun", pronunciation: "ʒaʁdɛ̃"} + ] + }, + { + id: 2, + original: "Dans le jardin, il y a beaucoup de belles fleurs.", + translation: "花园里有很多美丽的花。", + words: [ + {word: "Dans", translation: "在", type: "preposition", pronunciation: "dɑ̃"}, + {word: "le", translation: "这个", type: "article", pronunciation: "lə"}, + {word: "jardin", translation: "花园", type: "noun", pronunciation: "ʒaʁdɛ̃"}, + {word: "il y a", translation: "有", type: "verb", pronunciation: "il i a"}, + {word: "beaucoup", translation: "很多", type: "adverb", pronunciation: "boku"}, + {word: "de", translation: "的", type: "preposition", pronunciation: "də"}, + {word: "belles", translation: "美丽的", type: "adjective", pronunciation: "bɛl"}, + {word: "fleurs", translation: "花", type: "noun", pronunciation: "flœʁ"} + ] + }, + { + id: 3, + original: "Il y a des fleurs rouges et des arbres verts.", + translation: "有红花和绿树。", + words: [ + {word: "Il y a", translation: "有", type: "verb", pronunciation: "il i a"}, + {word: "des", translation: "一些", type: "article", pronunciation: "de"}, + {word: "fleurs", translation: "花", type: "noun", pronunciation: "flœʁ"}, + {word: "rouges", translation: "红色的", type: "adjective", pronunciation: "ʁuʒ"}, + {word: "et", translation: "和", type: "conjunction", pronunciation: "e"}, + {word: "des", translation: "一些", type: "article", pronunciation: "de"}, + {word: "arbres", translation: "树", type: "noun", pronunciation: "aʁbʁ"}, + {word: "verts", translation: "绿色的", type: "adjective", pronunciation: "vɛʁ"} + ] + }, + { + id: 4, + original: "J'aime beaucoup mon jardin.", + translation: "我很喜欢我的花园。", + words: [ + {word: "J'", translation: "我", type: "pronoun", pronunciation: "ʒ"}, + {word: "aime", translation: "喜欢", type: "verb", pronunciation: "ɛm"}, + {word: "beaucoup", translation: "很", type: "adverb", pronunciation: "boku"}, + {word: "mon", translation: "我的", type: "pronoun", pronunciation: "mɔ̃"}, + {word: "jardin", translation: "花园", type: "noun", pronunciation: "ʒaʁdɛ̃"} + ] + } + ] + }, + { + title: "第二章:小猫来了 (Chapitre 2: Le Petit Chat Arrive)", + sentences: [ + { + id: 5, + original: "一天,一只小猫来到花园。", + translation: "Un jour, un petit chat est venu dans le jardin.", + words: [ + {word: "一天", translation: "un jour", type: "noun", pronunciation: "yì tiān"}, + {word: "一只", translation: "un (classificateur)", type: "number", pronunciation: "yì zhī"}, + {word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"}, + {word: "来到", translation: "venir à", type: "verb", pronunciation: "lái dào"}, + {word: "花园", translation: "jardin", type: "noun", pronunciation: "huā yuán"} + ] + }, + { + id: 6, + original: "小猫看花,我看小猫。", + translation: "Le petit chat regarde les fleurs, moi je regarde le petit chat.", + words: [ + {word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"}, + {word: "看", translation: "regarder", type: "verb", pronunciation: "kàn"}, + {word: "花", translation: "fleur", type: "noun", pronunciation: "huā"}, + {word: "我", translation: "je", type: "pronoun", pronunciation: "wǒ"}, + {word: "看", translation: "regarder", type: "verb", pronunciation: "kàn"}, + {word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"} + ] + }, + { + id: 7, + original: "小猫很可爱。", + translation: "Le petit chat est très mignon.", + words: [ + {word: "小猫", translation: "petit chat", type: "noun", pronunciation: "xiǎo māo"}, + {word: "很", translation: "très", type: "adverb", pronunciation: "hěn"}, + {word: "可爱", translation: "mignon", type: "adjective", pronunciation: "kě ài"} + ] + }, + { + id: 8, + original: "现在,花园更美了。", + translation: "Maintenant, le jardin est encore plus beau.", + words: [ + {word: "现在", translation: "maintenant", type: "adverb", pronunciation: "xiàn zài"}, + {word: "花园", translation: "jardin", type: "noun", pronunciation: "huā yuán"}, + {word: "更", translation: "encore plus", type: "adverb", pronunciation: "gèng"}, + {word: "美", translation: "beau", type: "adjective", pronunciation: "měi"}, + {word: "了", translation: "particule d'aspect", type: "particle", pronunciation: "le"} + ] + } + ] + } + ] + }, + + // === EXERCICES DE COMPRÉHENSION === + fillInBlanks: [ + { + sentence: "J'___ un petit jardin", + options: ["ai", "es", "regarde", "très"], + correctAnswer: "ai", + explanation: "使用 'ai' 表示拥有 - Use 'ai' to express possession (I have)" + }, + { + sentence: "Le jardin est ___ beau", + options: ["ai", "très", "dans", "de"], + correctAnswer: "très", + explanation: "使用 'très' + 形容词 - Use 'très' + adjective" + }, + { + sentence: "Le petit chat ___ les fleurs", + options: ["regarde", "ai", "très", "de"], + correctAnswer: "regarde", + explanation: "'regarde' 意思是看/观察 - 'regarde' means to look/observe" + }, + { + sentence: "J'___ beaucoup mon jardin", + options: ["aime", "beau", "petit", "rouge"], + correctAnswer: "aime", + explanation: "'aime' 表示喜爱 - 'aime' expresses liking" + }, + { + sentence: "Dans le jardin, il y ___ beaucoup de fleurs", + options: ["a", "regarde", "très", "petit"], + correctAnswer: "a", + explanation: "'il y a' 表示存在 - 'il y a' expresses existence" + } + ], + + // === CORRECTIONS D'ERREURS === + corrections: [ + { + incorrect: "Je suis aimer le jardin", + correct: "J'aime le jardin", + explanation: "不需要 'suis',直接用动词 'aime' - No need for 'suis', use verb 'aime' directly" + }, + { + incorrect: "Le jardin beau", + correct: "Le jardin est très beau", + explanation: "需要动词 'est' 和副词 'très' - Need verb 'est' and adverb 'très'" + }, + { + incorrect: "Le petit chat a regardé les fleurs", + correct: "Le petit chat regarde les fleurs", + explanation: "简单动作用现在时 - Simple actions use present tense" + } + ], + + // === PHRASES D'EXEMPLE === + sentences: [ + { + french: "J'ai un beau jardin", + chinese: "我有一个美丽的花园", + pronunciation: "ʒe œ̃ bo ʒaʁdɛ̃" + }, + { + french: "Le petit chat est très mignon", + chinese: "小猫很可爱", + pronunciation: "lə pəti ʃa ɛ tʁɛ miɲɔ̃" + }, + { + french: "Dans le jardin il y a des fleurs rouges et des arbres verts", + chinese: "花园里有红花和绿树", + pronunciation: "dɑ̃ lə ʒaʁdɛ̃ il i a de flœʁ ʁuʒ e dez‿aʁbʁ vɛʁ" + }, + { + french: "Je regarde le petit chat, le petit chat regarde les fleurs", + chinese: "我看小猫,小猫看花", + pronunciation: "ʒə ʁəɡaʁd lə pəti ʃa, lə pəti ʃa ʁəɡaʁd le flœʁ" + } + ] +}; \ No newline at end of file diff --git a/js/content/grammar-lesson-le.js b/js/content/grammar-lesson-le.js new file mode 100644 index 0000000..c2f6d7a --- /dev/null +++ b/js/content/grammar-lesson-le.js @@ -0,0 +1,293 @@ +// === GRAMMAR LESSON: 了 ASPECT PARTICLE === +// Dedicated grammar course focused on the 了 particle in Chinese + +window.ContentModules = window.ContentModules || {}; + +window.ContentModules.GrammarLessonLe = { + id: "grammar-lesson-le", + name: "Grammar Lesson: 了 (le) Aspect Particle", + description: "Complete lesson on the Chinese aspect particle 了 - completion and change of state", + difficulty: "intermediate", + language: "zh-CN", + type: "grammar_course", + + // === MAIN GRAMMAR LESSON === + grammar: { + "le-aspect-particle": { + title: "The 了 (le) Aspect Particle - 动态助词了", + explanation: "了 is one of the most important particles in Chinese. It indicates completion of an action or a change of state. Unlike English past tense, 了 focuses on the aspect (how the action is viewed) rather than when it happened.", + + mainRules: [ + "了 shows that an action has been completed", + "了 indicates a change from one state to another", + "了 can appear after the verb (了1) or at the end of sentence (了2)", + "了 does NOT simply mean 'past tense' - it's about completion/change" + ], + + detailedExplanation: { + "completion": { + title: "1. Completion of Action (动作完成)", + explanation: "了 after a verb shows the action has been completed", + pattern: "Subject + Verb + 了 + Object", + examples: [ + { + chinese: "我吃了饭", + english: "I ate (have eaten) the meal", + pronunciation: "wǒ chī le fàn", + breakdown: "我(I) + 吃(eat) + 了(completed) + 饭(meal)", + explanation: "The eating action is completed" + }, + { + chinese: "他买了一本书", + english: "He bought a book", + pronunciation: "tā mǎi le yì běn shū", + breakdown: "他(he) + 买(buy) + 了(completed) + 一本书(a book)", + explanation: "The buying action is finished" + }, + { + chinese: "老师讲了三个故事", + english: "The teacher told three stories", + pronunciation: "lǎo shī jiǎng le sān gè gù shì", + breakdown: "老师(teacher) + 讲(tell) + 了(completed) + 三个故事(three stories)", + explanation: "The telling action is complete" + } + ] + }, + + "change-of-state": { + title: "2. Change of State (状态变化)", + explanation: "了 at the end of a sentence shows a change in situation or state", + pattern: "Subject + Verb + Object + 了", + examples: [ + { + chinese: "天黑了", + english: "It has gotten dark / It's dark now", + pronunciation: "tiān hēi le", + breakdown: "天(sky) + 黑(dark) + 了(change of state)", + explanation: "Change from light to dark" + }, + { + chinese: "我饿了", + english: "I'm hungry now / I've become hungry", + pronunciation: "wǒ è le", + breakdown: "我(I) + 饿(hungry) + 了(change of state)", + explanation: "Change from not hungry to hungry" + }, + { + chinese: "下雨了", + english: "It's raining now / It started to rain", + pronunciation: "xià yǔ le", + breakdown: "下雨(rain) + 了(change of state)", + explanation: "Change from not raining to raining" + } + ] + }, + + "double-le": { + title: "3. Double 了 Construction", + explanation: "Sometimes 了 appears both after the verb AND at the end of sentence", + pattern: "Subject + Verb + 了 + Object + 了", + examples: [ + { + chinese: "我买了三本书了", + english: "I have bought three books (and the situation has changed)", + pronunciation: "wǒ mǎi le sān běn shū le", + breakdown: "我 + 买了(completed buying) + 三本书 + 了(new situation)", + explanation: "Action completed AND situation changed" + }, + { + chinese: "他吃了两个苹果了", + english: "He has eaten two apples (and is now full/satisfied)", + pronunciation: "tā chī le liǎng gè píng guǒ le", + breakdown: "他 + 吃了(completed eating) + 两个苹果 + 了(new state)", + explanation: "Eating completed AND state changed" + } + ] + } + }, + + commonMistakes: [ + { + mistake: "Using 了 for all past actions", + wrong: "我昨天了去学校", + correct: "我昨天去了学校 / 我昨天去学校", + explanation: "了 shows completion, not just past time. Don't add 了 randomly to past time expressions." + }, + { + mistake: "Forgetting 了 for completed actions", + wrong: "我吃饭,现在很饱", + correct: "我吃了饭,现在很饱", + explanation: "Need 了 to show the eating is completed before being full" + }, + { + mistake: "Using 了 with ongoing actions", + wrong: "我正在吃了饭", + correct: "我正在吃饭", + explanation: "Can't use 了 with 正在 (ongoing) - they're contradictory" + } + ], + + practicePoints: [ + "Ask yourself: Is the action completed? Use 了 after verb", + "Ask yourself: Has the situation changed? Use 了 at end", + "Remember: 了 ≠ past tense. It's about completion/change", + "Pay attention to context - sometimes 了 is not needed even for past actions" + ] + } + }, + + // === VOCABULARY FOR THE LESSON === + vocabulary: { + "了": { + translation: "aspect particle (completion/change)", + type: "particle", + pronunciation: "le", + usage: "Shows completed action or change of state" + }, + "吃": { + translation: "to eat", + type: "verb", + pronunciation: "chī" + }, + "买": { + translation: "to buy", + type: "verb", + pronunciation: "mǎi" + }, + "讲": { + translation: "to tell/speak", + type: "verb", + pronunciation: "jiǎng" + }, + "饭": { + translation: "meal/food", + type: "noun", + pronunciation: "fàn" + }, + "书": { + translation: "book", + type: "noun", + pronunciation: "shū" + }, + "故事": { + translation: "story", + type: "noun", + pronunciation: "gù shì" + }, + "天": { + translation: "sky/day", + type: "noun", + pronunciation: "tiān" + }, + "黑": { + translation: "dark/black", + type: "adjective", + pronunciation: "hēi" + }, + "饿": { + translation: "hungry", + type: "adjective", + pronunciation: "è" + }, + "下雨": { + translation: "to rain", + type: "verb", + pronunciation: "xià yǔ" + } + }, + + // === FILL IN THE BLANKS EXERCISES === + fillInBlanks: [ + { + sentence: "我吃___饭,现在很饱", + options: ["了", "的", "在", "着"], + correctAnswer: "了", + explanation: "Use 了 to show the eating action is completed before being full", + grammarFocus: "completion" + }, + { + sentence: "天黑___,我们回家吧", + options: ["了", "的", "在", "着"], + correctAnswer: "了", + explanation: "Use 了 to show change of state - it has become dark", + grammarFocus: "change-of-state" + }, + { + sentence: "他买___三本书___", + options: ["了...了", "的...的", "在...在", "着...着"], + correctAnswer: "了...了", + explanation: "Double 了: action completed (买了) AND situation changed (了)", + grammarFocus: "double-le" + }, + { + sentence: "我昨天___学校", + options: ["去了", "了去", "去的", "的去"], + correctAnswer: "去了", + explanation: "了 comes after the verb to show completed action", + grammarFocus: "word-order" + }, + { + sentence: "下雨___,路很湿", + options: ["了", "的", "在", "着"], + correctAnswer: "了", + explanation: "Change of state: it has started raining (wasn't raining before)", + grammarFocus: "change-of-state" + } + ], + + // === CORRECTION EXERCISES === + corrections: [ + { + incorrect: "我昨天了去学校", + correct: "我昨天去了学校", + explanation: "了 should come after the verb, not before it", + grammarFocus: "word-order" + }, + { + incorrect: "我正在吃了饭", + correct: "我正在吃饭", + explanation: "Cannot use 了 (completion) with 正在 (ongoing action)", + grammarFocus: "aspect-conflict" + }, + { + incorrect: "我吃饭,现在很饱", + correct: "我吃了饭,现在很饱", + explanation: "Need 了 to show eating is completed before the result (being full)", + grammarFocus: "completion" + }, + { + incorrect: "他很高了的人", + correct: "他是很高的人", + explanation: "Don't use 了 in descriptions with 的. 了 is for actions/changes, not permanent descriptions", + grammarFocus: "inappropriate-usage" + } + ], + + // === TRANSLATION EXERCISES === + sentences: [ + { + english: "I finished my homework", + chinese: "我做完了作业", + pronunciation: "wǒ zuò wán le zuò yè", + grammarFocus: "completion" + }, + { + english: "It's gotten cold", + chinese: "天气冷了", + pronunciation: "tiān qì lěng le", + grammarFocus: "change-of-state" + }, + { + english: "He bought two books and now has them", + chinese: "他买了两本书了", + pronunciation: "tā mǎi le liǎng běn shū le", + grammarFocus: "double-le" + }, + { + english: "The teacher finished the lesson", + chinese: "老师讲完了课", + pronunciation: "lǎo shī jiǎng wán le kè", + grammarFocus: "completion" + } + ] +}; \ No newline at end of file diff --git a/js/content/sbs-level-7-8-new-converted.js b/js/content/sbs-level-7-8-new-converted.js deleted file mode 100644 index 4350cef..0000000 --- a/js/content/sbs-level-7-8-new-converted.js +++ /dev/null @@ -1,239 +0,0 @@ -const content = { - vocabulary: { - // Housing and Places - central: { user_language: { user_language: "中心的;中央的", type: "noun" }, type: { user_language: "adjective", type: "noun" }, }, - avenue: { user_language: { user_language: "大街;林荫道", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - refrigerator: { user_language: { user_language: "冰箱", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - closet: { user_language: { user_language: "衣柜;壁橱", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - elevator: { user_language: { user_language: "电梯", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - building: { user_language: { user_language: "建筑物;大楼", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - "air conditioner": { user_language: { user_language: "空调", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - superintendent: { user_language: { user_language: "主管;负责人", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - "bus stop": { user_language: { user_language: "公交车站", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - jacuzzi: { user_language: { user_language: "按摩浴缸", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - machine: { user_language: { user_language: "机器;设备", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - "two and a half": { user_language: { user_language: "两个半", type: "noun" }, type: { user_language: "number", type: "noun" }, }, - "in the center of": { user_language: { user_language: "在……中心", type: "noun" }, type: { user_language: "preposition", type: "noun" }, }, - town: { user_language: { user_language: "城镇", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - "a lot of": { user_language: { user_language: "许多", type: "noun" }, type: { user_language: "determiner", type: "noun" }, }, - noise: { user_language: { user_language: "噪音", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - sidewalks: { user_language: { user_language: "人行道", type: "noun" }, type: { user_language: "noun", type: "noun" }, }, - "all day and all night": { user_language: { user_language: "整日整夜", type: "noun" }, type: { user_language: "adverb", type: "noun" }, }, - convenient: { user_language: { user_language: "便利的", type: "adjective" }, type: { user_language: "adjective", type: "noun" }, }, - upset: { user_language: { user_language: "失望的", type: "adjective" }, type: { user_language: "adjective", type: "noun" }, }, - - // Clothing and Accessories - shirt: { user_language: "衬衫", type: "noun" }, - coat: { user_language: "外套、大衣", type: "noun" }, - dress: { user_language: "连衣裙", type: "noun" }, - skirt: { user_language: "短裙", type: "noun" }, - blouse: { user_language: "女式衬衫", type: "noun" }, - jacket: { user_language: "夹克、短外套", type: "noun" }, - sweater: { user_language: "毛衣、针织衫", type: "noun" }, - suit: { user_language: "套装、西装", type: "noun" }, - tie: { user_language: "领带", type: "noun" }, - pants: { user_language: "裤子", type: "noun" }, - jeans: { user_language: "牛仔裤", type: "noun" }, - belt: { user_language: "腰带、皮带", type: "noun" }, - hat: { user_language: "帽子", type: "noun" }, - glove: { user_language: "手套", type: "noun" }, - "purse/pocketbook": { user_language: "手提包、女式小包", type: "noun" }, - glasses: { user_language: "眼镜", type: "noun" }, - pajamas: { user_language: "睡衣", type: "noun" }, - socks: { user_language: "袜子", type: "noun" }, - shoes: { user_language: "鞋子", type: "noun" }, - bathrobe: { user_language: "浴袍", type: "noun" }, - "tee shirt": { user_language: "T恤", type: "phrase" }, - scarf: { user_language: "围巾", type: "noun" }, - wallet: { user_language: "钱包", type: "noun" }, - ring: { user_language: "戒指", type: "noun" }, - sandals: { user_language: "凉鞋", type: "noun" }, - slippers: { user_language: "拖鞋", type: "noun" }, - sneakers: { user_language: "运动鞋", type: "noun" }, - shorts: { user_language: "短裤", type: "noun" }, - "sweat pants": { user_language: "运动裤", type: "phrase" }, - - // Places and Areas - "urban areas": { user_language: "cities", type: "phrase" }, - "suburban areas": { user_language: "places near cities", type: "phrase" }, - "rural areas": { user_language: "places in the countryside, far from cities", type: "phrase" }, - farmhouse: { user_language: "农舍", type: "noun" }, - hut: { user_language: "小屋", type: "noun" }, - houseboat: { user_language: "船屋", type: "noun" }, - "mobile home": { user_language: "移动房屋", type: "phrase" }, - trailer: { user_language: "拖车房", type: "noun" }, - - // Store Items - jackets: { user_language: "夹克", type: "noun" }, - gloves: { user_language: "手套", type: "noun" }, - blouses: { user_language: "女式衬衫", type: "noun" }, - bracelets: { user_language: "手镯", type: "noun" }, - ties: { user_language: "领带", type: "noun" }, - }, - - sentences: [ - { - english: { user_language: "Amy's apartment building is in the center of town.", type: "noun" }, - chinese: { user_language: "艾米的公寓楼在城镇中心。", type: "adjective" }, - }, - { - english: { user_language: "There's a lot of noise near Amy's apartment building.", type: "noun" }, - chinese: { user_language: "艾米的公寓楼附近有很多噪音。", type: "adjective" }, - }, - { - english: { user_language: "It's a very busy place, but it's a convenient place to live.", type: "noun" }, - chinese: { user_language: "那是个非常热闹的地方,但也是个居住很方便的地方。", type: "adjective" }, - }, - { - english: { user_language: "Around the corner from the building, there are two supermarkets.", type: "noun" }, - chinese: { user_language: "从这栋楼拐个弯,就有两家超市。", type: "noun" }, - }, - { - english: { user_language: "I'm looking for a shirt.", type: "noun" }, - chinese: { user_language: "我在找一件衬衫。", type: "noun" }, - }, - { - english: { user_language: "Shirts are over there.", type: "noun" }, - chinese: { user_language: "衬衫在那边。", type: "noun" }, - } - ], - - texts: [ - { - title: { user_language: "People's Homes", type: "noun" }, - content: { user_language: "Homes are different all around the world. This family is living in a farmhouse. This family is living in a hut. This family is living in a houseboat. These people are living in a mobile home (a trailer). What different kinds of homes are there in your country?", type: "noun" }, - }, - { - title: { user_language: "Urban, Suburban, and Rural", type: "noun" }, - content: { user_language: "urban areas = cities, suburban areas = places near cities, rural areas = places in the countryside, far from cities. About 50% (percent) of the world's population is in urban and suburban areas. About 50% (percent) of the world's population is in rural areas.", type: "noun" }, - }, - { - title: { user_language: "Global Exchange - RosieM", type: "noun" }, - content: { user_language: "My apartment is in a wonderful neighborhood. There's a big, beautiful park across from my apartment building. Around the corner, there's a bank, a post office, and a laundromat. There are also many restaurants and stores in my neighborhood. It's a noisy place, but it's a very interesting place. There are a lot of people on the sidewalks all day and all night. How about your neighborhood? Tell me about it.", type: "noun" }, - }, - { - title: { user_language: "Clothing, Colors, and Cultures", type: "noun" }, - content: { user_language: "Blue and pink aren't children's clothing colors all around the world. The meanings of colors are sometimes very different in different cultures. For example, in some cultures, blue is a common clothing color for little boys, and pink is a common clothing color for little girls. In other cultures, other colors are common for boys and girls. There are also different colors for special days in different cultures. For example, white is the traditional color of a wedding dress in some cultures, but other colors are traditional in other cultures. For some people, white is a happy color. For others, it's a sad color. For some people, red is a beautiful and lucky color. For others, it's a very sad color. What are the meanings of different colors in YOUR culture?", type: "noun" }, - } - ], - - grammar: { - thereBe: { - topic: { user_language: "There be 句型的用法", type: "adjective" }, - singular: { - form: { user_language: "there is (there's) + 名词单数/不可数名词", type: "noun" }, - explanation: { user_language: "在某地方有什么人或东西", type: "noun" }, - examples: [ - "There's a bank.", - "There's some water.", - "There's a book store on Main Street." - ], - forms: { - positive: { user_language: "There's a stove in the kitchen.", type: "noun" }, - negative: { user_language: "There isn't a stove in the kitchen.", type: "noun" }, - question: { user_language: "Is there a stove in the kitchen?", type: "noun" }, - shortAnswers: { user_language: "Yes, there is. / No, there isn't.", type: "noun" }, - } - }, - plural: { - form: { user_language: "there are (there're) + 复数名词", type: "noun" }, - examples: [ - "There're two hospitals.", - "There're many rooms in this apartment." - ], - forms: { - positive: { user_language: "There're two windows in the kitchen.", type: "noun" }, - negative: { user_language: "There aren't two windows in the kitchen.", type: "noun" }, - question: { user_language: "Are there two windows in the kitchen?", type: "noun" }, - shortAnswers: { user_language: "Yes, there are. / No, there aren't.", type: "noun" }, - } - } - }, - plurals: { - topic: { user_language: "可数名词复数", type: "noun" }, - pronunciation: { - rules: [ - { - condition: { user_language: "在清辅音/-p,-k/后", type: "noun" }, - pronunciation: { user_language: "/-s/", type: "noun" }, - example: { user_language: "socks中-k是清辅音/-k/,所以-s读/-s/", type: "noun" }, - }, - { - condition: { user_language: "在浊辅音和元音音标后", type: "noun" }, - pronunciation: { user_language: "/-z/", type: "noun" }, - example: { user_language: "jeans中-n是浊辅音/-n/, 所以-s读/-z/; tie的读音是/tai/,以元音结尾,所以-s读/-z/", type: "adjective" }, - }, - { - condition: { user_language: "以/-s,-z,-ʃ,-ʒ,-tʃ,-dʒ/发音结尾的名词", type: "adjective" }, - pronunciation: { user_language: "/-iz/", type: "noun" }, - example: { user_language: "watches中-ch读/-tʃ/,所以-es读/-iz/", type: "noun" }, - } - ] - }, - formation: { - regular: { - rule: { user_language: "一般在词尾加-s", type: "noun" }, - examples: ["shirts", "shoes"] - }, - special: { - rule: { user_language: "以-s,-sh,-ch,-x,以及辅音字母o结尾的词在词尾加-es", type: "adjective" }, - examples: ["boxes", "buses", "potatoes", "tomatoes", "heroes"] - }, - irregular: { - rule: { user_language: "特殊的复数形式", type: "adjective" }, - examples: { - "man": { user_language: "men", type: "noun" }, - "woman": { user_language: "women", type: "noun" }, - "child": { user_language: "children", type: "noun" }, - "tooth": { user_language: "teeth", type: "noun" }, - "mouse": { user_language: "mice", type: "noun" }, - } - } - } - } - }, - - listening: { - jMartShopping: { - title: { user_language: "Attention, J-Mart Shoppers!", type: "noun" }, - items: [ - { item: { user_language: "jackets", type: "noun" }, aisle: { user_language: "Aisle 9", type: "noun" }, }, - { item: { user_language: "gloves", type: "noun" }, aisle: { user_language: "Aisle 7", type: "noun" }, }, - { item: { user_language: "blouses", type: "noun" }, aisle: { user_language: "Aisle 9", type: "noun" }, }, - { item: { user_language: "bracelets", type: "noun" }, aisle: { user_language: "Aisle 11", type: "noun" }, }, - { item: { user_language: "ties", type: "noun" }, aisle: { user_language: "Aisle 5", type: "noun" }, } - ] - } - }, - - exercises: { - sentenceCompletion: [ - "That's a very nice _______.", - "Those are very nice _______." - ], - questions: [ - "What different kinds of homes are there in your country?", - "How about your neighborhood? Tell me about it.", - "What are the meanings of different colors in YOUR culture?" - ] - } -}; - -// Export pour le système de modules web -window.ContentModules = window.ContentModules || {}; -window.ContentModules.SBSLevel78New = { - name: { user_language: "SBS Level 7-8 (New)", type: "noun" }, - description: { user_language: "Format simple et clair - Homes, Clothing & Cultures", type: "noun" }, - difficulty: { user_language: "intermediate", type: "noun" }, - vocabulary: content.vocabulary, - sentences: content.sentences, - texts: content.texts, - grammar: content.grammar, - listening: content.listening, - exercises: content.exercises -}; - -// Export Node.js (optionnel) -if (typeof module !== 'undefined' && module.exports) { - module.exports = content; -} \ No newline at end of file diff --git a/js/content/sbs-level-7-8-new-fixed.js b/js/content/sbs-level-7-8-new-fixed.js deleted file mode 100644 index 4f03400..0000000 --- a/js/content/sbs-level-7-8-new-fixed.js +++ /dev/null @@ -1,239 +0,0 @@ -const content = { - vocabulary: { - // Housing and Places - central: { user_language: "中心的;中央的", type: { user_language: "adjective", type: "noun" }, }, - avenue: { user_language: "大街;林荫道", type: { user_language: "noun", type: "noun" }, }, - refrigerator: { user_language: "冰箱", type: { user_language: "noun", type: "noun" }, }, - closet: { user_language: "衣柜;壁橱", type: { user_language: "noun", type: "noun" }, }, - elevator: { user_language: "电梯", type: { user_language: "noun", type: "noun" }, }, - building: { user_language: "建筑物;大楼", type: { user_language: "noun", type: "noun" }, }, - "air conditioner": { user_language: "空调", type: { user_language: "noun", type: "noun" }, }, - superintendent: { user_language: "主管;负责人", type: { user_language: "noun", type: "noun" }, }, - "bus stop": { user_language: "公交车站", type: { user_language: "noun", type: "noun" }, }, - jacuzzi: { user_language: "按摩浴缸", type: { user_language: "noun", type: "noun" }, }, - machine: { user_language: "机器;设备", type: { user_language: "noun", type: "noun" }, }, - "two and a half": { user_language: "两个半", type: { user_language: "number", type: "noun" }, }, - "in the center of": { user_language: "在……中心", type: { user_language: "preposition", type: "noun" }, }, - town: { user_language: "城镇", type: { user_language: "noun", type: "noun" }, }, - "a lot of": { user_language: "许多", type: { user_language: "determiner", type: "noun" }, }, - noise: { user_language: "噪音", type: { user_language: "noun", type: "noun" }, }, - sidewalks: { user_language: "人行道", type: { user_language: "noun", type: "noun" }, }, - "all day and all night": { user_language: "整日整夜", type: { user_language: "adverb", type: "noun" }, }, - convenient: { user_language: "便利的", type: { user_language: "adjective", type: "noun" }, }, - upset: { user_language: "失望的", type: { user_language: "adjective", type: "noun" }, }, - - // Clothing and Accessories - shirt: { user_language: "衬衫", type: "noun" }, - coat: { user_language: "外套、大衣", type: "noun" }, - dress: { user_language: "连衣裙", type: "noun" }, - skirt: { user_language: "短裙", type: "noun" }, - blouse: { user_language: "女式衬衫", type: "noun" }, - jacket: { user_language: "夹克、短外套", type: "noun" }, - sweater: { user_language: "毛衣、针织衫", type: "noun" }, - suit: { user_language: "套装、西装", type: "noun" }, - tie: { user_language: "领带", type: "noun" }, - pants: { user_language: "裤子", type: "noun" }, - jeans: { user_language: "牛仔裤", type: "noun" }, - belt: { user_language: "腰带、皮带", type: "noun" }, - hat: { user_language: "帽子", type: "noun" }, - glove: { user_language: "手套", type: "noun" }, - "purse/pocketbook": "手提包、女式小包", - glasses: { user_language: "眼镜", type: "noun" }, - pajamas: { user_language: "睡衣", type: "noun" }, - socks: { user_language: "袜子", type: "noun" }, - shoes: { user_language: "鞋子", type: "noun" }, - bathrobe: { user_language: "浴袍", type: "noun" }, - "tee shirt": { user_language: "T恤", type: "noun" }, - scarf: { user_language: "围巾", type: "noun" }, - wallet: { user_language: "钱包", type: "noun" }, - ring: { user_language: "戒指", type: "noun" }, - sandals: { user_language: "凉鞋", type: "noun" }, - slippers: { user_language: "拖鞋", type: "noun" }, - sneakers: { user_language: "运动鞋", type: "noun" }, - shorts: { user_language: "短裤", type: "noun" }, - "sweat pants": { user_language: "运动裤", type: "noun" }, - - // Places and Areas - "urban areas": { user_language: "cities", type: "noun" }, - "suburban areas": { user_language: "places near cities", type: "noun" }, - "rural areas": { user_language: "places in the countryside, far from cities", type: "noun" }, - farmhouse: { user_language: "农舍", type: "noun" }, - hut: { user_language: "小屋", type: "noun" }, - houseboat: { user_language: "船屋", type: "noun" }, - "mobile home": { user_language: "移动房屋", type: "noun" }, - trailer: { user_language: "拖车房", type: "noun" }, - - // Store Items - jackets: { user_language: "夹克", type: "noun" }, - gloves: { user_language: "手套", type: "noun" }, - blouses: { user_language: "女式衬衫", type: "noun" }, - bracelets: { user_language: "手镯", type: "noun" }, - ties: { user_language: "领带", type: "noun" }, - }, - - sentences: [ - { - english: { user_language: "Amy's apartment building is in the center of town.", type: "noun" }, - chinese: { user_language: "艾米的公寓楼在城镇中心。", type: "noun" }, - }, - { - english: { user_language: "There's a lot of noise near Amy's apartment building.", type: "noun" }, - chinese: { user_language: "艾米的公寓楼附近有很多噪音。", type: "noun" }, - }, - { - english: { user_language: "It's a very busy place, but it's a convenient place to live.", type: "noun" }, - chinese: { user_language: "那是个非常热闹的地方,但也是个居住很方便的地方。", type: "noun" }, - }, - { - english: { user_language: "Around the corner from the building, there are two supermarkets.", type: "noun" }, - chinese: { user_language: "从这栋楼拐个弯,就有两家超市。", type: "noun" }, - }, - { - english: { user_language: "I'm looking for a shirt.", type: "noun" }, - chinese: { user_language: "我在找一件衬衫。", type: "noun" }, - }, - { - english: { user_language: "Shirts are over there.", type: "noun" }, - chinese: { user_language: "衬衫在那边。", type: "noun" }, - } - ], - - texts: [ - { - title: { user_language: "People's Homes", type: "noun" }, - content: { user_language: "Homes are different all around the world. This family is living in a farmhouse. This family is living in a hut. This family is living in a houseboat. These people are living in a mobile home (a trailer). What different kinds of homes are there in your country?", type: "noun" }, - }, - { - title: { user_language: "Urban, Suburban, and Rural", type: "noun" }, - content: { user_language: "urban areas = cities, suburban areas = places near cities, rural areas = places in the countryside, far from cities. About 50% (percent) of the world's population is in urban and suburban areas. About 50% (percent) of the world's population is in rural areas.", type: "noun" }, - }, - { - title: { user_language: "Global Exchange - RosieM", type: "noun" }, - content: { user_language: "My apartment is in a wonderful neighborhood. There's a big, beautiful park across from my apartment building. Around the corner, there's a bank, a post office, and a laundromat. There are also many restaurants and stores in my neighborhood. It's a noisy place, but it's a very interesting place. There are a lot of people on the sidewalks all day and all night. How about your neighborhood? Tell me about it.", type: "noun" }, - }, - { - title: { user_language: "Clothing, Colors, and Cultures", type: "noun" }, - content: { user_language: "Blue and pink aren't children's clothing colors all around the world. The meanings of colors are sometimes very different in different cultures. For example, in some cultures, blue is a common clothing color for little boys, and pink is a common clothing color for little girls. In other cultures, other colors are common for boys and girls. There are also different colors for special days in different cultures. For example, white is the traditional color of a wedding dress in some cultures, but other colors are traditional in other cultures. For some people, white is a happy color. For others, it's a sad color. For some people, red is a beautiful and lucky color. For others, it's a very sad color. What are the meanings of different colors in YOUR culture?", type: "noun" }, - } - ], - - grammar: { - thereBe: { - topic: { user_language: "There be 句型的用法", type: "noun" }, - singular: { - form: { user_language: "there is (there's) + 名词单数/不可数名词", type: "noun" }, - explanation: { user_language: "在某地方有什么人或东西", type: "noun" }, - examples: [ - "There's a bank.", - "There's some water.", - "There's a book store on Main Street." - ], - forms: { - positive: { user_language: "There's a stove in the kitchen.", type: "noun" }, - negative: { user_language: "There isn't a stove in the kitchen.", type: "noun" }, - question: { user_language: "Is there a stove in the kitchen?", type: "noun" }, - shortAnswers: { user_language: "Yes, there is. / No, there isn't.", type: "noun" }, - } - }, - plural: { - form: { user_language: "there are (there're) + 复数名词", type: "noun" }, - examples: [ - "There're two hospitals.", - "There're many rooms in this apartment." - ], - forms: { - positive: { user_language: "There're two windows in the kitchen.", type: "noun" }, - negative: { user_language: "There aren't two windows in the kitchen.", type: "noun" }, - question: { user_language: "Are there two windows in the kitchen?", type: "noun" }, - shortAnswers: { user_language: "Yes, there are. / No, there aren't.", type: "noun" }, - } - } - }, - plurals: { - topic: { user_language: "可数名词复数", type: "noun" }, - pronunciation: { - rules: [ - { - condition: { user_language: "在清辅音/-p,-k/后", type: "noun" }, - pronunciation: { user_language: "/-s/", type: "noun" }, - example: { user_language: "socks中-k是清辅音/-k/,所以-s读/-s/", type: "noun" }, - }, - { - condition: { user_language: "在浊辅音和元音音标后", type: "noun" }, - pronunciation: { user_language: "/-z/", type: "noun" }, - example: { user_language: "jeans中-n是浊辅音/-n/, 所以-s读/-z/; tie的读音是/tai/,以元音结尾,所以-s读/-z/", type: "noun" }, - }, - { - condition: { user_language: "以/-s,-z,-ʃ,-ʒ,-tʃ,-dʒ/发音结尾的名词", type: "noun" }, - pronunciation: { user_language: "/-iz/", type: "noun" }, - example: { user_language: "watches中-ch读/-tʃ/,所以-es读/-iz/", type: "noun" }, - } - ] - }, - formation: { - regular: { - rule: { user_language: "一般在词尾加-s", type: "noun" }, - examples: ["shirts", "shoes"] - }, - special: { - rule: { user_language: "以-s,-sh,-ch,-x,以及辅音字母o结尾的词在词尾加-es", type: "noun" }, - examples: ["boxes", "buses", "potatoes", "tomatoes", "heroes"] - }, - irregular: { - rule: { user_language: "特殊的复数形式", type: "noun" }, - examples: { - "man": { user_language: "men", type: "noun" }, - "woman": { user_language: "women", type: "noun" }, - "child": { user_language: "children", type: "noun" }, - "tooth": { user_language: "teeth", type: "noun" }, - "mouse": { user_language: "mice", type: "noun" }, - } - } - } - } - }, - - listening: { - jMartShopping: { - title: { user_language: "Attention, J-Mart Shoppers!", type: "noun" }, - items: [ - { item: { user_language: "jackets", type: "noun" }, aisle: { user_language: "Aisle 9", type: "noun" }, }, - { item: { user_language: "gloves", type: "noun" }, aisle: { user_language: "Aisle 7", type: "noun" }, }, - { item: { user_language: "blouses", type: "noun" }, aisle: { user_language: "Aisle 9", type: "noun" }, }, - { item: { user_language: "bracelets", type: "noun" }, aisle: { user_language: "Aisle 11", type: "noun" }, }, - { item: { user_language: "ties", type: "noun" }, aisle: { user_language: "Aisle 5", type: "noun" }, } - ] - } - }, - - exercises: { - sentenceCompletion: [ - "That's a very nice _______.", - "Those are very nice _______." - ], - questions: [ - "What different kinds of homes are there in your country?", - "How about your neighborhood? Tell me about it.", - "What are the meanings of different colors in YOUR culture?" - ] - } -}; - -// Export pour le système de modules web -window.ContentModules = window.ContentModules || {}; -window.ContentModules.SBSLevel78New = { - name: { user_language: "SBS Level 7-8 (New)", type: "noun" }, - description: { user_language: "Format simple et clair - Homes, Clothing & Cultures", type: "noun" }, - difficulty: { user_language: "intermediate", type: "noun" }, - vocabulary: content.vocabulary, - sentences: content.sentences, - texts: content.texts, - grammar: content.grammar, - listening: content.listening, - exercises: content.exercises -}; - -// Export Node.js (optionnel) -if (typeof module !== 'undefined' && module.exports) { - module.exports = content; -} \ No newline at end of file diff --git a/js/content/test-compatibility.js b/js/content/test-compatibility.js deleted file mode 100644 index 763ccc6..0000000 --- a/js/content/test-compatibility.js +++ /dev/null @@ -1,160 +0,0 @@ -// === CONTENU DE TEST POUR LA COMPATIBILITÉ === - -window.ContentModules = window.ContentModules || {}; - -// Contenu avec seulement 2 mots (devrait être incompatible avec whack-a-mole) -window.ContentModules.TestMinimalContent = { - id: "test-minimal-content", - name: "Test Minimal (2 mots)", - description: "Contenu minimal pour tester la compatibilité", - difficulty: "easy", - language: "en-US", - - vocabulary: { - "hello": "bonjour", - "world": "monde" - } -}; - -// Contenu riche (devrait être compatible avec tous les jeux) -window.ContentModules.TestRichContent = { - id: "test-rich-content", - name: "Test Riche (complet)", - description: "Contenu riche pour tester la compatibilité maximale", - difficulty: "medium", - - vocabulary: { - "apple": { - translation: "pomme", - prononciation: "apple", - type: "noun", - pronunciation: "audio/apple.mp3" - }, - "book": { - translation: "livre", - prononciation: "book", - type: "noun" - }, - "car": { - translation: "voiture", - prononciation: "car", - type: "noun" - }, - "dog": { - translation: "chien", - prononciation: "dog", - type: "noun" - }, - "eat": { - translation: "manger", - prononciation: "eat", - type: "verb" - }, - "friend": { - translation: "ami", - prononciation: "friend", - type: "noun" - }, - "good": { - translation: "bon", - prononciation: "good", - type: "adjective" - }, - "house": { - translation: "maison", - prononciation: "house", - type: "noun" - } - }, - - sentences: [ - { - english: "I have a red apple", - french: "J'ai une pomme rouge", - prononciation: "ai hav a red apple" - }, - { - english: "The dog is in the house", - french: "Le chien est dans la maison", - prononciation: "ze dog iz in ze house" - }, - { - english: "My friend has a car", - french: "Mon ami a une voiture", - prononciation: "mai friend haz a car" - }, - { - english: "I like to read books", - french: "J'aime lire des livres", - prononciation: "ai laik tu rid books" - }, - { - english: "This is a good book", - french: "C'est un bon livre", - prononciation: "zis iz a gud book" - } - ], - - dialogues: [ - { - title: "Au restaurant", - conversation: [ - { speaker: "Waiter", english: "What would you like to eat?", french: "Que voulez-vous manger ?" }, - { speaker: "Customer", english: "I would like an apple", french: "Je voudrais une pomme" }, - { speaker: "Waiter", english: "Good choice!", french: "Bon choix !" } - ] - } - ], - - fillInBlanks: [ - { - sentence: "I have a red _____", - options: ["apple", "book", "car", "dog"], - correctAnswer: "apple", - explanation: "Apple fits the context" - }, - { - sentence: "The _____ is good", - options: ["book", "apple", "house", "friend"], - correctAnswer: "book", - explanation: "Books can be described as good" - } - ], - - grammar: { - articles: { - title: "Articles (a, an, the)", - explanation: "Use 'a' before consonants, 'an' before vowels", - examples: [ - { english: "a book", french: "un livre" }, - { english: "an apple", french: "une pomme" } - ] - } - }, - - audio: { - withText: [ - { - title: "Vocabulary pronunciation", - transcript: "Apple, book, car, dog, eat, friend, good, house", - translation: "Pomme, livre, voiture, chien, manger, ami, bon, maison" - } - ] - } -}; - -// Contenu avec seulement des phrases (bon pour text-reader, limité pour memory-match) -window.ContentModules.TestSentenceOnly = { - id: "test-sentence-only", - name: "Test Phrases Seulement", - description: "Contenu avec seulement des phrases pour tester la compatibilité spécialisée", - difficulty: "medium", - - sentences: [ - { english: "The weather is nice today", french: "Le temps est beau aujourd'hui" }, - { english: "I am going to school", french: "Je vais à l'école" }, - { english: "She likes to read books", french: "Elle aime lire des livres" }, - { english: "We are learning English", french: "Nous apprenons l'anglais" }, - { english: "They play football every day", french: "Ils jouent au football tous les jours" } - ] -}; \ No newline at end of file diff --git a/js/content/test-minimal.js b/js/content/test-minimal.js deleted file mode 100644 index c40e19c..0000000 --- a/js/content/test-minimal.js +++ /dev/null @@ -1,16 +0,0 @@ -// === CONTENU DE TEST MINIMAL === - -window.ContentModules = window.ContentModules || {}; - -window.ContentModules.TestMinimal = { - id: "test-minimal", - name: "Test Minimal (2 mots)", - description: "Contenu minimal pour tester la compatibilité - seulement 2 mots", - difficulty: "easy", - language: "en-US", - - vocabulary: { - "hello": "bonjour", - "world": "monde" - } -}; \ No newline at end of file diff --git a/js/content/test-rich.js b/js/content/test-rich.js deleted file mode 100644 index e62e230..0000000 --- a/js/content/test-rich.js +++ /dev/null @@ -1,98 +0,0 @@ -// === CONTENU DE TEST RICHE === - -window.ContentModules = window.ContentModules || {}; - -window.ContentModules.TestRich = { - id: "test-rich", - name: "Test Riche (complet)", - description: "Contenu riche pour tester la compatibilité maximale", - difficulty: "medium", - language: "en-US", - - vocabulary: { - "apple": { - translation: "pomme", - prononciation: "apple", - type: "noun", - pronunciation: "audio/apple.mp3" - }, - "book": { - translation: "livre", - prononciation: "book", - type: "noun" - }, - "car": { - translation: "voiture", - prononciation: "car", - type: "noun" - }, - "dog": { - translation: "chien", - prononciation: "dog", - type: "noun" - }, - "eat": { - translation: "manger", - prononciation: "eat", - type: "verb" - }, - "friend": { - translation: "ami", - prononciation: "friend", - type: "noun" - } - }, - - sentences: [ - { - english: "I have a red apple", - french: "J'ai une pomme rouge", - prononciation: "ai hav a red apple" - }, - { - english: "The dog is in the house", - french: "Le chien est dans la maison", - prononciation: "ze dog iz in ze house" - }, - { - english: "My friend has a car", - french: "Mon ami a une voiture", - prononciation: "mai friend haz a car" - }, - { - english: "I like to read books", - french: "J'aime lire des livres", - prononciation: "ai laik tu rid books" - } - ], - - dialogues: [ - { - title: "Au restaurant", - conversation: [ - { speaker: "Waiter", english: "What would you like to eat?", french: "Que voulez-vous manger ?" }, - { speaker: "Customer", english: "I would like an apple", french: "Je voudrais une pomme" }, - { speaker: "Waiter", english: "Good choice!", french: "Bon choix !" } - ] - } - ], - - fillInBlanks: [ - { - sentence: "I have a red _____", - options: ["apple", "book", "car", "dog"], - correctAnswer: "apple", - explanation: "Apple fits the context" - } - ], - - audio: { - withText: [ - { - title: "Vocabulary pronunciation", - transcript: "Apple, book, car, dog, eat, friend", - translation: "Pomme, livre, voiture, chien, manger, ami" - } - ] - } -}; \ No newline at end of file diff --git a/js/core/content-engine.js b/js/core/content-engine.js index 28b6b09..5260cde 100644 --- a/js/core/content-engine.js +++ b/js/core/content-engine.js @@ -48,15 +48,97 @@ class ContentEngine { logSh('Migration ancien format vers nouveau format...', 'INFO'); return this.migrator.migrateToNewFormat(rawContent); } - + + // Enrichir le contenu avec la structure letters si présente + if (rawContent.letters) { + rawContent = this.enrichContentWithLetters(rawContent); + } + // Valider le nouveau format if (!this.validator.validate(rawContent)) { throw new Error('Format de contenu invalide'); } - + return rawContent; } + // Enrichir le contenu avec les mots extraits de la structure letters + enrichContentWithLetters(content) { + logSh('🔤 Enrichissement du contenu avec structure letters...', 'INFO'); + + const enrichedContent = { ...content }; + + // Extraire tous les mots de la structure letters + const letterWords = this.extractWordsFromLetters(content.letters); + + // Si pas de vocabulaire existant, créer depuis letters + if (!enrichedContent.vocabulary) { + enrichedContent.vocabulary = {}; + } + + // Ajouter les mots de letters au vocabulaire (sans écraser) + letterWords.forEach(wordData => { + if (!enrichedContent.vocabulary[wordData.word]) { + enrichedContent.vocabulary[wordData.word] = { + translation: wordData.translation, + user_language: wordData.translation, + type: wordData.type, + pronunciation: wordData.pronunciation, + prononciation: wordData.pronunciation, + gender: wordData.gender + }; + } + }); + + // Ajouter les métadonnées pour Letter Discovery + enrichedContent.supportsLetterDiscovery = true; + enrichedContent.totalLetters = Object.keys(content.letters).length; + enrichedContent.totalLetterWords = letterWords.length; + + logSh(`📝 ${letterWords.length} mots extraits de ${Object.keys(content.letters).length} lettres`, 'INFO'); + + return enrichedContent; + } + + // Extraire tous les mots de la structure letters + extractWordsFromLetters(letters) { + const allWords = []; + + Object.keys(letters).forEach(letter => { + const wordsForLetter = letters[letter]; + if (Array.isArray(wordsForLetter)) { + wordsForLetter.forEach(wordData => { + allWords.push({ + word: wordData.word, + translation: wordData.translation, + pronunciation: wordData.pronunciation, + type: wordData.type, + gender: wordData.gender, + letter: letter + }); + }); + } + }); + + return allWords; + } + + // Obtenir les mots pour une lettre spécifique + getWordsForLetter(content, letter) { + if (content.letters && content.letters[letter]) { + return content.letters[letter]; + } + return []; + } + + // Obtenir toutes les lettres disponibles + getAvailableLetters(content) { + if (content.letters) { + return Object.keys(content.letters).sort(); + } + return []; + } + isOldFormat(content) { // Détecter l'ancien format (simple array vocabulary) return content.vocabulary && Array.isArray(content.vocabulary) && diff --git a/js/core/content-scanner.js b/js/core/content-scanner.js index 4790f39..d58bffa 100644 --- a/js/core/content-scanner.js +++ b/js/core/content-scanner.js @@ -98,12 +98,15 @@ class ContentScanner { async preloadKnownFiles() { const knownFiles = [ 'sbs-level-7-8-new.js', // Local JS file + 'SBS-level-1.js', // Side by Side Level 1 - English introduction with Chinese translation 'english-class-demo.json', // Remote JSON file 'english-exemple-commented.js', // Module JS complet nouvellement créé 'story-test.js', // Story test module 'story-prototype-1000words.js', // 1000-word story prototype 'story-complete-1000words.js', // Complete 1000-word story with pronunciation 'chinese-long-story.js', // Chinese story with English translation and pinyin + 'french-beginner-story.js', // French beginner story for English speakers + 'WTA1B1.js', // English letters and pets story with Chinese translation 'story-prototype-optimized.js', // Optimized story with centralized vocabulary 'test-compatibility.js', // Test content for compatibility system 'test-minimal.js', // Minimal test content @@ -285,6 +288,9 @@ class ContentScanner { 'StoryPrototype1000words': 'story-prototype-1000words.js', 'StoryComplete1000words': 'story-complete-1000words.js', 'ChineseLongStory': 'chinese-long-story.js', + 'FrenchBeginnerStory': 'french-beginner-story.js', + 'WTA1B1': 'WTA1B1.js', + 'SBSLevel1': 'SBS-level-1.js', 'StoryPrototypeOptimized': 'story-prototype-optimized.js', // AJOUT: Test compatibility modules 'TestMinimalContent': 'test-compatibility.js', @@ -483,7 +489,10 @@ class ContentScanner { getModuleName(contentId) { const mapping = { 'sbs-level-7-8-new': 'SBSLevel78New', + 'sbs-level-1': 'SBSLevel1', 'chinese-long-story': 'ChineseLongStory', + 'french-beginner-story': 'FrenchBeginnerStory', + 'wta1b1': 'WTA1B1', 'story-prototype-optimized': 'StoryPrototypeOptimized', 'test-compatibility': 'TestMinimalContent', 'test-minimal': 'TestMinimal', @@ -728,8 +737,11 @@ class ContentScanner { // Mapping spécifique pour certains noms de fichiers const specialMappings = { 'sbs-level-7-8-new': 'SBSLevel78New', + 'sbs-level-1': 'SBSLevel1', 'english-class-demo': 'EnglishClassDemo', 'chinese-long-story': 'ChineseLongStory', + 'french-beginner-story': 'FrenchBeginnerStory', + 'wta1b1': 'WTA1B1', 'story-prototype-optimized': 'StoryPrototypeOptimized' }; diff --git a/js/core/game-loader.js b/js/core/game-loader.js index 2500387..7ca5729 100644 --- a/js/core/game-loader.js +++ b/js/core/game-loader.js @@ -322,8 +322,11 @@ const GameLoader = { 'adventure-reader': 'AdventureReader', 'chinese-study': 'ChineseStudy', 'story-reader': 'StoryReader', + 'grammar-discovery': 'GrammarDiscovery', 'word-storm': 'WordStorm', - 'word-discovery': 'WordDiscovery' + 'word-discovery': 'WordDiscovery', + 'letter-discovery': 'LetterDiscovery', + 'river-run': 'RiverRun' }; return names[gameType] || gameType; }, @@ -332,8 +335,13 @@ const GameLoader = { // Utilise la même logique que le ContentScanner const mapping = { 'sbs-level-7-8-new': 'SBSLevel78New', + 'sbs-level-1': 'SBSLevel1', 'basic-chinese': 'BasicChinese', 'english-class-demo': 'EnglishClassDemo', + 'chinese-long-story': 'ChineseLongStory', + 'french-beginner-story': 'FrenchBeginnerStory', + 'wta1b1': 'WTA1B1', + 'story-prototype-optimized': 'StoryPrototypeOptimized', 'test-minimal-content': 'TestMinimalContent', 'test-rich-content': 'TestRichContent', 'test-sentence-only': 'TestSentenceOnly', diff --git a/js/core/navigation.js b/js/core/navigation.js index ca72eb6..980e94d 100644 --- a/js/core/navigation.js +++ b/js/core/navigation.js @@ -103,6 +103,24 @@ const AppNavigation = { name: 'Word Discovery', icon: '🔍', description: 'Learn new words with images and interactive practice!' + }, + 'letter-discovery': { + enabled: true, + name: 'Letter Discovery', + icon: '🔤', + description: 'Discover letters first, then explore words that start with each letter!' + }, + 'river-run': { + enabled: true, + name: 'River Run', + icon: '🌊', + description: 'Navigate the river and catch your target words while avoiding obstacles!' + }, + 'grammar-discovery': { + enabled: true, + name: 'Grammar Discovery', + icon: '📚', + description: 'Discover and learn grammar patterns through interactive examples!' } }, content: { @@ -148,6 +166,12 @@ const AppNavigation = { icon: '🐉', description: 'Chinese story with English translation and pinyin pronunciation' }, + 'chinese-beginner-story': { + enabled: true, + name: 'Le Jardin Magique (Chinese → French)', + icon: '🌸', + description: 'Simple Chinese story for beginners with French translation' + }, 'story-prototype-optimized': { enabled: true, name: 'The Magical Library (Optimized)', @@ -668,6 +692,7 @@ const AppNavigation = { 'basic-chinese': 'BasicChinese', 'english-class-demo': 'EnglishClassDemo', 'chinese-long-story': 'ChineseLongStory', + 'chinese-beginner-story': 'ChineseBeginnerStory', 'test-minimal': 'TestMinimal', 'test-rich': 'TestRich' }; diff --git a/js/games/adventure-reader.js b/js/games/adventure-reader.js index 569b519..698b570 100644 --- a/js/games/adventure-reader.js +++ b/js/games/adventure-reader.js @@ -22,6 +22,13 @@ class AdventureReaderGame { this.isPlayerMoving = false; this.isPlayerInvulnerable = false; this.invulnerabilityTimeout = null; + + // TTS settings + this.autoPlayTTS = true; + this.ttsEnabled = true; + + // Expose content globally for SettingsManager TTS language detection + window.currentGameContent = this.content; // Content extraction this.vocabulary = this.extractVocabulary(this.content); @@ -702,7 +709,14 @@ class AdventureReaderGame { popup.style.display = 'block'; popup.classList.add('show'); - + + // Auto-play TTS for vocabulary + if (this.autoPlayTTS && this.ttsEnabled) { + setTimeout(() => { + this.speakText(vocab.original_language, { rate: 0.8 }); + }, 400); // Small delay to let popup appear + } + setTimeout(() => { popup.classList.remove('show'); setTimeout(() => { @@ -747,6 +761,13 @@ class AdventureReaderGame { modal.style.display = 'flex'; modal.classList.add('show'); + + // Auto-play TTS for sentence + if (this.autoPlayTTS && this.ttsEnabled) { + setTimeout(() => { + this.speakText(sentence.original_language, { rate: 0.8 }); + }, 600); // Longer delay for modal animation + } } closeModal() { @@ -1202,7 +1223,61 @@ class AdventureReaderGame { this.generateDecorations(); } + // TTS Methods + speakText(text, options = {}) { + if (!text || !this.ttsEnabled) return; + + // Use SettingsManager if available for better language support + if (window.SettingsManager && window.SettingsManager.speak) { + const ttsOptions = { + lang: this.getContentLanguage(), + rate: options.rate || 0.8, + ...options + }; + + window.SettingsManager.speak(text, ttsOptions) + .catch(error => { + console.warn('🔊 SettingsManager TTS failed:', error); + this.fallbackTTS(text, ttsOptions); + }); + } else { + this.fallbackTTS(text, options); + } + } + + fallbackTTS(text, options = {}) { + if ('speechSynthesis' in window && text) { + // Cancel any ongoing speech + speechSynthesis.cancel(); + + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = this.getContentLanguage(); + utterance.rate = options.rate || 0.8; + utterance.volume = 1.0; + + speechSynthesis.speak(utterance); + } + } + + getContentLanguage() { + // Get language from content or use sensible defaults + if (this.content.language) { + const langMap = { + 'chinese': 'zh-CN', + 'english': 'en-US', + 'french': 'fr-FR', + 'spanish': 'es-ES' + }; + return langMap[this.content.language] || this.content.language; + } + return 'en-US'; // Default fallback + } + destroy() { + // Cancel any ongoing TTS + if ('speechSynthesis' in window) { + speechSynthesis.cancel(); + } this.container.innerHTML = ''; } } diff --git a/js/games/grammar-discovery.js b/js/games/grammar-discovery.js new file mode 100644 index 0000000..b1f83d5 --- /dev/null +++ b/js/games/grammar-discovery.js @@ -0,0 +1,1185 @@ +// === GRAMMAR DISCOVERY GAME === +// Interactive game for discovering and learning grammar patterns + +class GrammarDiscovery { + constructor({ container, content, onScoreUpdate, onGameEnd }) { + this.container = container; + this.content = content; + this.onScoreUpdate = onScoreUpdate; + this.onGameEnd = onGameEnd; + + this.score = 0; + + // ROTATION SYSTEM FOR FOCUSED CONCEPT LEARNING + this.rotationSteps = [ + 'explanation-basic', // 1. Explication de base (langue originale) + 'examples-simple', // 2. Exemples simples + 'exercise-basic', // 3. Exercices de base + 'explanation-detailed', // 4. Explication détaillée + 'examples-complex', // 5. Exemples complexes + 'exercise-intermediate', // 6. Exercices intermédiaires + 'summary', // 7. Résumé du concept + 'exercise-global' // 8. Exercice global final + ]; + + this.currentStep = 0; + this.grammarConcept = null; // Single focused concept (user selected) + this.conceptData = {}; + this.stepProgress = {}; + this.availableConcepts = []; // All available grammar concepts + this.conceptSelected = false; // Whether user has chosen a concept + + this.injectCSS(); + this.extractAvailableConcepts(); + this.init(); + } + + injectCSS() { + if (document.getElementById('grammar-discovery-styles')) return; + + const styleSheet = document.createElement('style'); + styleSheet.id = 'grammar-discovery-styles'; + styleSheet.textContent = ` + .grammar-discovery { + display: flex; + flex-direction: column; + height: 100%; + font-family: 'Arial', sans-serif; + } + + .grammar-hud { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 15px; + display: flex; + justify-content: space-between; + align-items: center; + border-radius: 10px 10px 0 0; + } + + .grammar-phase { + font-size: 18px; + font-weight: bold; + display: flex; + align-items: center; + gap: 10px; + } + + .phase-icon { + font-size: 24px; + } + + .grammar-content { + flex: 1; + display: flex; + flex-direction: column; + padding: 20px; + background: linear-gradient(145deg, #f8f9ff, #e6e9ff); + overflow-y: auto; + } + + .rule-card { + background: white; + border-radius: 15px; + padding: 25px; + margin-bottom: 20px; + box-shadow: 0 8px 32px rgba(102, 126, 234, 0.1); + border: 2px solid transparent; + transition: all 0.3s ease; + } + + .rule-card.active { + border-color: #667eea; + transform: translateY(-2px); + box-shadow: 0 12px 40px rgba(102, 126, 234, 0.2); + } + + .rule-title { + font-size: 24px; + font-weight: bold; + color: #4c51bf; + margin-bottom: 15px; + display: flex; + align-items: center; + gap: 10px; + } + + .rule-explanation { + font-size: 16px; + line-height: 1.6; + color: #4a5568; + margin-bottom: 20px; + background: #f7fafc; + padding: 15px; + border-radius: 8px; + border-left: 4px solid #667eea; + } + + .examples-section { + margin-top: 20px; + } + + .example-item { + background: #ffffff; + border: 2px solid #e2e8f0; + border-radius: 12px; + padding: 20px; + margin-bottom: 15px; + transition: all 0.3s ease; + cursor: pointer; + } + + .example-item:hover { + border-color: #667eea; + transform: translateX(5px); + } + + .example-item.revealed { + border-color: #48bb78; + background: linear-gradient(135deg, #f0fff4, #c6f6d5); + } + + .chinese-text { + font-size: 22px; + font-weight: bold; + color: #2d3748; + margin-bottom: 8px; + } + + .english-text { + font-size: 18px; + color: #4a5568; + margin-bottom: 8px; + } + + .pronunciation { + font-size: 16px; + color: #718096; + font-style: italic; + margin-bottom: 10px; + } + + .explanation-text { + font-size: 14px; + color: #667eea; + background: #edf2f7; + padding: 10px; + border-radius: 6px; + display: none; + } + + .example-item.revealed .explanation-text { + display: block; + animation: fadeIn 0.5s ease; + } + + .discovery-controls { + display: flex; + gap: 15px; + margin-top: 20px; + justify-content: center; + } + + .discover-btn { + background: linear-gradient(135deg, #667eea, #764ba2); + color: white; + border: none; + padding: 12px 25px; + border-radius: 25px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); + } + + .discover-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4); + } + + .discover-btn:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; + } + + .practice-question { + background: white; + border-radius: 15px; + padding: 25px; + margin-bottom: 20px; + box-shadow: 0 8px 32px rgba(102, 126, 234, 0.1); + } + + .question-text { + font-size: 20px; + color: #2d3748; + margin-bottom: 20px; + line-height: 1.6; + } + + .options-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 15px; + margin-bottom: 20px; + } + + .option-btn { + background: white; + border: 2px solid #e2e8f0; + border-radius: 12px; + padding: 15px; + font-size: 16px; + cursor: pointer; + transition: all 0.3s ease; + text-align: center; + } + + .option-btn:hover { + border-color: #667eea; + background: #f7fafc; + } + + .option-btn.correct { + border-color: #48bb78; + background: linear-gradient(135deg, #f0fff4, #c6f6d5); + color: #22543d; + } + + .option-btn.incorrect { + border-color: #f56565; + background: linear-gradient(135deg, #fed7d7, #feb2b2); + color: #742a2a; + } + + .feedback { + background: #f7fafc; + border-radius: 10px; + padding: 15px; + margin-top: 15px; + border-left: 4px solid #667eea; + display: none; + } + + .feedback.show { + display: block; + animation: slideIn 0.3s ease; + } + + .progress-bar { + background: #e2e8f0; + height: 6px; + border-radius: 3px; + margin: 10px 0; + overflow: hidden; + } + + .progress-fill { + background: linear-gradient(90deg, #667eea, #764ba2); + height: 100%; + border-radius: 3px; + transition: width 0.5s ease; + } + + @keyframes fadeIn { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } + } + + @keyframes slideIn { + from { opacity: 0; transform: translateX(-20px); } + to { opacity: 1; transform: translateX(0); } + } + + .concept-selector { + background: white; + border-radius: 15px; + padding: 30px; + margin: 20px 0; + box-shadow: 0 8px 32px rgba(102, 126, 234, 0.1); + text-align: center; + } + + .selector-title { + font-size: 24px; + font-weight: bold; + color: #4c51bf; + margin-bottom: 20px; + } + + .selector-description { + font-size: 16px; + color: #4a5568; + margin-bottom: 25px; + line-height: 1.6; + } + + .concept-dropdown { + width: 100%; + max-width: 400px; + padding: 15px; + font-size: 16px; + border: 2px solid #e2e8f0; + border-radius: 10px; + background: white; + margin-bottom: 20px; + cursor: pointer; + transition: all 0.3s ease; + } + + .concept-dropdown:hover { + border-color: #667eea; + } + + .concept-dropdown:focus { + outline: none; + border-color: #667eea; + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); + } + + .concept-preview { + background: #f7fafc; + border-radius: 10px; + padding: 20px; + margin: 20px 0; + text-align: left; + display: none; + } + + .concept-preview.show { + display: block; + animation: fadeIn 0.3s ease; + } + + .preview-title { + font-size: 18px; + font-weight: bold; + color: #2d3748; + margin-bottom: 10px; + } + + .preview-explanation { + font-size: 14px; + color: #4a5568; + margin-bottom: 15px; + } + + .preview-stats { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 10px; + font-size: 12px; + color: #718096; + } + + .stat-item { + background: white; + padding: 8px; + border-radius: 6px; + text-align: center; + } + + .phase-complete { + text-align: center; + padding: 40px; + background: linear-gradient(135deg, #f0fff4, #c6f6d5); + border-radius: 15px; + margin: 20px 0; + } + + .complete-icon { + font-size: 48px; + margin-bottom: 20px; + } + + .complete-title { + font-size: 24px; + font-weight: bold; + color: #22543d; + margin-bottom: 15px; + } + + .tts-controls { + display: flex; + gap: 10px; + align-items: center; + margin-top: 10px; + } + + .tts-btn { + background: #667eea; + color: white; + border: none; + border-radius: 20px; + padding: 8px 15px; + cursor: pointer; + font-size: 14px; + transition: all 0.2s ease; + } + + .tts-btn:hover { + background: #5a67d8; + transform: scale(1.05); + } + `; + document.head.appendChild(styleSheet); + } + + extractAvailableConcepts() { + if (!this.content || !this.content.grammar) { + console.error('No grammar content found for Grammar Discovery game'); + return; + } + + // GET ALL AVAILABLE CONCEPTS for selection + this.availableConcepts = Object.entries(this.content.grammar).map(([key, conceptData]) => ({ + id: key, + title: conceptData.title, + explanation: conceptData.explanation, + data: conceptData + })); + + console.log(`🎯 Found ${this.availableConcepts.length} grammar concepts available for selection`); + } + + selectConcept(conceptId) { + const selectedConcept = this.availableConcepts.find(c => c.id === conceptId); + if (!selectedConcept) { + console.error('Concept not found:', conceptId); + return; + } + + this.grammarConcept = conceptId; + this.conceptData = selectedConcept.data; + this.conceptSelected = true; + + console.log(`🎯 Selected concept: ${this.grammarConcept}`); + + // ORGANIZE CONTENT BY DIFFICULTY LEVELS + this.organizeConceptContent(); + + // Start the rotation cycle + this.startRotationCycle(); + } + + organizeConceptContent() { + const concept = this.conceptData; + + // BASIC EXAMPLES (first 2-3 simple ones) + this.simpleExamples = []; + this.complexExamples = []; + + if (concept.examples) { + this.simpleExamples = concept.examples.slice(0, 3); + this.complexExamples = concept.examples.slice(3); + } + + // Get examples from detailed explanation sections + if (concept.detailedExplanation) { + Object.values(concept.detailedExplanation).forEach(section => { + if (section.examples) { + // First 2 go to simple, rest to complex + const sectionSimple = section.examples.slice(0, 2); + const sectionComplex = section.examples.slice(2); + + this.simpleExamples.push(...sectionSimple); + this.complexExamples.push(...sectionComplex); + } + }); + } + + // ORGANIZE EXERCISES BY DIFFICULTY + this.basicExercises = []; + this.intermediateExercises = []; + this.globalExercises = []; + + if (this.content.fillInBlanks) { + this.content.fillInBlanks.forEach(exercise => { + // Accept all fillInBlanks for focused grammar lesson content + const isGrammarLesson = this.content.type === 'grammar_course'; + const isRelevant = isGrammarLesson || + exercise.grammarFocus === this.grammarConcept || + exercise.grammarFocus === 'completion' || + exercise.grammarFocus === 'change-of-state'; + + if (isRelevant) { + // Determine difficulty by sentence complexity + if (exercise.sentence.length < 15) { + this.basicExercises.push(exercise); + } else { + this.intermediateExercises.push(exercise); + } + } + }); + } + + if (this.content.corrections) { + this.content.corrections.forEach(correction => { + // Accept all corrections for focused grammar lesson content + const isGrammarLesson = this.content.type === 'grammar_course'; + const isRelevant = isGrammarLesson || + correction.grammarFocus === this.grammarConcept; + + if (isRelevant) { + this.intermediateExercises.push({ + type: 'correction', + question: 'Which sentence is correct?', + options: [correction.correct, correction.incorrect], + correctAnswer: correction.correct, + explanation: correction.explanation + }); + } + }); + } + + // Global exercises combine multiple concepts + this.globalExercises = [...this.basicExercises, ...this.intermediateExercises]; + this.globalExercises = this.shuffleArray(this.globalExercises).slice(0, 5); + + console.log(`📊 Content organized: + - Simple examples: ${this.simpleExamples.length} + - Complex examples: ${this.complexExamples.length} + - Basic exercises: ${this.basicExercises.length} + - Intermediate exercises: ${this.intermediateExercises.length} + - Global exercises: ${this.globalExercises.length}`); + } + + init() { + this.container.innerHTML = ` +
+
+
+ 🎯 + Select Grammar Concept +
+ +
Score: 0
+
+
+ +
+
+ `; + + this.showConceptSelector(); + } + + showConceptSelector() { + if (this.availableConcepts.length === 0) { + console.error('No concepts available for selection'); + return; + } + + const contentDiv = document.getElementById('grammar-content'); + + contentDiv.innerHTML = ` +
+
+ 🎯 Choose Grammar Concept +
+
+ Select which grammar concept you want to focus on for intensive study with our 8-step rotation system. +
+ + + +
+ +
+ + +
+ `; + + // Store reference for global access + window.currentGrammarGame = this; + } + + previewConcept(conceptId) { + const previewDiv = document.getElementById('concept-preview'); + const startBtn = document.getElementById('start-btn'); + + if (!conceptId) { + previewDiv.classList.remove('show'); + startBtn.disabled = true; + return; + } + + const concept = this.availableConcepts.find(c => c.id === conceptId); + if (!concept) return; + + // Calculate stats for this concept + this.grammarConcept = conceptId; + this.conceptData = concept.data; + this.organizeConceptContent(); + + previewDiv.innerHTML = ` +
${concept.title}
+
${concept.explanation}
+
+
+ ${this.simpleExamples.length}
+ Simple Examples +
+
+ ${this.complexExamples.length}
+ Complex Examples +
+
+ ${this.basicExercises.length}
+ Basic Exercises +
+
+ ${this.intermediateExercises.length}
+ Intermediate Exercises +
+
+ ${this.globalExercises.length}
+ Final Test Questions +
+
+ `; + + previewDiv.classList.add('show'); + startBtn.disabled = false; + } + + startSelectedConcept() { + const dropdown = document.getElementById('concept-dropdown'); + const selectedConceptId = dropdown.value; + + if (!selectedConceptId) { + alert('Please select a grammar concept first!'); + return; + } + + // Update UI to show rotation progress + document.getElementById('step-progress').style.display = 'block'; + document.getElementById('phase-icon').textContent = '📚'; + document.getElementById('phase-text').textContent = 'Basic Explanation'; + + this.selectConcept(selectedConceptId); + } + + startRotationCycle() { + this.currentStep = 0; + this.showCurrentStep(); + } + + showCurrentStep() { + const stepType = this.rotationSteps[this.currentStep]; + const stepNumber = this.currentStep + 1; + + document.getElementById('current-step').textContent = stepNumber; + + // Update phase icon and text based on step + const phaseInfo = this.getPhaseInfo(stepType); + document.getElementById('phase-icon').textContent = phaseInfo.icon; + document.getElementById('phase-text').textContent = phaseInfo.text; + + // Store reference for global access + window.currentGrammarGame = this; + + // Show content for current step + switch (stepType) { + case 'explanation-basic': + this.showBasicExplanation(); + break; + case 'examples-simple': + this.showSimpleExamples(); + break; + case 'exercise-basic': + this.showBasicExercises(); + break; + case 'explanation-detailed': + this.showDetailedExplanation(); + break; + case 'examples-complex': + this.showComplexExamples(); + break; + case 'exercise-intermediate': + this.showIntermediateExercises(); + break; + case 'summary': + this.showSummary(); + break; + case 'exercise-global': + this.showGlobalExercises(); + break; + } + } + + getPhaseInfo(stepType) { + const phaseMap = { + 'explanation-basic': { icon: '📚', text: 'Basic Explanation' }, + 'examples-simple': { icon: '💡', text: 'Simple Examples' }, + 'exercise-basic': { icon: '✏️', text: 'Basic Practice' }, + 'explanation-detailed': { icon: '🔍', text: 'Detailed Explanation' }, + 'examples-complex': { icon: '🧩', text: 'Complex Examples' }, + 'exercise-intermediate': { icon: '💪', text: 'Intermediate Practice' }, + 'summary': { icon: '📝', text: 'Summary' }, + 'exercise-global': { icon: '🏆', text: 'Final Test' } + }; + return phaseMap[stepType] || { icon: '❓', text: 'Unknown' }; + } + + nextStep() { + this.currentStep++; + if (this.currentStep >= this.rotationSteps.length) { + this.completeRotation(); + } else { + this.showCurrentStep(); + } + } + + completeRotation() { + const contentDiv = document.getElementById('grammar-content'); + contentDiv.innerHTML = ` +
+
🎉
+
Grammar Concept Mastered!
+

You've completed the full rotation for: ${this.conceptData.title}

+

Final Score: ${this.score}

+ +
+ `; + + this.onGameEnd(this.score); + } + + // === ROTATION STEP IMPLEMENTATIONS === + + showBasicExplanation() { + const concept = this.conceptData; + const contentDiv = document.getElementById('grammar-content'); + + contentDiv.innerHTML = ` +
+
+ 📚 ${concept.title} +
+
+ ${concept.explanation} +
+ + ${concept.mainRules ? ` +
+

🎯 Key Rules:

+ +
+ ` : ''} + +
+ +
+
+ `; + } + + showSimpleExamples() { + const contentDiv = document.getElementById('grammar-content'); + + contentDiv.innerHTML = ` +
+
+ 💡 Simple Examples +
+
+ ${this.simpleExamples.map((example, index) => ` +
+
${example.chinese}
+
${example.english}
+
${example.pronunciation || ''}
+
${example.explanation || example.breakdown || ''}
+
+ + +
+
+ `).join('')} +
+ +
+ +
+
+ `; + + // Auto-play first example + if (this.simpleExamples.length > 0) { + setTimeout(() => { + this.speakChinese(this.simpleExamples[0].chinese); + }, 1000); + } + } + + showBasicExercises() { + if (this.basicExercises.length === 0) { + this.nextStep(); + return; + } + + this.currentExerciseSet = this.basicExercises; + this.currentExerciseIndex = 0; + this.showExercise('basic'); + } + + showDetailedExplanation() { + const concept = this.conceptData; + const contentDiv = document.getElementById('grammar-content'); + + let detailsHtml = ''; + if (concept.detailedExplanation) { + detailsHtml = Object.entries(concept.detailedExplanation).map(([key, section]) => ` +
+

🔍 ${section.title}

+

${section.explanation}

+ ${section.pattern ? `
Pattern: ${section.pattern}
` : ''} +
+ `).join(''); + } + + contentDiv.innerHTML = ` +
+
+ 🔍 Detailed Explanation +
+ + ${detailsHtml} + + ${concept.commonMistakes ? ` +
+

⚠️ Common Mistakes:

+ ${concept.commonMistakes.map(mistake => ` +
+
❌ ${mistake.wrong}
+
✅ ${mistake.correct}
+
${mistake.explanation}
+
+ `).join('')} +
+ ` : ''} + +
+ +
+
+ `; + } + + showComplexExamples() { + const contentDiv = document.getElementById('grammar-content'); + + contentDiv.innerHTML = ` +
+
+ 🧩 Complex Examples +
+
+ ${this.complexExamples.map((example, index) => ` +
+
${example.chinese}
+
${example.english}
+
${example.pronunciation || ''}
+
${example.explanation || example.breakdown || ''}
+
+ + +
+
+ `).join('')} +
+ +
+ +
+
+ `; + } + + showIntermediateExercises() { + if (this.intermediateExercises.length === 0) { + this.nextStep(); + return; + } + + this.currentExerciseSet = this.intermediateExercises; + this.currentExerciseIndex = 0; + this.showExercise('intermediate'); + } + + showSummary() { + const concept = this.conceptData; + const contentDiv = document.getElementById('grammar-content'); + + contentDiv.innerHTML = ` +
+
+ 📝 Summary: ${concept.title} +
+ +
+

🎯 What You've Learned:

+
+
+ Basic Concept:
+ ${concept.explanation} +
+ ${concept.mainRules ? ` +
+ Key Rules:
+ ${concept.mainRules.map(rule => `• ${rule}`).join('
')} +
+ ` : ''} + ${concept.practicePoints ? ` +
+ Practice Points:
+ ${concept.practicePoints.map(point => `• ${point}`).join('
')} +
+ ` : ''} +
+
+ +
+ +
+
+ `; + } + + showGlobalExercises() { + if (this.globalExercises.length === 0) { + this.completeRotation(); + return; + } + + this.currentExerciseSet = this.globalExercises; + this.currentExerciseIndex = 0; + this.showExercise('global'); + } + + // === UNIFIED EXERCISE SYSTEM === + + showExercise(type) { + const exercise = this.currentExerciseSet[this.currentExerciseIndex]; + if (!exercise) { + this.nextStep(); + return; + } + + const contentDiv = document.getElementById('grammar-content'); + const typeInfo = { + 'basic': { title: '✏️ Basic Practice', color: '#48bb78' }, + 'intermediate': { title: '💪 Intermediate Practice', color: '#3182ce' }, + 'global': { title: '🏆 Final Test', color: '#805ad5' } + }; + + const info = typeInfo[type] || typeInfo['basic']; + + if (exercise.type === 'correction') { + contentDiv.innerHTML = ` +
+
+ ${info.title} +
+
+ ${exercise.question} +
+
+ ${exercise.options.map(option => ` + + `).join('')} +
+
+ Explanation: ${exercise.explanation} +
+
+ Exercise ${this.currentExerciseIndex + 1} of ${this.currentExerciseSet.length} +
+
+ `; + } else { + // Fill in the blank + contentDiv.innerHTML = ` +
+
+ ${info.title} +
+
+ Fill in the blank: ${exercise.sentence} +
+
+ ${exercise.options.map(option => ` + + `).join('')} +
+
+ Explanation: ${exercise.explanation} +
+
+ Exercise ${this.currentExerciseIndex + 1} of ${this.currentExerciseSet.length} +
+
+ `; + } + } + + selectAnswer(selected, correct, exerciseType) { + const buttons = document.querySelectorAll('.option-btn'); + const feedback = document.getElementById('feedback'); + + buttons.forEach(btn => { + btn.disabled = true; + if (btn.textContent.trim() === correct) { + btn.classList.add('correct'); + } else if (btn.textContent.trim() === selected && selected !== correct) { + btn.classList.add('incorrect'); + } + }); + + // Scoring based on exercise type + if (selected === correct) { + const points = exerciseType === 'global' ? 30 : (exerciseType === 'intermediate' ? 20 : 15); + this.score += points; + } else { + this.score = Math.max(0, this.score - 5); + } + + this.onScoreUpdate(this.score); + document.getElementById('score-value').textContent = this.score; + + feedback.classList.add('show'); + + setTimeout(() => { + this.currentExerciseIndex++; + if (this.currentExerciseIndex >= this.currentExerciseSet.length) { + // Finished this exercise set + this.nextStep(); + } else { + // Show next exercise in set + this.showExercise(exerciseType); + } + }, 2500); + } + + restart() { + this.score = 0; + this.currentStep = 0; + this.currentExerciseIndex = 0; + this.conceptSelected = false; + this.grammarConcept = null; + this.conceptData = {}; + + // Reset UI to concept selector + document.getElementById('score-value').textContent = '0'; + document.getElementById('phase-icon').textContent = '🎯'; + document.getElementById('phase-text').textContent = 'Select Grammar Concept'; + document.getElementById('step-progress').style.display = 'none'; + + this.onScoreUpdate(0); + this.showConceptSelector(); + } + + // TTS Functions + speakChinese(text) { + if (window.SettingsManager && window.SettingsManager.speak) { + window.SettingsManager.speak(text, { + lang: 'zh-CN', + rate: 0.8 + }); + } + } + + speakEnglish(text) { + if (window.SettingsManager && window.SettingsManager.speak) { + window.SettingsManager.speak(text, { + lang: 'en-US', + rate: 0.9 + }); + } + } + + // Utility Functions + shuffleArray(array) { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; + } + + restart() { + this.score = 0; + this.currentPhase = 'discovery'; + this.currentRule = 0; + this.currentExampleIndex = 0; + this.currentPracticeIndex = 0; + this.practiceQuestions = this.shuffleArray(this.practiceQuestions); + + document.getElementById('phase-text').innerHTML = ` + 🔍 Discovery Phase + `; + document.getElementById('score-value').textContent = '0'; + + this.onScoreUpdate(0); + this.startDiscovery(); + } + + start() { + // Game starts automatically in constructor + } + + destroy() { + // Cleanup + const styleSheet = document.getElementById('grammar-discovery-styles'); + if (styleSheet) { + styleSheet.remove(); + } + + if (window.currentGrammarGame === this) { + delete window.currentGrammarGame; + } + } +} + +// Export to global +window.GameModules = window.GameModules || {}; +window.GameModules.GrammarDiscovery = GrammarDiscovery; \ No newline at end of file diff --git a/js/games/letter-discovery.js b/js/games/letter-discovery.js new file mode 100644 index 0000000..c130b14 --- /dev/null +++ b/js/games/letter-discovery.js @@ -0,0 +1,774 @@ +// === LETTER DISCOVERY GAME === +// Discover letters first, then explore words that start with each letter + +class LetterDiscovery { + constructor({ container, content, onScoreUpdate, onGameEnd }) { + this.container = container; + this.content = content; + this.onScoreUpdate = onScoreUpdate; + this.onGameEnd = onGameEnd; + + // Game state + this.currentPhase = 'letter-discovery'; // letter-discovery, word-exploration, practice + this.currentLetterIndex = 0; + this.discoveredLetters = []; + this.currentLetter = null; + this.currentWordIndex = 0; + this.discoveredWords = []; + this.score = 0; + this.lives = 3; + + // Content processing + this.letters = []; + this.letterWords = {}; // Map letter -> words starting with that letter + + // Practice system + this.practiceLevel = 1; + this.practiceRound = 0; + this.maxPracticeRounds = 8; + this.practiceCorrectAnswers = 0; + this.practiceErrors = 0; + this.currentPracticeItems = []; + + this.injectCSS(); + this.extractContent(); + this.init(); + } + + injectCSS() { + if (document.getElementById('letter-discovery-styles')) return; + + const styleSheet = document.createElement('style'); + styleSheet.id = 'letter-discovery-styles'; + styleSheet.textContent = ` + .letter-discovery-wrapper { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + padding: 20px; + position: relative; + overflow-y: auto; + } + + .letter-discovery-hud { + display: flex; + justify-content: space-between; + align-items: center; + background: rgba(255,255,255,0.1); + padding: 15px 20px; + border-radius: 15px; + backdrop-filter: blur(10px); + margin-bottom: 20px; + flex-wrap: wrap; + gap: 10px; + } + + .hud-group { + display: flex; + align-items: center; + gap: 15px; + } + + .hud-item { + color: white; + font-weight: bold; + font-size: 1.1em; + } + + .phase-indicator { + background: rgba(255,255,255,0.2); + padding: 8px 16px; + border-radius: 20px; + font-size: 0.9em; + color: white; + backdrop-filter: blur(5px); + } + + .letter-discovery-main { + background: rgba(255,255,255,0.1); + border-radius: 20px; + padding: 30px; + backdrop-filter: blur(10px); + min-height: 70vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .game-content { + width: 100%; + max-width: 900px; + text-align: center; + } + + /* Letter Display Styles */ + .letter-card { + background: rgba(255,255,255,0.95); + border-radius: 25px; + padding: 60px 40px; + margin: 30px auto; + max-width: 400px; + box-shadow: 0 20px 40px rgba(0,0,0,0.1); + transform: scale(0.8); + animation: letterAppear 0.8s ease-out forwards; + } + + @keyframes letterAppear { + to { transform: scale(1); } + } + + .letter-display { + font-size: 8em; + font-weight: bold; + color: #667eea; + margin-bottom: 20px; + text-shadow: 0 4px 8px rgba(0,0,0,0.1); + font-family: 'Arial Black', Arial, sans-serif; + } + + .letter-info { + font-size: 1.5em; + color: #333; + margin-bottom: 15px; + } + + .letter-pronunciation { + font-size: 1.2em; + color: #666; + font-style: italic; + margin-bottom: 25px; + } + + .letter-controls { + display: flex; + gap: 15px; + justify-content: center; + margin-top: 30px; + } + + /* Word Exploration Styles */ + .word-exploration-header { + background: rgba(255,255,255,0.1); + padding: 20px; + border-radius: 15px; + margin-bottom: 30px; + backdrop-filter: blur(5px); + } + + .exploring-letter { + font-size: 3em; + color: white; + margin-bottom: 10px; + font-weight: bold; + } + + .word-progress { + color: rgba(255,255,255,0.8); + font-size: 1.1em; + } + + .word-card { + background: rgba(255,255,255,0.95); + border-radius: 20px; + padding: 40px 30px; + margin: 25px auto; + max-width: 500px; + box-shadow: 0 15px 30px rgba(0,0,0,0.1); + transform: translateY(20px); + animation: wordSlideIn 0.6s ease-out forwards; + } + + @keyframes wordSlideIn { + to { transform: translateY(0); } + } + + .word-text { + font-size: 2.5em; + color: #667eea; + margin-bottom: 15px; + font-weight: bold; + } + + .word-translation { + font-size: 1.3em; + color: #333; + margin-bottom: 10px; + } + + .word-pronunciation { + font-size: 1.1em; + color: #666; + font-style: italic; + } + + /* Practice Challenge Styles */ + .practice-challenge { + text-align: center; + margin-bottom: 30px; + } + + .challenge-text { + font-size: 1.8em; + color: white; + margin-bottom: 25px; + text-shadow: 0 2px 4px rgba(0,0,0,0.3); + } + + .practice-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 15px; + max-width: 800px; + margin: 0 auto; + } + + .practice-option { + background: rgba(255,255,255,0.9); + border: none; + border-radius: 15px; + padding: 20px; + font-size: 1.2em; + cursor: pointer; + transition: all 0.3s ease; + color: #333; + font-weight: 500; + } + + .practice-option:hover { + background: rgba(255,255,255,1); + transform: translateY(-3px); + box-shadow: 0 8px 20px rgba(0,0,0,0.2); + } + + .practice-option.correct { + background: #4CAF50; + color: white; + animation: correctPulse 0.6s ease; + } + + .practice-option.incorrect { + background: #F44336; + color: white; + animation: incorrectShake 0.6s ease; + } + + @keyframes correctPulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.05); } + } + + @keyframes incorrectShake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-5px); } + 75% { transform: translateX(5px); } + } + + .practice-stats { + display: flex; + justify-content: space-around; + margin-top: 20px; + color: white; + font-size: 1.1em; + } + + .stat-item { + text-align: center; + padding: 10px; + background: rgba(255,255,255,0.1); + border-radius: 10px; + backdrop-filter: blur(5px); + } + + /* Control Buttons */ + .discovery-btn { + background: linear-gradient(45deg, #667eea, #764ba2); + color: white; + border: none; + padding: 15px 30px; + border-radius: 25px; + font-size: 1.1em; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + margin: 0 10px; + } + + .discovery-btn:hover { + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(0,0,0,0.3); + } + + .discovery-btn:active { + transform: translateY(0); + } + + .audio-btn { + background: none; + border: none; + font-size: 2em; + cursor: pointer; + color: #667eea; + margin-left: 15px; + transition: all 0.3s ease; + } + + .audio-btn:hover { + transform: scale(1.2); + color: #764ba2; + } + + /* Completion Message */ + .completion-message { + text-align: center; + padding: 40px; + background: rgba(255,255,255,0.1); + border-radius: 20px; + backdrop-filter: blur(10px); + color: white; + } + + .completion-title { + font-size: 2.5em; + margin-bottom: 20px; + color: #00ff88; + text-shadow: 0 2px 10px rgba(0,255,136,0.3); + } + + .completion-stats { + font-size: 1.3em; + margin-bottom: 30px; + line-height: 1.6; + } + + /* Responsive Design */ + @media (max-width: 768px) { + .letter-discovery-wrapper { + padding: 15px; + } + + .letter-display { + font-size: 6em; + } + + .word-text { + font-size: 2em; + } + + .challenge-text { + font-size: 1.5em; + } + + .practice-grid { + grid-template-columns: 1fr; + } + } + `; + document.head.appendChild(styleSheet); + } + + extractContent() { + logSh('🔍 Letter Discovery - Extracting content...', 'INFO'); + + // Check if content has letter structure + if (this.content.letters) { + this.letters = Object.keys(this.content.letters); + this.letterWords = this.content.letters; + logSh(`📝 Found ${this.letters.length} letters with words`, 'INFO'); + } else { + // Fallback: Create letter structure from vocabulary + this.generateLetterStructure(); + } + + if (this.letters.length === 0) { + throw new Error('No letters found in content'); + } + + logSh(`🎯 Letter Discovery ready: ${this.letters.length} letters`, 'INFO'); + } + + generateLetterStructure() { + logSh('🔧 Generating letter structure from vocabulary...', 'INFO'); + + const letterMap = {}; + + if (this.content.vocabulary) { + Object.keys(this.content.vocabulary).forEach(word => { + const firstLetter = word.charAt(0).toUpperCase(); + if (!letterMap[firstLetter]) { + letterMap[firstLetter] = []; + } + + const wordData = this.content.vocabulary[word]; + letterMap[firstLetter].push({ + word: word, + translation: typeof wordData === 'string' ? wordData : wordData.translation || wordData.user_language, + pronunciation: wordData.pronunciation || wordData.prononciation, + type: wordData.type, + image: wordData.image, + audioFile: wordData.audioFile + }); + }); + } + + this.letters = Object.keys(letterMap).sort(); + this.letterWords = letterMap; + + logSh(`📝 Generated ${this.letters.length} letters from vocabulary`, 'INFO'); + } + + init() { + this.container.innerHTML = ` +
+
+
+
Score: ${this.score}
+
Lives: ${this.lives}
+
+
Letter Discovery
+
+
Progress: 0/${this.letters.length}
+
+
+
+
+ +
+
+
+ `; + + this.updateHUD(); + } + + start() { + this.showLetterCard(); + } + + updateHUD() { + const scoreDisplay = document.getElementById('score-display'); + const livesDisplay = document.getElementById('lives-display'); + const progressDisplay = document.getElementById('progress-display'); + const phaseIndicator = document.getElementById('phase-indicator'); + + if (scoreDisplay) scoreDisplay.textContent = this.score; + if (livesDisplay) livesDisplay.textContent = this.lives; + + if (this.currentPhase === 'letter-discovery') { + if (progressDisplay) progressDisplay.textContent = `${this.currentLetterIndex}/${this.letters.length}`; + if (phaseIndicator) phaseIndicator.textContent = 'Letter Discovery'; + } else if (this.currentPhase === 'word-exploration') { + if (progressDisplay) progressDisplay.textContent = `${this.currentWordIndex}/${this.letterWords[this.currentLetter].length}`; + if (phaseIndicator) phaseIndicator.textContent = `Exploring Letter "${this.currentLetter}"`; + } else if (this.currentPhase === 'practice') { + if (progressDisplay) progressDisplay.textContent = `Round ${this.practiceRound + 1}/${this.maxPracticeRounds}`; + if (phaseIndicator) phaseIndicator.textContent = `Practice - Level ${this.practiceLevel}`; + } + } + + showLetterCard() { + if (this.currentLetterIndex >= this.letters.length) { + this.showCompletion(); + return; + } + + const letter = this.letters[this.currentLetterIndex]; + const gameContent = document.getElementById('game-content'); + + gameContent.innerHTML = ` +
+
${letter}
+
Letter "${letter}"
+
${this.getLetterPronunciation(letter)}
+
+ + +
+
+ `; + + // Store reference for button callbacks + window.currentLetterGame = this; + + // Auto-play letter sound + setTimeout(() => this.playLetterSound(letter), 500); + } + + getLetterPronunciation(letter) { + // Basic letter pronunciation guide + const pronunciations = { + 'A': 'ay', 'B': 'bee', 'C': 'see', 'D': 'dee', 'E': 'ee', + 'F': 'ef', 'G': 'gee', 'H': 'aych', 'I': 'eye', 'J': 'jay', + 'K': 'kay', 'L': 'el', 'M': 'em', 'N': 'en', 'O': 'oh', + 'P': 'pee', 'Q': 'cue', 'R': 'ar', 'S': 'ess', 'T': 'tee', + 'U': 'you', 'V': 'vee', 'W': 'double-you', 'X': 'ex', 'Y': 'why', 'Z': 'zee' + }; + return pronunciations[letter] || letter.toLowerCase(); + } + + playLetterSound(letter) { + if (window.SettingsManager && window.SettingsManager.speak) { + const speed = 0.8; // Slower for letters + window.SettingsManager.speak(letter, { + lang: this.content.language || 'en-US', + rate: speed + }).catch(error => { + console.warn('🔊 TTS failed for letter:', error); + }); + } + } + + discoverLetter() { + const letter = this.letters[this.currentLetterIndex]; + this.discoveredLetters.push(letter); + this.score += 10; + this.onScoreUpdate(this.score); + + // Start word exploration for this letter + this.currentLetter = letter; + this.currentPhase = 'word-exploration'; + this.currentWordIndex = 0; + + this.updateHUD(); + this.showWordExploration(); + } + + showWordExploration() { + const words = this.letterWords[this.currentLetter]; + + if (!words || this.currentWordIndex >= words.length) { + // Finished exploring words for this letter + this.currentPhase = 'letter-discovery'; + this.currentLetterIndex++; + this.updateHUD(); + this.showLetterCard(); + return; + } + + const word = words[this.currentWordIndex]; + const gameContent = document.getElementById('game-content'); + + gameContent.innerHTML = ` +
+
Letter "${this.currentLetter}"
+
Word ${this.currentWordIndex + 1} of ${words.length}
+
+
+
${word.word}
+
${word.translation}
+ ${word.pronunciation ? `
[${word.pronunciation}]
` : ''} +
+ + +
+
+ `; + + // Add word to discovered list + this.discoveredWords.push(word); + + // Auto-play word sound + setTimeout(() => this.playWordSound(word.word), 500); + } + + playWordSound(word) { + if (window.SettingsManager && window.SettingsManager.speak) { + const speed = 0.9; + window.SettingsManager.speak(word, { + lang: this.content.language || 'en-US', + rate: speed + }).catch(error => { + console.warn('🔊 TTS failed for word:', error); + }); + } + } + + nextWord() { + this.currentWordIndex++; + this.score += 5; + this.onScoreUpdate(this.score); + this.updateHUD(); + this.showWordExploration(); + } + + showCompletion() { + const gameContent = document.getElementById('game-content'); + const totalWords = Object.values(this.letterWords).reduce((sum, words) => sum + words.length, 0); + + gameContent.innerHTML = ` +
+
🎉 All Letters Discovered!
+
+ Letters Discovered: ${this.discoveredLetters.length}
+ Words Learned: ${this.discoveredWords.length}
+ Final Score: ${this.score} +
+
+ + +
+
+ `; + } + + startPractice() { + this.currentPhase = 'practice'; + this.practiceLevel = 1; + this.practiceRound = 0; + this.practiceCorrectAnswers = 0; + this.practiceErrors = 0; + + // Create mixed practice from all discovered words + this.currentPracticeItems = this.shuffleArray([...this.discoveredWords]); + + this.updateHUD(); + this.showPracticeChallenge(); + } + + showPracticeChallenge() { + if (this.practiceRound >= this.maxPracticeRounds) { + this.endPractice(); + return; + } + + const currentItem = this.currentPracticeItems[this.practiceRound % this.currentPracticeItems.length]; + const gameContent = document.getElementById('game-content'); + + // Generate options (correct + 3 random) + const allWords = this.discoveredWords.filter(w => w.word !== currentItem.word); + const randomOptions = this.shuffleArray([...allWords]).slice(0, 3); + const options = this.shuffleArray([currentItem, ...randomOptions]); + + gameContent.innerHTML = ` +
+
What does "${currentItem.word}" mean?
+
+ ${options.map((option, index) => ` + + `).join('')} +
+
+
Correct: ${this.practiceCorrectAnswers}
+
Errors: ${this.practiceErrors}
+
Round: ${this.practiceRound + 1}/${this.maxPracticeRounds}
+
+
+ `; + + // Store correct answer for checking + this.currentCorrectAnswer = currentItem.word; + + // Auto-play word + setTimeout(() => this.playWordSound(currentItem.word), 500); + } + + selectPracticeAnswer(selectedIndex, selectedWord) { + const buttons = document.querySelectorAll('.practice-option'); + const isCorrect = selectedWord === this.currentCorrectAnswer; + + if (isCorrect) { + buttons[selectedIndex].classList.add('correct'); + this.practiceCorrectAnswers++; + this.score += 10; + this.onScoreUpdate(this.score); + } else { + buttons[selectedIndex].classList.add('incorrect'); + this.practiceErrors++; + // Show correct answer + buttons.forEach((btn, index) => { + if (btn.textContent.trim() === this.discoveredWords.find(w => w.word === this.currentCorrectAnswer)?.translation) { + btn.classList.add('correct'); + } + }); + } + + setTimeout(() => { + this.practiceRound++; + this.updateHUD(); + this.showPracticeChallenge(); + }, 1500); + } + + endPractice() { + const accuracy = Math.round((this.practiceCorrectAnswers / this.maxPracticeRounds) * 100); + const gameContent = document.getElementById('game-content'); + + gameContent.innerHTML = ` +
+
🏆 Practice Complete!
+
+ Accuracy: ${accuracy}%
+ Correct Answers: ${this.practiceCorrectAnswers}/${this.maxPracticeRounds}
+ Final Score: ${this.score} +
+
+ +
+
+ `; + + // End game + setTimeout(() => { + this.onGameEnd(this.score); + }, 3000); + } + + shuffleArray(array) { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; + } + + restart() { + this.currentPhase = 'letter-discovery'; + this.currentLetterIndex = 0; + this.discoveredLetters = []; + this.currentLetter = null; + this.currentWordIndex = 0; + this.discoveredWords = []; + this.score = 0; + this.lives = 3; + this.practiceLevel = 1; + this.practiceRound = 0; + this.practiceCorrectAnswers = 0; + this.practiceErrors = 0; + this.currentPracticeItems = []; + + this.updateHUD(); + this.start(); + } + + destroy() { + // Cleanup + if (window.currentLetterGame === this) { + delete window.currentLetterGame; + } + + const styleSheet = document.getElementById('letter-discovery-styles'); + if (styleSheet) { + styleSheet.remove(); + } + } +} + +// Register the game module +window.GameModules = window.GameModules || {}; +window.GameModules.LetterDiscovery = LetterDiscovery; \ No newline at end of file diff --git a/js/games/river-run.js b/js/games/river-run.js new file mode 100644 index 0000000..aef4f7c --- /dev/null +++ b/js/games/river-run.js @@ -0,0 +1,1001 @@ +// === RIVER RUN GAME === +// Endless runner on a river with floating words - avoid obstacles, catch target words! + +class RiverRun { + constructor({ container, content, onScoreUpdate, onGameEnd }) { + this.container = container; + this.content = content; + this.onScoreUpdate = onScoreUpdate; + this.onGameEnd = onGameEnd; + + // Game state + this.isRunning = false; + this.score = 0; + this.lives = 3; + this.level = 1; + this.speed = 2; // River flow speed + this.wordsCollected = 0; + + // Player + this.player = { + x: 50, // Percentage from left + y: 80, // Percentage from top + targetX: 50, + targetY: 80, + size: 40 + }; + + // Game objects + this.floatingWords = []; + this.currentTarget = null; + this.targetQueue = []; + this.powerUps = []; + + // River animation + this.riverOffset = 0; + this.particles = []; + + // Timing + this.lastSpawn = 0; + this.spawnInterval = 1000; // ms between word spawns (2x faster) + this.gameStartTime = Date.now(); + + // Word management + this.availableWords = []; + this.usedTargets = []; + + // Target word guarantee system + this.wordsSpawnedSinceTarget = 0; + this.maxWordsBeforeTarget = 10; // Guarantee target within 10 words + + this.injectCSS(); + this.extractContent(); + this.init(); + } + + injectCSS() { + if (document.getElementById('river-run-styles')) return; + + const styleSheet = document.createElement('style'); + styleSheet.id = 'river-run-styles'; + styleSheet.textContent = ` + .river-run-wrapper { + background: linear-gradient(180deg, #87CEEB 0%, #4682B4 50%, #2F4F4F 100%); + position: relative; + overflow: hidden; + height: 100vh; + cursor: crosshair; + } + + .river-run-hud { + position: absolute; + top: 20px; + left: 20px; + right: 20px; + display: flex; + justify-content: space-between; + z-index: 100; + color: white; + font-weight: bold; + text-shadow: 0 2px 4px rgba(0,0,0,0.5); + } + + .hud-left, .hud-right { + display: flex; + gap: 20px; + align-items: center; + } + + .target-display { + background: rgba(255,255,255,0.9); + color: #333; + padding: 10px 20px; + border-radius: 25px; + font-size: 1.2em; + font-weight: bold; + box-shadow: 0 4px 15px rgba(0,0,0,0.2); + animation: targetGlow 2s ease-in-out infinite alternate; + } + + @keyframes targetGlow { + from { box-shadow: 0 4px 15px rgba(0,0,0,0.2); } + to { box-shadow: 0 4px 20px rgba(255,215,0,0.6); } + } + + .river-canvas { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: + radial-gradient(ellipse at center top, rgba(135,206,235,0.3) 0%, transparent 70%), + linear-gradient(0deg, + rgba(70,130,180,0.1) 0%, + rgba(135,206,235,0.05) 50%, + rgba(173,216,230,0.1) 100% + ); + } + + .river-waves { + position: absolute; + width: 120%; + height: 100%; + background: + repeating-linear-gradient( + 0deg, + transparent 0px, + rgba(255,255,255,0.1) 2px, + transparent 4px, + transparent 20px + ); + animation: riverFlow 3s linear infinite; + } + + @keyframes riverFlow { + from { transform: translateY(-20px); } + to { transform: translateY(0px); } + } + + .player { + position: absolute; + width: 40px; + height: 40px; + background: linear-gradient(45deg, #8B4513, #A0522D); + border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; + box-shadow: + 0 2px 10px rgba(0,0,0,0.3), + inset 0 2px 5px rgba(255,255,255,0.3); + transition: all 0.3s ease-out; + z-index: 50; + transform-origin: center; + } + + .player::before { + content: '🛶'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 20px; + filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3)); + } + + .player.moving { + animation: playerRipple 0.5s ease-out; + } + + @keyframes playerRipple { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } + } + + .floating-word { + position: absolute; + background: rgba(255,255,255,0.95); + border: 3px solid #4682B4; + border-radius: 15px; + padding: 8px 15px; + font-size: 1.1em; + font-weight: bold; + color: #333; + cursor: pointer; + transition: all 0.2s ease; + z-index: 40; + box-shadow: + 0 4px 15px rgba(0,0,0,0.2), + 0 0 0 0 rgba(70,130,180,0.4); + animation: wordFloat 3s ease-in-out infinite alternate; + } + + @keyframes wordFloat { + from { transform: translateY(0px) rotate(-1deg); } + to { transform: translateY(-5px) rotate(1deg); } + } + + .floating-word:hover { + transform: scale(1.1) translateY(-3px); + box-shadow: + 0 6px 20px rgba(0,0,0,0.3), + 0 0 20px rgba(70,130,180,0.6); + } + + /* Words are neutral at spawn - styling happens at interaction */ + + .floating-word.collected { + animation: wordCollected 0.8s ease-out forwards; + } + + @keyframes wordCollected { + 0% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.3); + opacity: 0.8; + } + 100% { + transform: scale(0) translateY(-50px); + opacity: 0; + } + } + + .floating-word.missed { + animation: wordMissed 0.6s ease-out forwards; + } + + @keyframes wordMissed { + 0% { + transform: scale(1); + opacity: 1; + background: rgba(255,255,255,0.95); + } + 100% { + transform: scale(0.8); + opacity: 0; + background: rgba(220,20,60,0.8); + } + } + + .power-up { + position: absolute; + width: 35px; + height: 35px; + border-radius: 50%; + background: linear-gradient(45deg, #FF6B35, #F7931E); + display: flex; + align-items: center; + justify-content: center; + font-size: 1.2em; + cursor: pointer; + z-index: 45; + animation: powerUpFloat 2s ease-in-out infinite alternate; + box-shadow: 0 4px 15px rgba(255,107,53,0.4); + } + + @keyframes powerUpFloat { + from { transform: translateY(0px) scale(1); } + to { transform: translateY(-8px) scale(1.05); } + } + + .game-over-modal { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(255,255,255,0.95); + padding: 40px; + border-radius: 20px; + text-align: center; + z-index: 200; + box-shadow: 0 10px 30px rgba(0,0,0,0.3); + backdrop-filter: blur(10px); + } + + .game-over-title { + font-size: 2.5em; + margin-bottom: 20px; + color: #4682B4; + text-shadow: 0 2px 4px rgba(0,0,0,0.1); + } + + .game-over-stats { + font-size: 1.3em; + margin-bottom: 30px; + line-height: 1.6; + color: #333; + } + + .river-btn { + background: linear-gradient(45deg, #4682B4, #5F9EA0); + color: white; + border: none; + padding: 15px 30px; + border-radius: 25px; + font-size: 1.1em; + font-weight: bold; + cursor: pointer; + margin: 0 10px; + transition: all 0.3s ease; + box-shadow: 0 4px 15px rgba(70,130,180,0.3); + } + + .river-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(70,130,180,0.4); + } + + .particle { + position: absolute; + width: 4px; + height: 4px; + background: rgba(255,255,255,0.7); + border-radius: 50%; + pointer-events: none; + z-index: 30; + } + + .level-indicator { + position: absolute; + top: 70px; + left: 20px; + background: rgba(255,255,255,0.9); + color: #333; + padding: 5px 15px; + border-radius: 15px; + font-size: 0.9em; + font-weight: bold; + z-index: 100; + } + + /* Responsive */ + @media (max-width: 768px) { + .river-run-hud { + flex-direction: column; + gap: 10px; + } + + .floating-word { + font-size: 1em; + padding: 6px 12px; + } + + .target-display { + font-size: 1em; + padding: 8px 15px; + } + } + `; + document.head.appendChild(styleSheet); + } + + extractContent() { + logSh('🌊 River Run - Extracting vocabulary...', 'INFO'); + + // Extract words from various content formats + if (this.content.vocabulary) { + Object.keys(this.content.vocabulary).forEach(word => { + const wordData = this.content.vocabulary[word]; + this.availableWords.push({ + french: word, + english: typeof wordData === 'string' ? wordData : + wordData.translation || wordData.user_language || 'unknown', + pronunciation: wordData.pronunciation || wordData.prononciation + }); + }); + } + + // Fallback: extract from letter structure if available + if (this.content.letters && this.availableWords.length === 0) { + Object.values(this.content.letters).forEach(letterWords => { + letterWords.forEach(wordData => { + this.availableWords.push({ + french: wordData.word, + english: wordData.translation, + pronunciation: wordData.pronunciation + }); + }); + }); + } + + if (this.availableWords.length === 0) { + throw new Error('No vocabulary found for River Run'); + } + + logSh(`🎯 River Run ready: ${this.availableWords.length} words available`, 'INFO'); + this.generateTargetQueue(); + } + + generateTargetQueue() { + // Create queue of targets, ensuring variety + this.targetQueue = this.shuffleArray([...this.availableWords]).slice(0, Math.min(10, this.availableWords.length)); + this.usedTargets = []; + } + + init() { + this.container.innerHTML = ` +
+
+
+
Score: ${this.score}
+
Lives: ${this.lives}
+
Words: ${this.wordsCollected}
+
+
+ Click to Start! +
+
+
Level: ${this.level}
+
Speed: ${this.speed.toFixed(1)}x
+
+
+ +
+
+
+
+
+ `; + + this.setupEventListeners(); + this.updateHUD(); + } + + setupEventListeners() { + const riverGame = document.getElementById('river-game'); + + riverGame.addEventListener('click', (e) => { + if (!this.isRunning) { + this.start(); + return; + } + + const rect = riverGame.getBoundingClientRect(); + const clickX = ((e.clientX - rect.left) / rect.width) * 100; + const clickY = ((e.clientY - rect.top) / rect.height) * 100; + + this.movePlayer(clickX, clickY); + }); + + // Handle floating word clicks + riverGame.addEventListener('click', (e) => { + if (e.target.classList.contains('floating-word')) { + e.stopPropagation(); + this.handleWordClick(e.target); + } + }); + } + + start() { + if (this.isRunning) return; + + this.isRunning = true; + this.gameStartTime = Date.now(); + this.setNextTarget(); + + // Start game loop + this.gameLoop(); + + logSh('🌊 River Run started!', 'INFO'); + } + + gameLoop() { + if (!this.isRunning) return; + + const now = Date.now(); + + // Spawn new words + if (now - this.lastSpawn > this.spawnInterval) { + this.spawnFloatingWord(); + this.lastSpawn = now; + } + + // Update game objects + this.updateFloatingWords(); + this.updatePlayer(); + this.updateParticles(); + this.checkCollisions(); + + // Increase difficulty over time + this.updateDifficulty(); + + // Update UI + this.updateHUD(); + + // Continue loop + requestAnimationFrame(() => this.gameLoop()); + } + + setNextTarget() { + if (this.targetQueue.length === 0) { + this.generateTargetQueue(); + } + + this.currentTarget = this.targetQueue.shift(); + this.usedTargets.push(this.currentTarget); + + // Reset the word counter for new target + this.wordsSpawnedSinceTarget = 0; + + const targetDisplay = document.getElementById('target-display'); + if (targetDisplay) { + targetDisplay.innerHTML = `Find: ${this.currentTarget.english}`; + } + } + + spawnFloatingWord() { + const riverCanvas = document.getElementById('river-canvas'); + if (!riverCanvas) return; + + // Determine if we should force the target word + let word; + if (this.wordsSpawnedSinceTarget >= this.maxWordsBeforeTarget) { + // Force target word to appear + word = this.currentTarget; + this.wordsSpawnedSinceTarget = 0; // Reset counter + logSh(`🎯 Forcing target word: ${word.french}`, 'DEBUG'); + } else { + // Spawn random word + word = this.getRandomWord(); + this.wordsSpawnedSinceTarget++; + } + + const wordElement = document.createElement('div'); + wordElement.className = 'floating-word'; // No target/obstacle class at spawn + + // Add spaces based on level for increased difficulty + const spacePadding = ' '.repeat(this.level * 2); // 2 spaces per level on each side + wordElement.textContent = spacePadding + word.french + spacePadding; + + wordElement.style.left = `${Math.random() * 80 + 10}%`; + wordElement.style.top = '-60px'; + + // Store word data only + wordElement.wordData = word; + + riverCanvas.appendChild(wordElement); + this.floatingWords.push({ + element: wordElement, + y: -60, + x: parseFloat(wordElement.style.left), + wordData: word + }); + + // Occasional power-up spawn + if (Math.random() < 0.1) { + this.spawnPowerUp(); + } + } + + getRandomWord() { + // Simply return any random word from available vocabulary + return this.availableWords[Math.floor(Math.random() * this.availableWords.length)]; + } + + spawnPowerUp() { + const riverCanvas = document.getElementById('river-canvas'); + if (!riverCanvas) return; + + const powerUpElement = document.createElement('div'); + powerUpElement.className = 'power-up'; + powerUpElement.innerHTML = '⚡'; + powerUpElement.style.left = `${Math.random() * 80 + 10}%`; + powerUpElement.style.top = '-40px'; + + riverCanvas.appendChild(powerUpElement); + this.powerUps.push({ + element: powerUpElement, + y: -40, + x: parseFloat(powerUpElement.style.left), + type: 'slowTime' + }); + } + + updateFloatingWords() { + this.floatingWords = this.floatingWords.filter(word => { + word.y += this.speed; + word.element.style.top = `${word.y}px`; + + // Remove words that went off screen + if (word.y > window.innerHeight + 60) { + // CHECK AT EXIT TIME: Was this the target word? + if (word.wordData.french === this.currentTarget.french) { + // Missed target word - lose life + this.loseLife(); + } + word.element.remove(); + return false; + } + + return true; + }); + + // Update power-ups + this.powerUps = this.powerUps.filter(powerUp => { + powerUp.y += this.speed; + powerUp.element.style.top = `${powerUp.y}px`; + + if (powerUp.y > window.innerHeight + 40) { + powerUp.element.remove(); + return false; + } + + return true; + }); + } + + movePlayer(targetX, targetY) { + this.player.targetX = Math.max(5, Math.min(95, targetX)); + this.player.targetY = Math.max(10, Math.min(90, targetY)); + + const playerElement = document.getElementById('player'); + if (playerElement) { + playerElement.classList.add('moving'); + setTimeout(() => { + playerElement.classList.remove('moving'); + }, 500); + } + + // Create ripple effect + this.createRippleEffect(targetX, targetY); + } + + updatePlayer() { + // Smooth movement towards target + const speed = 0.1; + this.player.x += (this.player.targetX - this.player.x) * speed; + this.player.y += (this.player.targetY - this.player.y) * speed; + + const playerElement = document.getElementById('player'); + if (playerElement) { + playerElement.style.left = `calc(${this.player.x}% - 20px)`; + playerElement.style.top = `calc(${this.player.y}% - 20px)`; + } + } + + createRippleEffect(x, y) { + for (let i = 0; i < 5; i++) { + setTimeout(() => { + const particle = document.createElement('div'); + particle.className = 'particle'; + particle.style.left = `${x}%`; + particle.style.top = `${y}%`; + particle.style.animation = `particleSpread 1s ease-out forwards`; + + const riverCanvas = document.getElementById('river-canvas'); + if (riverCanvas) { + riverCanvas.appendChild(particle); + + setTimeout(() => { + particle.remove(); + }, 1000); + } + }, i * 100); + } + } + + updateParticles() { + // Create water particles occasionally + if (Math.random() < 0.1) { + const particle = document.createElement('div'); + particle.className = 'particle'; + particle.style.left = `${Math.random() * 100}%`; + particle.style.top = '-5px'; + particle.style.animation = `particleFlow 3s linear forwards`; + + const riverCanvas = document.getElementById('river-canvas'); + if (riverCanvas) { + riverCanvas.appendChild(particle); + + setTimeout(() => { + particle.remove(); + }, 3000); + } + } + } + + checkCollisions() { + const playerRect = this.getPlayerRect(); + + // Check word collisions + this.floatingWords.forEach((word, index) => { + const wordRect = this.getElementRect(word.element); + + if (this.isColliding(playerRect, wordRect)) { + this.handleWordCollision(word, index); + } + }); + + // Check power-up collisions + this.powerUps.forEach((powerUp, index) => { + const powerUpRect = this.getElementRect(powerUp.element); + + if (this.isColliding(playerRect, powerUpRect)) { + this.handlePowerUpCollision(powerUp, index); + } + }); + } + + getPlayerRect() { + const playerElement = document.getElementById('player'); + if (!playerElement) return { x: 0, y: 0, width: 0, height: 0 }; + + const rect = playerElement.getBoundingClientRect(); + const canvas = document.getElementById('river-canvas').getBoundingClientRect(); + + return { + x: rect.left - canvas.left, + y: rect.top - canvas.top, + width: rect.width, + height: rect.height + }; + } + + getElementRect(element) { + const rect = element.getBoundingClientRect(); + const canvas = document.getElementById('river-canvas').getBoundingClientRect(); + + return { + x: rect.left - canvas.left, + y: rect.top - canvas.top, + width: rect.width, + height: rect.height + }; + } + + isColliding(rect1, rect2) { + return rect1.x < rect2.x + rect2.width && + rect1.x + rect1.width > rect2.x && + rect1.y < rect2.y + rect2.height && + rect1.y + rect1.height > rect2.y; + } + + handleWordClick(wordElement) { + const wordData = wordElement.wordData; + + // CHECK AT PICK TIME: Is this the target word? + if (wordData.french === this.currentTarget.french) { + // Correct target word clicked + this.collectWord(wordElement, true); + } else { + // Wrong word clicked - it's an obstacle + this.missWord(wordElement); + } + } + + handleWordCollision(word, index) { + // CHECK AT COLLISION TIME: Is this the target word? + if (word.wordData.french === this.currentTarget.french) { + this.collectWord(word.element, true); + } else { + // Collision with non-target word = obstacle hit + this.missWord(word.element); + } + + // Remove from array + this.floatingWords.splice(index, 1); + } + + collectWord(wordElement, isCorrect) { + wordElement.classList.add('collected'); + + if (isCorrect) { + this.score += 10 + (this.level * 2); + this.wordsCollected++; + this.onScoreUpdate(this.score); + + // Set next target + this.setNextTarget(); + + // Play success sound + this.playSuccessSound(wordElement.textContent); + } + + setTimeout(() => { + wordElement.remove(); + }, 800); + } + + missWord(wordElement) { + wordElement.classList.add('missed'); + this.loseLife(); + + setTimeout(() => { + wordElement.remove(); + }, 600); + } + + handlePowerUpCollision(powerUp, index) { + this.activatePowerUp(powerUp.type); + powerUp.element.remove(); + this.powerUps.splice(index, 1); + } + + activatePowerUp(type) { + switch (type) { + case 'slowTime': + this.speed *= 0.5; + setTimeout(() => { + this.speed *= 2; + }, 3000); + break; + } + } + + updateDifficulty() { + const timeElapsed = Date.now() - this.gameStartTime; + const newLevel = Math.floor(timeElapsed / 30000) + 1; // Level up every 30 seconds + + if (newLevel > this.level) { + this.level = newLevel; + this.speed += 0.5; + this.spawnInterval = Math.max(500, this.spawnInterval - 100); // More aggressive spawn increase + } + } + + playSuccessSound(word) { + if (window.SettingsManager && window.SettingsManager.speak) { + window.SettingsManager.speak(word, { + lang: this.content.language || 'fr-FR', + rate: 1.0 + }).catch(error => { + console.warn('🔊 TTS failed:', error); + }); + } + } + + loseLife() { + this.lives--; + + if (this.lives <= 0) { + this.gameOver(); + } + } + + gameOver() { + this.isRunning = false; + + const riverGame = document.getElementById('river-game'); + const accuracy = this.wordsCollected > 0 ? Math.round((this.wordsCollected / (this.wordsCollected + (3 - this.lives))) * 100) : 0; + + const gameOverModal = document.createElement('div'); + gameOverModal.className = 'game-over-modal'; + gameOverModal.innerHTML = ` +
🌊 River Complete!
+
+ Final Score: ${this.score}
+ Words Collected: ${this.wordsCollected}
+ Level Reached: ${this.level}
+ Accuracy: ${accuracy}% +
+
+ + +
+ `; + + riverGame.appendChild(gameOverModal); + + // Store reference for button callbacks + window.currentRiverGame = this; + + setTimeout(() => { + this.onGameEnd(this.score); + }, 5000); + } + + updateHUD() { + const scoreDisplay = document.getElementById('score-display'); + const livesDisplay = document.getElementById('lives-display'); + const wordsDisplay = document.getElementById('words-display'); + const levelDisplay = document.getElementById('level-display'); + const speedDisplay = document.getElementById('speed-display'); + + if (scoreDisplay) scoreDisplay.textContent = this.score; + if (livesDisplay) livesDisplay.textContent = this.lives; + if (wordsDisplay) wordsDisplay.textContent = this.wordsCollected; + if (levelDisplay) levelDisplay.textContent = this.level; + if (speedDisplay) speedDisplay.textContent = this.speed.toFixed(1) + 'x'; + } + + shuffleArray(array) { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; + } + + restart() { + // Reset game state + this.isRunning = false; + this.score = 0; + this.lives = 3; + this.level = 1; + this.speed = 2; + this.wordsCollected = 0; + this.riverOffset = 0; + + // Reset player position + this.player.x = 50; + this.player.y = 80; + this.player.targetX = 50; + this.player.targetY = 80; + + // Clear game objects + this.floatingWords = []; + this.powerUps = []; + this.particles = []; + + // Reset timing + this.lastSpawn = 0; + this.spawnInterval = 1000; // 2x faster spawn rate + this.gameStartTime = Date.now(); + + // Reset targets and word counter + this.wordsSpawnedSinceTarget = 0; + this.generateTargetQueue(); + + // Cleanup DOM + const riverCanvas = document.getElementById('river-canvas'); + if (riverCanvas) { + const words = riverCanvas.querySelectorAll('.floating-word'); + const powerUps = riverCanvas.querySelectorAll('.power-up'); + const particles = riverCanvas.querySelectorAll('.particle'); + + words.forEach(word => word.remove()); + powerUps.forEach(powerUp => powerUp.remove()); + particles.forEach(particle => particle.remove()); + } + + const gameOverModal = document.querySelector('.game-over-modal'); + if (gameOverModal) { + gameOverModal.remove(); + } + + // Reset target display + const targetDisplay = document.getElementById('target-display'); + if (targetDisplay) { + targetDisplay.textContent = 'Click to Start!'; + } + + this.updateHUD(); + + logSh('🔄 River Run restarted', 'INFO'); + } + + destroy() { + this.isRunning = false; + + // Cleanup + if (window.currentRiverGame === this) { + delete window.currentRiverGame; + } + + const styleSheet = document.getElementById('river-run-styles'); + if (styleSheet) { + styleSheet.remove(); + } + } +} + +// Add CSS animations +const additionalCSS = ` + @keyframes particleSpread { + 0% { + transform: scale(1) translate(0, 0); + opacity: 1; + } + 100% { + transform: scale(0) translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px); + opacity: 0; + } + } + + @keyframes particleFlow { + 0% { + transform: translateY(0); + opacity: 0.7; + } + 100% { + transform: translateY(100vh); + opacity: 0; + } + } +`; + +// Inject additional CSS +const additionalStyleSheet = document.createElement('style'); +additionalStyleSheet.textContent = additionalCSS; +document.head.appendChild(additionalStyleSheet); + +// Register the game module +window.GameModules = window.GameModules || {}; +window.GameModules.RiverRun = RiverRun; \ No newline at end of file diff --git a/js/games/story-reader.js b/js/games/story-reader.js index 9fbf3b5..2c046a8 100644 --- a/js/games/story-reader.js +++ b/js/games/story-reader.js @@ -18,6 +18,8 @@ class StoryReader { // Story data this.story = null; + this.availableStories = []; + this.currentStoryIndex = 0; this.vocabulary = {}; // UI state @@ -31,6 +33,13 @@ class StoryReader { this.totalReadingTime = 0; this.readingTimer = null; + // TTS settings + this.autoPlayTTS = true; + this.ttsEnabled = true; + + // Expose content globally for SettingsManager TTS language detection + window.currentGameContent = this.content; + this.init(); } @@ -39,18 +48,30 @@ class StoryReader { logSh(`🔍 Story field exists: ${!!this.content.story}`, 'DEBUG'); logSh(`🔍 RawContent exists: ${!!this.content.rawContent}`, 'DEBUG'); - // Vérifier d'abord le contenu brut (rawContent) puis le contenu adapté - const storyData = this.content.rawContent?.story || this.content.story; + // Discover all available stories + this.discoverAvailableStories(); - if (!storyData) { + if (this.availableStories.length === 0) { logSh('No story content found in content or rawContent', 'ERROR'); - this.showError('This content does not contain a story for reading.'); + this.showError('This content does not contain any stories for reading.'); return; } - this.story = storyData; + // Get URL params to check if specific story is requested + const urlParams = new URLSearchParams(window.location.search); + const requestedStory = urlParams.get('story'); + + if (requestedStory) { + const storyIndex = this.availableStories.findIndex(story => + story.id === requestedStory || story.title.toLowerCase().includes(requestedStory.toLowerCase()) + ); + if (storyIndex !== -1) { + this.currentStoryIndex = storyIndex; + } + } + + this.selectStory(this.currentStoryIndex); this.vocabulary = this.content.rawContent?.vocabulary || this.content.vocabulary || {}; - this.calculateTotalSentences(); logSh(`📖 Story Reader initialized: "${this.story.title}" (${this.totalSentences} sentences)`, 'INFO'); @@ -59,6 +80,62 @@ class StoryReader { this.renderCurrentSentence(); } + discoverAvailableStories() { + this.availableStories = []; + + // Check main story field + const mainStory = this.content.rawContent?.story || this.content.story; + if (mainStory && mainStory.title) { + this.availableStories.push({ + id: 'main', + title: mainStory.title, + data: mainStory, + source: 'main' + }); + } + + // Check additionalStories field (like in WTA1B1) + const additionalStories = this.content.rawContent?.additionalStories || this.content.additionalStories; + if (additionalStories && Array.isArray(additionalStories)) { + additionalStories.forEach((story, index) => { + if (story && story.title) { + this.availableStories.push({ + id: `additional_${index}`, + title: story.title, + data: story, + source: 'additional' + }); + } + }); + } + + logSh(`📚 Discovered ${this.availableStories.length} stories:`, this.availableStories.map(s => s.title), 'INFO'); + } + + selectStory(storyIndex) { + if (storyIndex >= 0 && storyIndex < this.availableStories.length) { + this.currentStoryIndex = storyIndex; + this.story = this.availableStories[storyIndex].data; + this.calculateTotalSentences(); + + // Reset reading position for new story + this.currentSentence = 0; + this.wordsRead = 0; + + // Update URL to include story parameter + this.updateUrlForStory(); + + logSh(`📖 Selected story: "${this.story.title}" (${this.totalSentences} sentences)`, 'INFO'); + } + } + + updateUrlForStory() { + const urlParams = new URLSearchParams(window.location.search); + urlParams.set('story', this.availableStories[this.currentStoryIndex].id); + const newUrl = `${window.location.pathname}?${urlParams.toString()}`; + window.history.replaceState({}, '', newUrl); + } + showError(message) { this.container.innerHTML = `
@@ -77,8 +154,22 @@ class StoryReader { } createInterface() { + // Create story selector dropdown if multiple stories available + const storySelector = this.availableStories.length > 1 ? ` +
+ + +
+ ` : ''; + this.container.innerHTML = `
+ ${storySelector} +
@@ -92,6 +183,7 @@ class StoryReader {
+
@@ -115,6 +207,19 @@ class StoryReader {
+
+ + +
+
+ + +
@@ -136,6 +241,7 @@ class StoryReader { +
@@ -180,6 +286,43 @@ class StoryReader { line-height: 1.6; } + .story-selector { + background: #f8fafc; + border: 2px solid #e2e8f0; + border-radius: 10px; + padding: 15px 20px; + margin-bottom: 25px; + display: flex; + align-items: center; + gap: 15px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + } + + .story-selector label { + font-weight: 600; + color: #2d3748; + font-size: 1.1em; + min-width: 120px; + } + + .story-selector select { + flex: 1; + padding: 8px 12px; + border: 2px solid #cbd5e0; + border-radius: 6px; + background: white; + font-size: 1em; + color: #2d3748; + cursor: pointer; + transition: border-color 0.2s; + } + + .story-selector select:focus { + outline: none; + border-color: #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); + } + .story-header { display: flex; justify-content: space-between; @@ -219,6 +362,33 @@ class StoryReader { .story-controls { display: flex; gap: 10px; + flex-wrap: wrap; + } + + .control-btn { + padding: 8px 12px; + border: 2px solid #e2e8f0; + background: white; + border-radius: 6px; + cursor: pointer; + font-size: 0.9em; + transition: all 0.2s; + white-space: nowrap; + } + + .control-btn:hover { + background: #f7fafc; + border-color: #cbd5e0; + } + + .control-btn.secondary { + background: #f8fafc; + color: #4a5568; + } + + .control-btn.secondary:hover { + background: #e2e8f0; + color: #2d3748; } .settings-panel { @@ -308,6 +478,13 @@ class StoryReader { color: #d69e2e; } + .punctuation { + color: #2d3748; + font-weight: normal; + cursor: default; + user-select: none; + } + .word-with-pronunciation { position: relative; display: inline-block; @@ -379,6 +556,28 @@ class StoryReader { font-style: italic; } + .word-tts-btn { + position: absolute; + top: 5px; + right: 5px; + background: #3b82f6; + color: white; + border: none; + border-radius: 50%; + width: 24px; + height: 24px; + cursor: pointer; + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.2s; + } + + .word-tts-btn:hover { + background: #2563eb; + } + .story-navigation { display: flex; justify-content: center; @@ -463,12 +662,19 @@ class StoryReader { } setupEventListeners() { + // Story selector (if multiple stories) + const storySelect = document.getElementById('story-select'); + if (storySelect) { + storySelect.addEventListener('change', (e) => this.changeStory(parseInt(e.target.value))); + } + // Navigation document.getElementById('prev-btn').addEventListener('click', () => this.previousSentence()); document.getElementById('next-btn').addEventListener('click', () => this.nextSentence()); document.getElementById('bookmark-btn').addEventListener('click', () => this.saveBookmark()); // Controls + document.getElementById('play-sentence-btn').addEventListener('click', () => this.playSentenceTTS()); document.getElementById('settings-btn').addEventListener('click', () => this.toggleSettings()); document.getElementById('toggle-translation-btn').addEventListener('click', () => this.toggleTranslations()); document.getElementById('pronunciation-toggle-btn').addEventListener('click', () => this.togglePronunciations()); @@ -476,6 +682,8 @@ class StoryReader { // Settings document.getElementById('font-size-select').addEventListener('change', (e) => this.changeFontSize(e.target.value)); document.getElementById('reading-mode-select').addEventListener('change', (e) => this.changeReadingMode(e.target.value)); + document.getElementById('auto-play-tts').addEventListener('change', (e) => this.toggleAutoPlayTTS(e.target.checked)); + document.getElementById('tts-speed-select').addEventListener('change', (e) => this.changeTTSSpeed(e.target.value)); // Keyboard shortcuts document.addEventListener('keydown', (e) => { @@ -486,6 +694,7 @@ class StoryReader { this.nextSentence(); } if (e.key === 't' || e.key === 'T') this.toggleTranslations(); + if (e.key === 's' || e.key === 'S') this.playSentenceTTS(); }); // Click outside to close word popup @@ -520,8 +729,8 @@ class StoryReader { const matchedWords = []; words.forEach(token => { - // Skip whitespace and punctuation tokens - if (/^\s+$/.test(token) || /^[.,!?;:"'()[\]{}\-–—]+$/.test(token)) { + // Handle whitespace tokens + if (/^\s+$/.test(token)) { matchedWords.push({ original: token, hasVocab: false, @@ -530,6 +739,16 @@ class StoryReader { return; } + // Handle pure punctuation tokens (preserve them as non-clickable) + if (/^[.,!?;:"'()[\]{}\-–—]+$/.test(token)) { + matchedWords.push({ + original: token, + hasVocab: false, + isPunctuation: true + }); + return; + } + // Clean word (remove punctuation for matching) const cleanWord = token.toLowerCase().replace(/[.,!?;:"'()[\]{}\-–—]/g, ''); @@ -616,6 +835,9 @@ class StoryReader { wordsHtml = matchedWords.map(wordInfo => { if (wordInfo.isWhitespace) { return wordInfo.original; + } else if (wordInfo.isPunctuation) { + // Render punctuation as non-clickable text + return `${wordInfo.original}`; } else if (wordInfo.hasVocab) { const pronunciation = this.showPronunciation && wordInfo.pronunciation ? wordInfo.pronunciation : ''; @@ -647,6 +869,12 @@ class StoryReader { // Update stats this.updateStats(); + + // Auto-play TTS if enabled + if (this.autoPlayTTS && this.ttsEnabled) { + // Small delay to let the sentence render + setTimeout(() => this.playSentenceTTS(), 300); + } } showWordPopup(event) { @@ -663,6 +891,10 @@ class StoryReader { return; } + // Store reference to story reader for TTS button + popup.storyReader = this; + popup.currentWord = word; + document.getElementById('popup-word').textContent = word; document.getElementById('popup-translation').textContent = translation; @@ -760,6 +992,106 @@ class StoryReader { // Mode implementation can be extended later } + changeStory(storyIndex) { + if (storyIndex !== this.currentStoryIndex) { + // Save progress for current story before switching + this.saveProgress(); + + // Select new story + this.selectStory(storyIndex); + + // Load progress for new story + this.loadProgress(); + + // Update the interface title and progress + this.updateStoryTitle(); + this.renderCurrentSentence(); + + logSh(`📖 Switched to story: "${this.story.title}"`, 'INFO'); + } + } + + updateStoryTitle() { + const titleElement = document.querySelector('.story-title h2'); + if (titleElement) { + titleElement.textContent = this.story.title; + } + } + + // TTS Methods + playSentenceTTS() { + const sentenceData = this.getCurrentSentenceData(); + if (!sentenceData || !this.ttsEnabled) return; + + const text = sentenceData.data.original; + this.speakText(text); + } + + speakText(text, options = {}) { + if (!text || !this.ttsEnabled) return; + + // Use SettingsManager if available for better language support + if (window.SettingsManager && window.SettingsManager.speak) { + const ttsOptions = { + lang: this.getContentLanguage(), + rate: parseFloat(document.getElementById('tts-speed-select')?.value || '0.8'), + ...options + }; + + window.SettingsManager.speak(text, ttsOptions) + .catch(error => { + console.warn('🔊 SettingsManager TTS failed:', error); + this.fallbackTTS(text, ttsOptions); + }); + } else { + this.fallbackTTS(text, options); + } + } + + fallbackTTS(text, options = {}) { + if ('speechSynthesis' in window && text) { + // Cancel any ongoing speech + speechSynthesis.cancel(); + + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = this.getContentLanguage(); + utterance.rate = options.rate || 0.8; + utterance.volume = 1.0; + + speechSynthesis.speak(utterance); + } + } + + getContentLanguage() { + // Get language from content or use sensible defaults + if (this.content.language) { + const langMap = { + 'chinese': 'zh-CN', + 'english': 'en-US', + 'french': 'fr-FR', + 'spanish': 'es-ES' + }; + return langMap[this.content.language] || this.content.language; + } + return 'en-US'; // Default fallback + } + + toggleAutoPlayTTS(enabled) { + this.autoPlayTTS = enabled; + logSh(`🔊 Auto-play TTS ${enabled ? 'enabled' : 'disabled'}`, 'INFO'); + } + + changeTTSSpeed(speed) { + logSh(`🔊 TTS speed changed to ${speed}x`, 'INFO'); + } + + speakWordFromPopup() { + const popup = document.getElementById('word-popup'); + if (popup && popup.currentWord) { + this.speakText(popup.currentWord, { rate: 0.7 }); // Slower for individual words + } + } + updateStats() { const sentenceData = this.getCurrentSentenceData(); if (sentenceData) { @@ -774,18 +1106,35 @@ class StoryReader { wordsRead: this.wordsRead, timestamp: Date.now() }; - localStorage.setItem(`story_progress_${this.content.name}`, JSON.stringify(progressData)); + const progressKey = this.getProgressKey(); + localStorage.setItem(progressKey, JSON.stringify(progressData)); } loadProgress() { - const saved = localStorage.getItem(`story_progress_${this.content.name}`); + const progressKey = this.getProgressKey(); + const saved = localStorage.getItem(progressKey); if (saved) { - const data = JSON.parse(saved); - this.currentSentence = data.currentSentence || 0; - this.wordsRead = data.wordsRead || 0; + try { + const data = JSON.parse(saved); + this.currentSentence = data.currentSentence || 0; + this.wordsRead = data.wordsRead || 0; + } catch (error) { + logSh('Error loading progress:', error, 'WARN'); + this.currentSentence = 0; + this.wordsRead = 0; + } + } else { + // No saved progress - start fresh + this.currentSentence = 0; + this.wordsRead = 0; } } + getProgressKey() { + const storyId = this.availableStories[this.currentStoryIndex]?.id || 'main'; + return `story_progress_${this.content.name}_${storyId}`; + } + saveBookmark() { this.saveProgress(); const toast = document.createElement('div'); diff --git a/js/games/word-discovery.js b/js/games/word-discovery.js index fcf0a54..b16cbd4 100644 --- a/js/games/word-discovery.js +++ b/js/games/word-discovery.js @@ -605,15 +605,6 @@ class WordDiscovery { } } - startPracticePhase() { - this.currentPhase = 'practice'; - this.practiceLevel = 1; - this.practiceRound = 0; - this.practiceCorrectAnswers = 0; - this.practiceErrors = 0; - this.updatePhaseIndicator(); - this.showPracticeChallenge(); - } updatePhaseIndicator() { const phaseIndicator = this.container.querySelector('.phase-indicator'); @@ -863,6 +854,11 @@ class WordDiscovery { this.practiceRound = 0; this.practiceCorrectAnswers = 0; this.practiceErrors = 0; + + // SHUFFLE words again for new difficulty level + this.currentPracticeWords = this.shuffleArray([...this.discoveredWords]); + console.log(`🔀 Shuffled words for Level ${this.practiceLevel} - new variation order`); + this.updatePhaseIndicator(); setTimeout(() => { @@ -950,8 +946,9 @@ class WordDiscovery { this.practiceCorrectAnswers = 0; this.practiceErrors = 0; - // Create mixed practice selection from all discovered words + // SHUFFLE discovered words for varied practice order this.currentPracticeWords = this.shuffleArray([...this.discoveredWords]); + console.log(`🔀 Shuffled ${this.currentPracticeWords.length} words for practice variation`); this.updatePhaseIndicator(); this.showMixedPracticeChallenge(); diff --git a/test-chinese-beginner-grammar.html b/test-chinese-beginner-grammar.html new file mode 100644 index 0000000..d87f2c5 --- /dev/null +++ b/test-chinese-beginner-grammar.html @@ -0,0 +1,96 @@ + + + + + Test Chinese Beginner + Grammar Discovery + + + +

🌸 Test: Chinese Beginner Story + Grammar Discovery

+ +
+

🎯 Test du nouveau module "Le Jardin Magique"

+

Objectif : Vérifier que le contenu chinois → français fonctionne avec Grammar Discovery

+

Attendu : Sélecteur de concept avec "Structure de Phrase de Base"

+
+ +
+ + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/test-grammar-discovery.html b/test-grammar-discovery.html new file mode 100644 index 0000000..6680440 --- /dev/null +++ b/test-grammar-discovery.html @@ -0,0 +1,92 @@ + + + + + Grammar Discovery Test + + + +

🎓 Grammar Discovery Game Test

+ +
+ + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/test-letter-discovery.html b/test-letter-discovery.html new file mode 100644 index 0000000..889a0ea --- /dev/null +++ b/test-letter-discovery.html @@ -0,0 +1,106 @@ + + + + + Test Letter Discovery Game + + + +

🔤 Test: Letter Discovery Game

+ +
+

🎯 Testing Letter Discovery with French Beginner Story

+

Flow: Letter discovery → Word exploration → Practice

+

Expected: Letters A, B, C, F, G, J, M, P, R, T, V with their words

+
+ +
+ + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/test-river-run.html b/test-river-run.html new file mode 100644 index 0000000..8bca8bf --- /dev/null +++ b/test-river-run.html @@ -0,0 +1,108 @@ + + + + + Test River Run Game + + + +

🌊 Test: River Run Game

+ +
+

🎯 Testing River Run with French Beginner Story

+

Gameplay:

+ +

Expected: Endless river runner with French vocabulary

+
+ +
+ + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/test-word-discovery-quick.html b/test-word-discovery-quick.html new file mode 100644 index 0000000..1a03f7f --- /dev/null +++ b/test-word-discovery-quick.html @@ -0,0 +1,98 @@ + + + + + Quick Word Discovery Test + + + +

🔍 Test: Word Discovery with Shuffle

+ +
+

🎯 Testing Word Discovery Game

+

Objective: Verify that Word Discovery loads and shuffles work

+

Expected: Game starts with discovery phase, then shuffled practice

+
+ +
+ + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/test-wta1b1.html b/test-wta1b1.html new file mode 100644 index 0000000..6f19e03 --- /dev/null +++ b/test-wta1b1.html @@ -0,0 +1,159 @@ + + + + + Test WTA1B1 Content Module + + + +

🧪 Test: WTA1B1 Content Module

+ +
+

🎯 Testing WTA1B1 Integration

+

Expected: English letters U, V, T & pets vocabulary with Chinese translation

+

Features: Grammar lessons, vocabulary, story content, corrections

+
+ +
+ + + + \ No newline at end of file