""" ContentPack — Hardcoded KLP 1-7 vocabulary and KLP 7-10 grammar rules. Can be replaced at runtime via the load_content_pack socket event. """ # --------------------------------------------------------------------------- # KLP 7 Vocabulary (from XLSX, lesson-tagged) # --------------------------------------------------------------------------- KLP7_VOCAB = [ # Nouns (good for sentence construction) {"korean": "학생", "english": "student", "type": "noun"}, {"korean": "의사", "english": "doctor", "type": "noun"}, {"korean": "선생님", "english": "teacher", "type": "noun"}, {"korean": "친구", "english": "friend", "type": "noun"}, {"korean": "사과", "english": "apple", "type": "noun"}, {"korean": "책", "english": "book", "type": "noun"}, {"korean": "커피", "english": "coffee", "type": "noun"}, {"korean": "음료", "english": "beverage", "type": "noun"}, {"korean": "날씨", "english": "weather", "type": "noun"}, {"korean": "마음", "english": "mind/heart", "type": "noun"}, {"korean": "일상", "english": "daily life", "type": "noun"}, {"korean": "역할", "english": "role", "type": "noun"}, {"korean": "분위기", "english": "atmosphere", "type": "noun"}, {"korean": "등록금", "english": "tuition", "type": "noun"}, {"korean": "생활비", "english": "living expense", "type": "noun"}, {"korean": "시급", "english": "hourly wage", "type": "noun"}, {"korean": "이력서", "english": "resume", "type": "noun"}, {"korean": "약국", "english": "pharmacy", "type": "noun"}, {"korean": "병원", "english": "hospital", "type": "noun"}, {"korean": "목", "english": "throat/neck", "type": "noun"}, {"korean": "열", "english": "fever", "type": "noun"}, {"korean": "두통", "english": "headache", "type": "noun"}, {"korean": "식사", "english": "meal", "type": "noun"}, {"korean": "공부", "english": "studying", "type": "noun"}, {"korean": "운동", "english": "exercise", "type": "noun"}, {"korean": "음악", "english": "music", "type": "noun"}, {"korean": "영화", "english": "movie", "type": "noun"}, {"korean": "여행", "english": "travel", "type": "noun"}, {"korean": "회의", "english": "meeting", "type": "noun"}, {"korean": "선물", "english": "gift", "type": "noun"}, # Pronouns / common subjects {"korean": "저", "english": "I (formal)", "type": "pronoun"}, {"korean": "나", "english": "I (informal)", "type": "pronoun"}, {"korean": "우리", "english": "we/my", "type": "pronoun"}, {"korean": "이것", "english": "this", "type": "pronoun"}, {"korean": "그것", "english": "that", "type": "pronoun"}, # Verbs (stems for conjugation) {"korean": "가다", "english": "to go", "type": "verb", "stem": "가"}, {"korean": "오다", "english": "to come", "type": "verb", "stem": "오"}, {"korean": "먹다", "english": "to eat", "type": "verb", "stem": "먹"}, {"korean": "마시다", "english": "to drink", "type": "verb", "stem": "마시"}, {"korean": "공부하다", "english": "to study", "type": "verb", "stem": "공부하"}, {"korean": "운동하다", "english": "to exercise", "type": "verb", "stem": "운동하"}, {"korean": "일하다", "english": "to work", "type": "verb", "stem": "일하"}, {"korean": "쉬다", "english": "to rest", "type": "verb", "stem": "쉬"}, {"korean": "만나다", "english": "to meet", "type": "verb", "stem": "만나"}, {"korean": "도와주다", "english": "to help", "type": "verb", "stem": "도와주"}, {"korean": "사다", "english": "to buy", "type": "verb", "stem": "사"}, {"korean": "팔다", "english": "to sell", "type": "verb", "stem": "팔"}, {"korean": "읽다", "english": "to read", "type": "verb", "stem": "읽"}, {"korean": "쓰다", "english": "to write", "type": "verb", "stem": "쓰"}, {"korean": "자다", "english": "to sleep", "type": "verb", "stem": "자"}, # Adjectives {"korean": "크다", "english": "to be big", "type": "adjective", "stem": "크"}, {"korean": "작다", "english": "to be small", "type": "adjective", "stem": "작"}, {"korean": "좋다", "english": "to be good", "type": "adjective", "stem": "좋"}, {"korean": "나쁘다", "english": "to be bad", "type": "adjective", "stem": "나쁘"}, {"korean": "맛있다", "english": "to be delicious", "type": "adjective", "stem": "맛있"}, {"korean": "재미있다", "english": "to be fun", "type": "adjective", "stem": "재미있"}, {"korean": "힘들다", "english": "to be difficult", "type": "adjective", "stem": "힘들"}, {"korean": "바쁘다", "english": "to be busy", "type": "adjective", "stem": "바쁘"}, {"korean": "춥다", "english": "to be cold", "type": "adjective", "stem": "춥"}, {"korean": "덥다", "english": "to be hot", "type": "adjective", "stem": "덥"}, {"korean": "피곤하다", "english": "to be tired", "type": "adjective", "stem": "피곤하"}, {"korean": "달콤하다", "english": "to be sweet", "type": "adjective", "stem": "달콤하"}, ] # Lesson-specific vocab from KLP7-10 slides KLP7_LESSON_VOCAB = [ {"korean": "음료", "english": "beverage", "type": "noun"}, {"korean": "달콤하다", "english": "to be sweet", "type": "adjective", "stem": "달콤하"}, {"korean": "분위기", "english": "atmosphere", "type": "noun"}, {"korean": "날씨", "english": "weather", "type": "noun"}, {"korean": "김이 나다", "english": "to steam", "type": "verb", "stem": "나"}, {"korean": "긴장을 풀다", "english": "to relax", "type": "verb", "stem": "풀"}, {"korean": "마음", "english": "mind/heart", "type": "noun"}, {"korean": "일상", "english": "daily life", "type": "noun"}, {"korean": "역할", "english": "role", "type": "noun"}, {"korean": "따뜻함", "english": "warmth", "type": "noun"}, {"korean": "수업", "english": "class/lesson", "type": "noun"}, {"korean": "숙제", "english": "homework", "type": "noun"}, {"korean": "시험", "english": "exam", "type": "noun"}, {"korean": "친구", "english": "friend", "type": "noun"}, {"korean": "커피", "english": "coffee", "type": "noun"}, {"korean": "저녁", "english": "dinner/evening", "type": "noun"}, {"korean": "담배", "english": "cigarette", "type": "noun"}, {"korean": "노래", "english": "song", "type": "noun"}, {"korean": "춤", "english": "dance", "type": "noun"}, ] # --------------------------------------------------------------------------- # Grammar Rule Definitions # --------------------------------------------------------------------------- GRAMMAR_RULES = { "topic_marker": { "id": "topic_marker", "name": "Topic Marker 은/는", "lesson": "KLP7-base", "description": "Attach 은 to consonant-ending nouns, 는 to vowel-ending nouns", "examples": [ {"word": "학생", "result": "학생은", "note": "consonant ending"}, {"word": "사과", "result": "사과는", "note": "vowel ending"}, ], "difficulty": 1, }, "copula": { "id": "copula", "name": "Copula 이에요/예요", "lesson": "KLP7-base", "description": "이에요 after consonant endings, 예요 after vowel endings", "examples": [ {"word": "학생", "result": "학생이에요", "note": "consonant ending"}, {"word": "의사", "result": "의사예요", "note": "vowel ending"}, ], "difficulty": 1, }, "negative_copula": { "id": "negative_copula", "name": "Negative Copula 이/가 아니에요", "lesson": "KLP7-base", "description": "이 아니에요 after consonant, 가 아니에요 after vowel/ㄹ", "examples": [ {"sentence": "저는 의사가 아니에요", "translation": "I am not a doctor"}, {"sentence": "이것은 사과가 아니에요", "translation": "This is not an apple"}, ], "difficulty": 1, }, "indirect_quote_dago": { "id": "indirect_quote_dago", "name": "Indirect Quotation -다고", "lesson": "KLP7-8", "description": "Quote statements. Verb: -ㄴ/는다고. Adjective/있다: -다고. Past: -었/았다고. Future: -ㄹ 거라고.", "conjugation_table": { "adjective_present": "-다고", "verb_present_consonant": "-는다고", "verb_present_vowel": "-ㄴ다고", "past": "-었/았다고", "future": "-ㄹ/을 거라고", "copula": "-(이)라고", }, "examples": [ {"direct": "감기 때문에 많이 아파요", "indirect": "감기 때문에 많이 아프다고 했어요"}, {"direct": "다음 달에 독일에 돌아가요", "indirect": "독일에 돌아간다고 해요"}, ], "difficulty": 2, }, "indirect_quote_commands": { "id": "indirect_quote_commands", "name": "Indirect Quotation -(으)라고 / -지 말라고 / -달라고 / -주라고", "lesson": "KLP7-9", "description": "Quote commands, negative commands, and requests", "forms": { "command": "V + (으)라고 하다", "neg_command": "V + 지 말라고 하다", "request_me": "V + 아/어 달라고 하다", "request_other": "V + 아/어 주라고 하다", }, "examples": [ {"direct": "약을 먹으세요", "indirect": "약을 먹으라고 했어요", "form": "command"}, {"direct": "담배를 피지 마세요", "indirect": "담배를 피지 말라고 했어요", "form": "neg_command"}, {"direct": "도와주세요", "indirect": "도와 달라고 했어요", "form": "request_me"}, ], "difficulty": 2, }, "indirect_quote_questions": { "id": "indirect_quote_questions", "name": "Indirect Quotation -냐고 / -느냐고", "lesson": "KLP7-10", "description": "Quote questions", "forms": { "question": "V/Adj + 냐고 하다 (drop ㄹ)", "formal_question": "V + 느냐고 하다 (drop ㄹ)", }, "examples": [ {"direct": "어디 가요?", "indirect": "어디 가냐고 했어요"}, {"direct": "사는 곳이 어디예요?", "indirect": "사는 곳이 어디냐고 물어봤어요"}, ], "difficulty": 2, }, "indirect_quote_suggestions": { "id": "indirect_quote_suggestions", "name": "Indirect Quotation -자고", "lesson": "KLP7-10", "description": "Quote suggestions", "forms": {"suggestion": "V + 자고 하다"}, "examples": [ {"direct": "같이 운동하자", "indirect": "운동을 같이 하자고 했어요"}, {"direct": "수업을 시작해요", "indirect": "수업을 시작하자고 하셨어요"}, ], "difficulty": 2, }, "regret_expression": { "id": "regret_expression", "name": "Expressing Regrets -(으)ㄹ 걸 그랬다", "lesson": "KLP7-10", "description": "Express regret about past actions. Positive: -(으)ㄹ 걸 그랬다. Negative: -지 말 걸 그랬다", "examples": [ {"sentence": "더 일찍 일어날 걸", "translation": "I should have gotten up earlier", "negative": False}, {"sentence": "라면을 먹지 말 걸 그랬어", "translation": "I shouldn't have eaten ramyeon", "negative": True}, ], "difficulty": 2, }, } # --------------------------------------------------------------------------- # Sentence Templates for Question Generation # --------------------------------------------------------------------------- # Each template has slots that Gemini or rule engine fills SENTENCE_TEMPLATES = { "topic_marker": [ { "template": "{noun}____ {noun2}{copula}", "slots": ["topic_marker", "copula"], "translation_pattern": "{noun_en} is a/an {noun2_en}", } ], "copula": [ { "template": "{subject}{topic} {noun}____", "slots": ["copula"], "translation_pattern": "{subject_en} is a/an {noun_en}", } ], "negative_copula": [ { "template": "{subject}{topic} {noun}____ 아니에요", "slots": ["neg_subject_marker"], "translation_pattern": "{subject_en} is not a/an {noun_en}", } ], "scrabble": [ { "pattern": "[Subject+Topic] [Object+Object_marker] [Verb]", "example": "저는 커피를 마셔요", "translation": "I drink coffee", }, { "pattern": "[Subject+Topic] [Noun+Copula]", "example": "저는 학생이에요", "translation": "I am a student", }, { "pattern": "[Subject+Topic] [Adj]", "example": "날씨가 추워요", "translation": "The weather is cold", }, ], "indirect_quote_dago": [ { "pattern": "[Person]이/가 [sentence]다고 했어요/해요", "example": "민호가 감기 때문에 아프다고 했어요", "translation": "Minho said he is sick because of a cold", } ], "indirect_quote_commands": [ { "pattern": "[Person]이/가 [verb](으)라고 했어요", "example": "의사가 약을 먹으라고 했어요", "translation": "The doctor said to take medicine", } ], "indirect_quote_questions": [ { "pattern": "[Person]이/가 [question]냐고 물었어요", "example": "친구가 어디 가냐고 물었어요", "translation": "My friend asked where I was going", } ], "indirect_quote_suggestions": [ { "pattern": "[Person]이/가 [verb]자고 했어요", "example": "친구가 같이 밥 먹자고 했어요", "translation": "My friend suggested eating together", } ], "regret_expression": [ { "pattern": "[verb stem](으)ㄹ 걸 그랬다", "example": "더 열심히 공부할 걸 그랬어", "translation": "I should have studied harder", } ], } # --------------------------------------------------------------------------- # Active Content Pack (can be replaced at runtime) # --------------------------------------------------------------------------- _active_pack = { "version": "1.0", "lesson": "KLP7-10", "vocab": KLP7_VOCAB + KLP7_LESSON_VOCAB, "grammar_rules": GRAMMAR_RULES, "templates": SENTENCE_TEMPLATES, "metadata": { "source": "hardcoded", "description": "KLP 7-10 Korean Learning Program", } } def get_active_pack() -> dict: return _active_pack def replace_active_pack(new_pack: dict) -> dict: """ Replace the active content pack with a parsed one from an uploaded file. Merges with defaults to ensure required keys always exist. """ global _active_pack _active_pack = { "version": new_pack.get("version", "1.0"), "lesson": new_pack.get("lesson", "custom"), "vocab": new_pack.get("vocab", KLP7_VOCAB), "grammar_rules": new_pack.get("grammar_rules", GRAMMAR_RULES), "templates": new_pack.get("templates", SENTENCE_TEMPLATES), "metadata": { "source": "uploaded", "description": new_pack.get("description", "Custom content pack"), } } return _active_pack def get_nouns(pack: dict = None) -> list: pack = pack or get_active_pack() return [v for v in pack["vocab"] if v["type"] == "noun"] def get_pronouns(pack: dict = None) -> list: pack = pack or get_active_pack() return [v for v in pack["vocab"] if v["type"] == "pronoun"] def get_verbs(pack: dict = None) -> list: pack = pack or get_active_pack() return [v for v in pack["vocab"] if v["type"] == "verb"] def get_adjectives(pack: dict = None) -> list: pack = pack or get_active_pack() return [v for v in pack["vocab"] if v["type"] == "adjective"]