In this article

“Heap, Heap, Array!”

Allonsay’s language classification algorithm

How you can detect the language of texts, without using the “ML” word.

“You can’t name this thing Allonsay and not include support for French”
Time-travelling spacecraft disguised as a police box

“You can’t name this thing Allonsay and not include support for French”

macOS comes with a say command that reads texts out loud using the system’s default voice. Unfortunately, voices are monolingual so if you often consume content in different languages you’re out of luck. Allonsay is a tiny command-line application that detects which language (and thus voice) you need. How does it work?

In an ideal sitation there’s no need to guess which language a text is written in. Web pages (like this one) often include metadata that tell you which language is used on the page. Some websites will also tell you when they use foreign words, so your screen reader knows how to pronounce them correctly.

Such information is rarely available in practice though, so we need a way to detect what language a text is written in.

Sometimes we’re lucky, and we only need to identify a single language that has its own unique writing systems (e.g. Thai and Hebrew). Text written in these languages will use Unicode code points that you won’t find in any other language. In all other cases we need a more sophisticated solution…

The easiest and most intuitive solution by far is to simply look up words of a text in dictionaries for various languages until you’ve found enough matches in one of the dictionaries. But this solution doesn’t scale very well. Any tool that needs to identify languages now needs to ship with dictionaries for every supported language. This can be expensive in terms of disk or bandwidth usage and (possibly) licensing costs, so we need to look for a solution that works without dictionaries!

Letter frequencies

Fortunately we don’t have to look very far, because the characters that occur in a text can also tell us a lot about its language!

This might sound strange at first, because for western languages most of these characters will be in the a–z range. However, the frequency at which characters occur still differs between languages.

Here’s a table from Wikipedia that shows the distribution of characters a–z in English and Dutch:

CharacterEnglish (%)Dutch (%)
a8.1677.486
b1.4921.584
c2.7821.242
d4.2535.933
e12.70218.910
f2.2280.805
g2.0153.403
h6.0942.380
i6.9666.499
j0.1531.460
k0.7722.248
l4.0253.568
m2.4062.213
n6.74910.032
o7.5076.063
p1.9291.570
q0.0950.009
r5.9876.411
s6.3273.730
t9.0566.790
u2.7581.990
v0.9782.850
w2.3601.520
x0.1500.036
y1.9740.035
z0.0741.390

The distributions are largely similar – both are part of the West Germanic language group after all – but there are some clear differences too. For example, the character e occurs a lot more in Dutch than in English, while y is basically non-existent in Dutch.

We can use this to guess the language of a text. The overall idea is pretty simple. First, we determine the distribution of characters in a text and then check which language has the most similar distribution.

Example

Let’s say we have the following input text:

People assume that time is a strict progression of cause to effect, but, actually, from a non-linear, non-subjective viewpoint, it’s more like a big ball of wibbly-wobbly… timey-wimey… stuff

This text has the following character distribution:

CharacterCountRelative frequency (%)
a106.667
b85.333
c53.333
d00.000
e1610.667
f74.667
g21.333
h10.667
i149.333
j10.667
k10.667
l96.000
m64.000
n74.667
o128.000
p42.667
q00.000
r64.000
s106.667
t149.333
u64.000
v21.333
w42.667
x00.000
y53.333
z00.000

We can compare the distribution of the input text with the overall distributions in the English and Dutch languages:

CharacterEnglish (%)Dutch (%)Input text (%)Difference with English (pp.)Difference with Dutch (pp.)
a8.1677.4866.6671.5000.819
b1.4921.5845.3333.8413.749
c2.7821.2423.3330.5512.091
d4.2535.9330.0004.2535.933
e12.70218.91010.6672.0358.243
f2.2280.8054.6672.4393.862
g2.0153.4031.3330.6822.070
h6.0942.3800.6675.4271.713
i6.9666.4999.3332.3672.834
j0.1531.4600.6670.5140.793
k0.7722.2480.6670.1051.581
l4.0253.5686.0001.9752.432
m2.4062.2134.0001.5941.787
n6.74910.0324.6672.0825.365
o7.5076.0638.0000.4931.937
p1.9291.5702.6670.7381.097
q0.0950.0090.0000.0950.009
r5.9876.4114.0001.9872.411
s6.3273.7306.6670.3402.937
t9.0566.7909.3330.2772.543
u2.7581.9904.0001.2422.010
v0.9782.8501.3330.3551.517
w2.3601.5202.6670.3071.147
x0.1500.0360.0000.1500.036
y1.9740.0353.3331.3593.298
z0.0741.3900.0000.0741.390
Total100.000100.000100.00036.78263.604

The last row shows the sum of the percentage point differences between the input text and the two languages. Our input text is more similar to English (36.782) than to Dutch (63.604), so we can assume that it’s written in English!

Summary

  1. Language have their own, unique distribution of characters

  2. The language of a text can be classified by comparing the distribution of its characters with known distributions of languages

This article appears in