The Complete Guide to Text Case Conversion

Case conversion looks trivial until you hit Unicode edge cases, locale-dependent rules, and the dozen competing conventions across programming ecosystems. This guide covers what matters.


Case conventions in programming

Every naming convention exists for a reason — usually readability within a specific context:

Mixing conventions within a codebase isn't just ugly — linters will flag it, and it makes grep-based refactoring unreliable.


Unicode and case mapping

"hello".toUpperCase() works fine. But case conversion is not a simple ASCII lookup table.

German ß: Uppercasing ß produces SS — a one-to-many mapping. "straße".toUpperCase() returns "STRASSE". There's no round-trip: lowercasing "STRASSE" gives "strasse", not "straße".

Turkish İ/i: Turkish has two distinct letters — dotted İ/i and dotless I/ı. Standard toUpperCase() converts iI, which is wrong in Turkish (it should be İ). Use locale-aware methods:

"title".toLocaleUpperCase("tr"); // "TİTLE"
"TITLE".toLocaleLowerCase("tr"); // "tıtle"

Greek final sigma: Lowercase sigma has two forms — σ (medial) and ς (final, appears at the end of a word). Proper lowercasing of "ΟΔΟΣ" produces "οδός" with the final sigma.

These aren't obscure edge cases — they affect real users in production.


Case conversion in code

JavaScript/TypeScript:

str.toUpperCase();          // locale-independent (usually fine)
str.toLocaleUpperCase("tr"); // locale-aware

Libraries for programming cases:

// lodash
_.camelCase("user name");   // "userName"
_.snakeCase("userName");    // "user_name"
_.kebabCase("userName");    // "user-name"
_.startCase("userName");    // "User Name"

CSS (display-only, doesn't change the DOM):

text-transform: uppercase;
text-transform: capitalize; /* Title Case */
text-transform: lowercase;

Regex-based conversion for custom logic:

// snake_case → camelCase
str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());

// camelCase → snake_case
str.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);

Naming conventions by ecosystem

Language/Tool Variables & Functions Types & Classes Constants Files
JavaScript/TS camelCase PascalCase SCREAMING_SNAKE camelCase or kebab-case
Python snake_case PascalCase SCREAMING_SNAKE snake_case
Go camelCase (unexported), PascalCase (exported) PascalCase PascalCase or SCREAMING_SNAKE snake_case
Rust snake_case PascalCase SCREAMING_SNAKE snake_case
CSS/SCSS kebab-case kebab-case
SQL snake_case (columns) UPPERCASE keywords
Ruby snake_case PascalCase SCREAMING_SNAKE snake_case

Go is unusual — capitalization controls visibility. UserName is exported (public); userName is unexported (private). Getting this wrong means compilation errors, not just style violations.


SEO and readability

Case choices affect search and scannability:


Troubleshooting

Turkish locale produces unexpected results — Turkish has special casing rules for i/İ and ı/I. If your app runs with a Turkish locale, "TITLE".toLowerCase() may produce "tıtle". Use toLocaleLowerCase("en") when you need English-standard behavior regardless of runtime locale.

Acronyms become mangled in Title Case ("API" → "Api") — Most naive Title Case implementations lowercase everything first, then capitalize the first letter of each word. To preserve acronyms, maintain an allow-list of terms that should stay uppercase (API, URL, HTML, CSS, ID) and skip them during conversion.

Unicode characters don't convert — Not all Unicode code points have case mappings. Symbols, numbers, and many scripts (CJK, Arabic) have no uppercase/lowercase distinction. If toUpperCase() returns the input unchanged, the character likely has no case variant.

Converting between programming cases loses word boundaries — Going from kebab-case to camelCase is reliable because hyphens mark boundaries. Going from camelCase to snake_case requires splitting on uppercase letters, which breaks on acronyms: parseHTML splits as parse_h_t_m_l instead of parse_html. Use a library like lodash or change-case that handles these edge cases with dictionaries.