Detecting the Language of a Text, and Why Short Texts Fail
Published 8/14/2026 · 16 min read · Text & language tools
Daniel Okonkwo — Front-end developer and tech writer at Allin
Web performance · File formats
Checked against 3 sources
The tool works in four stages. It first counts the letters in eleven Unicode script ranges, and if more than 30% of the text sits in one of them the alphabet answers the question outright — Korean, kana, Han, Greek, Arabic, Hebrew, Devanagari, Thai, Armenian and Georgian need no statistics. Cyrillic gets a second guess inside its own family. Latin text goes to a floor: below twelve letters it refuses to answer and says so. Above twelve it hands the text to a trigram model restricted to Latin-script languages, then breaks near-ties with function-word lists and per-language spelling patterns. The floor was measured and it holds — twelve of twelve truncations at ten and eleven letters were declined. The problem is that above it the tool never declines again. Fed 90 short phrases people genuinely paste — greetings, sign-offs, order confirmations — across the six languages of this site, it answered all 90 and got 68 right: 76% overall, 64% in the twelve-to-fifteen-letter band. Nine of the twenty-two errors came back at 75% confidence or more and four at exactly 100%: a French sign-off read as Italian, a Spanish order status read as Italian, a Portuguese confirmation read as Spanish, and a German farewell read as Indonesian. The confidence figure is computed from the gap to the runner-up, so it measures how separated the candidates are, not whether the winner is right. On running prose the picture is much better: across 348 truncations of 24 sentences, accuracy is 85–93% between fifteen and thirty letters and 200 out of 201 above forty. Treat anything under about forty letters as a suggestion and read the runners-up.
Measured, not asserted: 90 short real phrases across six languages, none declined and 68 right — 76%, falling to 64% under sixteen letters. Four of the wrong answers came back at 100% confidence.
What the tool actually does, in four stages
Stage one is the alphabet, and it is the cheapest reliable signal there is. The code holds eleven regular expressions over Unicode blocks — Hangul, kana, Han, Cyrillic, Greek, Arabic, Hebrew, Devanagari, Thai, Armenian and Georgian — counts how many of the text's letters fall in each, and if any one of them takes more than 30% of the letters it answers immediately. No trigram model improves on that, and it is why a line of Japanese or Greek comes back at full confidence from four or five characters, well below the floor that applies to Latin text.
Stage two is that floor: twelve letters. Not twelve characters — the code counts letters with a Unicode property, so digits, spaces and punctuation do not help. Below twelve it returns nothing at all and the interface says the text is too short. This is the part that works, and it was tested by truncating sentences word by word: at ten and eleven letters, twelve inputs out of twelve were declined.
Stage three is a trigram model. It slices the text into overlapping three-character sequences and compares their frequencies with a stored profile for each language; the language whose profile is closest wins. This is the classic approach to the problem and it is genuinely good on paragraphs, because three-letter patterns like "ent", "ung", "ção" and "gli" are distributed very differently across languages. It is weak on fragments for the obvious reason: a three-word phrase yields a dozen trigrams, and a dozen samples do not identify a distribution.
Stage four is a tie-break, and only a tie-break. Candidates whose trigram score sits within 0.4 of the leader are re-ranked using two extra pieces of evidence: hand-written lists of function words, each word weighted by one over the number of lists it appears in and dropped entirely if it appears in more than three, and a regular expression per language capturing spelling that all but rules the language in — ß and sch for German, ñ and the inverted question mark for Spanish, ã, nh and lh for Portuguese, gli and zione for Italian. Applied globally this made results worse, because "du" belongs to French, German and Swedish at once. Restricted to candidates the trigram model has already tied, it settles the confusions that matter.
Where it starts being wrong, measured
Two benches were run. The first truncated 24 sentences — four in each of the six languages of this site — at every word boundary, giving 348 inputs from six letters up to about ninety. The results by band: everything at ten and eleven letters was declined; twelve to fourteen letters scored 92%, fifteen to nineteen 85%, twenty to twenty-four 85%, twenty-five to twenty-nine 93%, thirty to thirty-nine 96%, and above forty letters 200 out of 201. The point at which a sentence became right and stayed right had a median of fifteen letters, but the worst case was forty-six: a Portuguese sentence beginning "Ela abriu a janela porque o quarto" was read as Spanish, at 100% confidence at twenty-one letters, all the way up to forty-five.
The second bench is the one that matters, because it is made of what people actually paste: 90 short phrases across the six languages — greetings, sign-offs, order confirmations, error messages, "please find attached" — from twelve to thirty-four letters. Not one was declined. Sixty-eight were right: 76% overall, 64% in the twelve-to-fifteen band, 78% from sixteen to nineteen, 82% from twenty to twenty-four. On the material this tool is most often given, roughly one answer in four is wrong.
The errors are not shy about it. "Merci beaucoup", thirteen letters, comes back as Indonesian at 100% confidence. "Bis Montag dann" comes back as Indonesian at 100%. "À lundi prochain" and "Tu pedido ha salido" both come back as Italian at 100%. "Encomenda confirmada" comes back as Spanish at 100%. "Buenos días a todos" comes back as Portuguese at 91%. Nine of the twenty-two errors were at 75% or above, which is the band the interface paints green.
Confidence measures separation, not correctness
The percentage beside the answer is not a probability of being right, and reading it as one is the main way this tool misleads. It is computed from the relative gap between the winner's score and the runner-up's, multiplied by three and offset — a wide gap gives 100%, a narrow one gives something near 45%, and the whole thing is clamped between 35% and 100%.
That means a confident answer is one where no other language came close, not one that is probably right. On "Bis Montag dann" nothing came close to Indonesian, so the tool printed 100% and was wrong. On the other hand it also means the low numbers are informative in the right direction: when the tool says 46%, the second candidate is genuinely competitive and often correct, and the interface does the honest thing by listing the runners-up underneath with their own bars.
So the reading rule is simple and it inverts the usual instinct. A green bar on a long text is trustworthy. A green bar on three words tells you only that the model was not torn, which on three words it rarely is. Below about forty letters, look at the second candidate before you believe the first.
Nineteen languages the tool lists and can never answer
This is the structural defect, and it is invisible from the interface. The code holds a list of thirty-five Latin-script languages it asks the trigram model to consider, and a table of fifty-two language names written in their own language, ready to print. The trigram model, franc-min, only has profiles for forty-four Latin-script languages, and seventeen of the thirty-five requested are among them. The other eighteen are silently dropped, along with Macedonian on the Cyrillic side.
Nineteen of the fifty-two names in that table can therefore never be the answer: Danish, Norwegian bokmål, Finnish, Slovak, Slovene, Lithuanian, Latvian, Estonian, Catalan, Basque, Galician, Afrikaans, Malay, Albanian, Welsh, Irish, Icelandic, Esperanto and Macedonian. Paste Danish and you do not get "not sure": you get something else, confidently. "Jeg vil gerne bestille en kop kaffe og et stykke kage, tak." comes back as Dutch at 75%. The Norwegian version of the same sentence also comes back as Dutch, at 48%. The Swedish version is correct, because Swedish is one of the seventeen.
The Cyrillic branch has a milder version of the same problem. Six of the seven languages it considers do exist in the model, so Russian and Ukrainian come out right, but the second guess inside a family is still a guess: "Здравейте, как сте?" is Bulgarian and comes back as Russian at 85%. The script stage got the script right and the family stage got the language wrong, which is the honest summary of what alphabet detection can and cannot do.
Single words, close cousins, proper nouns and things that are not prose
A single word that belongs to several languages is handled correctly, and by accident. "information" is eleven letters, "important" nine, "national" eight — all below the twelve-letter floor, so the tool declines all three, which is the right answer for a word that is genuinely both English and French. String them together into "innovation, information, important" and you cross the floor at thirty letters, and the answer is English at 46% — with French scoring exactly the same trigram value, 1.00 against 1.00, and only the tie-break separating them. The low number is the tool telling you the truth.
Closely related pairs behave exactly as the literature predicts: fine on sentences, hopeless on fragments. Six full Spanish and Portuguese sentences were all identified correctly, including the near-minimal pair "Es importante para todos." and "É importante para todos.", separated only by an accent. Shorten them and it collapses: "Pedido confirmado" is read as Portuguese at 95%, "Encomenda confirmada" as Spanish at 100%, "Buenos días a todos" as Portuguese at 91%. Danish and Norwegian are worse than confusable here — neither can be returned at all.
A proper noun on its own is the clearest case of the tool answering a question it cannot answer. "Emmanuel Macron" comes back as Tagalog at 100%. "Jean-Baptiste Poquelin" comes back as Dutch at 100%. "Wolfgang Amadeus Mozart" comes back as Tagalog at 79%. "Antonio Vivaldi" comes back as Italian at 100%, right by luck rather than by evidence. A name carries no grammar and almost no function words; the trigram model is comparing letter patterns with nothing else to go on.
The same applies to anything that is not running prose. A line of SQL comes back as English at 100%, which is defensible since its keywords are English words. A line of JavaScript comes back as Portuguese at 58%. A snippet of HTML — a div with a class attribute wrapping a span containing the word Hello — comes back as Spanish at 91%. A URL comes back as Portuguese at 71%, a French street address as Spanish at 49%, the first twelve letters of the alphabet as Dutch at 100%, and sixteen consecutive letter a's as Dutch at 61%. There is no stage in the pipeline that asks whether the input is language at all; the only gate is the letter count.
Mixed text, accents, and how to use the thing
Mixed-language text gets one answer, and it is the majority language rather than a warning. "Hello, merci beaucoup for your quick reply." comes back as English at 100%. "Bonjour, I would like to confirm notre réunion de demain matin à dix heures." comes back as French at 67%, with English second at 0.93 against 1.00 — close enough that the runner-up list actually tells you something. If your text is a bilingual email or a translation memory row, split it before you ask.
One expectation turned out to be wrong, which is worth recording. Removing every accent from a sentence looks as though it should wreck the detector, since the tie-break leans on ñ, ã, ß, ç and the rest. It does not. Five long sentences were stripped to plain ASCII and all five came back with the same language and the same 100% confidence: the trigram model was already deciding, and the orthography table only ever gets to speak when the model is torn. Accents matter for the short cases, and the short cases were already unreliable.
So: give it a paragraph. Three or four full sentences of the same language put you above forty letters with room to spare, and above forty letters it was right on 200 of 201 inputs. If all you have is a fragment, read the runners-up, and treat a name, an address, a URL or a line of code as unanswerable regardless of what percentage appears beside it. And if the answer you expect is Danish, Norwegian, Finnish, Catalan, Basque, Galician, Icelandic or one of the other twelve on that list, no amount of text will produce it.
| Input | Answer | Correct? |
|---|---|---|
| Merci beaucoup — 13 letters, French | Indonesian, 100% | No — and nothing came near it, which is why the figure is 100 |
| Bis Montag dann — 13 letters, German | Indonesian, 100% | No — too few trigrams for the model to see German at all |
| Encomenda confirmada — 19 letters, Portuguese | Spanish, 100% | No — the classic Spanish/Portuguese collapse on a fragment |
| information — 11 letters | Declined: too short | Yes — the word is both English and French; declining is the right answer |
| Emmanuel Macron — 14 letters, a name | Tagalog, 100% | Unanswerable — a name carries no grammar and almost no function words |
| A Danish sentence about ordering coffee and cake | Dutch, 75% | No — Danish is one of nineteen names the tool lists but can never return |
| The first twelve letters of the alphabet | Dutch, 100% | No — exactly twelve letters clears the floor, and nothing else is checked |
| A snippet of HTML wrapping the word Hello | Spanish, 91% | Unanswerable — no stage asks whether the input is language at all |
| A full French sentence of 67 letters | French, 100% | Yes — above forty letters the tool was right on 200 of 201 inputs |
Frequently asked questions
- How much text does it actually need?
- About forty letters of running prose, which is three or four clauses. The hard floor in the code is twelve letters, below which it declines outright, and that part works: twelve of twelve truncations at ten and eleven letters were refused. Between twelve and forty letters it always answers and is often wrong — on 348 truncations of 24 sentences, accuracy ran 92% at twelve to fourteen letters, 85% from fifteen to twenty-four, 93% from twenty-five to twenty-nine, 96% from thirty to thirty-nine, and 200 out of 201 above forty. On standalone short phrases rather than truncated sentences it is worse still: 68 right out of 90, none declined. So one full sentence is usually enough and a paragraph is comfortably enough; a greeting is not.
- Why did it say 100% and get it wrong?
- Because the percentage is not a probability of being right. It is derived from the relative gap between the top candidate's score and the second's: a wide gap gives 100, a narrow one gives around 45, and the result is clamped between 35 and 100. A confident answer therefore means no other language came close, not that the answer is likely correct. On thirteen letters of German, no other language came close to Indonesian, so it printed 100 and was wrong. The usual instinct — high number means trustworthy — is exactly backwards on short input. What the number is genuinely good for is the other direction: when it says 46, the runner-up is competitive and often the right one, and the interface lists the runners-up underneath for precisely that reason.
- It says Dutch for my Danish text. Is that a bug?
- Yes, and a structural one. The code asks its trigram model to consider thirty-five Latin-script languages, but the model — franc-min — only has profiles for seventeen of them. The other eighteen are dropped without a word, so Danish, Norwegian bokmål, Finnish, Slovak, Slovene, Lithuanian, Latvian, Estonian, Catalan, Basque, Galician, Afrikaans, Malay, Albanian, Welsh, Irish, Icelandic and Esperanto can never be the answer, and neither can Macedonian on the Cyrillic side. That is nineteen of the fifty-two names the tool is prepared to print. A Danish sentence about ordering coffee and cake came back as Dutch at 75%, and the Norwegian version of the same sentence as Dutch at 48%. Swedish works, because Swedish is one of the seventeen. If your language is on that list, no amount of extra text will help.
- Can it tell Spanish from Portuguese, or Danish from Norwegian?
- Spanish from Portuguese, yes, on a full sentence: six complete sentences were tested and all six were right, including the near-minimal pair "Es importante para todos." and "É importante para todos.", which differ by one accent. The tie-break stage exists mainly for that job — it holds spelling patterns like ã, nh, lh and ção for Portuguese and ñ and the inverted question mark for Spanish. On fragments it fails: "Pedido confirmado" was read as Portuguese, "Encomenda confirmada" as Spanish, both at 95% or more. Danish from Norwegian, no, and not because they are similar — neither language exists in the underlying model, so both come back as Dutch. Swedish and Dutch are the two Germanic languages of that region the tool can actually name.
- Will removing accents from my text break the detection?
- Not on a full sentence, which was surprising enough to be worth testing. Five long sentences in French, Spanish, Portuguese, German and Italian were stripped to plain ASCII and all five came back with the same language and the same 100% confidence as before. The reason is the pipeline's shape: the orthography patterns only run as a tie-break, on candidates the trigram model has already scored within 0.4 of each other, and on a long sentence the model is not torn. Accents do carry weight on short input — but short input was already unreliable for other reasons, so the practical advice does not change. If you are removing accents to normalise a column for search or for sorting, detect the language before you strip, not after.
Articles you may find interesting
All guides →Related tools
Everything here describes what these four tools do today, checked by running their own code against the exact inputs printed in each article — not what a standard obliges them to do. Where a tool gets a case wrong, that is said plainly rather than worked around, and nothing was changed to make an article read better. Two consequences follow. Run any transform over a copy first and compare both ends: a text tool that deletes something is silent about it. And treat a secret the moment it leaves the page as a secret you have shared — pasting one into a chat, a ticket or a repository burns it however well it was generated.
Sources
- wooorm (GitHub) — franc — the trigram language-detection library, and the reduced franc-min model this tool loads
- Unicode Consortium — UAX #24: Unicode Script Property — the block and script data behind alphabet detection
- SIL International — ISO 639-3 code tables — the three-letter language codes the tool returns
Spotted a mistake in this article?