Some checks failed
Deploy Frontend to Staging / Deploy frontend to staging (push) Has been cancelled
Builds the §24 product on top of the LangGraph runtime from §22:
Phase A (Sources / RAG)
- encoach.course.plan.source model (file | url | text)
- SourceIndexer extracts PDF (pypdf), DOCX (python-docx), HTML, plain
text and embeds chunks via the existing pgvector pipeline scoped to
plan_id, so resources.search only returns the plan's own corpus
- Endpoints: list/create/upload/reindex/delete + plan-scoped retrieval
Phase B (Deliverables)
- services.deliverables.compute_deliverables walks the plan, derives
{planned, generated, ready} per week from material + media state
- GET /api/ai/course-plan/<id>/deliverables drives the new wizard
preview step and the live progress strip on the detail page
Phase C (Multi-modal media)
- encoach.course.plan.media model + MediaService:
audio: AWS Polly (default) or ElevenLabs
image: OpenAI DALL-E 3, capped per plan via system parameter
video: local ffmpeg subprocess (image + audio -> MP4 1280x720)
- Three new agent tools (media.synthesize_audio / generate_image /
compose_video), wired into course_week_materials and a new
course_media_director agent
- Endpoints per material + week-level batch generator
Phase D (Assignments)
- encoach.course.plan.assignment supports mode='batch' (op.batch) or
mode='students' (res.users), with due_date + message + state
- REST endpoints to list / create / delete assignments
Phase E (Student view)
- /api/student/course-plans + /api/student/course-plans/<id>
enforce visibility via assignment.expand_user_ids()
- New /student/course-plans list + read-only drilldown rendering
audio/image/video tiles from /web/content/<attachment_id>
Cross-cutting
- encoach.ai.tool.category: + media (so the new tools register)
- encoach.embedding gains a plan_id filter for plan-scoped RAG
- Wizard adds Sources + Multimedia steps; AdminCoursePlanDetail
rewritten with DeliverablesStrip + SourcesCard + AssignmentsCard +
per-material MediaDrawer
- ~280 new EN + AR i18n keys (full RTL coverage)
- smoke_course_plan.py exercises every phase via odoo-bin shell;
last run: PASS A/B/D/E + DALL-E 3 image (753 KB), Polly audio
fails cleanly when AWS creds aren't configured (expected)
Documentation: §24 added to docs/PROJECT_SUMMARY.md with phase-by-phase
artefact list, endpoints, smoke test, ops notes, and gotchas.
Made-with: Cursor
1091 lines
40 KiB
TypeScript
1091 lines
40 KiB
TypeScript
/** English translations. Keep keys nested by feature area.
|
||
*
|
||
* We deliberately annotate the literal with `Translations` (not `as const`)
|
||
* so sibling locale files can widen their string values without fighting
|
||
* literal-string type mismatches.
|
||
*
|
||
* Adding a new key: add it here FIRST (English is the source of truth), then
|
||
* mirror it in `ar.ts`. The `Translations` interface is intentionally
|
||
* permissive (`Record<string, string>` per group) so we don't have to update
|
||
* a giant type every time we add a string.
|
||
*/
|
||
export interface Translations {
|
||
common: Record<string, string>;
|
||
auth: Record<string, string>;
|
||
nav: Record<string, string>;
|
||
sidebarGroup: Record<string, string>;
|
||
breadcrumb: Record<string, string>;
|
||
userMenu: Record<string, string>;
|
||
chrome: Record<string, string>;
|
||
notifications: Record<string, string>;
|
||
theme: Record<string, string>;
|
||
language: Record<string, string>;
|
||
roles: Record<string, string>;
|
||
dashboard: Record<string, string>;
|
||
studentDash: Record<string, string>;
|
||
teacherDash: Record<string, string>;
|
||
adminDash: Record<string, string>;
|
||
examPopup: Record<string, string>;
|
||
errors: Record<string, string>;
|
||
ai: Record<string, string>;
|
||
privacy: Record<string, string>;
|
||
feedback: Record<string, string>;
|
||
// quickSetup mixes flat strings (page chrome) and nested blocks
|
||
// ("admin.step1.title", "teacher.quick.discussion.title") so its values
|
||
// can be either strings or nested records. We intentionally use a loose
|
||
// shape here rather than maintain a hand-authored deep type.
|
||
quickSetup: Record<string, unknown>;
|
||
/** Smart Wizard Hub + per-scenario step-by-step wizards. */
|
||
wizardHub: Record<string, unknown>;
|
||
wizard: Record<string, unknown>;
|
||
/** AI course-plan generator — list, detail, wizard. */
|
||
coursePlan: Record<string, unknown>;
|
||
/** AI Agents & Tools configurator (the /admin/ai/prompts page). */
|
||
aiAdmin: Record<string, unknown>;
|
||
agents: Record<string, unknown>;
|
||
tools: Record<string, unknown>;
|
||
}
|
||
|
||
const en: Translations = {
|
||
common: {
|
||
cancel: "Cancel",
|
||
save: "Save",
|
||
delete: "Delete",
|
||
edit: "Edit",
|
||
close: "Close",
|
||
back: "Back",
|
||
next: "Next",
|
||
search: "Search",
|
||
loading: "Loading…",
|
||
error: "Error",
|
||
retry: "Retry",
|
||
yes: "Yes",
|
||
no: "No",
|
||
actions: "Actions",
|
||
status: "Status",
|
||
settings: "Settings",
|
||
signOut: "Sign out",
|
||
viewAll: "View All",
|
||
dismiss: "Dismiss",
|
||
pending: "Pending",
|
||
active: "Active",
|
||
inactive: "Inactive",
|
||
available: "Available",
|
||
unavailable: "Unavailable",
|
||
name: "Name",
|
||
email: "Email",
|
||
user: "User",
|
||
home: "Home",
|
||
saving: "Saving…",
|
||
disabled: "Disabled",
|
||
},
|
||
auth: {
|
||
signIn: "Sign in",
|
||
signInTitle: "Sign in to EnCoach",
|
||
welcomeBack: "Welcome back",
|
||
signInDescription: "Sign in to your account to continue",
|
||
email: "Email",
|
||
emailPlaceholder: "you@example.com",
|
||
password: "Password",
|
||
rememberMe: "Remember me",
|
||
forgotPassword: "Forgot password?",
|
||
needAccount: "Don't have an account?",
|
||
signUp: "Sign up",
|
||
invalidCredentials: "Invalid email or password",
|
||
missingCredentials: "Please enter email and password",
|
||
loginFailedTitle: "Login Failed",
|
||
errorTitle: "Error",
|
||
},
|
||
nav: {
|
||
smartWizard: "Smart Wizard",
|
||
quickSetup: "Smart Setup",
|
||
coursePlans: "Course Plans (AI)",
|
||
adminDashboard: "Admin Dashboard",
|
||
platformDashboard: "Platform Dashboard",
|
||
dashboard: "Dashboard",
|
||
courses: "Courses",
|
||
myCourses: "My Courses",
|
||
myCoursePlans: "My Course Plans",
|
||
students: "Students",
|
||
teachers: "Teachers",
|
||
batches: "Batches",
|
||
timetable: "Timetable",
|
||
reports: "Reports",
|
||
assignments: "Assignments",
|
||
examsList: "Exams List",
|
||
examStructures: "Exam Structures",
|
||
rubrics: "Rubrics",
|
||
generation: "Generation",
|
||
reviewQueue: "Review Queue",
|
||
aiPrompts: "AI Agents & Tools",
|
||
aiFeedback: "AI Feedback",
|
||
approvalWorkflows: "Approval Workflows",
|
||
taxonomy: "Taxonomy",
|
||
resources: "Resources",
|
||
resourceLibrary: "Resource Library",
|
||
academicYears: "Academic Years",
|
||
departments: "Departments",
|
||
admissions: "Admissions",
|
||
admissionRegister: "Admission Register",
|
||
examSessions: "Exam Sessions",
|
||
marksheets: "Marksheets",
|
||
studentLeave: "Student Leave",
|
||
fees: "Fees & Payments",
|
||
lessons: "Lessons",
|
||
gradebook: "Gradebook",
|
||
studentProgress: "Student Progress",
|
||
library: "Library",
|
||
activities: "Activities",
|
||
facilities: "Facilities",
|
||
users: "Users",
|
||
entities: "Entities",
|
||
classrooms: "Classrooms",
|
||
userRoles: "User Roles",
|
||
rolesPermissions: "Roles & Permissions",
|
||
authorityMatrix: "Authority Matrix",
|
||
studentPerformance: "Student Performance",
|
||
statsCorporate: "Stats Corporate",
|
||
record: "Record",
|
||
vocabulary: "Vocabulary",
|
||
grammar: "Grammar",
|
||
faqManager: "FAQ Manager",
|
||
notificationRules: "Notification Rules",
|
||
approvalConfig: "Approval Config",
|
||
paymentRecord: "Payment Record",
|
||
tickets: "Tickets",
|
||
settings: "Settings",
|
||
profile: "Profile",
|
||
privacy: "Privacy Center",
|
||
subjectRegistration: "Subject Registration",
|
||
mySubjects: "My Subjects",
|
||
grades: "Grades",
|
||
attendance: "Attendance",
|
||
myJourney: "My Journey",
|
||
discussions: "Discussions",
|
||
messages: "Messages",
|
||
announcements: "Announcements",
|
||
},
|
||
sidebarGroup: {
|
||
overview: "Overview",
|
||
lms: "LMS",
|
||
adaptiveLearning: "Adaptive Learning",
|
||
institutional: "Institutional",
|
||
academic: "Academic",
|
||
management: "Management",
|
||
reports: "Reports",
|
||
configuration: "Configuration",
|
||
training: "Training",
|
||
support: "Support",
|
||
learning: "Learning",
|
||
progress: "Progress",
|
||
communication: "Communication",
|
||
account: "Account",
|
||
teaching: "Teaching",
|
||
},
|
||
breadcrumb: {
|
||
home: "Home",
|
||
admin: "Admin",
|
||
dashboard: "Dashboard",
|
||
platform: "Platform",
|
||
exam: "Exam",
|
||
review: "Review",
|
||
ai: "AI",
|
||
feedback: "Feedback",
|
||
},
|
||
userMenu: {
|
||
profile: "Profile",
|
||
settings: "Settings",
|
||
logout: "Logout",
|
||
userFallback: "User",
|
||
},
|
||
chrome: {
|
||
needHelp: "Need help?",
|
||
ticketsTooltip: "Support tickets",
|
||
aiSearchPlaceholder: "Ask anything... e.g. 'Show students with low attendance'",
|
||
aiSearching: "AI is searching…",
|
||
aiRelatedQueries: "Related queries",
|
||
aiNoResults: "No results for \"{{q}}\". Try a different question or keyword.",
|
||
aiSearchFailedTitle: "Search failed",
|
||
aiSearchFailedDesc: "Could not complete AI search.",
|
||
portalSuffix: "Portal",
|
||
adminAlt: "EnCoach",
|
||
adminAltLong: "EnCoach — Unlock your potential with AI powered platform",
|
||
toggleSidebar: "Toggle sidebar",
|
||
},
|
||
notifications: {
|
||
title: "Notifications",
|
||
empty: "You're all caught up.",
|
||
ariaLabel: "Notifications",
|
||
},
|
||
theme: {
|
||
title: "Theme",
|
||
light: "Light",
|
||
dark: "Dark",
|
||
system: "System",
|
||
toggleLabel: "Toggle theme",
|
||
},
|
||
language: {
|
||
change: "Change language",
|
||
label: "Language",
|
||
},
|
||
roles: {
|
||
student: "student",
|
||
teacher: "teacher",
|
||
admin: "admin",
|
||
developer: "developer",
|
||
corporate: "corporate",
|
||
mastercorporate: "master corporate",
|
||
agent: "agent",
|
||
},
|
||
dashboard: {
|
||
greetingFallback: "Student",
|
||
},
|
||
studentDash: {
|
||
welcome: "Welcome back, {{name}}!",
|
||
subtitle: "Here's an overview of your learning progress.",
|
||
enrolledCourses: "Enrolled Courses",
|
||
overallProgress: "Overall Progress",
|
||
averageGrade: "Average Grade",
|
||
totalChapters: "Total Chapters",
|
||
myCourses: "My Courses",
|
||
noEnrolledCourses: "No enrolled courses yet.",
|
||
chaptersMaterials: "{{chapters}} chapters · {{materials}} materials",
|
||
quickActions: "Quick Actions",
|
||
enrollToStart: "Enroll in a course to get started.",
|
||
continue: "Continue",
|
||
start: "Start",
|
||
chaptersDone: "{{done}}/{{total}} chapters done",
|
||
recentGrades: "Recent Grades",
|
||
noGradesYet: "No grades yet.",
|
||
},
|
||
teacherDash: {
|
||
title: "Teacher Dashboard",
|
||
subtitle: "Overview of your teaching activities.",
|
||
activeCourses: "Active Courses",
|
||
totalStudents: "Total Students",
|
||
pendingGrading: "Pending Grading",
|
||
avgPassRate: "Avg. Pass Rate",
|
||
myCourses: "My Courses",
|
||
colCourse: "Course",
|
||
colStudents: "Students",
|
||
colStatus: "Status",
|
||
recentActivity: "Recent Activity",
|
||
submitted: "Submitted {{when}}",
|
||
pending: "Pending",
|
||
},
|
||
adminDash: {
|
||
title: "Admin Dashboard",
|
||
subtitle: "Platform overview and key metrics.",
|
||
addStudent: "Add Student",
|
||
newCourse: "New Course",
|
||
totalStudents: "Total Students",
|
||
activeCourses: "Active Courses",
|
||
teachers: "Teachers",
|
||
activeBatches: "Active Batches",
|
||
openTickets: "Open Tickets",
|
||
revenue: "Revenue",
|
||
departments: "Departments",
|
||
classrooms: "Classrooms",
|
||
subjects: "Subjects",
|
||
resources: "Resources",
|
||
coursesOverview: "Courses Overview",
|
||
batchCapacity: "Batch Capacity",
|
||
noCourseData: "No course data available.",
|
||
noBatchData: "No batch data available.",
|
||
quickSummary: "Quick Summary",
|
||
colModule: "Module",
|
||
colCount: "Count",
|
||
colStatus: "Status",
|
||
exams: "Exams",
|
||
examSessionsCount: "{{n}} sessions",
|
||
assignments: "Assignments",
|
||
acrossCourses: "across courses",
|
||
supportTickets: "Support Tickets",
|
||
ticketsOpenCount: "{{n}} open",
|
||
payments: "Payments",
|
||
revenueTotal: "{{amount}} total",
|
||
chartCapacity: "Capacity",
|
||
chartEnrolled: "Enrolled",
|
||
},
|
||
examPopup: {
|
||
title: "Upcoming Exams ({{n}})",
|
||
activeNow: "Active Now",
|
||
from: "From:",
|
||
to: "To:",
|
||
startExam: "Start Exam",
|
||
notAvailableYet: "Not Available Yet",
|
||
willBeAvailable: "Exam will be available once it becomes active",
|
||
notActive: "Exam is not currently active",
|
||
},
|
||
errors: {
|
||
somethingWrong: "Something went wrong",
|
||
unexpectedDescription: "An unexpected error occurred. Please try refreshing the page.",
|
||
errorDetails: "Error details",
|
||
refreshPage: "Refresh Page",
|
||
notFoundCode: "404",
|
||
notFoundMessage: "Oops! Page not found",
|
||
returnHome: "Return to Home",
|
||
},
|
||
ai: {
|
||
assistantLabel: "AI Assistant",
|
||
assistantTitle: "EnCoach AI Assistant",
|
||
placeholder: "Ask anything...",
|
||
send: "Send",
|
||
thinking: "Thinking…",
|
||
emptyLine1: "Ask me anything about the platform,",
|
||
emptyLine2: "or click a quick action above.",
|
||
fallbackReply: "Sorry, I could not reach the assistant. Please try again in a moment.",
|
||
replyErrorTitle: "Could not get a reply",
|
||
replyErrorDesc: "Something went wrong. Try again.",
|
||
quickHealth: "Platform health summary",
|
||
quickDropouts: "Show dropout risks",
|
||
quickCompare: "Compare course performance",
|
||
quickBatch: "Suggest batch improvements",
|
||
tipLabel: "AI Tip",
|
||
insightLabel: "AI Insight",
|
||
recommendationLabel: "AI Recommendation",
|
||
categoryTipLabel: "AI {{category}} Tip",
|
||
loadingTip: "Loading AI tip…",
|
||
loadingInsights: "Loading insights…",
|
||
loadingAlerts: "Loading alerts…",
|
||
couldNotLoadTip: "Could not load tip.",
|
||
couldNotLoadInsights: "Could not load insights.",
|
||
couldNotLoadAlerts: "Could not load alerts.",
|
||
noTipAvailable: "No tip available.",
|
||
noTipForContext: "No tip for this context yet.",
|
||
noInsightsAvailable: "No insights available for this view.",
|
||
noAlertsRightNow: "No AI alerts right now.",
|
||
alertsUnavailable: "Alerts unavailable",
|
||
insightsUnavailable: "Insights unavailable",
|
||
platformInsightsTitle: "AI Platform Insights",
|
||
},
|
||
privacy: {
|
||
title: "Privacy Center",
|
||
description:
|
||
"Manage your personal data held by EnCoach under GDPR and equivalent regulations.",
|
||
exportTitle: "Download your data",
|
||
exportDescription:
|
||
"We'll package your profile, entity memberships, exam attempts, answers, AI feedback, and tickets into a single JSON file.",
|
||
exportButton: "Download my data",
|
||
exportPreparing: "Preparing export…",
|
||
exportSuccess: "Data export downloaded",
|
||
deleteTitle: "Delete my account",
|
||
deleteDescription:
|
||
"This anonymises your profile and removes personal fields from our records. Exam attempts are retained (without identifying information) for statistical integrity. This action cannot be undone.",
|
||
deleteButton: "Erase my account",
|
||
deleteErasing: "Erasing…",
|
||
confirmTitle: "Erase your account?",
|
||
confirmDescription:
|
||
"We'll anonymise your personal data and deactivate your login. Aggregate analytics will remain but will no longer be linked to you.",
|
||
confirmTypeDelete: "Type DELETE to confirm",
|
||
confirmErased: "Your account has been erased. Signing out…",
|
||
},
|
||
feedback: {
|
||
thumbsUp: "Thumbs up",
|
||
thumbsDown: "Thumbs down",
|
||
whatWentWrong: "What went wrong?",
|
||
commentPlaceholder: "e.g. Wrong answer, confusing wording, off-topic…",
|
||
commentRequired: "Please tell us what was wrong.",
|
||
submit: "Submit feedback",
|
||
},
|
||
// Smart-setup wizard. Nested so i18next's default "." key separator
|
||
// resolves e.g. t("quickSetup.admin.step1.title").
|
||
quickSetup: {
|
||
adminTitle: "Smart Setup",
|
||
adminSubtitle:
|
||
"Everything you need to launch an exam-ready platform, in the recommended order. Tick each step off as you go — the wizard auto-detects progress.",
|
||
teacherTitle: "Smart Setup",
|
||
teacherSubtitle:
|
||
"Launch a new course end-to-end, then jump to common day-to-day tasks.",
|
||
progressLabel: "Progress",
|
||
recommendedFlow: "Recommended flow",
|
||
otherQuickCreates: "Other quick creates",
|
||
ready: "Ready",
|
||
start: "Start",
|
||
review: "Review",
|
||
open: "Open",
|
||
helpAria: "Show help",
|
||
admin: {
|
||
step1: {
|
||
title: "Create a rubric",
|
||
description:
|
||
"Define the grading criteria for Writing and Speaking tasks. Rubrics are referenced later when generating or approving exams.",
|
||
help: "A rubric is a scoring grid (bands × criteria). You can start from a template or compose one from predefined criteria.",
|
||
},
|
||
step2: {
|
||
title: "Define an exam structure",
|
||
description:
|
||
"Blueprint the sections, tasks, and parts that every generated or custom exam of this type must contain.",
|
||
help: "Structures enforce consistency. E.g. an IELTS Writing structure has Task 1 (150w) + Task 2 (250w); generation will refuse to submit if either is missing.",
|
||
},
|
||
step3: {
|
||
title: "Generate or create an exam",
|
||
description:
|
||
"Use AI generation (fastest) or hand-build a custom exam. Save as draft or submit for approval.",
|
||
help: "Generation picks a structure + rubric and produces questions. The custom builder gives you full control for pilot/test exams.",
|
||
},
|
||
step4: {
|
||
title: "Review & approve",
|
||
description:
|
||
"Approvers sign off on exams before students can see them. Configure the workflow once, then route submissions automatically.",
|
||
help: "The approval queue lists every exam waiting for sign-off. Reject sends it back to the author; approve publishes it.",
|
||
},
|
||
step5: {
|
||
title: "Assign to students",
|
||
description:
|
||
"Schedule the published exam, pick a cohort, and send it out. Students see it immediately in their portal.",
|
||
help: "Assignments can target individual students, batches, or classrooms. Timezone-aware windows are supported.",
|
||
},
|
||
quick: {
|
||
course: { title: "New course", description: "Stand up a course shell for teachers to fill with chapters." },
|
||
resource: { title: "Upload resource", description: "Add PDFs, audio, video, or links to the shared library." },
|
||
student: { title: "Add student", description: "Create a student account and assign them to a batch." },
|
||
teacher: { title: "Add teacher", description: "Invite a teacher and grant teaching permissions." },
|
||
classroom: { title: "New classroom", description: "Group students for scheduling and attendance." },
|
||
examSession: { title: "Schedule exam session", description: "Create a proctored sitting for an institutional exam." },
|
||
customExam: { title: "Custom exam", description: "Hand-build an exam from scratch with full control." },
|
||
ticket: { title: "Open ticket", description: "Raise a support ticket on behalf of a user." },
|
||
},
|
||
},
|
||
teacher: {
|
||
step1: {
|
||
title: "Create a course",
|
||
description: "Give your course a name, pick a subject, and set its level. You can edit chapters after creation.",
|
||
help: "Courses are the container for chapters, materials, and assignments.",
|
||
},
|
||
step2: {
|
||
title: "Add chapters & content",
|
||
description: "Open your course and add chapters with lessons, videos, quizzes, and practice tasks.",
|
||
help: "Chapters organise learning objectives. Use the AI workbench to auto-draft content.",
|
||
},
|
||
step3: {
|
||
title: "Upload resources",
|
||
description: "Share supporting PDFs, audio, or video with your students via the library.",
|
||
help: "Large files are fine — the server accepts up to 128 MB per upload.",
|
||
},
|
||
step4: {
|
||
title: "Create an assignment",
|
||
description: "Turn a published exam or task into an assignment with a due date and target cohort.",
|
||
help: "Assignments auto-surface in each student's dashboard and on their timetable.",
|
||
},
|
||
step5: {
|
||
title: "Track student progress",
|
||
description: "Monitor submissions, attendance, and adaptive learning insights for your class.",
|
||
help: "The adaptive engine flags at-risk students so you can intervene early.",
|
||
},
|
||
quick: {
|
||
discussion: { title: "New discussion", description: "Start a topic thread for your class." },
|
||
announcement: { title: "New announcement", description: "Broadcast a message to all your students." },
|
||
attendance: { title: "Mark attendance", description: "Record today's attendance for a session." },
|
||
},
|
||
},
|
||
},
|
||
wizardHub: {
|
||
title: "Smart Wizard",
|
||
subtitle:
|
||
"Pick any scenario and the wizard will walk you through it step by step. No need to hunt through settings — every Next button moves you closer to done.",
|
||
recommendedOrder: "Recommended order",
|
||
order: {
|
||
rubric: "Create rubrics (Writing / Speaking)",
|
||
structure: "Define exam structures (sections, tasks, durations)",
|
||
generate: "Generate or create exams",
|
||
approve: "Review & approve pending exams",
|
||
assign: "Assign exams to students",
|
||
},
|
||
guided: "Guided wizards",
|
||
advanced: "Full pages (advanced)",
|
||
advancedBadge: "Advanced",
|
||
aiBadge: "AI",
|
||
startWizard: "Start wizard",
|
||
openPage: "Open page",
|
||
cards: {
|
||
rubric: {
|
||
title: "Create a rubric",
|
||
description: "Name → skill → criteria → descriptors → review. Writing and Speaking only.",
|
||
},
|
||
examStructure: {
|
||
title: "Define an exam structure",
|
||
description: "Name → modules → writing tasks → review. Reusable blueprint for generation.",
|
||
},
|
||
course: {
|
||
title: "Create a course",
|
||
description: "Title → level & capacity → review. Chapters can be added from the course page.",
|
||
},
|
||
coursePlan: {
|
||
title: "Generate a course plan (AI)",
|
||
description: "Describe the course → AI writes objectives, per-skill outcomes, grammar scope and a week-by-week delivery plan, then produces real teaching materials per week.",
|
||
},
|
||
generation: {
|
||
title: "Generate an exam",
|
||
description: "Full AI generation page with rubric/structure pickers and per-module controls.",
|
||
},
|
||
approval: {
|
||
title: "Approval workflows",
|
||
description: "Configure who reviews which exams and see pending requests.",
|
||
},
|
||
assign: {
|
||
title: "Assign to students",
|
||
description: "Full scheduling page with student picker, windows, and timezone support.",
|
||
},
|
||
resource: {
|
||
title: "Upload resource",
|
||
description: "Add PDFs, audio, video, or links to the shared library.",
|
||
},
|
||
student: {
|
||
title: "Add student",
|
||
description: "Create a student account and enroll them in a batch.",
|
||
},
|
||
},
|
||
},
|
||
wizard: {
|
||
back: "Back",
|
||
next: "Next",
|
||
finish: "Finish",
|
||
backToHub: "Back to wizards",
|
||
stepOf: "Step {{current}} of {{total}}",
|
||
rubric: {
|
||
title: "Create a rubric",
|
||
subtitle: "Grading grid for Writing or Speaking tasks. Referenced later when generating or approving exams.",
|
||
finish: "Create rubric",
|
||
toastSuccess: "Rubric created",
|
||
toastError: "Could not create rubric",
|
||
addCriterion: "Add criterion",
|
||
removeCriterion: "Remove criterion",
|
||
unnamedCriterion: "(unnamed criterion)",
|
||
descriptorHint: "Descriptors are optional. They appear to graders as guidance for each criterion.",
|
||
maxLabel: "Max",
|
||
moduleHint:
|
||
"Rubrics only apply to Writing and Speaking. Listening and Reading are auto-graded and don't need one.",
|
||
step1: {
|
||
title: "Basics",
|
||
description: "Give the rubric a name and pick the skill it applies to.",
|
||
},
|
||
step2: {
|
||
title: "Criteria",
|
||
description: "Add the criteria you want graders to score. Each criterion has a max score (e.g. 9 for IELTS).",
|
||
},
|
||
step3: {
|
||
title: "Descriptors",
|
||
description: "Optional: describe what each criterion measures. Graders see these as hints.",
|
||
},
|
||
step4: {
|
||
title: "Review",
|
||
description: "Double-check and create.",
|
||
},
|
||
labels: {
|
||
name: "Rubric name",
|
||
module: "Skill",
|
||
description: "Description",
|
||
criterionName: "Criterion name",
|
||
maxScore: "Max score",
|
||
},
|
||
placeholders: {
|
||
name: "e.g. IELTS Writing Task 2",
|
||
description: "Short description shown to graders.",
|
||
criterionName: "e.g. Task Response",
|
||
descriptor: "What graders should check for this criterion.",
|
||
},
|
||
modules: {
|
||
writing: "Writing",
|
||
speaking: "Speaking",
|
||
},
|
||
errors: {
|
||
nameRequired: "Please enter a name for this rubric.",
|
||
moduleRestricted: "Rubrics apply only to Writing or Speaking.",
|
||
criterionRequired: "Add at least one criterion.",
|
||
criterionName: "Every criterion needs a name.",
|
||
criterionScore: "Max score must be between 1 and 100.",
|
||
},
|
||
},
|
||
structure: {
|
||
title: "Define an exam structure",
|
||
subtitle: "Blueprint the sections, tasks, and parts every generated or custom exam of this type must contain.",
|
||
finish: "Create structure",
|
||
toastSuccess: "Exam structure created",
|
||
toastError: "Could not create exam structure",
|
||
industryHint: "Optional: e.g. 'Business English', 'Healthcare'.",
|
||
writingSkipped: "Writing is not in the selected modules — this step is skipped.",
|
||
wordsSuffix: "words",
|
||
addTask: "Add task",
|
||
removeTask: "Remove task",
|
||
step1: {
|
||
title: "Basics",
|
||
description: "Name the structure and pick its exam type.",
|
||
},
|
||
step2: {
|
||
title: "Modules",
|
||
description: "Select which modules this structure covers. Students will see these sections.",
|
||
},
|
||
step3: {
|
||
title: "Writing tasks",
|
||
description: "For writing, define each task's minimum word count.",
|
||
},
|
||
step4: {
|
||
title: "Review",
|
||
description: "Double-check and create.",
|
||
},
|
||
labels: {
|
||
name: "Structure name",
|
||
industry: "Industry / context",
|
||
examType: "Exam type",
|
||
modules: "Modules",
|
||
taskLabel: "Task label",
|
||
minWords: "Min words",
|
||
},
|
||
placeholders: {
|
||
name: "e.g. IELTS Academic Writing",
|
||
industry: "e.g. Business English",
|
||
},
|
||
examTypes: {
|
||
academic: "Academic",
|
||
general: "General",
|
||
},
|
||
modules: {
|
||
listening: {
|
||
title: "Listening",
|
||
description: "Audio-based questions (auto-graded).",
|
||
},
|
||
reading: {
|
||
title: "Reading",
|
||
description: "Passage-based questions (auto-graded).",
|
||
},
|
||
writing: {
|
||
title: "Writing",
|
||
description: "Essay tasks graded with a rubric.",
|
||
},
|
||
speaking: {
|
||
title: "Speaking",
|
||
description: "Oral tasks graded with a rubric.",
|
||
},
|
||
},
|
||
errors: {
|
||
nameRequired: "Please enter a name for this structure.",
|
||
moduleRequired: "Select at least one module.",
|
||
writingTaskRequired: "Writing needs at least one task.",
|
||
writingTaskLabel: "Each writing task needs a label.",
|
||
writingTaskWords: "Minimum words must be at least 1.",
|
||
},
|
||
},
|
||
course: {
|
||
title: "Create a course",
|
||
subtitle: "A lightweight course skeleton. You can add chapters, materials and enroll students after creation.",
|
||
finish: "Create course",
|
||
toastSuccess: "Course created",
|
||
toastError: "Could not create course",
|
||
codeHint: "Leave blank to auto-generate from the title.",
|
||
difficultyHint: "How challenging is this course overall? Used for search & filtering.",
|
||
cefrHint: "If this course targets a specific CEFR band, pick it here.",
|
||
step1: {
|
||
title: "Basics",
|
||
description: "Give your course a name and a short description.",
|
||
},
|
||
step2: {
|
||
title: "Level & capacity",
|
||
description: "Set difficulty, target CEFR level, and how many students can enroll.",
|
||
},
|
||
step3: {
|
||
title: "Review",
|
||
description: "Double-check and create.",
|
||
},
|
||
labels: {
|
||
title: "Course title",
|
||
code: "Course code",
|
||
description: "Description",
|
||
difficulty: "Difficulty",
|
||
cefrLevel: "CEFR level",
|
||
capacity: "Max capacity",
|
||
},
|
||
placeholders: {
|
||
title: "e.g. Foundation English Level 1",
|
||
code: "Auto-generated if empty",
|
||
description: "Who is this course for? What will they learn?",
|
||
difficulty: "Select a difficulty",
|
||
cefr: "Select a CEFR level",
|
||
},
|
||
difficulty: {
|
||
beginner: "Beginner",
|
||
intermediate: "Intermediate",
|
||
advanced: "Advanced",
|
||
},
|
||
errors: {
|
||
titleRequired: "Please enter a course title.",
|
||
capacityRequired: "Max capacity must be at least 1.",
|
||
},
|
||
},
|
||
},
|
||
coursePlan: {
|
||
listTitle: "Course Plans",
|
||
listSubtitle:
|
||
"AI-generated curriculum outlines: objectives, per-skill learning outcomes, grammar scope, a week-by-week delivery plan, and on-demand teaching materials for each week.",
|
||
generateNew: "Generate new plan",
|
||
searchPlaceholder: "Search plans by name…",
|
||
loadFailed: "Couldn't load course plans.",
|
||
deleted: "Plan deleted.",
|
||
deleteFailed: "Couldn't delete plan.",
|
||
delete: "Delete",
|
||
confirmDelete: "Delete plan \"{{name}}\"? This removes all weeks and materials.",
|
||
emptyTitle: "No course plans yet",
|
||
emptySubtitle:
|
||
"Use the AI wizard to generate your first plan — it only takes about a minute.",
|
||
open: "Open",
|
||
backToList: "Back to course plans",
|
||
noDescription: "No description.",
|
||
weeksCount_one: "{{count}} week",
|
||
weeksCount_other: "{{count}} weeks",
|
||
materialsCount_one: "{{count}} material",
|
||
materialsCount_other: "{{count}} materials",
|
||
hoursPerWeek: "{{count}} hrs/week",
|
||
weekN: "Week {{n}}",
|
||
generateMaterials: "Generate Week materials (AI)",
|
||
regenerateMaterials: "Regenerate Week materials",
|
||
generating: "Generating…",
|
||
generateHint:
|
||
"AI will produce a reading text, listening script, speaking prompts, writing prompt, grammar mini-lesson and vocabulary for this week.",
|
||
weekMaterialsGenerated: "Week materials generated.",
|
||
weekMaterialsFailed: "Couldn't generate week materials.",
|
||
generateSuccess: "Course plan generated.",
|
||
generateFailed: "Couldn't generate course plan.",
|
||
deliveryHint:
|
||
"Expand any week to view the planned outcomes and generate ready-to-use teaching materials.",
|
||
status: {
|
||
draft: "Draft",
|
||
generated: "Generated",
|
||
approved: "Approved",
|
||
archived: "Archived",
|
||
},
|
||
sections: {
|
||
objectives: "Course objectives",
|
||
outcomes: "Learning outcomes by skill",
|
||
grammar: "Grammar scope",
|
||
assessment: "Assessment",
|
||
resources: "Resources",
|
||
delivery: "Weekly delivery plan",
|
||
},
|
||
skill: {
|
||
reading: "Reading",
|
||
writing: "Writing",
|
||
listening: "Listening",
|
||
speaking: "Speaking",
|
||
grammar: "Grammar",
|
||
vocabulary: "Vocabulary",
|
||
integrated: "Integrated",
|
||
},
|
||
materialType: {
|
||
reading_text: "Reading text",
|
||
listening_script: "Listening script",
|
||
speaking_prompt: "Speaking prompt",
|
||
writing_prompt: "Writing prompt",
|
||
grammar_lesson: "Grammar lesson",
|
||
vocabulary_list: "Vocabulary list",
|
||
practice: "Practice",
|
||
other: "Material",
|
||
},
|
||
table: {
|
||
skill: "Skill",
|
||
outcomes: "Outcomes",
|
||
remarks: "Remarks",
|
||
},
|
||
wizard: {
|
||
title: "Generate a course plan",
|
||
subtitle:
|
||
"Describe the course once. The AI writes the full outline — objectives, outcomes, grammar, weekly plan — and you can generate Week 1 teaching material in one click afterwards.",
|
||
finish: "Generate plan",
|
||
reviewHint:
|
||
"Click Generate plan to start the AI. This usually takes 30–60 seconds; you'll land on the plan page once it's done.",
|
||
steps: {
|
||
basics: "Basics",
|
||
basicsDesc: "Name the course and set level, duration, and weekly hours.",
|
||
coverage: "Coverage",
|
||
coverageDesc: "Tell the AI how hours split across skills and who the learners are.",
|
||
sources: "Reference sources",
|
||
sourcesDesc:
|
||
"Drop PDFs, URLs, or pasted text. The AI uses them as grounding when generating each week's materials.",
|
||
scope: "Scope",
|
||
scopeDesc: "Optional: grammar focus, resources to reference, free-form notes.",
|
||
media: "Multimedia",
|
||
mediaDesc:
|
||
"Pick which media the AI should auto-produce alongside the text materials.",
|
||
review: "Review",
|
||
reviewDesc: "Double-check your brief before kicking off the generation.",
|
||
},
|
||
fields: {
|
||
title: "Course title",
|
||
cefr: "CEFR level",
|
||
cefrPlaceholder: "Pick a level",
|
||
totalWeeks: "Total weeks",
|
||
contactHours: "Contact hours / week",
|
||
skillsDivision: "Skills division",
|
||
skillsDivisionHint:
|
||
"Free-form — e.g. \"10 hrs/wk Reading & Writing + 8 hrs/wk Listening & Speaking\". Leave blank to let the AI decide.",
|
||
learnerProfile: "Learner profile",
|
||
learnerProfilePlaceholder:
|
||
"Who are the learners? Age range, L1, prior study, goals…",
|
||
grammarFocus: "Grammar focus (press Enter to add)",
|
||
grammarFocusPlaceholder: "e.g. present simple",
|
||
resources: "Resources to reference (press Enter to add)",
|
||
resourcesPlaceholder: "e.g. Pathways 1 (National Geographic)",
|
||
notes: "Additional notes",
|
||
notesPlaceholder: "Anything else the AI should know.",
|
||
},
|
||
errors: {
|
||
titleRequired: "Please enter a course title.",
|
||
cefrRequired: "Please pick a CEFR level.",
|
||
weeksRange: "Total weeks must be at least 1.",
|
||
},
|
||
review: {
|
||
sourcesCount_one: "{{count}} reference source",
|
||
sourcesCount_other: "{{count}} reference sources",
|
||
noSources: "No reference sources",
|
||
mediaNone: "No media generation",
|
||
},
|
||
},
|
||
sections_extras: {
|
||
sources: "Reference sources",
|
||
progress: "Generation progress",
|
||
assignments: "Assignments",
|
||
media: "Generated media",
|
||
},
|
||
sources: {
|
||
sectionTitle: "Reference sources (RAG)",
|
||
sectionDesc:
|
||
"Upload PDFs, paste URLs, or drop in raw text. The AI will use the indexed content as grounding when it writes weekly materials.",
|
||
collected_one: "{{count}} source ready",
|
||
collected_other: "{{count}} sources ready",
|
||
empty: "No reference sources yet.",
|
||
dropTitle: "Add reference materials",
|
||
dropHint: "PDF, DOCX, TXT, HTML, or plain text — up to a few MB each.",
|
||
uploadFiles: "Upload files",
|
||
urlLabel: "Add a URL",
|
||
textLabel: "Or paste text directly",
|
||
textPlaceholder: "Paste a passage to ground the AI on…",
|
||
uploadFailed: "Couldn't upload \"{{name}}\".",
|
||
reindex: "Reindex",
|
||
reindexed: "Source reindexed.",
|
||
reindexFailed: "Reindex failed.",
|
||
deleted: "Source deleted.",
|
||
deleteFailed: "Couldn't delete source.",
|
||
status: {
|
||
pending: "Pending",
|
||
indexing: "Indexing…",
|
||
indexed: "Indexed",
|
||
failed: "Failed",
|
||
},
|
||
kindLabel: {
|
||
file: "File",
|
||
url: "URL",
|
||
text: "Text",
|
||
},
|
||
chunks_one: "{{count}} chunk",
|
||
chunks_other: "{{count}} chunks",
|
||
},
|
||
sourceKind: {
|
||
file: "File",
|
||
url: "URL",
|
||
text: "Inline text",
|
||
},
|
||
deliverables: {
|
||
title: "Generation progress",
|
||
subtitle:
|
||
"What the AI is going to produce, what it has produced, and what is fully ready (materials + media).",
|
||
summary: {
|
||
planned_one: "{{count}} planned",
|
||
planned_other: "{{count}} planned",
|
||
generated_one: "{{count}} generated",
|
||
generated_other: "{{count}} generated",
|
||
ready_one: "{{count}} ready",
|
||
ready_other: "{{count}} ready",
|
||
},
|
||
mediaCounts: "Audio {{audio}} · Image {{image}} · Video {{video}}",
|
||
status: {
|
||
planned: "Planned",
|
||
generated: "Generated",
|
||
ready: "Ready",
|
||
},
|
||
perWeek: "Per-week breakdown",
|
||
noWeeks: "No weeks yet — generate the plan first.",
|
||
},
|
||
media: {
|
||
audio: "Audio",
|
||
image: "Image",
|
||
video: "Video",
|
||
audioTitle: "Narration audio",
|
||
audioDesc:
|
||
"Generate spoken audio (Polly / ElevenLabs) for listening scripts and speaking prompts.",
|
||
imageTitle: "Cover images",
|
||
imageDesc:
|
||
"Generate DALL·E 3 images for reading texts and listening scripts. Counts against the per-plan image budget.",
|
||
videoTitle: "Slideshow video",
|
||
videoDesc:
|
||
"Combine audio + image into a short MP4 with ffmpeg. Slowest option.",
|
||
hint:
|
||
"Audio is on by default. Video is off by default — turn it on once you've reviewed the audio + image.",
|
||
drawerTitle: "Media for \"{{title}}\"",
|
||
drawerSubtitle: "Generate or remove audio, images, and slideshow video.",
|
||
generateAudio: "Generate audio",
|
||
generateImage: "Generate image",
|
||
generateVideo: "Generate video",
|
||
generating: "Generating…",
|
||
audioReady: "Audio ready",
|
||
imageReady: "Image ready",
|
||
videoReady: "Video ready",
|
||
audioFailed: "Audio generation failed.",
|
||
imageFailed: "Image generation failed.",
|
||
videoFailed: "Video generation failed.",
|
||
deleted: "Media deleted.",
|
||
deleteFailed: "Couldn't delete media.",
|
||
open: "Open",
|
||
download: "Download",
|
||
noMedia: "No media yet for this material.",
|
||
mediaForMaterial: "Media",
|
||
bulk: "Generate week media",
|
||
bulkSuccess: "Week media generation done.",
|
||
bulkFailed: "Couldn't generate week media.",
|
||
provider: "Provider",
|
||
},
|
||
assignments: {
|
||
title: "Assigned to",
|
||
empty: "Not assigned to anyone yet.",
|
||
assign: "Assign",
|
||
assignTitle: "Assign this course plan",
|
||
assignSubtitle:
|
||
"Pick a class (batch) for everyone, or individual students. They'll see the plan in their student dashboard.",
|
||
modeBatch: "Whole class (batch)",
|
||
modeStudents: "Individual students",
|
||
pickBatch: "Select a batch",
|
||
pickStudents: "Pick students",
|
||
noBatches: "No batches available.",
|
||
noStudents: "No students available.",
|
||
dueDate: "Due date (optional)",
|
||
message: "Message to learners (optional)",
|
||
messagePlaceholder: "Welcome note, expectations, deadlines…",
|
||
created: "Plan assigned.",
|
||
createFailed: "Couldn't assign plan.",
|
||
removed: "Assignment removed.",
|
||
removeFailed: "Couldn't remove assignment.",
|
||
remove: "Remove",
|
||
confirmRemove: "Remove this assignment?",
|
||
assignedBy: "Assigned by {{name}}",
|
||
students_one: "{{count}} student",
|
||
students_other: "{{count}} students",
|
||
cancel: "Cancel",
|
||
save: "Assign",
|
||
},
|
||
student: {
|
||
listTitle: "My course plans",
|
||
listSubtitle:
|
||
"Personalised AI-generated curricula assigned by your teacher. Open one to see the weekly plan, materials, and media.",
|
||
empty: "No plans assigned to you yet.",
|
||
open: "Open course",
|
||
assignedBy: "Assigned by {{name}}",
|
||
due: "Due {{date}}",
|
||
noDue: "No deadline",
|
||
backToList: "Back to my plans",
|
||
},
|
||
wizardSourcesStep: "Sources",
|
||
},
|
||
aiAdmin: {
|
||
title: "AI Agents & Tools",
|
||
subtitle:
|
||
"Configure the LangGraph-backed agents that power course planning, exam generation, exercise generation, the LMS tutor, and grading. Defaults are pre-seeded and ready to use.",
|
||
tabs: {
|
||
agents: "Agents",
|
||
tools: "Tools",
|
||
prompts: "Prompts",
|
||
},
|
||
},
|
||
agents: {
|
||
list: {
|
||
title: "Agents",
|
||
subtitle: "Pre-configured for every platform pillar — edit defaults below.",
|
||
search: "Search agents…",
|
||
empty: "No agents match your search.",
|
||
},
|
||
detail: {
|
||
configure: "Configure",
|
||
empty: "Select an agent to inspect its configuration.",
|
||
graph: "Graph",
|
||
model: "Model",
|
||
temperature: "Temperature",
|
||
tokens: "Max tokens",
|
||
format: "Output format",
|
||
fallback: "Fallback",
|
||
revisions: "Max revisions",
|
||
promptKey: "Prompt key",
|
||
toolsTitle: "Enabled tools",
|
||
noTools: "This agent has no tools enabled.",
|
||
systemPrompt: "System prompt",
|
||
noPrompt: "(empty)",
|
||
},
|
||
config: {
|
||
title: "Configure",
|
||
saved: "Agent configuration saved",
|
||
name: "Display name",
|
||
promptKey: "Prompt key (versioned)",
|
||
description: "Description",
|
||
systemPrompt: "System prompt",
|
||
systemPromptHint:
|
||
"If a prompt key is set above, the active version of that prompt overrides this field at runtime.",
|
||
model: "Model",
|
||
fallbackModel: "Fallback model",
|
||
responseFormat: "Output format",
|
||
text: "Text",
|
||
temperature: "Temperature",
|
||
maxTokens: "Max tokens",
|
||
maxRevisions: "Max revisions",
|
||
graphType: "Graph topology",
|
||
qualityChecks: "Quality checks (comma-separated tool keys)",
|
||
tools: "Enabled tools",
|
||
mutates: "writes",
|
||
active: "Agent is active",
|
||
},
|
||
test: {
|
||
title: "Test runner",
|
||
subtitle:
|
||
"Send a small payload and inspect the agent's output, tool calls, and quality issues.",
|
||
variables: "Variables (JSON)",
|
||
payload: "Payload (text or JSON)",
|
||
payloadPlaceholder:
|
||
"What should the agent do? e.g. 'Generate 5 MCQ for B1 reading.'",
|
||
run: "Run agent",
|
||
running: "Running…",
|
||
ok: "Agent ran successfully",
|
||
output: "Output",
|
||
toolTrace: "Tool trace",
|
||
iterations: "Iterations",
|
||
revisions: "Revisions",
|
||
toolCalls: "Tool calls",
|
||
retrievalHits: "Retrieval hits",
|
||
qualityIssues: "Quality issues",
|
||
badVarsJson: "Variables must be valid JSON.",
|
||
},
|
||
graph: {
|
||
simple: "Simple",
|
||
planReviewRevise: "Plan • Review • Revise",
|
||
rag: "RAG",
|
||
react: "ReAct (tool-calling)",
|
||
},
|
||
},
|
||
tools: {
|
||
title: "Agent tools",
|
||
subtitle:
|
||
"Capabilities your agents can call. Toggle a tool off to take it out of every agent without editing each one.",
|
||
search: "Search tools…",
|
||
empty: "No tools match your search.",
|
||
writes: "writes",
|
||
toggle: {
|
||
enabled: "Tool enabled",
|
||
disabled: "Tool disabled",
|
||
},
|
||
col: {
|
||
key: "Key",
|
||
name: "Name",
|
||
category: "Category",
|
||
description: "Description",
|
||
params: "Params",
|
||
active: "Active",
|
||
},
|
||
},
|
||
};
|
||
|
||
export default en;
|