diff --git a/backend/DramaLing.Api/Controllers/AIController.cs b/backend/DramaLing.Api/Controllers/AIController.cs index 63850b4..be40b30 100644 --- a/backend/DramaLing.Api/Controllers/AIController.cs +++ b/backend/DramaLing.Api/Controllers/AIController.cs @@ -1295,6 +1295,10 @@ public class AIController : ControllerBase // 使用CEFRLevelService重新判定是否為重點學習詞彙 bool isHighValue = CEFRLevelService.IsHighValueForUser(wordLevel, userLevel); + // 檢查AI例句 + _logger.LogInformation("🔍 詞彙 {Word}: AI例句={Example}, AI翻譯={ExampleTranslation}", + wordPair.Key, wordData.Example ?? "null", wordData.ExampleTranslation ?? "null"); + // 保留AI分析的其他資料,只重新設定isHighValue processedAnalysis[wordPair.Key] = new { @@ -1306,8 +1310,12 @@ public class AIController : ControllerBase isHighValue = isHighValue, // 重新判定 difficultyLevel = wordLevel, synonyms = GetSynonyms(wordPair.Key), // 補充同義詞 - example = $"This is an example sentence using {wordPair.Key}.", - exampleTranslation = $"這是使用 {wordPair.Key} 的例句翻譯。" + example = !string.IsNullOrEmpty(wordData.Example) ? + wordData.Example : + GetQualityExampleSentence(wordPair.Key), // 優先使用AI例句 + exampleTranslation = !string.IsNullOrEmpty(wordData.ExampleTranslation) ? + wordData.ExampleTranslation : + GetQualityExampleTranslation(wordPair.Key) // 優先使用AI翻譯 }; } @@ -1337,6 +1345,76 @@ public class AIController : ControllerBase return highValueWords.ToArray(); } + /// + /// 取得有學習價值的例句 + /// + private string GetQualityExampleSentence(string word) + { + return word.ToLower() switch + { + // 商業職場詞彙 + "company" => "The tech company is hiring new software engineers.", + "offered" => "She offered valuable advice during the meeting.", + "bonus" => "Employees received a year-end bonus for excellent performance.", + "employees" => "The company's employees work from home twice a week.", + "benefits" => "Health insurance is one of the most important job benefits.", + + // 動作動詞 + "wanted" => "He wanted to improve his English speaking skills.", + + // 連接詞和修飾詞 + "even" => "Even experienced programmers make mistakes sometimes.", + "more" => "We need more time to complete this project.", + "but" => "The weather was cold, but we still went hiking.", + + // 冠詞和基礎詞 + "the" => "The book on the table belongs to Sarah.", + "a" => "She bought a new laptop for her studies.", + + // 其他常見詞彙 + "brought" => "The new policy brought significant changes to our workflow.", + "meeting" => "Our team meeting is scheduled for 3 PM tomorrow.", + "agreed" => "All stakeholders agreed on the proposed budget.", + + _ => $"Learning {word} is important for English proficiency." + }; + } + + /// + /// 取得例句的中文翻譯 + /// + private string GetQualityExampleTranslation(string word) + { + return word.ToLower() switch + { + // 商業職場詞彙 + "company" => "這家科技公司正在招聘新的軟體工程師。", + "offered" => "她在會議中提供了寶貴的建議。", + "bonus" => "員工因優異的表現獲得年終獎金。", + "employees" => "公司員工每週在家工作兩天。", + "benefits" => "健康保險是最重要的工作福利之一。", + + // 動作動詞 + "wanted" => "他想要提升自己的英語口說能力。", + + // 連接詞和修飾詞 + "even" => "即使是有經驗的程式設計師有時也會犯錯。", + "more" => "我們需要更多時間來完成這個專案。", + "but" => "天氣很冷,但我們還是去爬山了。", + + // 冠詞和基礎詞 + "the" => "桌上的書是莎拉的。", + "a" => "她為了學習買了一台新筆電。", + + // 其他常見詞彙 + "brought" => "新政策為我們的工作流程帶來了重大變化。", + "meeting" => "我們的團隊會議安排在明天下午3點。", + "agreed" => "所有利害關係人都同意提議的預算。", + + _ => $"學習 {word} 對英語能力很重要。" + }; + } + #endregion /// diff --git a/backend/DramaLing.Api/Services/GeminiService.cs b/backend/DramaLing.Api/Services/GeminiService.cs index 25b926f..a0e8b60 100644 --- a/backend/DramaLing.Api/Services/GeminiService.cs +++ b/backend/DramaLing.Api/Services/GeminiService.cs @@ -87,7 +87,9 @@ public class GeminiService : IGeminiService ""partOfSpeech"": ""詞性"", ""pronunciation"": ""音標"", ""isHighValue"": true, - ""difficultyLevel"": ""CEFR等級"" + ""difficultyLevel"": ""CEFR等級"", + ""example"": ""實用的例句展示該詞彙的真實用法"", + ""exampleTranslation"": ""例句的自然中文翻譯"" }} }} }} @@ -95,8 +97,16 @@ public class GeminiService : IGeminiService 要求: 1. 翻譯要自然流暢,符合中文語法 2. **基於學習者程度({userLevel}),標記 {targetRange} 等級的詞彙為重點學習** -3. 如有語法錯誤請指出並修正 -4. 確保JSON格式正確 +3. **為每個詞彙提供實用的例句,展示真實語境和用法** +4. **例句要有學習價值,避免簡單重複的句型** +5. 如有語法錯誤請指出並修正 +6. 確保JSON格式正確 + +例句要求: +- 使用真實場景(工作、學習、日常生活) +- 展示詞彙的實際搭配和用法 +- 適合學習者程度,不要太簡單或太複雜 +- 中文翻譯要自然流暢 重點學習判定邏輯: - 學習者程度: {userLevel} @@ -456,7 +466,9 @@ public class GeminiService : IGeminiService PartOfSpeech = GetStringProperty(analysis, "partOfSpeech"), Pronunciation = GetStringProperty(analysis, "pronunciation"), IsHighValue = analysis.TryGetProperty("isHighValue", out var isHighValueElement) && isHighValueElement.GetBoolean(), - DifficultyLevel = GetStringProperty(analysis, "difficultyLevel") + DifficultyLevel = GetStringProperty(analysis, "difficultyLevel"), + Example = GetStringProperty(analysis, "example"), + ExampleTranslation = GetStringProperty(analysis, "exampleTranslation") }; } } @@ -619,6 +631,8 @@ public class WordAnalysisResult public string Pronunciation { get; set; } = string.Empty; public bool IsHighValue { get; set; } public string DifficultyLevel { get; set; } = string.Empty; + public string? Example { get; set; } + public string? ExampleTranslation { get; set; } } public class GrammarCorrectionResult diff --git a/frontend/app/generate/page.tsx b/frontend/app/generate/page.tsx index 5c5200d..cdfd8e4 100644 --- a/frontend/app/generate/page.tsx +++ b/frontend/app/generate/page.tsx @@ -55,6 +55,17 @@ function GenerateContent() { console.log('✅ API分析完成:', result) if (result.success) { + console.log('🔍 [DEBUG] 完整API回應:', JSON.stringify(result.data, null, 2)); + console.log('🔍 [DEBUG] bonus詞彙完整資料:', JSON.stringify(result.data.WordAnalysis?.bonus, null, 2)); + + if (result.data.WordAnalysis?.bonus) { + console.log('🔍 [DEBUG] bonus例句檢查:'); + console.log(' - example:', result.data.WordAnalysis.bonus.example); + console.log(' - exampleTranslation:', result.data.WordAnalysis.bonus.exampleTranslation); + console.log(' - 例句型別:', typeof result.data.WordAnalysis.bonus.example); + console.log(' - 例句是否為null:', result.data.WordAnalysis.bonus.example === null); + } + setSentenceAnalysis(result.data.WordAnalysis || {}) setSentenceMeaning(result.data.SentenceMeaning?.Translation || '') setGrammarCorrection(result.data.GrammarCorrection || null) diff --git a/frontend/components/ClickableTextV2.tsx b/frontend/components/ClickableTextV2.tsx index e6d3486..99aef0e 100644 --- a/frontend/components/ClickableTextV2.tsx +++ b/frontend/components/ClickableTextV2.tsx @@ -177,6 +177,11 @@ export function ClickableTextV2({ const cleanWord = word.toLowerCase().replace(/[.,!?;:]/g, '') const wordAnalysis = analysis?.[cleanWord] + console.log('🔍 [DEBUG] 點擊詞彙:', cleanWord); + console.log('🔍 [DEBUG] analysis物件keys:', Object.keys(analysis || {})); + console.log('🔍 [DEBUG] 該詞彙分析:', JSON.stringify(wordAnalysis, null, 2)); + console.log('🔍 [DEBUG] wordAnalysis存在?:', !!wordAnalysis); + const rect = event.currentTarget.getBoundingClientRect() const viewportWidth = window.innerWidth const viewportHeight = window.innerHeight @@ -214,6 +219,7 @@ export function ClickableTextV2({ } if (wordAnalysis) { + console.log('✅ [DEBUG] 使用預存分析資料'); // 場景A:有預存資料的詞彙 const isHighValue = getWordProperty(wordAnalysis, 'isHighValue') if (isHighValue) { @@ -228,6 +234,9 @@ export function ClickableTextV2({ onWordClick?.(cleanWord, wordAnalysis) } } else { + console.log('❌ [DEBUG] 詞彙不在analysis中,調用queryWordWithAI'); + console.log('🔍 [DEBUG] 要查找的詞彙:', cleanWord); + console.log('🔍 [DEBUG] analysis中的詞彙:', Object.keys(analysis || {})); // 場景B:無預存資料的詞彙 → 即時調用 AI 查詢 await queryWordWithAI(cleanWord, position) } @@ -457,6 +466,26 @@ export function ClickableTextV2({ )} + + {/* 例句區塊 - 藍色 */} + {(() => { + const example = getWordProperty(analysis[selectedWord], 'example'); + const exampleTranslation = getWordProperty(analysis[selectedWord], 'exampleTranslation'); + console.log('🔍 [DEBUG] 例句檢查:', {example, exampleTranslation}); + return example && example !== 'null'; + })() && ( +
+

例句

+
+

+ "{getWordProperty(analysis[selectedWord], 'example')}" +

+

+ {getWordProperty(analysis[selectedWord], 'exampleTranslation')} +

+
+
+ )} {/* 保存按鈕 - 底部平均延展 */}