全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2025-08-06_程序员自己开发的法语学习工具,帮我收获了爱情

您的位置:首页 >> 新闻 >> 行业资讯

程序员自己开发的法语学习工具,帮我收获了爱情 (金石瓜分计划强势上线,速戳上图了解详情)前言大家好,我是奈德丽。 今天女朋友突然跟我说:"我下周就要法语考试了,但是完全不知道该怎么复习,冠词和过去时复合搞得我头都大了..." 女朋友让我帮她背法语,这我一个工科男怎么会法语啊,太高估我了吧,但记住咱是程序员呀,秉着程序为人而服务的理念,我想着能不能给她做个小工具,帮她来复习呢,现在Ai这么强大,刚好年费的Cursor不能闲着,让它来帮我辅助开发了一款工具,让女朋友学起来就跟玩游戏一样,虽然会有点耗费时间,但是最终也是如愿以偿,得到了不错的效果。 先来看下受到女朋友连连好评的程序长什么样吧! 那就简单做一下需求分析吧女朋友向我抱怨了一堆话,总结起来就是: 「语法规则记不住」- 法语冠词有定冠词、不定冠词、部分冠词,各种变化规则「动词变位太复杂」- 不规则动词的过去分词总是记混「缺乏练习工具」- 课本上的练习题做完就没了,想多练都没地方「学习效率低」- 翻书查资料浪费时间,没有系统性哪有那么难,说到底还是上课没好好听! [狗头保命 !!] 那么怎么去实现这样的工具呢?我把它分成了五步 第一步:语法知识库 - 把复杂的规则可视化初版实现:静态页面我先从最基础的开始,把她需要的语法知识整理成一个结构化的页面: !DOCTYPEhtml htmllang="zh-CN" head metacharset="UTF-8" metaname="viewport"content="width=device-width, initial-scale=1.0" title法语语法总结/title /head body divclass="container" h1???? 法语语法总结/h1 !-- 冠词部分 -- sectionclass="grammar-section" h2冠词 (Les Articles)/h2 divclass="rule-card" h3定冠词/h3 divclass="examples" spanclass="article masculine"le/spanlivre (阳性单数) spanclass="article feminine"la/spantable (阴性单数) spanclass="article plural"les/spanlivres (复数) /div /div /section !-- 过去时复合部分 -- sectionclass="grammar-section" h2过去时复合 (Le Passé Composé)/h2 divclass="rule-card" h3构成公式/h3 divclass="formula" 助动词(avoir/être) + 过去分词 /div /div /section /div /body /html 配上一些基础样式: .container{ max-width:1200px; margin:0auto; padding:20px; } .rule-card{ background:linear-gradient(135deg, #667eea0%, #764ba2100%); border-radius:15px; padding:25px; margin-bottom:20px; color: white; box-shadow:010px30pxrgba(0,0,0,0.1); transition: transform0.3sease; } .rule-card:hover{ transform:translateY(-5px); } .article{ padding:4px12px; border-radius:20px; font-weight: bold; margin:05px; } .masculine{background:#3498db; } .feminine{background:#e74c3c; } .plural{background:#f39c12; } 女朋友看了第一版说:"哇,这个比课本清楚多了!但是能不能加点练习?光看不练还是记不住。" 好吧,需求升级了。 第二步:动词卡片系统 - 让学习变成游戏核心数据结构设计既然要做练习,那就得先把数据结构设计好: // 不规则动词数据 constirregularVerbs = [ { infinitive:'avoir', participle:'eu', example:"J'ai eu de la chance", difficulty:'easy', pronunciation:'[a.vwa?]' }, { infinitive:'être', participle:'été', example:"Il a été malade", difficulty:'easy', pronunciation:'[?t?]' }, // ... 更多动词 ]; // être动词(需要用être作助动词的动词) constetreVerbs = [ { infinitive:'aller', participle:'allé(e)', example:"Je suis allé(e) au cinéma", difficulty:'easy' }, // ... 更多动词 ]; 3D翻转卡片实现这里是最有趣的部分,我想做一个可以翻转的卡片,正面显示动词原形,背面显示过去分词和例句: .card{ width:400px; height:250px; position: relative; transform-style: preserve-3d; transition: transform0.6s; cursor: pointer; } .card.flipped{ transform:rotateY(180deg); } .card-face{ position: absolute; width:100%; height:100%; backface-visibility: hidden; border-radius:20px; display: flex; flex-direction: column; justify-content: center; align-items: center; box-shadow:010px30pxrgba(0,0,0,0.3); } .card-front{ background:linear-gradient(135deg, #74b9ff, #0984e3); color: white; } .card-back{ background:linear-gradient(135deg, #00b894, #00a085); color: white; transform:rotateY(180deg); } JavaScript控制逻辑: classVerbCardManager{ constructor() { this.currentMode ='irregular'; this.currentCards = irregularVerbs; this.currentIndex =0; this.isFlipped =false; this.stats = { correct:0, total:0 } // 翻转卡片 flipCard() { constcard =document.getElementById('flashcard'); card.classList.toggle('flipped'); this.isFlipped = !this.isFlipped; } // 更新卡片内容 updateCard() { constcard =this.currentCards[this.currentIndex]; document.getElementById('verbInfinitive').textContent = card.infinitive; document.getElementById('pastParticiple').textContent = card.participle; document.getElementById('example').textContent = card.example; // 重置翻转状态 document.getElementById('flashcard').classList.remove('flipped'); this.isFlipped =false; this.updateStats(); this.updateProgress(); } // 下一张卡片 nextCard() { if(this.currentIndex this.currentCards.length -1) { this.currentIndex++; this.updateCard(); } } // 上一张卡片 previousCard() { if(this.currentIndex 0) { this.currentIndex--; this.updateCard(); } } } 女朋友试用后兴奋地说:"这个翻转效果太酷了!但是我想测试一下自己到底记住了多少,能不能加个测试模式?" 需求又升级了... 第三步:智能测试系统 - 四选一选择题测试题生成算法为了让测试更有挑战性,我设计了一个干扰项生成算法: functiongenerateQuizOptions(){ constcurrentCard = currentCards[currentIndex]; constcorrectAnswer = currentCard.participle; // 从所有动词中筛选出错误选项 constallParticiples = currentCards.map(card=card.participle); constwrongAnswers = allParticiples.filter(p=p !== correctAnswer); // 随机选择3个错误答案 constshuffledWrong = wrongAnswers.sort(()=0.5-Math.random()).slice(0,3); constoptions = [correctAnswer, ...shuffledWrong].sort(()=0.5-Math.random()); // 更新UI document.getElementById('verbInfinitive').textContent = currentCard.infinitive; document.getElementById('cardInfo').textContent =`${currentCard.infinitive}的过去分词是?`; // 生成选项HTML constoptionsContainer =document.getElementById('quizOptions'); optionsContainer.innerHTML =''; options.forEach(option={ constoptionElement =document.createElement('div'); optionElement.className ='option'; optionElement.textContent = option; optionElement.onclick =()=selectOption(option, correctAnswer, optionElement); optionsContainer.appendChild(optionElement); } functionselectOption(selected, correct, element){ totalAttempts++; // 禁用所有选项 constallOptions =document.querySelectorAll('.option'); allOptions.forEach(opt=opt.style.pointerEvents ='none'); if(selected === correct) { element.classList.add('correct'); correctAnswers++; }else{ element.classList.add('incorrect'); // 高亮正确答案 allOptions.forEach(opt={ if(opt.textContent === correct) { opt.classList.add('correct'); } } updateStats(); // 3秒后自动下一题 setTimeout(()={ if(currentIndex currentCards.length -1) { nextCard(); }else{ showTestResult(); } },3000); } 选项样式: .options{ display: grid; grid-template-columns:1fr1fr; gap:15px; max-width:500px; margin:20pxauto; } .option{ padding:15px; border:2pxsolidrgba(255,255,255,0.3); border-radius:10px; background:rgba(255,255,255,0.1); color: white; cursor: pointer; transition: all0.3sease; text-align: center; font-weight: bold; } .option:hover{ background:rgba(255,255,255,0.2); border-color:rgba(255,255,255,0.6); } .option.correct{ background:#00b894; border-color:#00b894; animation: correctPulse0.5sease; } .option.incorrect{ background:#e17055; border-color:#e17055; animation: shake0.5sease; } @keyframescorrectPulse { 0% {transform:scale(1); } 50% {transform:scale(1.05); } 100% {transform:scale(1); } } @keyframesshake { 0%, 100% {transform:translateX(0); } 25% {transform:translateX(-5px); } 75% {transform:translateX(5px); } } 女朋友做了几道题后说:"这个测试很有意思!但是我发现冠词的练习还没有,能不能也加上?" 好家伙,需求还在继续... 第四步:冠词填空练习 - 语法规则的实战应用练习数据设计冠词练习比动词练习复杂一些,因为需要考虑语境,当然这部分大多是借助的Ai,因为我不太懂这些专业词汇 constarticleExercises = [ { before:"Je vais à", after:"cinéma ce soir.", correct:"au", explanation:"au = à + le (缩合形式,阳性单数)", translation:"我今晚去电影院。", difficulty:'easy' }, { before:"Elle parle de", after:"enfants dans le parc.", correct:"des", explanation:"des = de + les (缩合形式,复数)", translation:"她谈论公园里的孩子们。", difficulty:'easy' }, { before:"Il boit", after:"café tous les matins.", correct:"du", explanation:"du (部分冠词,阳性,不可数名词)", translation:"他每天早上都喝咖啡。", difficulty:'medium' }, // ... 更多练习 ]; 交互式填空实现divclass="article-question" h3请选择正确的冠词/h3 divclass="sentence-container" spanid="sentenceBefore"/span selectid="articleSelect"onchange="checkArticle()" optionvalue=""选择冠词/option optionvalue="le"le/option optionvalue="la"la/option optionvalue="les"les/option optionvalue="l'"l'/option optionvalue="un"un/option optionvalue="une"une/option optionvalue="des"des/option optionvalue="du"du/option optionvalue="de la"de la/option optionvalue="de l'"de l'/option optionvalue="au"au/option optionvalue="aux"aux/option /select spanid="sentenceAfter"/span /div divclass="article-feedback"id="articleFeedback"/div divclass="article-translation"id="articleTranslation"/div divclass="article-explanation"id="articleExplanation"/div /div 检查答案的逻辑: functioncheckArticle(){ constselectedArticle =document.getElementById('articleSelect').value; constexercise = currentCards[currentIndex]; constfeedback =document.getElementById('articleFeedback'); constexplanation =document.getElementById('articleExplanation'); if(!selectedArticle)return; totalAttempts++; if(selectedArticle === exercise.correct) { feedback.textContent ="? 正确!"; feedback.className ="article-feedback correct"; correctAnswers++; }else{ feedback.textContent =`? 错误!正确答案是:${exercise.correct}`; feedback.className ="article-feedback incorrect"; } // 显示解释和翻译 explanation.textContent = exercise.explanation; explanation.classList.add('show'); consttranslation =document.getElementById('articleTranslation'); translation.textContent =`中文:${exercise.translation}`; updateStats(); // 3秒后自动下一题 setTimeout(()={ if(currentIndex currentCards.length -1) { nextCard(); }else{ showCompletionMessage(); } },3000); } 女朋友练习了一会儿说:"太棒了,你这个程序里面有很多刚好也是我老师之前讲到的内容,有一个程序员男朋友太棒了!" HAH, 受了一番表扬,我心里很舒服,特别开心,都要成翘嘴了,这谁听了谁不迷糊啊,于是我又主动给她优化了一下这个工具,增加了朗读功能 第五步:语音朗读功能 - 让学习更生动Web Speech API的使用现代浏览器都支持Web Speech API,正好可以用来实现朗读功能: // 语音相关变量 letspeechSynthesis =window.speechSynthesis; letvoices = []; letselectedVoice =null; letspeechRate =0.8; letisAutoSpeechEnabled =true;// 默认开启 letcurrentSpeech =null; // 加载可用语音 functionloadVoices(){ voices = speechSynthesis.getVoices(); constvoiceSelect =document.getElementById('voiceSelect'); voiceSelect.innerHTML ='option value=""选择语音/option'; // 优先显示法语语音 constfrenchVoices = voices.filter(voice= voice.lang.startsWith('fr') || voice.name.toLowerCase().includes('french') || voice.name.toLowerCase().includes('fran?ais') frenchVoices.forEach((voice, index) ={ constoption =document.createElement('option'); option.value = index; option.textContent =`${voice.name}(${voice.lang})`; voiceSelect.appendChild(option); // 默认选择第一个法语语音 if(frenchVoices.length 0) { voices = frenchVoices; selectedVoice = voices[0]; voiceSelect.value =0; } } // 朗读文本 functionspeakText(event, side){ event.stopPropagation(); if(!selectedVoice) { alert('请先选择一个语音!'); return; } // 停止当前播放 if(currentSpeech) { speechSynthesis.cancel(); } lettextToSpeak =''; constcurrentCard = currentCards[currentIndex]; if(currentMode ==='articles') { // 冠词模式:朗读完整句子 constexercise = currentCards[currentIndex]; constselectedArticle =document.getElementById('articleSelect').value ||'[冠词]'; textToSpeak =`${exercise.before}${selectedArticle}${exercise.after}`; }else{ // 动词模式 if(side ==='front') { textToSpeak = currentCard.infinitive; }else{ textToSpeak =`${currentCard.participle}.${currentCard.example}`; } } currentSpeech =newSpeechSynthesisUtterance(textToSpeak); currentSpeech.voice = selectedVoice; currentSpeech.rate = speechRate; currentSpeech.lang ='fr-FR'; // 添加视觉反馈 constspeechBtn = event.target; speechBtn.classList.add('speaking'); currentSpeech.onend =()={ speechBtn.classList.remove('speaking'); currentSpeech =null; speechSynthesis.speak(currentSpeech); } 结语这个法语学习工具的开发过程,让我重新思考了技术的意义。 为什么我会想到用程序去解决女朋友的烦恼,第一点当然是我不会法语,哈哈,第二点是我可能具备一些将需求抽象化的思想,(撒一波狗粮)当然最主要的还是我想让她开心一些,不要有那么多烦恼,不就一门考试嘛,就算没过的话可以补考。 「技术不是为了炫技,而是为了解决实际问题。」 对象已经拿着我写工具在给好闺蜜炫耀了,我的脸上也是有了藏不住的笑。我想说一下,作为程序员,我们有能力用代码改变身边人的生活,哪怕只是一个小小的学习工具,也能带来实实在在的帮助。 有对象没对象的朋友们,都快来开动自己的脑筋,给Ta去写一个小工具,说不定你也一样可以俘获另一半的心。 奥利给! 恩恩……女朋友驱动开发,真香! AI编程资讯AI Coding专区指南: https://aicoding.juejin.cn/aicoding 点击"阅读原文"了解详情~ 阅读原文

上一篇:2025-04-03_愚人节最好笑的整活,来自品牌集体改名 下一篇:2020-07-07_“信息茧房”这种概念,可能在误导你对技术的认知

TAG标签:

17
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价