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:
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { api, type VocabGrade, type VocabWord } from '../../api/client'
|
||||
import { speak, speechSupported, stopSpeech } from '../../audio/speech'
|
||||
import { docLang as docLocale, speak, speechSupported, stopSpeech } from '../../audio/speech'
|
||||
import { useFocusTrap } from '../../hooks/useFocusTrap'
|
||||
import { usePack, type Line } from '../../i18n'
|
||||
import { JournalView } from './JournalView'
|
||||
@@ -314,7 +314,21 @@ function GardenView({
|
||||
{blossom(w.reps)}
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-1 flex-col">
|
||||
<span className="truncate text-sm font-bold text-plum">{w.word}</span>
|
||||
<span className="flex min-w-0 items-center gap-1.5">
|
||||
<span className="truncate text-sm font-bold text-plum">{w.word}</span>
|
||||
{/* Only a card in her own language is marked. An English
|
||||
garden with a badge on every blossom would be a
|
||||
garden with no badges at all; the marker exists so
|
||||
the rare Portuguese word is legible among them. */}
|
||||
{w.lang === 'pair' && (
|
||||
<span
|
||||
className="shrink-0 rounded-full px-1.5 py-0.5 text-[9px] font-bold lowercase"
|
||||
style={{ background: 'var(--color-surface-alt)', color: 'var(--color-muted)' }}
|
||||
>
|
||||
{t.nativeName}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{(w.gloss || w.definition) && (
|
||||
<span
|
||||
className="truncate text-xs"
|
||||
@@ -354,7 +368,7 @@ function GardenView({
|
||||
{speechSupported() && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => speak(w.word)}
|
||||
onClick={() => speak(w.word, docLocale(w.word, w.lang))}
|
||||
className="rounded-full px-2.5 py-1 text-xs font-semibold"
|
||||
style={{ background: 'var(--color-surface-alt)' }}
|
||||
>
|
||||
@@ -477,7 +491,7 @@ function ReviewSession({
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => speak(card.word)}
|
||||
onClick={() => speak(card.word, docLocale(card.word, card.lang))}
|
||||
aria-label={`Pronounce ${card.word}`}
|
||||
className="flex h-6 w-6 items-center justify-center rounded-full text-xs"
|
||||
style={{ background: 'var(--color-surface)' }}
|
||||
@@ -488,7 +502,7 @@ function ReviewSession({
|
||||
worth hearing stretched out. */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => speak(card.word, undefined, true)}
|
||||
onClick={() => speak(card.word, docLocale(card.word, card.lang), true)}
|
||||
aria-label={`Pronounce ${card.word} slowly`}
|
||||
title={t.garden.readSlowly}
|
||||
className="flex h-6 w-6 items-center justify-center rounded-full text-xs"
|
||||
|
||||
Reference in New Issue
Block a user