slopware 101 · step 30 of 35
Why can’t the app find Işıl?
by Damla Su Bilge · 2 min read
We began by asking whether the screen helped someone do a particular job. We have since followed that job through storage, permissions, failures and costs. Return to the screen now with a person whose language, device or way of navigating differs from yours. A system can handle the request correctly while still making their task unnecessarily difficult.
Turkish has four characters sharing the job English gives to one letter i: dotless ı with its capital I, and dotted i with its capital İ. Write "istanbul".toUpperCase() in JavaScript and you get ISTANBUL. That is not a bug in the library. toUpperCase() follows the default Unicode case mapping, which is the same for every language, and in that default the capital of i is I. Turkish is one of the languages whose rules differ from the default, so the code has to say which language it is working in:
"istanbul".toUpperCase(); // "ISTANBUL"
"istanbul".toLocaleUpperCase("tr"); // "İSTANBUL"
"Işıl".toLowerCase(); // "işıl"
"Işıl".toLocaleLowerCase("tr"); // "ışıl"
It is easy to miss the language decision when the operation looks like arithmetic on characters.
Setting the encoding to UTF-8 does not settle this either. Storing a letter correctly and knowing what it becomes are separate questions, and only the first one has a checkbox.
Now follow the last two lines into a real feature. Your signup form lowercases usernames with the default rule, so Işıl is saved as işıl. Months later someone adds a search box that lowercases with the Turkish rule and looks up ışıl. The row is not found. Nothing threw. Each piece of code did exactly what it was told, and the two pieces were told different things.
The database can join in. Depending on which database you use and how it was configured, sorting and case-insensitive matching may follow a collation setting, and that setting can differ between your laptop and the server. The same query can then answer differently in two places, and you only ever look at one of them.
The fix is a decision, written down once:
- Keep what the user typed, unchanged, for display.
- If you need a normalized form for lookups, produce it in one function, with an explicit rule, and use that function everywhere.
- Check which collation your database actually uses in development and in production instead of assuming they match.
- Put
lang="tr"on the<html>element of Turkish pages, so CSS such astext-transform: uppercasefollows Turkish rules too. - Add the names Işıl and İsmail, and words such as ılık, to your test data. They are ordinary for your users, so make them ordinary for your tests.
Try those names through the whole path: create the account, save it, search for it and display the result. A name that survives one conversion has not yet travelled through the product. Once the words arrive correctly, the person still needs a way to reach and use the controls.
Further reading: MDN: locale-aware uppercase.


