diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 54f82e278..c27056dab 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -844,6 +844,90 @@ last_played: "Last played" leagues_explanation: "Play in a league against other clan members in these multiplayer arena instances." + courses: + course: "Course" + courses: "courses" + not_enrolled: "You are not enrolled in this course." + visit_pref: "Please visit the" + visit_suf: "page to enroll." + select_class: "Select one of your classes" + unnamed: "*unnamed*" + select: "Select" + unnamed_class: "Unnamed Class" + edit_settings: "edit class settings" + edit_settings1: "Edit Class Settings" + progress: "Class Progress" + add_students: "Add Students" + stats: "Statistics" + total_students: "Total students:" + average_time: "Average level play time:" + total_time: "Total play time:" + average_levels: "Average levels completed:" + total_levels: "Total levels completed:" + furthest_level: "Furthest level completed:" + concepts_covered: "Concepts Covered" + students: "Students" + students1: "students" + expand_details: "Expand details" + concepts: "Concepts" + levels: "levels" + played: "Played" + play_time: "Play time:" + completed: "Completed:" + invite_students: "Invite students to join this class." + enter_emails: "Enter student emails to invite, one per line" + send_invites: "Send Invites" + title: "Title" + description: "Description" + languages_available: "Select programming languages available to the class:" + all_lang: "All Languages" + show_progress: "Show student progress to everyone in the class" + creating_class: "Creating class..." + purchasing_course: "Purchasing course..." + buy_course: "Buy Course" + buy_course1: "Buy this course" + create_class: "Create Class" + select_all_courses: "Select 'All Courses' for a 50% discount!" + all_courses: "All Courses" + number_students: "Number of students" + enter_number_students: "Enter the number of students you need for this class." + name_class: "Name your class" + displayed_course_page: "This will be displayed on the course page for you and your students. It can be changed later." + buy: "Buy" + purchasing_for: "You are purchasing a license for" + creating_for: "You are creating a class for" + for: "for" # Like in 'for 30 students' + receive_code: "Afterwards you will receive an unlock code to distribute to your students, which they can use to enroll in your class." + free_trial: "Free trial for teachers!" + get_access: "to get individual access to all courses for evalutaion purposes." + questions: "Questions?" + faq: "Courses FAQ" + question: "Q:" # Like in 'Question' + question1: "What's the difference between these courses and the single player game?" + answer: "A:" # Like in 'Answer' + answer1: "The single player game is designed for individuals, while the courses are designed for classes." + answer2: "The single player game has items, gems, hero selection, leveling up, and in-app purchases. Courses have classroom management features and streamlined student-focused level pacing." + teachers_click: "Teachers Click Here" + students_click: "Students Click Here" + courses_on_coco: "Courses on CodeCombat" + designed_to: "Courses are designed to introduce computer science concepts using CodeCombat's fun and engaging environment. CodeCombat levels are organized around key topics to encourage progressive learning, over the course of 5 hours." + more_in_less: "Learn more in less time" + no_experience: "No coding experience necesssary" + easy_monitor: "Easily monitor student progress" + purchase_for_class: "Purchase a course for your entire class. It's easy to sign up your students!" + see_the: "See the" + more_info: "for more information." + choose_course: "Choose Your Course:" + enter_code: "Enter an unlock code" + enter_code1: "Enter unlock code" + enroll: "Enroll" + pick_from_classes: "Pick from your current classes" + enter: "Enter" + or: "Or" + topics: "Topics" + hours_content: "Hours of content:" + get_free: "Get FREE course" + classes: archmage_title: "Archmage" archmage_title_description: "(Coder)" @@ -1180,6 +1264,7 @@ bad_input: "Bad input." server_error: "Server error." unknown: "Unknown error." + error: "ERROR" resources: sessions: "Sessions" diff --git a/app/templates/courses/course-details.jade b/app/templates/courses/course-details.jade index 2bdec233e..7cb76457b 100644 --- a/app/templates/courses/course-details.jade +++ b/app/templates/courses/course-details.jade @@ -10,13 +10,13 @@ block content if (noCourseInstance || noCourseInstanceSelected) && course h1= course.get('name') if noCourseInstance - p You are not enrolled in this course. + p(data-i18n="courses.not_enrolled") p - span.spr Please visit the - a.spr(href="/courses") courses - span page to enroll. + span.spr(data-i18n="courses.visit_pref") + a(href="/courses", data-i18n="courses.courses") + span.spl(data-i18n="courses.visit_suf") else if noCourseInstanceSelected - p Select one of your classes + p(data-i18n="courses.select_class") .container-fluid .row .col-md-6 @@ -25,13 +25,13 @@ block content if courseInstance.get('name') option(value="#{courseInstance.id}")= courseInstance.get('name') else - option(value="#{courseInstance.id}") *unnamed* + option(value="#{courseInstance.id}", data-i18n="courses.unnamed") .col-md-6 - button.btn.btn-success.btn-select-instance Select + button.btn.btn-success.btn-select-instance(data-i18n="courses.select") else if !course || !courseInstance - h1 Loading... + h1(data-i18n="common.loading") Loading... else - h1= courseInstance.get('name') || 'Unnamed Class' + h1= courseInstance.get('name') || $.i18n.t('courses.unnamed_class') small.spl (#{course.get('name')}) p @@ -41,17 +41,17 @@ block content if adminMode && courseInstance +settings-dialog p - button.btn.btn-xs(data-toggle='modal', data-target='#settingsModal') edit class settings + button.btn.btn-xs(data-toggle='modal', data-target='#settingsModal', data-i18n="courses.edit_settings") div.well.well-sm(role='tabpanel') ul.nav.nav-pills(role='tablist') li.active(role='presentation') - a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class Progress + a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab', data-i18n="courses.progress") if adminMode li(role='presentation') - a(href='#invite', aria-controls='invite', role='tab', data-toggle='tab') Add Students + a(href='#invite', aria-controls='invite', role='tab', data-toggle='tab', data-i18n="courses.add_students") li(role='presentation') - a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels + a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab', data-i18n="nav.play") .tab-content .tab-pane.active#progress(role='tabpanel') +progress-tab @@ -71,38 +71,38 @@ mixin progress-tab +progress-members mixin progress-summary-stats - h3 Statistics + h3(data-i18n="courses.stats") table.progress-stats-container tr - td Total students: + td(data-i18n="courses.total_students") td if courseInstance div #{courseInstance.get('members').length} if instanceStats tr - td Average level play time: + td(data-i18n="courses.average_time") if instanceStats.averageLevelPlaytime > 0 td= moment.duration(instanceStats.averageLevelPlaytime, "seconds").humanize() else td 0 tr - td Total play time: + td(data-i18n="courses.total_time") if instanceStats.totalPlayTime > 0 td= moment.duration(instanceStats.totalPlayTime, "seconds").humanize() else td 0 tr - td Average levels completed: + td(data-i18n="courses.average_levels") td #{instanceStats.averageLevelsCompleted.toFixed(2)} tr - td Total levels completed: + td(data-i18n="courses.total_levels") td= instanceStats.totalLevelsCompleted tr - td Furthest level completed: + td(data-i18n="courses.furthest_level") td= instanceStats.furthestLevelCompleted.replace('Course: ', '') mixin progress-summary-concepts - h3 Concepts Covered + h3(data-i18n="courses.concepts_covered") if course && courseInstance && conceptsCompleted table.progress-concepts-container each concept in course.get('concepts') @@ -116,29 +116,29 @@ mixin progress-summary-concepts span - #{conceptCompletion}% mixin progress-members - h3 Students + h3(data-i18n="courses.students") table.table.table-condensed thead tr th - span.progress-member-header.spr Name + span.progress-member-header.spr(data-i18n="clans.name") if memberSort === 'nameAsc' span.progress-member-header.glyphicon.glyphicon-chevron-up else if memberSort === 'nameDesc' span.progress-member-header.glyphicon.glyphicon-chevron-down th - span.progress-header.spr Progress + span.progress-header.spr(data-i18n="clans.progress") if memberSort === 'progressAsc' span.progress-header.glyphicon.glyphicon-chevron-up else if memberSort === 'progressDesc' span.progress-header.glyphicon.glyphicon-chevron-down else span(style='padding-left:16px;') - span.progress-key.progress-key-complete complete - span.progress-key.progress-key-started started - span.progress-key not started + span.progress-key.progress-key-complete(data-i18n="clans.complete_1") + span.progress-key.progress-key-started(data-i18n="clans.started_1") + span.progress-key(data-i18n="clans.not_started_1") input.progress-expand-checkbox(type='checkbox') - span.spl.progress-expand-label Expand details + span.spl.progress-expand-label(data-i18n="courses.expand_details") tbody each memberID in sortedMembers tr @@ -146,19 +146,19 @@ mixin progress-members +progress-members-individual(memberID) td.progress-cell if showExpandedProgress - .progress-concepts-label Concepts + .progress-concepts-label(data-i18n="courses.concepts") +progress-members-concepts(memberID) - .progess-levels-label Levels + .progess-levels-label(data-i18n="nav.play") +progress-members-levels-expanded(memberID) else table tbody tr - td.progress-concepts-label Concepts + td.progress-concepts-label(data-i18n="courses.concepts") td.progress-condensed-cell +progress-members-concepts(memberID) tr - td.progess-levels-label Levels + td.progess-levels-label(data-i18n="nav.play") td.progress-condensed-cell +progress-members-levels-condensed(memberID) @@ -166,8 +166,12 @@ mixin progress-members-individual(memberID) - var name = memberUserMap[memberID] ? memberUserMap[memberID].get('name') : 'Anoner' a(href="/user/#{memberID}")= name || 'Anoner' if memberStats && memberStats[memberID] - div #{memberStats[memberID].totalLevelsCompleted} levels - div Played #{moment.duration(memberStats[memberID].totalPlayTime, "seconds").humanize()} + div + span #{memberStats[memberID].totalLevelsCompleted} + span.spl(data-i18n="courses.levels") + div + span.spr(data-i18n="courses.played") + span #{moment.duration(memberStats[memberID].totalPlayTime, "seconds").humanize()} mixin progress-members-concepts(memberID) if course && userLevelStateMap[memberID] @@ -215,41 +219,49 @@ mixin progress-members-levels-condensed(memberID) mixin progress-members-popup-completed(i, level) .progress-popup-container h3 #{i + 1}. #{level.name.replace('Course: ', '')} - p Play time: #{moment.duration(level.playtime, "seconds").humanize()} - p Completed: #{moment(level.changed).format('MMMM Do YYYY, h:mm:ss a')} + p + span.spr(data-i18n="courses.play_time") + span #{moment.duration(level.playtime, "seconds").humanize()} + p + span.spr(data-i18n="courses.completed") + span #{moment(level.changed).format('MMMM Do YYYY, h:mm:ss a')} if adminMode - strong Click to view solution. + strong(data-i18n="clans.view_solution") mixin progress-members-popup-started(i, level) .progress-popup-container h3 #{i + 1}. #{level.name.replace('Course: ', '')} - p Play time: #{moment.duration(level.playtime, "seconds").humanize()} - p Last played: #{moment(level.changed).format('MMMM Do YYYY, h:mm:ss a')} + p + span.spr(data-i18n="courses.play_time") + span #{moment.duration(level.playtime, "seconds").humanize()} + p + span.spr(data-i18n="clans.last_played") + span #{moment(level.changed).format('MMMM Do YYYY, h:mm:ss a')} if adminMode - strong Click to view solution. + strong(data-i18n="clans.view_solution") mixin invite-tab - p Invite students to join this class. + p(data-i18n="courses.invite_students") p TODO: Student unlock code p TODO: Class capacity - textarea.invite-emails(rows=3, placeholder="Enter student emails to invite, one per line") + textarea.invite-emails(rows=3, data-i18n="[placeholder]courses.enter_emails", placeholder="Enter student emails to invite, one per line") div(style='margin-top:10px;') - button.btn.btn-success.btn-invite Send Invites + button.btn.btn-success.btn-invite(data-i18n="courses.send_invites") mixin levels-tab table.table.table-striped.table-condensed thead tr th - th Status - th Level - th Concepts + th(data-i18n="clans.status") + th(data-i18n="resources.level") + th(data-i18n="courses.concepts") tbody if campaign each level, levelID in campaign.get('levels') tr td - button.btn.btn-success.btn-play-level(data-level-slug=level.slug) Play + button.btn.btn-success.btn-play-level(data-level-slug=level.slug, data-i18n="home.play") td if userLevelStateMap[me.id] div= userLevelStateMap[me.id][levelID] @@ -266,24 +278,24 @@ mixin settings-dialog .modal-header button.close(data-dismiss='modal') span × - h3.modal-title Edit Class Settings + h3.modal-title(data-i18n="courses.edit_settings1") .modal-body p - strong Title + strong(data-i18n="courses.title") p input.settings-name-input(type='text', value="#{courseInstance.get('name') || ''}") p - strong Description + strong(data-i18n="courses.description") p textarea.settings-description-input(rows=2)= courseInstance.get('description') - p Select programming languages available to the class: + p(data-i18n="courses.languages_available") p select.form-control.settings-language-select option(value="Python") Python option(value="JavaScript") JavaScript - option(value="All Languages") All Languages + option(value="All Languages", data-i18n="courses.all_lang") p input.settings-public-progress(type='checkbox', checked) - span.spl Show student progress to everyone in the class + span.spl(data-i18n="courses.show_progress") .modal-footer button.btn.btn-save-settings(data-i18n="common.save_changes") diff --git a/app/templates/courses/course-enroll.jade b/app/templates/courses/course-enroll.jade index 24fb8a1c9..c18d47006 100644 --- a/app/templates/courses/course-enroll.jade +++ b/app/templates/courses/course-enroll.jade @@ -9,67 +9,81 @@ block content if state === 'declined' || state === 'unknown_error' p - .alert.alert-danger ERROR #{stateMessage} + .alert.alert-danger + span.spr(data-i18n="loading_error.error") + span #{stateMessage} if state === 'creating' p - .alert.alert-info Creating class... + .alert.alert-info(data-i18n="courses.creating_class") else if state === 'purchasing' p - .alert.alert-info Purchasing course... + .alert.alert-info(data-i18n="courses.purchasing_course") else .well.well-lg.enroll-container if price > 0 - h1.center Buy Course + h1.center(data-i18n="courses.buy_course") else - h1.center Create Class - h3 1. Course + h1.center(data-i18n="courses.create_class") + h3 + span 1. + span.spl(data-i18n="courses.course") if courses.length > 2 - p Select 'All Courses' for a 50% discount! + p(data-i18n="courses.select_all_courses") .form-group select.form-control.course-select each course in courses option(value="#{course.id}")= course.get('name') if courses.length > 1 - option(value="All Courses") All Courses + option(value="All Courses", data-i18n="courses.all_courses") - h3 2. Number of students - p Enter the number of students you need for this class. + h3 + span 2. + span.spl(data-i18n="courses.number_students") + p(data-i18n="courses.enter_number_students") input.input-seats(type='text', value="#{seats}") - h3 3. Name your class - p This will be displayed on the course page for you and your students. It can be changed later. + h3 + span 3. + span.spl(data-i18n="courses.name_class") + p(data-i18n="courses.displayed_course_page") input.class-name(type='text', placeholder="Mrs. Smith's 4th Period", value="#{className ? className : ''}") if price > 0 - h3 4. Buy + h3 + span 4. + span.spl(data-i18n="courses.buy") Buy else - h3 4. Create Class + h3 + span 4. + span.spl(data-i18n="courses.create_class") p if price > 0 - span.spr You are purchasing a license for + span.spr(data-i18n="courses.purchasing_for") else - span.spr You are creating a class for + span.spr(data-i18n="courses.creating_for") strong.spr #{selectedCourseTitle} - span.spr for - strong #{seats} students - | . - p Afterwards you will receive an unlock code to distribute to your students, which they can use to enroll in your class. + span.spr(data-i18n="courses.for") + strong + span #{seats} + span.spl(data-i18n="courses.students1") + span . + p(data-i18n="courses.receive_code") p.center if price > 0 button.btn.btn-success.btn-lg.btn-buy $#{(price / 100.0).toFixed(2)} else - button.btn.btn-success.btn-lg.btn-buy Create Class + button.btn.btn-success.btn-lg.btn-buy(data-i18n="courses.create_class") +trial-and-questions mixin trial-and-questions - h3 Free trial for teachers! + h3(data-i18n="courses.free_trial") p - span.spr Please fill out our + span.spr(data-i18n="teachers.teacher_subs_1") a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2") - span.spl to get individual access to all courses for evalutaion purposes. + span.spl(data-i18n="courses.get_access") - h3 Questions? + h3(data-i18n="courses.questions") p - span Please contact + span(data-i18n="teachers_survey.contact_1") a.spl(href='mailto:team@codecombat.com') team@codecombat.com diff --git a/app/templates/courses/courses.jade b/app/templates/courses/courses.jade index ea8f4f8ea..7408d26bd 100644 --- a/app/templates/courses/courses.jade +++ b/app/templates/courses/courses.jade @@ -22,34 +22,34 @@ block content - i++ mixin student-main - button.btn.btn-warning.btn-teacher Teachers Click Here - h1.center Courses on CodeCombat + button.btn.btn-warning.btn-teacher(data-i18n="courses.teachers_click") + h1.center(data-i18n="courses.courses_on_coco") mixin teacher-main - button.btn.btn-warning.btn-student Students Click Here - h1.center Courses on CodeCombat + button.btn.btn-warning.btn-student(data-i18n="courses.students_click") + h1.center(data-i18n="courses.courses_on_coco") .info-container - p Courses are designed to introduce computer science concepts using CodeCombat's fun and engaging environment. CodeCombat levels are organized around key topics to encourage progressive learning, over the course of 5 hours. + p(data-i18n="courses.designed_to") .container-fluid .row .col-md-6 ul - li Learn more in less time - li No coding experience necesssary - li Easily monitor student progress - - p Purchase a course for your entire class. It's easy to sign up your students! + li(data-i18n="courses.more_in_less") + li(data-i18n="courses.no_experience") + li(data-i18n="courses.easy_monitor") + + p(data-i18n="courses.purchase_for_class") p.faq-blurb - span.spr See the courses - a.spr.courses-faq FAQ - span for more information. + span.spr(data-i18n="courses.see_the") + a.courses-faq(data-i18n="courses.faq") + span.spl(data-i18n="courses.more_info") .col-md-6 img.img-quote(src="/images/pages/courses/coco_complab.png") p .well.well-sm div.praise-quote "#{praise.quote}" div.praise-caption - #{praise.source} - h2.center Choose Your Course: + h2.center(data-i18n="courses.choose_course") mixin student-dialog(course) .modal.continue-dialog(id="continueModal#{course.id}") @@ -64,13 +64,13 @@ mixin student-dialog(course) .col-md-12 .well.well-sm p - div.instruction-label Enter an unlock code + div.instruction-label(data-i18n="courses.enter_code") .container-fluid .row .col-md-8 - input.code-input(type='text', placeholder="Enter unlock code") + input.code-input(type='text', data-i18n="[placeholder]courses.enter_code1", placeholder="Enter unlock code") .col-md-4 - button.btn.btn-success.btn-enroll Enroll + button.btn.btn-success.btn-enroll(data-i18n="courses.enroll") mixin teacher-dialog(course) .modal.continue-dialog(id="continueModal#{course.id}") @@ -86,7 +86,7 @@ mixin teacher-dialog(course) .col-md-12 .well.well-sm p - div.instruction-label Pick from your current classes + div.instruction-label(data-i18n="courses.pick_from_classes") .container-fluid .row .col-md-8 @@ -96,15 +96,15 @@ mixin teacher-dialog(course) if inst.get('name') option(value="#{inst.id}")= inst.get('name') else - option(value="#{inst.id}") *unnamed* + option(value="#{inst.id}", data-i18n="courses.unnamed") .col-md-4 - button.btn.btn-success.btn-enter(data-course-id="#{course.id}") Enter + button.btn.btn-success.btn-enter(data-course-id="#{course.id}", data-i18n="courses.enter") .row.button-row.center.row-pick-class .col-md-12 - div.or Or + div.or(data-i18n="courses.or") .row.button-row.center .col-md-12 - button.btn.btn-success.btn-lg.btn-buy(data-course-id="#{course.id}") Buy this course + button.btn.btn-success.btn-lg.btn-buy(data-course-id="#{course.id}", data-i18n="courses.buy_course1") mixin course-block(course) if studentMode @@ -125,18 +125,20 @@ mixin course-block(course) img.course-image(src="#{course.get('screenshot')}") .row.button-row .col-md-6 - strong Topics + strong(data-i18n="courses.topics") ul each concept in course.get('concepts') li(data-i18n="concepts." + concept) - strong Hours of content: #{course.get('duration')} + strong + span.spr(data-i18n="courses.hours_content") + span #{course.get('duration')} .col-md-6.center(style='margin-top: 40px;') if studentMode if enrolledCourses[course.id] - a.btn.btn-lg.btn-success.btn-continue(href="/courses/#{course.id}?student=true") Continue + a.btn.btn-lg.btn-success.btn-continue(href="/courses/#{course.id}?student=true", data-i18n="common.continue") else - button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal#{course.id}") Enter + button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal#{course.id}", data-i18n="courses.enter") Enter else if enrolledCourses[course.id] - button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal#{course.id}") Continue + button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal#{course.id}", data-i18n="common.continue") else - button.btn.btn-lg.btn-success.btn-buy(data-course-id="#{course.id}") #{course.get('pricePerSeat') === 0 ? 'Get FREE course' : 'Buy course'} + button.btn.btn-lg.btn-success.btn-buy(data-course-id="#{course.id}") #{course.get('pricePerSeat') === 0 ? $.i18n.t('courses.get_free') : $.i18n.t('courses.buy_course')} diff --git a/app/views/courses/CoursesView.coffee b/app/views/courses/CoursesView.coffee index 84a0942c3..26a65f176 100644 --- a/app/views/courses/CoursesView.coffee +++ b/app/views/courses/CoursesView.coffee @@ -45,10 +45,10 @@ module.exports = class CoursesView extends RootView @enrolledCourses[courseInstance.get('courseID')] = true for courseInstance in @courseInstances.models setupCoursesFAQPopover: -> - popoverTitle = "<h3>Courses FAQ<button type='button' class='close' onclick='$('.courses-faq').popover('hide');'>×</button></h3>" - popoverContent = "<p><strong>Q:</strong> What's the difference between these courses and the single player game?</p>" - popoverContent += "<p><strong>A:</strong> The single player game is designed for individuals, while the courses are designed for classes.</p>" - popoverContent += "<p>The single player game has items, gems, hero selection, leveling up, and in-app purchases. Courses have classroom management features and streamlined student-focused level pacing.</p>" + popoverTitle = "<h3>" + $.i18n.t('courses.faq') + "<button type='button' class='close' onclick='$('.courses-faq').popover('hide');'>×</button></h3>" + popoverContent = "<p><strong>" + $.i18n.t('courses.question') + "</strong> " + $.i18n.t('courses.question1') + "</p>" + popoverContent += "<p><strong>" + $.i18n.t('courses.answer') + "</strong> " + $.i18n.t('courses.answer1') + "</p>" + popoverContent += "<p>" + $.i18n.t('courses.answer2') + "</p>" @$el.find('.courses-faq').popover( animation: true html: true