Case Conversion in JavaScript, Python and SQL
Every language has toUpper and toLower. The details, such as locales, title case and Unicode, are where bugs come from.
By the Universal Case Converter Team
JavaScript
JavaScript has the string methods toUpperCase() and toLowerCase(), which follow the Unicode rules and return a new string. There are also toLocaleUpperCase() and toLocaleLowerCase(), which accept a language tag: 'i'.toLocaleUpperCase('tr') returns İ in Turkish, while 'i'.toUpperCase() returns I. There is no built-in method for title case or for capitalizing only the first letter.
A common pattern is s.charAt(0).toUpperCase() + s.slice(1) to capitalize the first letter of a string. For each word, a regular expression with a replacement function works: s.replace(/\b\p{L}/gu, c => c.toUpperCase()). The \p{L} syntax needs the u flag and matches letters from any script. In CSS, the property text-transform: capitalize changes the look of the first letter of each word without changing the text.
Python
Python's string methods are upper(), lower(), capitalize(), title(), swapcase() and casefold(). capitalize() converts the first character to a capital and lowercases the rest, so "hello WORLD".capitalize() gives "Hello world". title() capitalizes the first letter of each word, but the Python documentation itself warns about apostrophes: "they're friends".title() returns "They'Re Friends".
The function string.capwords() splits on whitespace and capitalizes each word, which avoids the apostrophe problem. For title case with small words, Python has no built-in function, and libraries or custom code are needed. casefold() is meant for caseless comparison: it is more aggressive than lower() and turns the German ß into ss.
SQL
The SQL standard has UPPER() and LOWER(), and every major database supports them. PostgreSQL and Oracle also have INITCAP(), which capitalizes the first letter of each word. MySQL and SQL Server have no such function, so title case needs a custom function or must be done in the application.
Case-insensitive search is more a question of collation than of functions. PostgreSQL compares text case-sensitively by default, and you can use ILIKE or LOWER(column) = LOWER(value). MySQL and SQL Server usually default to case-insensitive collations. Be careful with LOWER(column) in a WHERE clause: it can prevent the database from using an index, unless you create a functional index on the lowercase value.
Java, C# and PHP
Java has toUpperCase() and toLowerCase() on strings, and both accept a Locale. Without a locale, they use the default of the computer, which caused the well-known Turkish bug: on a Turkish system, "TITLE".toLowerCase() produced a dotless ı and broke comparisons. Use Locale.ROOT for identifiers and keywords. C# offers ToUpperInvariant() and ToLowerInvariant() for the same reason.
PHP has strtoupper(), strtolower(), ucfirst(), ucwords() and, for multibyte text, mb_strtoupper(), mb_strtolower() and mb_convert_case(), which supports a title case mode. Plain strtoupper() works on single-byte characters and does not handle accents in UTF-8, so use the mb_ functions for text in other languages.
The locale trap
The Turkish i is the best-known case: in Turkish, the uppercase of i is İ and the lowercase of I is ı. If a program converts a keyword such as title or file with the user's locale, and the user has a Turkish system, the result is not what the program expects. The bug has appeared in real products, in cases from file extensions to protocol keywords.
The rule is simple. Use a fixed, language-neutral conversion for anything that a program reads, such as identifiers, keys, file extensions and protocol names. Use the user's locale only for text that people read, such as a name in a welcome message. In JavaScript, toUpperCase() is language-neutral, and toLocaleUpperCase() with a tag is language-sensitive.
Length can change
Case conversion can change the number of characters. The German ß becomes SS in uppercase, the ligature fi becomes FI, and the Latin letter ʼn becomes ʼN. So a string of 6 characters can have 7 characters after toUpperCase(). Code that cuts a string by position after conversion, or that expects the length to stay the same, can fail.
If you need to preserve the positions of characters, work on the original string and map indexes, or use case folding for comparison and keep the original for display. The article on uppercase and lowercase in Unicode explains the details.
Using the converter to check your code
The converter on this page uses the same browser functions as the JavaScript methods above, so it is a convenient way to check what toUpperCase() or toLocaleLowerCase() does to a string, including the special cases of ß and the Greek final sigma. For naming styles, paste a list of names and compare the result with your own function for snake_case or camelCase.
If your own function and the converter disagree, look at the edge cases: acronyms such as XMLHttpRequest, digits, accents and consecutive capitals. These are the cases where implementations differ, and the reason is usually a different rule for splitting words.
Testing your conversions
Write tests for case code with the awkward inputs: an empty string, a single letter, text with digits, accented letters, ß, the Turkish i, and a string that is already in the target case. A function that is idempotent, meaning that applying it twice gives the same result as applying it once, is much easier to trust. For title case and naming styles, keep a small list of expected input and output pairs and run it whenever you change the code, because small changes to word splitting often break a case that worked before.
Check what upper, lower and title case do to your text with the free case converter.
Open the case converter