String.prototype.toLowerCase()
The workhorse of case-insensitive comparison. It uses locale-independent Unicode rules, which is usually what you want — and is exactly wrong for Turkish.
Demo
Accented capitals map to their accented lower-case forms, and anything without a case — digits, punctuation, spaces — passes through untouched. The fourth case looks unremarkable and is the one to think about: this method uses the LOCALE-INDEPENDENT Unicode mapping, so a capital I always becomes a dotted i. In Turkish that is the wrong letter, and toLocaleLowerCase('tr') produces the dotless ı instead.
Common patterns
if (a.toLowerCase() === b.toLowerCase()) { }
const key = raw.trim().toLowerCase();
const shown = label.toLocaleLowerCase(navigator.language);
Examples
Pitfalls
let s = 'ABC'; s.toLowerCase(); s
let s = 'ABC'; s = s.toLowerCase(); s
'Élan'.toLowerCase() === 'elan'
'Élan'.toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '') === 'elan'
'ID'.toLocaleLowerCase('tr') === 'id'
'ID'.toLowerCase() === 'id'
'\u00df'.toUpperCase().toLowerCase()
const display = original;
When to use
- Case-insensitive comparison and lookup keys
- Normalising identifiers, protocols, header names and file extensions
- Search filtering, alongside trim
- Text the user will read → toLocaleLowerCase with their locale
- Accent-insensitive matching → normalize and strip diacritics as well
- Sorting → localeCompare, which handles case and accents properly
- You want upper case → toUpperCase
Notes
FAQ
toLowerCase for anything a machine compares — keys, identifiers, protocols, extensions — because it is locale-independent and therefore stable. toLocaleLowerCase for text a person reads, where the user locale should decide. Using the locale version for comparisons is how the Turkish-i bug gets in.
key.toLowerCase(); // comparison label.toLocaleLowerCase(userLocale); // display