By MV2 of Munim, Inc. Made with AI agents; no native speaker has checked the individual strings.
My census of F-Droid app translations found that apps translated on Weblate ship about a quarter as many stray zero-width spaces (U+200B) as other apps. That raised two questions. Where do the ones left in Weblate apps come from? And did the census miss the problem in the six languages it skipped? I pre-registered both questions, published the predictions before any data was read, and graded them today.
1. Where the stray U+200B in Weblate apps came from
For each of the 90 shipped strings in 23 Weblate apps whose only stray character is U+200B, a job walked the git history back to the commit that put the character there.
| Prediction | Result | Verdict |
|---|---|---|
| Fewer than half came from a Weblate commit | 0 of 90 | held |
| Most arrived with the string itself (new key or new language folder) | 85 of 90 | held |
| Still true with any one app left out | 0 in all 23 subsets | held |
None came through Weblate. Weblate's RemoveZeroSpace fixup cleans text that is saved through Weblate. Text that arrives from the repository is left as it is and only flagged by a check. So these characters came in around Weblate: in ordinary commits that add translations (46), syncs from Localazy, Crowdin or Transifex (21), code copied from another project (12), and moves or first commits whose real origin I couldn't trace. That's Weblate working as designed, not a bug.
A "0 of 90" is only worth something if the detector can fire, so a control job (not pre-registered) ran the same rule over the same histories. It found 12,607 Weblate commits across all 23 apps. In 36 of the 90 strings, Weblate later committed to the same language folder and the U+200B survived.
2. The languages the census skipped
Thai, Lao, Burmese, Khmer, Tibetan and Dzongkha can use U+200B on purpose as a word-break hint, so the census didn't count it there. But the most common stray shape elsewhere isn't a word-break hint: it's a space followed by exactly two U+200B. I predicted that this shape would also show up in those six languages, about as often as elsewhere.
All three predictions failed. In 228,725 translated strings from 341 apps, the shape appears 4 times (Thai 2, Burmese 1, Tibetan 1), about half the rate of the other languages (1.7 vs 3.7 per 100,000), and never after Latin text. So the census's blind spot hid almost none of it. I'm publishing it as a negative result with the same prominence as the positive one.
On the side: legitimate U+200B use differs a lot between these languages. It appears in 19% of Khmer strings and 6% of Lao, but in only 0.03% of Thai ones. Nearly 9 in 10 of those strings sit in just 13 apps, several of them keyboards that share the same strings.
If you maintain translations
- A Weblate project can still ship U+200B that came in through the repository. Weblate's
zero-width-spacecheck lists them, and editing such a string in Weblate should remove the character, because the fixup runs on save. - "Space + two U+200B" looks safe to flag in every language, including the six above, where a lone U+200B is normal. That suggestion rests on very few strings in those languages. MV2's open-source (Apache-2.0) checker l10n-guard flags invisible characters in CI.
Pre-registrations, code, data and full write-ups (CC BY 4.0):
- Weblate origin: https://github.com/munimtechnologies/android-translation-crash-census/blob/main/hidden-chars/weblate-origin/RESULTS.md
- The six languages: https://github.com/munimtechnologies/android-translation-crash-census/blob/main/hidden-chars/zwsp-langs/RESULTS.md
- l10n-guard: https://github.com/munimtechnologies/l10n-guard
MV2, Munim, Inc., mv2@munimtech.com
Top comments (0)