Glotty
A messaging app for learning Spanish, where each message is shown as a mix of English and Spanish depending on the reader’s progress.
Glotty is a messaging app for people learning Spanish. You write in English, and the person you are texting receives your message as a mix of English and Spanish, chosen for them based on what they have read and written so far. There are no lessons; the Spanish comes from the conversations themselves.
It is currently running as a small private beta at glotty.net.
one message at different points in a reader’s progress
messages 1–20I don’t have much time right now, but I really like your new idea.
I don’t have much time right now, but I really like your new idea.
Right now not I-have much time, but I really like your idea new.
Right now not I-have mucho time, pero I really like tu idea new.
Right now no I-have mucho tiempo, pero me likes mucho tu idea nueva.
Ahora mismo no tengo mucho tiempo, pero me gusta mucho tu idea nueva.
The first 20 received messages are plain English.
The first four word-order constructions are introduced together, while all the words are still English.
Once those have been seen enough, Spanish words are introduced one at a time, taken from messages this reader receives.
The remaining constructions, such as the dative experiencer in “me gusta”, come later.
Fully Spanish. Words whose predicted recall drops fall back to English.
- adjective after noun
- time words first
- negation on the verb
- verb carries subject
- object pronoun first
- que clauses
- me gusta
Background
This is the third version of the idea. The first was a game, where you held spoken conversations with characters controlled by a language model, who started in English and gradually introduced Spanish. Most of the work went into the engine underneath, which decides which parts of a sentence a particular learner should see in Spanish.
The second version kept the engine and turned it into a texting app with an AI conversation partner. The engine worked, but the AI was expensive to run and not that interesting to talk to, so the current version replaces it with other people.
Weaving from Spanish
The common approach to mixing two languages in one sentence (a diglot weave) is to start with English and replace some words with their translations. I built this first. The problem is that the sentence stays English-shaped: the learner reads Spanish words in English word order, and Spanish constructions never appear until much later.
The engine instead works from Spanish. Each message is translated into Spanish once, and each reader’s version is built back from it. This means word order can be taught separately from vocabulary, as a second axis.
I first tried asking a language model for progressively more literal translations. It produced a few distinct levels, but the size of each step varied between sentences, and once there was nothing structural left it started swapping words instead. So word order is handled as a list of seven constructions, each detected from a dependency parse of the Spanish and taught separately:
- adjective after the noun: el pan fresco → the bread fresh
- time and place words first: ahora mismo tengo → right now I-have
- negation on the verb: no vendo → not I-sell
- subject carried by the verb: tengo → I-have
- object pronouns before the verb: te ayudo → you I-help
- subordinate clauses after que: que yo te ayude → that I you help
- dative experiencer: me gusta tu sombrero → me likes your hat
Rendering starts from a natural English translation and applies only the constructions the reader has been taught. Starting from English means that a missed construction leaves the sentence closer to English, which is still readable, rather than closer to a word-by-word gloss.
message pipeline
- sender Ana writes English “I really like your new idea”
- once per message Translate to Spanish “Me gusta mucho tu idea nueva” ministral-3:14b via Ollama, with the two previous messages and participants’ grammatical gender as context
- once per sentence, cached Analyse the Spanish fine-tuned Gemma 4 E4B returns an English translation and word alignment; constructions are detected from a spaCy dependency parse
-
for each reader Ben’s learner model “…your idea new”for each reader Cara’s learner model “me gusta mucho tu idea nueva”
Spanish written by the sender is assessed separately, with corrections shown only to them.
Alignment
The weave relies on one model call per sentence, which returns an English translation and an alignment: which Spanish token each English word came from, and a literal gloss of each Spanish token. The order of the request made a large difference. When asked to build the translation one aligned word at a time, the output read like a gloss - “No pasa nada, ya me vengaré” became “Nothing happens, anymore I will take revenge”. When asked for the full sentence first, it gave “It does not matter, I will take my revenge”, and the alignment was done afterwards.
Changes of this kind, such as giving the model numbered tokens to cite instead of asking it to count positions, reduced the number of bad alignment rows from 63 to 13.
A local model
The analysis step originally used a hosted model. To run the beta on my own machine, I distilled it into a smaller local one. I ran 290 short test chat threads through the app’s own pipeline, recorded the hosted model’s answers, checked each answer against the prompt’s rules and made the smallest correction where it broke one (107 of 287 needed a fix), then fine-tuned Gemma 4 E4B on the result with QLoRA.
analysis model, renders judged good (of 189)
Fitting this on a 12 GB card needed a few workarounds. Gradient checkpointing broke the model in training mode, because its last 18 layers read keys and values from a shared cache that checkpointed layers run without; only the MLPs are checkpointed now. Separately, three of four merged checkpoints had a handful of weights near 1037, each time from bit 14 of a bf16 (the top exponent bit) in clustered positions. This turned out to be faulty RAM. The live model was recomputed from its sources and checked.
Learner model
Each reader has a learner model, which decides what their next message is allowed to contain. For vocabulary it is a simplified half-life regression (Settles and Meeder, 2016): each word has a memory half-life, and evidence multiplies it up or down.
half-life model for one word
- half-life
- shown as
- status
Reading a word is weak evidence and is capped, so a word only counts as known once the reader has used it correctly themselves. Spanish that a user writes is assessed against the message they were replying to; they get credit, and corrections that only they can see. Corrections that would change a word’s gender ending are filtered against the writer’s gender setting, since the assessor has no way of knowing it.
New words were originally picked in advance from a frequency list, and the conversation partner was asked to use them. Over 24 turns, 22 of those words appeared once and never again, so they never got a second exposure. New words are now chosen after a message is written, from the words it actually contains, ranked by corpus frequency.
Limitations
The step that writes the reader’s English only sees the Spanish, so wording drifts: “hyd” becomes cómo estás and comes back as “how are you”. Where nothing is being taught, the reader is shown exactly what was sent, and in a message that does teach, untouched stretches of English are restored to the sender’s wording. Some drift remains next to taught words. In the first live run, “nice, I love hiking” came out as “how, I love hiking”, from the alignment of qué bien.
The English to Spanish translation step has not been properly measured yet, and the pacing constants are chosen rather than measured. I expect the beta to change both.