Files
encoach_frontend_v4/src/i18n/locales/en.ts
Yamen Ahmad c2a07a6e5c
Some checks failed
Deploy Frontend to Staging / Deploy frontend to staging (push) Has been cancelled
feat(course-plan): RAG sources + multi-modal media + assignments + student view
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
2026-04-25 17:13:01 +04:00

1091 lines
40 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 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 3060 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;