/** * Curriculum ordering utilities: reading the order files that define which * characters, vocabulary, and radicals are taught, and in what sequence. * * Order files live under data/lang/{targetLang}/order/. */ import { parse } from '@std/csv/parse' import { DATA_ROOT } from './fs.ts' import { readDictByHant, type Definition } from './dict.ts' /** * Reads a vocabulary or radical order CSV (e.g. lang/zh_CN/order/vocabulary.csv). * Returns an array of levels, each level being an array of slugs (traditional forms). */ export function readLessonOrder(input: string): string[][] { return parse(Deno.readTextFileSync(DATA_ROOT + input)) as string[][] } /** * Reads a character order file (e.g. lang/zh_CN/order/characters.txt). * Each line is one level; each character in the line is one slug. */ export function readCharacterOrder(targetLang: string): string[][] { const text = Deno.readTextFileSync(DATA_ROOT + `lang/${targetLang}/order/characters.txt`) return text.split('\n').map((row) => row.split('')) } /** * Returns the ordered set of definitions for items in the curriculum, deduplicated * and in curriculum order. Only includes items that are actually taught — not the * full dictionary. * * @param type - 'character' or 'vocabulary' * @param targetLang - target language code (default: 'zh_CN') */ export function readOrderedDefs( type: 'character' | 'vocabulary', targetLang = 'zh_CN', ): Definition[] { const [dictPath, slugsList] = type === 'character' ? ['lang/characters.tsv', readCharacterOrder(targetLang).flat()] : ['lang/vocabulary.tsv', readLessonOrder(`lang/${targetLang}/order/vocabulary.csv`).flat()] const byHant = readDictByHant(dictPath) const seen = new Set() const result: Definition[] = [] for (const slug of slugsList) { if (!slug || seen.has(slug)) continue seen.add(slug) const def = byHant[slug] if (def) result.push(def) } return result }