You open a page, see text in your language, and find part of it replaced by rectangles: small hollow boxes where characters should be. Designers call them tofu, and they are what a browser shows when it has the text but no font that contains the shapes.

This is a solved problem for English and an unsolved one for a good deal of Southeast Asia. We measured it on our own site, and the results were worse than we expected.

What a page needs to render your language

Three things have to line up. The text has to arrive correctly encoded. The browser has to know which language it is. And there has to be a font on the device containing every character used.

The third is the one that fails quietly. A website that self-hosts its fonts — as this one does, for privacy reasons — ships a specific set of characters and nothing else. Anything outside that set falls back to whatever font the reader's own device provides. On a current iPhone that is usually fine. On a budget Android handset, or a locked-down office desktop, it is a lottery.

We opened every font we serve

This site self-hosts 30 font files. We opened each one, read its character map, and pooled the lot: 30,066 distinct code points, which sounds like plenty.

Then we tested that pool against the characters each language actually uses — not against the Unicode block it belongs to, which is a softer test that a partial font passes. For the test text we used Article 1 of the Universal Declaration of Human Rights in each language, which gives real sentences with all their necessary combining marks.

LanguageCharacters usedCovered by our fontsCoverage
Burmese4000%
Khmer4300%
Lao4237.1%
Tamil3239.4%
Vietnamese353394.3%
Thai4141100%
Chinese (Simplified)3939100%
Indonesian, Malay, Tagalog, Englishall100%

Thai is fully covered because we deliberately ship a Thai font. Chinese is covered because we ship a Chinese one. For Burmese and Khmer we ship nothing at all — not one character of either script — and Lao and Tamil are covered only by a handful of shared punctuation marks that happen to fall inside them.

Coverage of ASEAN languages by the fonts this site self-hosts. Burmese needs 40 distinct characters and 0 are covered, Khmer needs 43 and 0 are covered, Lao needs 42 with only 3 covered at 7.1 percent, Tamil needs 32 with 3 covered at 9.4 percent, and Vietnamese needs 35 with 33 covered at 94.3 percent, missing only the letters o-horn and u-horn. Thai, Chinese, Indonesian, Malay, Tagalog and English are fully covered. A note records that 30 font files were opened and 30,066 distinct code points pooled.
Thirty thousand code points, and not one of them is Burmese.

Vietnamese is the interesting failure

Vietnamese is written in the Latin alphabet, so it looks like it should be safe, and it very nearly is: 33 of the 35 characters are present. The two that are missing are ơ and ư — o and u with a horn.

Those are not exotic. They are two of the twelve vowels of the Vietnamese alphabet, and they appear in the name of the language's own capital. To keep page weight down we subsetted our Latin fonts. The problem is that the subset was built for European languages, and those two Vietnamese vowels fell outside it.

It does not even recover through decomposition. The combining horn that would let a browser build ơ from a plain o is missing too, so there is no fallback path inside our own fonts at all.

This is the case a block-level check would have called a pass. "Latin: covered" is true and useless.

What actually happens to those readers

Being honest about severity matters here, because the failure is not usually tofu. The reality is more subtle.

When a character is missing from the page's font, the browser looks for it in the reader's system fonts. Most modern phones and desktops ship broad Unicode coverage, so in the common case the text appears — just in a different typeface, mid-sentence, at a slightly different size and weight. It looks like a typographic mistake rather than a technical one.

The true tofu case is rarer, confined to older or stripped-down devices, some Linux installations, and locked corporate images that lack, say, a Khmer font. That is a minority of readers, and it is not a minority you can identify or apologise to, because the page renders perfectly on the machine of everyone who would notice.

What to do about it

  • Test with real text, not with a language switcher. Paste a sentence in the script and look at it. A missing font is visible in a second and invisible in a specification.
  • Check coverage per character, not per block. A font can declare a script and omit the marks that script needs, which is exactly how Vietnamese lost two letters here.
  • Subset carefully, and know what you cut. Subsetting is the right call for page weight. Building the subset from European Latin and calling it Latin is not.
  • Name a fallback stack that includes the script. If you are not shipping a Khmer font, at least list the common system Khmer families in your CSS rather than leaving the browser to guess.
  • If a language matters to you, ship its font. We ship Thai because Thai readers matter to us. The gap between that and Burmese is a decision we made without noticing we were making it.

The counting problems in the other two guides in this set are about what a machine charges you and what a form allows you; this one is about whether the words arrive at all. All three are symptoms of one blind spot: systems built and tested in English, then pointed at the rest of the world.

The related measurements are in what the same sentence costs in eleven languages and why your name does not fit in the box.

What is measured here

We measured coverage on 21 August 2026 by opening all 30 self-hosted .woff2 files under our fonts directory, pooling their character maps, and testing that set against the distinct code points of UDHR Article 1 in each language. English is the control: it must come out at 100%, and the script refuses to report anything if it does not.

What this proves is what our own fonts contain. It is not a measurement of what any given reader sees, which depends on their device's own font set and is not observable from here. A language listed at 0% is not necessarily invisible to anyone — it means we are contributing nothing to rendering it, and have handed the outcome to the reader's hardware.

Article 1 is one paragraph. Since a longer text would only use more characters, it could only lower the coverage figures and never raise them, so 0% for Burmese and Khmer is not an artefact of a short sample.