Files
readest/apps/readest-app/.claude/memory/dict-lemmatization-4574.md
T
Huang Xin c2ac207945 refactor(wordlens): rename "Word Wise" to "Word Lens" (#4633)
"Word Wise" is a Kindle trademark, so rename the inline-gloss feature to
"Word Lens" throughout the product.

- User-facing strings → "Word Lens" across all 34 locales; brand translated
  for Chinese (zh-CN 单词透镜, zh-TW 單詞透鏡) and German (Word-Lens-Daten).
- Code identifiers: WordWise→WordLens, wordWise→wordLens, WORD_WISE→WORD_LENS.
- Files/dirs: src/services/wordwise→wordlens, WordWisePanel→WordLensPanel,
  wordwise{Ruby,Section}.ts, build/sync scripts, test dirs/fixtures,
  data/wordwise→data/wordlens.
- Storage paths: CDN base, R2 key, on-device cache dir, WORDLENS_R2_BUCKET env,
  pnpm wordlens:{manifest,sync}. manifest.json is path-agnostic so its
  sha256/bytes stay valid (verified).
- biome.json: point the formatter-ignore at data/wordlens so the generated
  one-line gloss packs aren't pretty-printed on commit.

Migration notes:
- Re-run `pnpm wordlens:sync` to upload packs to cdn.readest.com/wordlens/.
- Persisted view-settings keys renamed (wordWiseEnabled/Level/HintLang and
  wordWiseAutoDownload) — saved values reset to defaults once on upgrade.
- Cached packs under the old Data/wordwise/ orphan (harmless re-download).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 19:09:29 +02:00

3.2 KiB
Raw Blame History

name, description, metadata
name description metadata
dict-lemmatization-4574 Dictionary lookup lemmatizes inflected words (ran→run, mice→mouse) before lookup; pluggable per-language registry, English impl, candidate-chain integration
node_type type originSessionId
memory project d4206b72-47da-4c3d-adab-04df32c1137a

#4574 FR: dictionary lookup should normalize inflected forms before lookup. Dicts that store only base headwords (Oxford Dictionary of English, Cambridge, Longman) miss ran/mice/children/analyses/realised even though run/mouse/child/analysis/realise exist.

Integration point (single, central): src/services/dictionaries/lookupCandidates.ts buildLookupCandidates(word, lang?) — appends getLemmaCandidates(lower, lang) to the tail of [trimmed, lower, title, upper]. Lemmas sit AFTER exact/case so exact match always wins. The pre-existing lookup loop in DictionaryResultsView.tsx (useDictionaryResults, shared by desktop popup + mobile sheet) tries each candidate and breaks on first non-empty hit; wiring was a one-liner passing langCode (already in effect scope) as the 2nd arg. Applies to ALL definition providers (mdict/stardict/dict/slob + online builtins).

New module src/services/dictionaries/lemmatize/:

  • index.tsgetLemmaCandidates(word, lang) + Record<string, Lemmatizer> registry (Lemmatizer = (word)=>string[]). Lang normalized via normalizedLangCode (utils/lang.ts) to primary subtag. Missing/empty lang defaults to 'en' (normalizedLangCode(lang) || 'en'); explicit non-English with no registered lemmatizer → [] (we never force English onto e.g. fr/zh). Add a language = register one fn, no caller changes.
  • english.tslemmatizeEnglish(word): IRREGULAR_GROUPS (base→[forms], flattened to inflected→base at load) for suppletive verbs / irregular plurals / irregular comparatives, + regular suffix rules (plural -s/-es/-ies→y/-ves→f,fe/-ses→sis; past -ed/-d/-ied→y + de-double; -ing + e-restore + de-double + -ying→ie; comparative -er/-est/-ier→y; possessive 's; adverb -ly). ASCII-single-token guard /^[a-z][a-z'-]*$/ (no-op on phrases/numbers/CJK/accented). Lowercases input; never returns the input itself or single letters.

Key design insight: over-generate, let the dictionary validate. The lemmatizer need not be linguistically precise — a bogus stem just misses and the loop moves on. So rules can be liberal. Cost is bounded: lemmas only fire AFTER exact+case all return empty (genuine "not a headword"), and the English rules produce ~25 candidates.

Ordering gotcha: -ses→-sis rule must come BEFORE generic -es/-s so analysesanalysis (the issue's expected noun) is tried ahead of analyse (the verb). Both are linguistically valid for analyses; issue wants the noun.

Tests: __tests__/services/dictionaries/lemmatize/{english,index}.test.ts + extended lookupCandidates.test.ts (all 8 issue cases asserted). Existing trim test's spaced sample swapped to planet (non-inflecting) since no-lang path now defaults to English lemmatization. Pure functions, fully deterministic — no live MDX needed. Related: dict-lookup-browser-hijack-4559, wordlens-feature.