Skip to content

How to Capitalize Multilingual Strings Correctly

Learn how Python's .upper(), .title(), and .casefold() handle multilingual text, including German 'ß', Turkish dotted 'i', and apostrophe edge cases, with code examples and best practices.

When working with English text, Python's built-in string methods like .upper() or .title() work seamlessly. However, when dealing with multilingual text (Internationalization/i18n), these methods can produce incorrect results due to linguistic nuances, such as the German "sharp S" (ß) or the Turkish "dotted i".

This guide explores how to handle string capitalization and comparison correctly across different languages using Python's Unicode capabilities.

Standard Capitalization and Its Limits

Python provides .capitalize(), .title(), and .upper(). While they handle basic Unicode mapping, they apply generic rules that may not fit specific grammatical requirements.

Basic Usage

text = "élan"

print(f"Upper:      {text.upper()}")
print(f"Capitalize: {text.capitalize()}")

Output:

Upper:      ÉLAN
Capitalize: Élan

The Limit: The German 'ß'

In German, the "ß" (Eszett) typically capitalizes to "SS". Standard Python handles this correctly in .upper(), but you must be aware that the string length might change.

german_word = "Straße" # Street

upper_version = german_word.upper()

print(f"Original: {german_word} (Len: {len(german_word)})")
print(f"Upper:    {upper_version} (Len: {len(upper_version)})")

Output:

Original: Straße (Len: 6)
Upper:    STRASSE (Len: 7)

Handling Case-Insensitive Comparison (casefold)

When checking if two strings are equal regardless of case, developers often use .lower(). However, .lower() is not sufficient for all languages. The correct method for aggressive, language-agnostic normalization is .casefold().

str1 = "Fluß"   # German for River (old spelling)
str2 = "fluss"

is_equal_lower = str1.lower() == str2.lower()
print(f"Lower match:    {is_equal_lower}")

is_equal_casefold = str1.casefold() == str2.casefold()
print(f"Casefold match: {is_equal_casefold}")

Output:

Lower match:    False
Casefold match: True

The "Turkish I" Problem

This is the most famous edge case in internationalization.

  • English: i becomes I.
  • Turkish: i becomes İ (dotted I), and ı (dotless i) becomes I.

Python's standard .upper() uses the generic Unicode mapping (English-centric for 'i'), which is incorrect for Turkish.

turkish_word = "istanbul"

print(f"Standard Upper: {turkish_word.upper()}")


tr_map = {ord('i'): 'İ', ord('ı'): 'I'}
print(f"Turkish Upper:  {turkish_word.translate(tr_map).upper()}")

Output:

Standard Upper: ISTANBUL
Turkish Upper:  İSTANBUL

Correcting Title Case (Apostrophe Handling)

The built-in .title() method is simplistic: it capitalizes the first letter after any non-letter character. This breaks words with apostrophes, like "O'Connor" or "It's".

name = "o'connor"

text = "it's amazing"
print(f"Built-in Title: {text.title()}")

import re

def smart_title(s):
    return re.sub(r"[A-Za-z]+('[A-Za-z]+)?", 
                  lambda mo: mo.group(0)[0].upper() + mo.group(0)[1:].lower(), 
                  s)

print(f"Smart Title:    {smart_title(text)}")
print(f"Name Check:     {smart_title(name)}")

Output:

Built-in Title: It'S Amazing
Smart Title:    It's Amazing
Name Check:     O'connor

Conclusion

To handle multilingual strings correctly in Python:

  1. Use casefold() for case-insensitive comparisons, not lower().
  2. Be aware of Length Changes: upper() can expand string length (e.g., ß -> SS).
  3. Handle Locale Specifics: Standard methods fail on Turkish 'i'. Use mapping tables or the PyICU library for locale-aware transformations.
  4. Avoid .title() for text containing apostrophes; use Regex or custom functions instead.
Navigation