Call SCORM INTERACTIVE in the interface

Hanabank's users find this name easier to understand. Display text changes in
English and Indonesian, and "1.2" goes with it. The package format, internal
ids and schedule names stay as they are.

The chat's example now says INTERACTIVE, so the parser reads that word as well.
A bare "interactive" only counts when no other kind is named, so "kuis
interaktif" stays a quiz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Iqbal
2026-11-04 10:32:53 +07:00
parent 6d78476b2b
commit bdc65529bd
4 changed files with 45 additions and 21 deletions

View File

@@ -47,6 +47,23 @@ describe('parseInstruction', () => {
expect(content('1 modul scorm dengan kuis per minggu')[0].kind).toBe('scorm') expect(content('1 modul scorm dengan kuis per minggu')[0].kind).toBe('scorm')
}) })
it('reads INTERACTIVE, the name the interface shows, as SCORM', () => {
const rules = content('1 video per week, 2 podcasts per week and 1 INTERACTIVE module per week about onboarding')
expect(rules.map(r => [r.kind, r.count, r.per, r.topic])).toEqual([
['video', 1, 'week', 'onboarding'],
['audio', 2, 'week', 'onboarding'],
['scorm', 1, 'week', 'onboarding'],
])
expect(content('2 interactive per week')[0]).toMatchObject({ kind: 'scorm', count: 2 })
expect(content('1 modul interaktif dengan kuis per minggu')[0].kind).toBe('scorm')
})
it('keeps the named kind when interactive only describes it', () => {
expect(content('2 kuis interaktif per minggu')[0].kind).toBe('quiz')
expect(content('1 interactive video per week')[0].kind).toBe('video')
expect(content('2 summaries per week about interactive learning')[0].kind).toBe('summary')
})
it('reports clauses it did not understand', () => { it('reports clauses it did not understand', () => {
const { rules, unknown } = parseInstruction('2 summaries per day. hello there') const { rules, unknown } = parseInstruction('2 summaries per day. hello there')
expect(rules).toHaveLength(1) expect(rules).toHaveLength(1)

View File

@@ -19,7 +19,7 @@ export const kindLabel = (kind: ContentKind) => t(`kind.${kind}` as Key)
// Checked in order; the first match wins, so multi-word phrases come before their parts. // Checked in order; the first match wins, so multi-word phrases come before their parts.
const KIND_WORDS: [ContentKind, RegExp][] = [ const KIND_WORDS: [ContentKind, RegExp][] = [
// SCORM first: a SCORM module includes a quiz, so "SCORM module with a quiz" is still SCORM. // SCORM first: a SCORM module includes a quiz, so "SCORM module with a quiz" is still SCORM.
['scorm', /\b(scorm|skorm|skong|e-?learning)\b/], ['scorm', /\b(scorm|skorm|skong|e-?learning|interactive modules?|modul(e|es)? interaktif)\b/],
['flashcards', /\b(flash ?cards?|kartu (belajar|hafalan))\b/], ['flashcards', /\b(flash ?cards?|kartu (belajar|hafalan))\b/],
['quiz', /\b(kuis|quiz|quizzes|soal latihan|tes|tests?|ujian|exams?)\b/], ['quiz', /\b(kuis|quiz|quizzes|soal latihan|tes|tests?|ujian|exams?)\b/],
['roleplay', /\b(role ?-?plays?|bermain peran)\b/], ['roleplay', /\b(role ?-?plays?|bermain peran)\b/],
@@ -30,13 +30,18 @@ const KIND_WORDS: [ContentKind, RegExp][] = [
['summary', /\b(summary|summaries|ringkasan|rangkuman|artikel|articles?|teks|texts?)\b/], ['summary', /\b(summary|summaries|ringkasan|rangkuman|artikel|articles?|teks|texts?)\b/],
] ]
// The interface calls SCORM "INTERACTIVE", so the bare word means it too, but only when nothing else
// is named: "kuis interaktif" stays a quiz, and "summaries about interactive learning" stay summaries.
const INTERACTIVE = /\b(interactive|interaktif)\b/
/** Every content kind a text names, in the order it names them: "ringkasan dan kuis" → summary, quiz. */ /** Every content kind a text names, in the order it names them: "ringkasan dan kuis" → summary, quiz. */
export function kindsIn(text: string): ContentKind[] { export function kindsIn(text: string): ContentKind[] {
return KIND_WORDS const kinds = KIND_WORDS
.map(([kind, re]) => [kind, text.search(re)] as const) .map(([kind, re]) => [kind, text.search(re)] as const)
.filter(([, at]) => at >= 0) .filter(([, at]) => at >= 0)
.sort((a, b) => a[1] - b[1]) .sort((a, b) => a[1] - b[1])
.map(([kind]) => kind) .map(([kind]) => kind)
return kinds.length || !INTERACTIVE.test(text) ? kinds : ['scorm']
} }
// "10 konten", "5 materi pembelajaran", "10 pdf": content of no particular kind, so the agent asks which. // "10 konten", "5 materi pembelajaran", "10 pdf": content of no particular kind, so the agent asks which.
@@ -208,6 +213,8 @@ function parseClause(clause: string): Rule | null {
const m = re.exec(clause) const m = re.exec(clause)
if (m) return contentRule(clause, kind, countOf(clause, m.index)) if (m) return contentRule(clause, kind, countOf(clause, m.index))
} }
const interactive = INTERACTIVE.exec(clause)
if (interactive) return contentRule(clause, 'scorm', countOf(clause, interactive.index))
const g = GENERIC.exec(clause) const g = GENERIC.exec(clause)
if (g) { if (g) {
const count = countBefore(clause, g.index) const count = countBefore(clause, g.index)

View File

@@ -151,7 +151,7 @@ const en = {
'ins.ex2': 'Research the internet every day about leadership', 'ins.ex2': 'Research the internet every day about leadership',
'ins.ex3': '3 roleplays per week about negotiating with customers', 'ins.ex3': '3 roleplays per week about negotiating with customers',
'ins.ex4': 'Research every Monday about sales trends, then make 5 flashcards per week', 'ins.ex4': 'Research every Monday about sales trends, then make 5 flashcards per week',
'ins.ex5': '1 video per week, 2 podcasts per week and 1 SCORM module per week about onboarding', 'ins.ex5': '1 video per week, 2 podcasts per week and 1 INTERACTIVE module per week about onboarding',
'ins.previewTitle': 'Schedules to create', 'ins.previewTitle': 'Schedules to create',
'ins.readyCount': '{n} ready', 'ins.readyCount': '{n} ready',
'ins.approveNote': 'Once approved, schedules start right away. Content is created from the Brain without repeating existing content, using workspace credits within the limits above.', 'ins.approveNote': 'Once approved, schedules start right away. Content is created from the Brain without repeating existing content, using workspace credits within the limits above.',
@@ -199,7 +199,7 @@ const en = {
'kind.slide_deck': 'Slides', 'kind.slide_deck': 'Slides',
'kind.video': 'Video', 'kind.video': 'Video',
'kind.audio': 'Audio', 'kind.audio': 'Audio',
'kind.scorm': 'SCORM', 'kind.scorm': 'INTERACTIVE',
'day.0': 'Monday', 'day.1': 'Tuesday', 'day.2': 'Wednesday', 'day.3': 'Thursday', 'day.4': 'Friday', 'day.5': 'Saturday', 'day.6': 'Sunday', 'day.0': 'Monday', 'day.1': 'Tuesday', 'day.2': 'Wednesday', 'day.3': 'Thursday', 'day.4': 'Friday', 'day.5': 'Saturday', 'day.6': 'Sunday',
// Schedule // Schedule
@@ -296,7 +296,7 @@ const en = {
// Results // Results
'res.title': 'Results', 'res.title': 'Results',
'res.description': 'Everything Express creates: text, quizzes, roleplays, video, audio, and SCORM.', 'res.description': 'Everything Express creates: text, quizzes, roleplays, video, audio, and INTERACTIVE.',
'res.empty': 'No results yet', 'res.empty': 'No results yet',
'res.emptyCopy': 'Content and sources from your schedules appear here. Give an instruction to start a schedule.', 'res.emptyCopy': 'Content and sources from your schedules appear here. Give an instruction to start a schedule.',
'res.tabContent': 'Content', 'res.tabContent': 'Content',
@@ -326,7 +326,7 @@ const en = {
'res.loadFailed': 'Results could not be loaded', 'res.loadFailed': 'Results could not be loaded',
'res.noMatch': 'Nothing matches', 'res.noMatch': 'Nothing matches',
'res.noMatchCopy': 'Try another keyword, type, or status.', 'res.noMatchCopy': 'Try another keyword, type, or status.',
'res.noteContent': 'Download as PDF, Markdown or SCORM; slide decks also as PowerPoint, videos as MP4, audio scripts as text. Roleplay and simulation run interactively in Learning Studio.', 'res.noteContent': 'Download as PDF, Markdown or INTERACTIVE; slide decks also as PowerPoint, videos as MP4, audio scripts as text. Roleplay and simulation run interactively in Learning Studio.',
'res.noteResearch': 'Research finds web sources. Add the useful ones to the Library, then build their knowledge on the Brain page.', 'res.noteResearch': 'Research finds web sources. Add the useful ones to the Library, then build their knowledge on the Brain page.',
'res.sourceCount': '{n} sources', 'res.sourceCount': '{n} sources',
'res.sourceOne': '1 source', 'res.sourceOne': '1 source',
@@ -365,7 +365,7 @@ const en = {
// Media and SCORM (Phases 4, 5, 7) // Media and SCORM (Phases 4, 5, 7)
'ins.noteVideo': 'One video per run, about {seconds} seconds in {scenes} scenes, made by Video Studio. Each video waits for your review in Results.', 'ins.noteVideo': 'One video per run, about {seconds} seconds in {scenes} scenes, made by Video Studio. Each video waits for your review in Results.',
'ins.noteAudio': 'Written as a narration script. Results plays it with the device voice and downloads it as text (no MP3 yet: the engine has no audio service for agents).', 'ins.noteAudio': 'Written as a narration script. Results plays it with the device voice and downloads it as text (no MP3 yet: the engine has no audio service for agents).',
'ins.noteScorm': 'Each module is a lesson with a 10-question quiz, downloaded from Results as a SCORM 1.2 package for your LMS.', 'ins.noteScorm': 'Each module is a lesson with a 10-question quiz, downloaded from Results as an INTERACTIVE package for your LMS.',
'ins.videoCadence': 'A video schedule makes one video per run: choose 1 per day or 1 per week.', 'ins.videoCadence': 'A video schedule makes one video per run: choose 1 per day or 1 per week.',
'ins.videoQuoteFailed': 'The video price could not be checked: {message}', 'ins.videoQuoteFailed': 'The video price could not be checked: {message}',
'ins.budgetVideo': '{n} per video', 'ins.budgetVideo': '{n} per video',
@@ -379,7 +379,7 @@ const en = {
'agent.greeting': 'Hi! Tell me what to create from the Brain and how often, or what to research on the internet. If anything is unclear, I will ask you here.', 'agent.greeting': 'Hi! Tell me what to create from the Brain and how often, or what to research on the internet. If anything is unclear, I will ask you here.',
'agent.understood': 'Got it. Here is the plan so far:\n{list}', 'agent.understood': 'Got it. Here is the plan so far:\n{list}',
'agent.added': 'Added to the plan:\n{list}', 'agent.added': 'Added to the plan:\n{list}',
'agent.notUnderstood': 'I could not find a content type or research request in that. I can make summaries, quizzes, flashcards, roleplays, simulations, slides, videos, audio and SCORM modules from the Brain, or research the internet. Try something like “2 summaries per day about leadership”.', 'agent.notUnderstood': 'I could not find a content type or research request in that. I can make summaries, quizzes, flashcards, roleplays, simulations, slides, videos, audio and INTERACTIVE modules from the Brain, or research the internet. Try something like “2 summaries per day about leadership”.',
'agent.askKind': '{count} pieces of content{about}: which kind? Pick one, or name several (for example “summaries and quizzes”) to split them.', 'agent.askKind': '{count} pieces of content{about}: which kind? Pick one, or name several (for example “summaries and quizzes”) to split them.',
'agent.askPer': 'How often should this be made? You did not say, and I would rather not guess.\n{list}', 'agent.askPer': 'How often should this be made? You did not say, and I would rather not guess.\n{list}',
'agent.optMix': 'Mix (summaries, quizzes, flashcards)', 'agent.optMix': 'Mix (summaries, quizzes, flashcards)',
@@ -459,11 +459,11 @@ const en = {
'res.scriptHint': 'Narration text, for recording or a TTS tool', 'res.scriptHint': 'Narration text, for recording or a TTS tool',
'res.quiz': 'Quiz', 'res.quiz': 'Quiz',
'res.quizPending': 'The quiz is not ready yet.', 'res.quizPending': 'The quiz is not ready yet.',
'res.scormModule': 'SCORM 1.2 module', 'res.scormModule': 'INTERACTIVE module',
'res.scormMeta': 'Lesson + {n} questions · pass mark {mastery}%', 'res.scormMeta': 'Lesson + {n} questions · pass mark {mastery}%',
'res.scormHint': 'For an LMS: the lesson, marked complete when finished', 'res.scormHint': 'For an LMS: the lesson, marked complete when finished',
'res.scormQuizHint': 'For an LMS: lesson and quiz, reports score and pass/fail', 'res.scormQuizHint': 'For an LMS: lesson and quiz, reports score and pass/fail',
'res.scormReady': 'SCORM package downloaded. Upload the .zip to your LMS.', 'res.scormReady': 'INTERACTIVE package downloaded. Upload the .zip to your LMS.',
'scorm.submit': 'Check answers', 'scorm.submit': 'Check answers',
'scorm.retry': 'Try again', 'scorm.retry': 'Try again',
'scorm.complete': 'Mark as complete', 'scorm.complete': 'Mark as complete',
@@ -517,7 +517,7 @@ const en = {
'help.scheduleTitle': 'Schedule', 'help.scheduleTitle': 'Schedule',
'help.scheduleBody': 'Schedules run on their own. Pause them, change the topic, or produce now; each stays within its credit limit.', 'help.scheduleBody': 'Schedules run on their own. Pause them, change the topic, or produce now; each stays within its credit limit.',
'help.resultsTitle': 'Results', 'help.resultsTitle': 'Results',
'help.resultsBody': 'Review what was made, approve or reject it, and download it: PDF, Markdown, PowerPoint, MP4, audio script, or SCORM for your LMS.', 'help.resultsBody': 'Review what was made, approve or reject it, and download it: PDF, Markdown, PowerPoint, MP4, audio script, or INTERACTIVE for your LMS.',
'help.note': 'Content is Agent-generated: always review it before using it. Credits come from your organization’s workspace.', 'help.note': 'Content is Agent-generated: always review it before using it. Credits come from your organization’s workspace.',
'help.done': 'Got it', 'help.done': 'Got it',
@@ -676,7 +676,7 @@ const id: Record<Key, string> = {
'ins.ex2': 'Riset internet tiap hari soal leadership', 'ins.ex2': 'Riset internet tiap hari soal leadership',
'ins.ex3': '3 roleplay per minggu tentang negosiasi dengan pelanggan', 'ins.ex3': '3 roleplay per minggu tentang negosiasi dengan pelanggan',
'ins.ex4': 'Riset tiap Senin soal tren sales, lalu buat 5 flashcard per minggu', 'ins.ex4': 'Riset tiap Senin soal tren sales, lalu buat 5 flashcard per minggu',
'ins.ex5': '1 video per minggu, 2 podcast per minggu dan 1 modul SCORM per minggu soal onboarding', 'ins.ex5': '1 video per minggu, 2 podcast per minggu dan 1 modul INTERACTIVE per minggu soal onboarding',
'ins.previewTitle': 'Jadwal yang akan dibuat', 'ins.previewTitle': 'Jadwal yang akan dibuat',
'ins.readyCount': '{n} siap', 'ins.readyCount': '{n} siap',
'ins.approveNote': 'Setelah disetujui, jadwal langsung aktif. Konten dibuat dari materi di Otak tanpa mengulang yang sudah ada, dan memakai kredit ruang kerja sesuai batas di atas.', 'ins.approveNote': 'Setelah disetujui, jadwal langsung aktif. Konten dibuat dari materi di Otak tanpa mengulang yang sudah ada, dan memakai kredit ruang kerja sesuai batas di atas.',
@@ -723,7 +723,7 @@ const id: Record<Key, string> = {
'kind.slide_deck': 'Slide', 'kind.slide_deck': 'Slide',
'kind.video': 'Video', 'kind.video': 'Video',
'kind.audio': 'Audio', 'kind.audio': 'Audio',
'kind.scorm': 'SCORM', 'kind.scorm': 'INTERACTIVE',
'day.0': 'Senin', 'day.1': 'Selasa', 'day.2': 'Rabu', 'day.3': 'Kamis', 'day.4': 'Jumat', 'day.5': 'Sabtu', 'day.6': 'Minggu', 'day.0': 'Senin', 'day.1': 'Selasa', 'day.2': 'Rabu', 'day.3': 'Kamis', 'day.4': 'Jumat', 'day.5': 'Sabtu', 'day.6': 'Minggu',
'sch.title': 'Jadwal', 'sch.title': 'Jadwal',
@@ -818,7 +818,7 @@ const id: Record<Key, string> = {
'issue.snapshot_not_ready': 'peta pengetahuan Compose belum disiapkan', 'issue.snapshot_not_ready': 'peta pengetahuan Compose belum disiapkan',
'res.title': 'Hasil', 'res.title': 'Hasil',
'res.description': 'Semua konten yang dihasilkan Express: teks, kuis, roleplay, video, audio, dan SCORM.', 'res.description': 'Semua konten yang dihasilkan Express: teks, kuis, roleplay, video, audio, dan INTERACTIVE.',
'res.empty': 'Belum ada hasil', 'res.empty': 'Belum ada hasil',
'res.emptyCopy': 'Konten dan sumber dari jadwal Anda tampil di sini. Beri perintah untuk memulai jadwal.', 'res.emptyCopy': 'Konten dan sumber dari jadwal Anda tampil di sini. Beri perintah untuk memulai jadwal.',
'res.tabContent': 'Konten', 'res.tabContent': 'Konten',
@@ -848,7 +848,7 @@ const id: Record<Key, string> = {
'res.loadFailed': 'Hasil tidak dapat dimuat', 'res.loadFailed': 'Hasil tidak dapat dimuat',
'res.noMatch': 'Tidak ada yang cocok', 'res.noMatch': 'Tidak ada yang cocok',
'res.noMatchCopy': 'Coba kata kunci, jenis, atau status lain.', 'res.noMatchCopy': 'Coba kata kunci, jenis, atau status lain.',
'res.noteContent': 'Unduh sebagai PDF, Markdown, atau SCORM; slide deck juga sebagai PowerPoint, video sebagai MP4, naskah audio sebagai teks. Roleplay dan simulasi dijalankan secara interaktif di Learning Studio.', 'res.noteContent': 'Unduh sebagai PDF, Markdown, atau INTERACTIVE; slide deck juga sebagai PowerPoint, video sebagai MP4, naskah audio sebagai teks. Roleplay dan simulasi dijalankan secara interaktif di Learning Studio.',
'res.noteResearch': 'Riset menemukan sumber dari web. Masukkan yang berguna ke Pustaka, lalu bangun pengetahuannya di halaman Upload materi.', 'res.noteResearch': 'Riset menemukan sumber dari web. Masukkan yang berguna ke Pustaka, lalu bangun pengetahuannya di halaman Upload materi.',
'res.sourceCount': '{n} sumber', 'res.sourceCount': '{n} sumber',
'res.sourceOne': '1 sumber', 'res.sourceOne': '1 sumber',
@@ -887,7 +887,7 @@ const id: Record<Key, string> = {
// Media dan SCORM (Fase 4, 5, 7) // Media dan SCORM (Fase 4, 5, 7)
'ins.noteVideo': 'Satu video per proses, sekitar {seconds} detik dalam {scenes} adegan, dibuat oleh Video Studio. Setiap video menunggu tinjauan Anda di Hasil.', 'ins.noteVideo': 'Satu video per proses, sekitar {seconds} detik dalam {scenes} adegan, dibuat oleh Video Studio. Setiap video menunggu tinjauan Anda di Hasil.',
'ins.noteAudio': 'Dibuat sebagai naskah narasi. Hasil memutarnya dengan suara perangkat dan mengunduhnya sebagai teks (belum MP3: engine belum punya layanan audio untuk agen).', 'ins.noteAudio': 'Dibuat sebagai naskah narasi. Hasil memutarnya dengan suara perangkat dan mengunduhnya sebagai teks (belum MP3: engine belum punya layanan audio untuk agen).',
'ins.noteScorm': 'Setiap modul berisi materi dan kuis 10 soal, diunduh dari Hasil sebagai paket SCORM 1.2 untuk LMS Anda.', 'ins.noteScorm': 'Setiap modul berisi materi dan kuis 10 soal, diunduh dari Hasil sebagai paket INTERACTIVE untuk LMS Anda.',
'ins.videoCadence': 'Jadwal video membuat satu video per proses: pilih 1 per hari atau 1 per minggu.', 'ins.videoCadence': 'Jadwal video membuat satu video per proses: pilih 1 per hari atau 1 per minggu.',
'ins.videoQuoteFailed': 'Harga video tidak bisa dicek: {message}', 'ins.videoQuoteFailed': 'Harga video tidak bisa dicek: {message}',
'ins.budgetVideo': '{n} per video', 'ins.budgetVideo': '{n} per video',
@@ -901,7 +901,7 @@ const id: Record<Key, string> = {
'agent.greeting': 'Halo! Ceritakan konten apa yang perlu dibuat dari Otak dan seberapa sering, atau apa yang perlu diriset di internet. Kalau ada yang belum jelas, saya tanyakan di sini.', 'agent.greeting': 'Halo! Ceritakan konten apa yang perlu dibuat dari Otak dan seberapa sering, atau apa yang perlu diriset di internet. Kalau ada yang belum jelas, saya tanyakan di sini.',
'agent.understood': 'Siap. Ini rencananya sejauh ini:\n{list}', 'agent.understood': 'Siap. Ini rencananya sejauh ini:\n{list}',
'agent.added': 'Ditambahkan ke rencana:\n{list}', 'agent.added': 'Ditambahkan ke rencana:\n{list}',
'agent.notUnderstood': 'Saya belum menemukan jenis konten atau permintaan riset di situ. Saya bisa membuat ringkasan, kuis, flashcard, roleplay, simulasi, slide, video, audio, dan modul SCORM dari Otak, atau riset internet. Coba misalnya “2 ringkasan per hari soal leadership”.', 'agent.notUnderstood': 'Saya belum menemukan jenis konten atau permintaan riset di situ. Saya bisa membuat ringkasan, kuis, flashcard, roleplay, simulasi, slide, video, audio, dan modul INTERACTIVE dari Otak, atau riset internet. Coba misalnya “2 ringkasan per hari soal leadership”.',
'agent.askKind': '{count} konten{about}: jenisnya apa? Pilih satu, atau sebut beberapa (misalnya “ringkasan dan kuis”) untuk membaginya.', 'agent.askKind': '{count} konten{about}: jenisnya apa? Pilih satu, atau sebut beberapa (misalnya “ringkasan dan kuis”) untuk membaginya.',
'agent.askPer': 'Seberapa sering ini dibuat? Anda belum menyebutkannya, dan saya tidak mau menebak.\n{list}', 'agent.askPer': 'Seberapa sering ini dibuat? Anda belum menyebutkannya, dan saya tidak mau menebak.\n{list}',
'agent.optMix': 'Campuran (ringkasan, kuis, flashcard)', 'agent.optMix': 'Campuran (ringkasan, kuis, flashcard)',
@@ -981,11 +981,11 @@ const id: Record<Key, string> = {
'res.scriptHint': 'Teks narasi, untuk direkam atau alat TTS', 'res.scriptHint': 'Teks narasi, untuk direkam atau alat TTS',
'res.quiz': 'Kuis', 'res.quiz': 'Kuis',
'res.quizPending': 'Kuisnya belum siap.', 'res.quizPending': 'Kuisnya belum siap.',
'res.scormModule': 'Modul SCORM 1.2', 'res.scormModule': 'Modul INTERACTIVE',
'res.scormMeta': 'Materi + {n} soal · nilai lulus {mastery}%', 'res.scormMeta': 'Materi + {n} soal · nilai lulus {mastery}%',
'res.scormHint': 'Untuk LMS: materi, ditandai selesai setelah dibaca', 'res.scormHint': 'Untuk LMS: materi, ditandai selesai setelah dibaca',
'res.scormQuizHint': 'Untuk LMS: materi dan kuis, melaporkan nilai dan lulus/tidak', 'res.scormQuizHint': 'Untuk LMS: materi dan kuis, melaporkan nilai dan lulus/tidak',
'res.scormReady': 'Paket SCORM terunduh. Unggah file .zip ke LMS Anda.', 'res.scormReady': 'Paket INTERACTIVE terunduh. Unggah file .zip ke LMS Anda.',
'scorm.submit': 'Periksa jawaban', 'scorm.submit': 'Periksa jawaban',
'scorm.retry': 'Coba lagi', 'scorm.retry': 'Coba lagi',
'scorm.complete': 'Tandai selesai', 'scorm.complete': 'Tandai selesai',
@@ -1039,7 +1039,7 @@ const id: Record<Key, string> = {
'help.scheduleTitle': 'Jadwal', 'help.scheduleTitle': 'Jadwal',
'help.scheduleBody': 'Jadwal berjalan sendiri. Jeda, ganti topik, atau produksi sekarang; masing-masing tetap dalam batas kreditnya.', 'help.scheduleBody': 'Jadwal berjalan sendiri. Jeda, ganti topik, atau produksi sekarang; masing-masing tetap dalam batas kreditnya.',
'help.resultsTitle': 'Hasil', 'help.resultsTitle': 'Hasil',
'help.resultsBody': 'Tinjau hasilnya, setujui atau tolak, lalu unduh: PDF, Markdown, PowerPoint, MP4, naskah audio, atau SCORM untuk LMS.', 'help.resultsBody': 'Tinjau hasilnya, setujui atau tolak, lalu unduh: PDF, Markdown, PowerPoint, MP4, naskah audio, atau INTERACTIVE untuk LMS.',
'help.note': 'Konten dibuat Agent: selalu tinjau sebelum dipakai. Kredit diambil dari ruang kerja organisasi Anda.', 'help.note': 'Konten dibuat Agent: selalu tinjau sebelum dipakai. Kredit diambil dari ruang kerja organisasi Anda.',
'help.done': 'Mengerti', 'help.done': 'Mengerti',

View File

@@ -295,7 +295,7 @@ function DownloadMenu({ material: m, content, outputId }: { material: Material;
const scormItem = ( const scormItem = (
<button role="menuitem" onClick={() => { close(); scorm(m, content) }}> <button role="menuitem" onClick={() => { close(); scorm(m, content) }}>
<b>SCORM 1.2 (.zip)</b><small>{t(m.kind === 'scorm' || m.kind === 'quiz' ? 'res.scormQuizHint' : 'res.scormHint')}</small> <b>INTERACTIVE (.zip)</b><small>{t(m.kind === 'scorm' || m.kind === 'quiz' ? 'res.scormQuizHint' : 'res.scormHint')}</small>
</button> </button>
) )