The garden learns which language a card is in, and read-aloud stops guessing

Phase 28 (c), the last of the phase. A word met inside a Portuguese
document is a Portuguese card: migration 0018 mirrors documents.doc_lang
onto vocab_words, set server-side from the ownership lookup capture was
already making. Every card still reviews — filtering the queue to the
half she is learning would drop the words she actually met.

Read-aloud was the larger surprise. detectLang routed Han/kana to
Chinese and everything else to en-US, so the zh pair was accidentally
right and every Latin pair wrong. doc_lang now reaches the client
read-only on the document JSON, and docLang(text, verdict) answers for a
passage taken out of it — with the script test still winning, because
quoted Chinese must never be spelled out one "Chinese letter" at a time.

Claude-Session: https://claude.ai/code/session_01GJHNvirh7Hzhc9RL3HAvz7
This commit is contained in:
prosolis
2026-07-28 23:45:26 -07:00
parent 29eb2fe1fc
commit 466055020f
15 changed files with 352 additions and 57 deletions
+31 -1
View File
@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { nativeLang, speak, stopSpeech } from './speech'
import { docLang, nativeLang, speak, stopSpeech } from './speech'
import { resetPackForTests, setPackLang } from '../i18n'
// Read-aloud has two jobs beyond "make a sound": ask for the right pace, and ask
@@ -85,3 +85,33 @@ describe('nativeLang', () => {
expect(bodies.at(-1)).toMatchObject({ text: 'chat', lang: 'fr-FR' })
})
})
describe('docLang', () => {
// The document's verdict is the answer for a passage lifted out of it, because
// for a Latin pair there is no other answer available: an English sentence and
// a Portuguese one are the same letters.
it('reads a flipped document in her own language', () => {
setPackLang('pt-PT')
expect(docLang('Ontem foi difícil.', 'pair')).toBe('pt-PT')
})
it('reads an English document in English, and treats the backfill as English', () => {
setPackLang('pt-PT')
expect(docLang('Yesterday was hard.', 'en')).toBe('en-US')
expect(docLang('Yesterday was hard.', '')).toBe('en-US')
})
it('lets the script win over the verdict, so quoted Chinese is never spelled out', () => {
// An English document quoting Chinese is 'en' by verdict, and the English
// voice reads Han characters one "Chinese letter" at a time — the one
// failure worse than silence.
setPackLang('zh')
expect(docLang('你好世界', 'en')).toBe('zh-CN')
})
it('is what the editor selection and the garden card ask for', () => {
setPackLang('fr')
speak('Le chat dort.', docLang('Le chat dort.', 'pair'))
expect(bodies.at(-1)).toMatchObject({ text: 'Le chat dort.', lang: 'fr-FR' })
})
})