Stop asking users to pick a mode: search that guesses what you meant
A dictionary that accepts three input scripts can either ask the user to choose, or work it out. Choosing is easier to build and worse to use. Here is how we made the guess reliable.
KoiSpeak accepts three kinds of input in one box: Vietnamese, pinyin, and Chinese characters. The obvious design is a mode toggle. We deliberately did not build that, and the reasoning generalises well beyond dictionaries.
Why mode toggles fail
A toggle looks harmless. It costs the user one tap. But it costs it every time, and it fails in a specific, infuriating way: when the toggle is wrong, you get zero results, and zero results looks identical to "this word does not exist".
The user's actual mental model is not "I am now performing a pinyin search". It is "what does this mean". Every element of interface between that intention and the answer is friction, and friction compounds in a tool people open forty times a day.
There is a general rule hiding here. If the system can determine something from the input, it should not ask the user for it. A mode toggle is the interface admitting it did not want to do the work.
The three scripts are more separable than they look
The good news is that these inputs occupy largely distinct spaces.
Chinese characters are trivially detectable
Han characters sit in known Unicode blocks, principally CJK Unified Ideographs at U+4E00 to U+9FFF, with extensions elsewhere. If the input contains a character in those ranges, the user typed Chinese. This is close to a certainty rather than a guess.
Vietnamese has diacritics that pinyin does not use
Vietnamese is written in Latin script with its own set of marks: horn (ơ, ư), circumflex (â, ê, ô), breve (ă), stroked đ, and tone marks including the underdot (ạ) and hook above (ả).
Pinyin uses macron, acute, caron and grave, and only over a, e, i, o, u, ü. The sets barely overlap. A string containing đ, ơ, ư or an underdot is Vietnamese, full stop.
Pinyin is a small, closed set
This is the part people underestimate. Mandarin has only a few hundred legal syllables. zhang, xue, qiong are valid; blorp and strem are not.
So you can validate: try to segment the input into legal pinyin syllables. If it segments cleanly, pinyin is very likely. This catches toneless pinyin, which is what most people actually type.
The hard part is the overlap
Detection is easy in the clear cases and interesting in the ambiguous ones. Real collisions:
- Short strings that are both.
mais valid pinyin and a valid Vietnamese word.ba,co,canlikewise. - Unaccented Vietnamese. Plenty of people type Vietnamese without diacritics, which removes your strongest signal.
- English. Learners type English words, and English is not any of your three languages.
Trying to resolve these with more rules is a losing battle. The rules get long, contradict each other, and still get it wrong.
Do not classify. Search everything and rank.
The design that worked was to stop treating this as classification.
Instead of deciding what the input is and searching one index, run the query against all the interpretations, then merge the results with a score that accounts for how plausible each interpretation was.
Roughly:
- Score each interpretation: does it contain Han characters, does it carry Vietnamese-only diacritics, does it segment as legal pinyin.
- Query each index the interpretations imply.
- Merge, weighting each result by its interpretation's score, exactness of match, and word frequency.
The user sees one list. For ma they get the pinyin results and the Vietnamese results together, common words first, and the answer they wanted is on screen either way. Ambiguity stops being an error state and becomes a ranked list.
Classification forces a decision that can be wrong. Ranking lets you be uncertain and still be useful. When a system cannot be sure, prefer designs that degrade into "several plausible answers" rather than "one confident wrong answer".
What this costs
Honesty about the trade: you are doing more queries per keystroke. That is real, and it is why this design lives or dies on performance.
What made it affordable:
- Cheap interpretations first. Han detection is a range check on the first few characters. If it hits, skip the rest entirely.
- Prefix indexes per script, so each individual query is a cheap lookup rather than a scan.
- Frequency baked into the index, so ranking does not require a second pass over results.
- Debounce, and cancel in-flight queries when the input changes. Most keystrokes never produce a completed search.
Where it still needs an escape hatch
Auto-detection should not be the only route. Two cases genuinely need something else:
- You cannot type the character at all. That is what handwriting input and radical lookup are for.
- You want to force a direction. Occasionally a user really does want Vietnamese-to-Chinese only. We surface explicit direction controls, but as an override that is there when needed rather than a decision demanded upfront.
That ordering is the actual principle. Guess well by default, and let people correct the guess. Not: refuse to guess, and make everyone specify.
The general lesson
Every product has a moment where the easy implementation asks the user a question the system could have answered. A file type. A country. A currency. A search mode.
Each one is small. Together they are the difference between a tool people reach for without thinking and one they have to operate.