Unicode en tekencodering: waarom je app in andere talen kapotgaat
Unicode en tekencodering: hoe 'José' verandert in 'José', waarom dat gebeurt en wat UTF-8 overal in productie echt vereist.

Unicode is de standaard die aan elk teken van elk schriftsysteem, van "A" tot "あ" tot "🚀", een uniek nummer geeft. Tekencodering is de aparte stap die die nummers omzet in bytes die een computer kan opslaan en versturen. De meeste software die "kapotgaat in andere talen" faalt niet bij de vertaling. Ze faalt in die eerdere, onzichtbare laag, en de fout heeft een naam: mojibake, de verminkte tekst die je krijgt wanneer de bytes met de verkeerde codering worden teruggelezen.
We beloofden een close-up van die fout in meertalige softwareontwikkeling en in internationalisatie (i18n), uitgelegd, waar een klant met de naam José verandert in "José" op een etiket of een factuur. Hier ontleden we precies wat er gebeurt, waar coderingsbugs een bedrijf echt geld kosten, en wat "UTF-8 overal" in een echt systeem vereist.
Unicode en UTF-8 zijn niet hetzelfde
De twee termen worden door elkaar gebruikt, en die gewoonte ligt aan de basis van de meeste verwarring.
Unicode is een kaart. Het kent aan elk teken een code point toe: een nummer, geschreven als U+XXXX. De letter "A" is U+0041. Het Chinese teken "中" is U+4E2D. De kaart alleen zegt niet hoe je dat nummer als bytes op een schijf opslaat of over een netwerk verstuurt.
UTF-8 is één manier om die kaart in bytes te coderen. Het is de dominante manier: UTF-8 draagt vandaag 99% van alle websites waarvan de codering bekend is (W3Techs, 2026). Het stelt elk code point voor met 1 tot 4 bytes: gewone letters en cijfers zonder accent passen in 1 byte (identiek aan het oude ASCII, en daarom is UTF-8 achterwaarts compatibel ermee), Latijnse letters met accent nemen doorgaans 2 bytes, de meeste Chinese, Japanse en Koreaanse tekens nemen er 3, en emoji nemen er 4.
Oudere coderingen laten dezelfde bytewaarden overeenkomen met totaal andere tekens. Windows-1252 en Latin-1 (ISO-8859-1), beide gangbaar op oudere Windows-systemen en legacy-databases, gebruiken één byte per teken en dekken alleen West-Europese schriften. Geef UTF-8-bytes aan software die Windows-1252 verwacht, of andersom, en je krijgt uitvoer die geldig lijkt maar stilletjes fout is. Dat verschil is mojibake, en het is volledig mechanisch: er is niets "beschadigd" in de zin van verloren data, dezelfde bytes worden gewoon met het verkeerde regelboek teruggelezen.
Mojibake, uitgelegd: hoe "José" "José" wordt
Loop de echte bytes na en de fout ziet er niet langer mysterieus uit.
In UTF-8 codeert het teken met accent "é" als twee bytes: 0xC3 0xA9. "José" in UTF-8 is dus de bytereeks voor J, o, s, dan 0xC3 0xA9, dan het slotteken.
Stel nu dat die bytereeks bij een systeem terechtkomt dat ze als Windows-1252 leest in plaats van UTF-8, een verschil dat voortdurend optreedt wanneer data tussen een moderne app en een oudere database, exporttool of partnersysteem verhuist. Windows-1252 weet niet dat die twee bytes samen één teken vormen. Het leest ze als twee losse tekens: 0xC3 verschijnt als "Ã" en 0xA9 als "©". De ene letter "é" wordt de tekenreeks van twee tekens "é", en "José" wordt "José" op het scherm, het etiket of de factuur.
Hetzelfde mechanisme levert elk ander mojibake-patroon op dat je vast al gezien hebt: typografische aanhalingstekens die veranderen in reeksen als “ en â€\x9d, of een kastlijntje dat â€" wordt. Elk geval bestaat uit de bytes van de ene codering, gelezen door het regelboek van een andere. Zodra je de twee botsende coderingen kunt benoemen, is de oplossing meestal een configuratiewijziging, geen herschrijving.
Waar coderingsbugs een bedrijf echt raken
Mojibake op het scherm is het zichtbare symptoom. De duurdere fouten spelen zich eronder af.
- Verminkte namen op klantgerichte documenten. Verzendetiketten, facturen en contracten waar de naam van een klant als "José" verschijnt in plaats van "José" ogen op zijn best slordig, en belanden op het slechtste geval op het verkeerde adres, een reëel risico bij douanedocumenten en transporteursdata in de logistiek, waar een naam of adres exact moet overeenkomen met een officiële aangifte.
- Kapotte zoekacties en opzoekingen. Als een naam met de ene codering wordt opgeslagen en met een andere wordt gezocht, komen de bytereeksen niet overeen, en een klantfiche die echt bestaat geeft nul resultaten. Supportteams leren dit handmatig te omzeilen lang voordat iemand het onderliggende coderingsverschil diagnosticeert.
- Stille afkapping van data. Een databasekolom die in bytes is gedimensioneerd in plaats van in tekens kan een multibyteteken middenin een insert doormidden knippen, waardoor het laatste teken van een naam corrupt raakt of een gebroken bytereeks achterblijft die later, vaak in een heel ander deel van het systeem, de validatie niet haalt.
- Emoji en CJK-tekens die volledig verdwijnen. De historisch
utf8genoemde tekenset van MySQL sloeg altijd hooguit 3 bytes per teken op, genoeg voor de meeste Latijnse, Cyrillische en gangbare CJK-tekens, maar niet genoeg voor emoji of sommige CJK-uitbreidingstekens, die er 4 nodig hebben. Tekst met zulke tekens werd niet verminkt; ze werd geweigerd of stil afgekapt, zolang niet de echte volledige UTF-8-optie van MySQL,utf8mb4, werd gebruikt. - Legacy-exports die moderne systemen vergiftigen. Een CSV- of EDI-feed uit een oudere ERP of een partnersysteem, die nog standaard Windows-1252 uitstuurt, importeert op het eerste gezicht netjes en corrumpeert elke naam met accent zodra iemand het bestand in een UTF-8-native tool opent.
De valstrik voor Nederlands- en Vlaamstalige teams is geniepiger dan hij lijkt. Nederlands zelf komt er meestal ongeschonden door: doorlopende Nederlandse tekst is grotendeels ongeaccentueerd, en Latin-1 en Windows-1252 dekken de trema's op ë, ï en ü prima, zodat een Nederlands of Vlaams systeem jaren op een legacy-codering draait zonder dat iemand iets merkt. De rekening arriveert bij de eerste klant met een trema in haar naam zoals Zoë, bij de eerste klant Öztürk of Wojciech, bij de eerste leverancier die een feed in het Cyrillisch stuurt, of bij het eerste emoji in een opmerkingenveld. Precies daarom worden coderingsbugs zo vaak in productie ontdekt in plaats van in de testfase.
De legacy-coderingen die nog steeds problemen geven
Drie coderingen verklaren bijna elk verschil dat een zakelijk systeem nog tegenkomt: puur ASCII (alleen Engelse letters, cijfers en basisleestekens, geen accenten of niet-Latijnse schriften), Latin-1 / ISO-8859-1 (één byte per teken, alleen West-Europese accenten), en Windows-1252 (de bijna-superset van Latin-1 van Microsoft, de standaard op oudere Windows-software en een frequente bron van het mojibake-patroon met typografische aanhalingstekens hierboven).
Latin-1 verdient een aparte kanttekening in een euromarkt, want het bevat het euroteken "€" niet. Voor een Nederlandse of Vlaamse offerte, factuur of catalogus is dat geen randgeval maar het teken dat je het vaakst nodig hebt. Precies daarvoor bestaat Latin-9 (ISO-8859-15), de herziening die het euroteken toevoegde. Windows-1252 bevat "€" wel. Een legacy-systeem dat op de plek van "€ 1.500" terugvalt op "EUR 1.500" of een leeg vakje toont, maakt geen typfout: het verklapt zijn codering.
Ze blijven hangen op precieze, voorspelbare plekken: databases die jaren geleden zijn aangemaakt met een standaardtekenset die niemand ooit herzag, CSV-exports uit oudere boekhoud- of ERP-systemen, EDI-berichten van logistieke en vrachtpartners die op decennia-oude formaten draaien, en PDF-generatiebibliotheken die een smalle tekenset veronderstellen tenzij je ze anders instrueert. Elk van die plekken is een grens waar data van het ene systeem naar het andere oversteekt, en grenzen zijn precies waar coderingsaannames onuitgesproken en tegenstrijdig blijven.
Wat "UTF-8 overal" echt vereist
"Gebruik gewoon UTF-8" is een juist advies dat het werk onderschat. Volledige UTF-8-ondersteuning betekent dat je het expliciet instelt op elke laag, en niet aanneemt dat het zich vanzelf voortplant.
- Tekenset en collatie van de database. UTF-8 gedeclareerd op de database, elke tabel en elke kolom, niet alleen op de verbinding. Op MySQL in het bijzonder betekent dat
utf8mb4, niet de legacy-aliasutf8. - HTTP- en bestandsheaders. Elke API-respons en elke bestandsexport meldt zijn codering expliciet (
Content-Type: text/html; charset=utf-8), in plaats van clients te laten gokken. - CSV-exports met een BOM wanneer Excel de bestemming is. Excel leest een kale UTF-8-CSV vaak als Windows-1252 als er geen byte order mark (BOM) aanwezig is, het meest voorkomende supportticket van "waarom ziet mijn export er kapot uit in Excel".
- Glyphdekking van het lettertype. Een correct gecodeerd Cyrillisch of CJK-teken verschijnt alsnog als een leeg vierkantje ("tofu") als het gekozen lettertype er geen glyph voor heeft. Dat is een designprobleem, geen coderingsprobleem, maar voor de gebruiker ziet het er identiek uit.
- Validatie op elke grens. Uploads, API-invoer en partnerdatafeeds krijgen hun codering gecontroleerd en genormaliseerd naar UTF-8 aan de ingang, zodat verminkte bytes nooit in de opslag terechtkomen.
Zet die vijf één keer goed, op het niveau van de architectuur, en tekencodering houdt op iemand bezig te houden. Mis er één, en het duikt weer op als supportticket telkens wanneer een naam met accent, een emoji of een niet-Latijns schrift die ene grens bereikt.
Veelgestelde vragen over Unicode en tekencodering
Wat is Unicode?
Unicode is een standaard die een uniek nummer, een code point genoemd, toekent aan elk teken van elk groot schriftsysteem. Het is een kaart van tekens naar nummers. Het definieert op zichzelf niet hoe die nummers als bytes worden opgeslagen.
Wat is het verschil tussen Unicode en UTF-8?
Unicode is de tekenkaart; UTF-8 is een codering die Unicode-code points omzet in bytes, met 1 tot 4 bytes per teken. UTF-8 is de dominante codering op het web vandaag en draagt 99% van de sites waarvan de codering bekend is (W3Techs, 2026).
Wat is mojibake?
Mojibake is de verminkte tekst die ontstaat wanneer bytes die in de ene tekenset zijn gecodeerd, met een andere worden teruggelezen. Het is mechanisch, niet willekeurig: hetzelfde coderingsverschil levert altijd hetzelfde verminkte patroon op, zoals "José" dat "José" wordt.
Waarom toont mijn software rare tekens zoals "é" in plaats van letters met accent?
Een onderdeel in het systeem, vaak een database, exporttool of oudere partnerfeed, leest UTF-8-bytes als Windows-1252 of Latin-1. De oplossing is achterhalen waar het verschil optreedt en dat onderdeel expliciet op UTF-8 zetten, niet de data opnieuw invoeren.
Hoe los ik coderingsproblemen in bestaande software op?
Audit elke grens: tekenset en collatie van de database, bestandsexports, API-headers en partnerdatafeeds, en zet elke grens expliciet op UTF-8 (utf8mb4 als de database MySQL is). We voeren die audit uit bij het scopen van elke meertalige build; vraag een offerte met vaste prijs aan en hij zit erbij.
Krijg software die elk teken correct verwerkt
Vertel ons wat je software moet ondersteunen, inclusief de namen, schriften en partnerdatafeeds die ze correct moet verwerken. We antwoorden met een vaste scope, een vaste prijs en een leverdatum.
Verder lezen
Software nodig die vanaf dag één elke markt spreekt?
Vertel ons wat je wilt laten bouwen. Je krijgt een vaste scope, een vaste prijs en een opleverdatum in weken.


