16: Languages and Thesaurus - transcript
Liam: The QUILL Cast, episode sixteen. I'm Liam. Today: languages and thesaurus, two related ideas that meet inside one editing problem. When do I tell QUILL what kind of document this is, and how do I find a better word for the one I just wrote.
Jessica: I'm Jessica. Last episode was spelling and the offline word tools, dictionary, thesaurus, control F7 navigation. Today's the other half of that story: document language as a setting you pin, document language as something QUILL can guess, and the offline plus AI tiers of the thesaurus side by side. If you remember nothing else from this episode, remember this: the thesaurus and the spell check are different features with different contracts, and QUILL has both.
Liam: The big idea, before the details. Languages in QUILL means two different things, and the docs have been a little slippery about which one they mean. The first is the Document Language, a per-tab setting that controls how QUILL edits this file, what menu items appear, what smart features fire. That's a Python file, that's Markdown, that's plain text, and the file gets a profile attached. The second is the spell-check language, which is the dictionary the spell engine uses, English, Spanish, French, and friends. Same English word, two completely separate settings. We'll untangle both today.
Jessica: And before we go any further, the listener homework beat. This one's quick, so pause the audio if you need a minute.
Liam: Do this now. Open QUILL, take any document you have open, and press control shift L. The dialog that appears is the Set Document Language dialog. Don't change anything, just listen. Tab through the list. You'll hear Python, JavaScript, TypeScript, HTML, CSS, YAML, Go, C, C plus plus, C sharp, PHP, Rust, Kotlin, Shell, Markdown, JSON, TOML, SQL, and Plain text, plus an Auto-detect from file option at the top. Cancel out. Now you've met the cast. Resume the audio.
Jessica: Welcome back. That list, by the way, is exactly what's defined in quill slash core slash language underscore profile dot py. Sixteen real profiles plus plain, each with file extensions, indentation conventions, comment syntax, bracket pairs, and a keyword set for token classification. The data is plain Python, no database, no lookup service, fully offline. The data is also why Ctrl+Shift+L exists: any time you want QUILL to behave a certain way for a given file, you pin the profile here, and the editor takes its cues from that.
Liam: The behavioral consequences of pinning are real. Pin HTML on a file and QUILL gives you markup-aware commands. Pin Markdown and the heading and table menus light up. Pin a programming language and you get token navigation: alt right and alt left jump between code tokens, classified by the keyword set in the profile. Pin Plain text and all of that turns off, which is the right default for a draft. The status bar reflects the current language so you're never guessing.
Jessica: But what if you don't want to pin at all. That's where auto-detect lives. The first entry in the dialog, Auto-detect from file, is the default for new tabs. With it selected, QUILL looks at the file extension and chooses the profile. The file is named notes dot md, you get Markdown. The file is named app dot py, you get Python. No path and no extension, you get Plain text. Most of the time this is the right behavior, and you should not have to think about it.
Liam: The clever part, and the part most users never see, is what happens when the file has no useful extension. Paste a chunk of Python into a fresh untitled tab, and QUILL can notice. The code lives in quill slash core slash language underscore detect dot py, a wx-free classifier with no machine-learning model, just weighted regex signals. Shebangs score Python and Shell heavily. The doctype tag is HTML. Package main is Go. The detector is conservative: it returns a confident guess only when one language beats the runner-up by a clear margin. Tied scores come back as no idea, which is the right default for ambiguous text.
Jessica: What you experience as a user: after a short debounce, around eight hundred milliseconds, QUILL looks at your buffer. If it sees a strong signal and you're in an unpinned, generic-text document, something ending in txt or with no extension at all, it acts according to your language detection mode. That mode is a setting, Tools, Writing, Spell Check Language has a sibling called language detection mode. The default is off. The levels, in order of increasing boldness, are hint, prompt, and auto.
Liam: Hint means a quiet status bar message, looks like Python, press control shift L to set the language. Screen readers won't speak it, but it's there if you look. Prompt means QUILL announces, in a status line, that the document looks like Python and tells you the keyboard command to pin it. Auto means QUILL pins it for you, after a brief spoken note, detected Python, eighty-two percent. The screen reader is always told when auto fires, because a silent language switch would be a real accessibility bug.
Jessica: And there's a guardrail. Hysteresis, in the engineer sense, means changing an already-detected language requires a higher bar than setting the first one. So if you're writing prose in a tab and you paste a tiny Python snippet, then delete it, the language doesn't flip back and forth. The first detection is easy; subsequent flips are earned. That's the discipline that keeps the feature from being annoying.
Liam: One more important guardrail: detection only runs on unpinned, generic-text documents. If you explicitly set a tab to HTML, no detector is going to second-guess you. If your file is named script dot py, QUILL trusts the extension. The whole point of pinning is to win against the auto-detect, and the system respects that.
Jessica: Now the second meaning of languages, the spell-check language. Navigate, set Document Language controls the editor profile. Tools, Writing, Spell Check Language controls the dictionary. They look similar in the menus, they both use the word language, but the spell-check one is about which Hunspell dictionary, English United States, English Great Britain, Spanish, French, German, and more, the engine uses to flag misspellings and offer suggestions.
Liam: The architecture of that system: pyenchant is the engine, English ships bundled, everything else is an on-demand verified download. Pick a new language in the Spell Check Language dialog and QUILL offers to fetch the dictionary from the same verified release assets pipeline we covered in episode two. The download is SHA-256 checked, blocks in safe mode, and runs on a worker thread with a cancelable progress dialog. Once installed, the dictionary is offline and the engine discovers it through the enchant config directory environment variable. No network, no surprise, no license ambiguity.
Jessica: Why this matters in real life: you paste a French email into QUILL and the spell check lights up like a Christmas tree, every accent-marked word flagged because the English dictionary doesn't know it. Set the spell-check language to French and the false alarms vanish. Same with Spanish, with German, with Italian. The spell engine knows the language you picked; your ears get a clean proofreading pass.
Liam: A subtle gotcha worth knowing: the spell-check language is a global setting, stored in settings and saved when you change it. The Document Language, by contrast, is per tab. You can have ten tabs open, all in different document languages, but the spell check uses one dictionary at a time. That asymmetry is by design. Spell check is one engine; document editing is many profiles. If you find yourself flipping the dictionary often, that's a signal your work is genuinely multilingual and the Optional Components download from episode two will become a regular stop.
Jessica: Now the thesaurus, the other half of today's title. The offline thesaurus, the one in Tools, Writing, Thesaurus, is backed by the LibreOffice MyThes en US data file, about eighteen megabytes, and the path is quill slash data slash th underscore en underscore US underscore v2 dot dat. QUILL parses that file once on first use, holds an in-memory index keyed by lowercase headword, and reuses it for every lookup. The whole index is in memory but it's only tens of megabytes, well under a second of load time on a modern machine.
Liam: What the user sees is simpler than what's underneath. Press shift F7 with the caret on a word, or with a word selected, and the Thesaurus dialog opens. The dialog title is just Thesaurus, and the message reads synonyms for whatever your word is. Below the message is a list of choices, and the choices are grouped by part of speech, so you see things like noun, choice, pick, alternative, and verb, select, opt for, in distinct lines, each prefixed with the part of speech in brackets. That's the screen-reader-friendly grouping; the brackets announce naturally.
Jessica: If there's no selection and the caret isn't inside a word, the dialog opens with an empty text field and asks you to type a word. That covers the case where you want to look up something you're about to write. If the word isn't in the dictionary, the dialog announces no thesaurus entry and exits cleanly. If the word is in the dictionary but has no synonyms for some reason, same story. The system never pretends.
Liam: Once you pick a synonym, the dialog gives you two actions: replace word in editor, or copy to clipboard. The replace path is the magical one. Pick a synonym, choose replace, and QUILL replaces the original word in your text, preserving the original capitalization. So Good becomes Excellent, good becomes excellent, and your sentence never left your hands. The selection-based version is the fast path: caret on the word, shift F7, pick, replace, you never even typed the original word into a search box.
Jessica: One thing the docs and the existing scripts have been a little inconsistent about: which menu the Thesaurus lives under. Episode fifteen said Tools, Writing, Thesaurus, and that's still right. The keyboard binding is shift F7. But the menu label is exactly Thesaurus, three dots, with the ampersand mnemonic on the T. The existing thesaurus dialog, by the way, is a wx native single-choice dialog, not a fancy custom window. It does the job, it's accessible, it doesn't pretend to be more than it is.
Liam: And then there's the AI tier, the AI Thesaurus. It lives under the AI menu, in the More submenu, labeled AI Thesaurus. Same keyboard invocation, kind of: you can rebind it, but by default it doesn't have a default key. The dialog title is AI Thesaurus and it's a real custom window, six hundred by four-eighty pixels, with a Word field, a Look Up button, a Synonyms list with two columns, synonym plus usage note, and three buttons: Replace Word, Copy Synonym, Close. The status line at the bottom tells you what state you're in: enter a word, looking up synonyms, eight synonyms found, error, whatever.
Jessica: The difference between the two thesauri is the difference between a dictionary and a meaning. The offline thesaurus, the MyThes data file, gives you curated lists. Synonyms grouped by part of speech, drawn from a real word list, deterministic, instantly. The AI thesaurus takes your word, optionally the surrounding sentence as context, and asks the model to suggest six to ten alternatives ranked by similarity, with a one-sentence usage note for each. So for bank, the offline thesaurus gives you financial-bucket and riverbank-bucket separately. The AI thesaurus, given the sentence I'm going to bank the check tomorrow, returns synonyms that fit the financial meaning. The sentence is disambiguation.
Liam: This is the part where we have to be honest about what the code actually does, because the previous episode was a little aspirational. The AI thesaurus, quill slash core slash ai slash thesaurus dot py, makes a single round trip to the configured AI provider, parses a JSON array of synonym plus note objects, and returns them to the dialog. If the response isn't valid JSON, you get no results. If the model refuses, you get an error in the status line. If authentication fails, you get a coded error, QUILL-AI-THESAURUS-FAILED, surfaced through the normal error contract. Nothing is hidden; nothing is silently retried; nothing is sent without your explicit Look Up click.
Jessica: And the review-everything contract still applies. The AI doesn't edit your document directly. It returns a list. You read the list. You pick one, or you don't. Only then does the replace happen, and only for the word you originally highlighted. The AI thesaurus is a suggestion engine with a human in the loop, and that loop is the same loop the rest of QUILL's AI features use. If you haven't configured an AI provider, the menu item is still there but the Look Up button returns an error explaining what's missing. Graceful degradation, not a crash.
Liam: A code-verified walk through the two flows, the kind we promised in the brief. First, the offline thesaurus. Trigger: shift F7, or Tools, Writing, Thesaurus, or the context menu's Look Up in Thesaurus entry on a flagged word. The function is show underscore thesaurus, defined in main underscore frame dot py around line seventeen thousand two hundred. First check: is the thesaurus data file present, thesaurus underscore engine dot is available. If not, a friendly dialog explains the thesaurus isn't installed and points at the expected path, quill slash data slash th underscore en underscore US underscore v2 dot dat. No crash, no broken menu.
Jessica: If the data file is present, the function reads the editor's text, checks for a selection, and if there is one, uses the selected word. If there isn't a selection, it calls thesaurus engine dot word at to find the word at the caret position, walking backwards to the start of the alphabetic run and forwards to the end, including apostrophes for contractions. If neither yields a word, the function prompts with a text entry dialog, Look up word in thesaurus, where you can type a fresh word.
Liam: Then thesaurus engine dot lookup does the dictionary hit. The lookup is case-insensitive: you type Good, the engine lowercases it and finds good. As a courtesy, if the lowercased word ends in S and is more than three characters long, the engine tries the singular form, so cats falls through to cat. That's a small piece of English-specific kindness for plural lookups. If nothing matches, the user gets a clear no-entry message and a status line announcement.
Jessica: For each meaning, the function builds a line of the form bracket part of speech bracket space synonym, and collects the bare synonyms separately. Then a single-choice dialog presents the lines and the user picks. After the pick, if the original word had a known location in the editor, a second single-choice dialog asks replace or copy. Replace preserves the original capitalization, uppercases the first letter of the chosen synonym if the original started with one, then uses editor dot replace to swap the text in place. Copy puts the synonym on the system clipboard. Either way, the verb is final and obvious.
Liam: Second flow, the AI thesaurus. Trigger: AI menu, More, AI Thesaurus. The function open underscore ai thesaurus constructs an AIThesaurusDialog from quill slash ui slash ai underscore thesaurus underscore dialog dot py, passing in the parent, the initial word, the context sentence containing that word, the show modal dialog gate, and two callbacks, one for the lookup, one for the replace.
Jessica: The dialog is structured the way you'd expect: a search row with a Word field and a Look Up button, a context line if a sentence was extracted, a Synonyms list ctrl with two columns, a status line, and the three buttons Replace Word, Copy Synonym, Close. If the initial word is non-empty, the dialog auto-runs the lookup on open. The lookup happens in a background thread, the GATE-40-OK comment in the code is the team's explicit exception to the no-background-threads-in-UI rule for AI round trips, and results are pushed back through wx dot CallAfter so the UI thread stays clean.
Liam: The background worker calls the on underscore lookup callback, which in practice routes to quill slash core slash ai slash thesaurus dot py's get underscore synonyms. That function builds a system prompt and a user prompt, calls the configured AI provider through generate underscore assistant underscore response, with a thirty second timeout and a max tokens cap of five hundred and twelve. If the call returns a string, the function strips any markdown fences, regex-finds the JSON array, parses it, and walks the items, each item must be a dict with a synonym string and a note string, synonyms that are empty are dropped.
Jessica: On success, the dialog populates the list, selects the first row, and enables Replace and Copy. On error, the status line shows error, colon, the message. The user is never blocked; the dialog stays open; they can try a different word or close. The replace callback fires only when the user actively clicks Replace Word or double-clicks a row, and the callback is the one passed in by main frame, which knows how to swap the word in the editor. Same replace semantics as the offline thesaurus: known location in the editor, capitalized first letter preserved.
Liam: An honest correction versus the older scripts, because the brief told us to flag drift. Episode fifteen, the spelling episode, described the thesaurus as a synonym list you can pick from and replace with. That's still right for the offline thesaurus. It also gestured at a separate AI thesaurus tier that would come in part five, and the AI thesaurus does exist and lives in part five, but the exact menu path is AI, More, AI Thesaurus, not Tools, Writing. The two thesauri are siblings, not parent and child, and the right mental model is: offline is the default, AI is the upgrade, both end with the same replace verb.
Jessica: Another drift worth flagging. The existing episode fifteen script mentioned a context menu route to the thesaurus on a flagged word, and the code does support that. There's a Look Up in Thesaurus entry in the spelling context menu, line fifty-eight-thirteen of main frame. It calls show underscore thesaurus for the current word. So the thesaurus and the dictionary share a context menu sibling, and the Look Up in Dictionary entry sits one line above. The thesaurus context menu entry is gated on dict underscore on, the dictionary is enabled setting, and on the thesaurus data file actually being available. If you don't see it, check the dictionary setting first.
Liam: And one more nuance for the writers and editors in the audience. The thesaurus's part-of-speech groupings are not editorial guidance, they're just data. A noun synonym and an adjective synonym are listed separately because English words really do have multiple parts of speech, and the MyThes database stores them that way. If you pick a noun synonym for an adjective slot in your sentence, the result is grammatical, not semantic. The AI thesaurus, by contrast, sees your sentence and can recommend only adjectives when the context demands an adjective. That's a real advantage when you're working in a complex sentence and aren't sure which part of speech you actually need.
Jessica: Workflow recipe. The spelling episode gave you a finish pass, control F7 walk, read aloud, AI grammar. Today's addition is the language-aware finish pass. Step one, before you proofread, glance at the status bar and confirm the document language matches your intent. If you started with a txt file and pasted in code, the auto-detect may have already pinned it, or you may need to control shift L and choose. Step two, confirm the spell-check language in Tools, Writing, Spell Check Language. The right dictionary on the right text is the difference between clean proofreading and a wall of false alarms. Step three, shift F7 on any word that bugs you, replace or copy. Step four, if AI is configured and you have a word with subtle meaning, AI, More, AI Thesaurus for context-aware suggestions. The four-step finish pass takes five minutes and produces a noticeably cleaner document.
Liam: One accessibility note before homework, because we promised to call these out. The Thesaurus dialog uses wx native single-choice dialogs, which means your screen reader announces the list, your arrow keys navigate, enter confirms. The AI Thesaurus dialog uses a custom wx ListCtrl with two columns, synonym and usage note; the list is named Synonyms, your screen reader will announce the list and the selected row's content, arrow keys move, enter activates. Both flows are keyboard complete. Mouse is never required. If you find one isn't, that's a bug, and Help, Report a Bug is the right next step.
Jessica: Homework. Four steps, as the brief asked. One: open the Set Document Language dialog, control shift L, on a real document in your QUILL. Tab through the list. Cancel. If you have a file whose extension doesn't match its content, pin the correct language and feel how the editor responds. Two: paste a chunk of code into a fresh untitled tab, give it a few hundred milliseconds, and watch the language detection mode you have configured. If you're on off, change it to prompt in settings, restart, try again. Three: in a real document, pick a word you overuse, hit shift F7, replace it with a synonym, save. Notice how the replacement kept the original capitalization. Four: if you have an AI provider configured, open AI, More, AI Thesaurus, and try one lookup. If you don't have a provider, the menu is still there, the error message will tell you what's missing, and you can stop. Each step, two minutes tops.
Liam: That wraps the word-tools arc. Next episode closes the everyday-editor arc, and it covers never losing work. Autosave, crash recovery, versions, snapshots, the full safety stack, and how knowing the stack lets you write braver. Episode seventeen is the final episode of part two, and we have a lot of ground left in part three, including the file format conversations, Markdown first, then Word, EPUB, PDF, and friends.
Jessica: For reference, that's episode seventeen of fifty-four total in the series. The series is structured in parts, every-day editor, document formats, AI, advanced workflows, extensions, accessibility, power user, and we are exactly on plan.
Liam: Spell with intent, write with the right language pinned, and pick the synonym that actually means what you mean.
Jessica: I'm Jessica.
Liam: I'm Liam. This has been The QUILL Cast.