rubyguides

String#downcase

str.downcase(*options) -> string

String#downcase returns a new string with every uppercase character converted to lowercase. The original string stays untouched. This method is useful for normalizing user input, case-insensitive comparisons, and converting text to a consistent format.

Syntax

string.downcase
string.downcase(*options)

The optional options parameter changes how Unicode characters are handled.

Parameters

ParameterDescription
*optionsOptional. One or more symbols (:ascii, :turkic, :lithuanian, :fold) controlling case mapping.

Return Value

Returns a new string. The original string is never modified. If no characters needed changing, returns self (no new object allocated).

Options

Ruby’s downcase supports several options beyond the simple A-Z mapping:

OptionEffect
:asciiOnly downcase ASCII characters A-Z. Non-ASCII Unicode letters stay unchanged. Alias: :ascii_only.
:turkicApply Turkic language case rules. In Turkish, uppercase I (U+0049) becomes ı (U+0131), not i.
:lithuanianApply Lithuanian-specific rules for dotted and dotless i.
:foldUse Unicode case folding instead of case mapping.

You can combine :ascii or :fold with :turkic or :lithuanian. Passing incompatible combinations raises ArgumentError.

Examples

Basic usage

"Hello World".downcase
# => "hello world"

"RUBY".downcase
# => "ruby"

"PyThOn".downcase
# => "python"

Each call returns a new string, leaving the original untouched. This non-destructive behavior makes downcase safe to use in pipelines and comparisons without worrying about side effects.

Unicode characters (default behavior)

Ruby applies full Unicode case mapping by default, so accented characters convert correctly.

"ÜBER".downcase
# => "über"

"ÅNGSTRÖM".downcase
# => "ångström"

"Straße".downcase
# => "straße"

Ruby applies full Unicode case mapping by default, so accented and special characters like German eszett and Scandinavian letters convert correctly without any extra options.

Digits, punctuation, and whitespace are left completely unchanged by the transformation. Only alphabetic characters with uppercase equivalents are affected.

"NUMBERS 123".downcase
# => "numbers 123"

"symbols: !@#".downcase
# => "symbols: !@#"

Non-letter characters pass through unchanged by the downcase operation. The method only transforms uppercase letters to their lowercase equivalents, so numbers, spaces, and punctuation marks are always safe from transformation.

The :ascii option

Sometimes you want to leave non-ASCII characters alone. Pass :ascii to restrict downcasing to ASCII letters only.

"ÜBER".downcase
# => "über"

"ÜBER".downcase(:ascii)
# => "ÜBER"

The :ascii option restricts downcasing to ASCII letters A-Z only. Non-ASCII characters like accented letters stay as they are, which is helpful when working with mixed encodings where you want to preserve accented characters.

This is useful when dealing with mixed encodings or when you need to preserve accented characters as-is.

The :turkic option (Turkish locale)

Turkish has two unusual case mappings that trip up the standard approach:

  • Latin uppercase I (U+0049) → Latin lowercase ı (U+0131) — not i
  • Latin uppercase İ (U+0130) → Latin lowercase i with dot above — not i
"ID".downcase
# => "id"

"ID".downcase(:turkic)
# => "ıd"

Turkish has an unusual case mapping where uppercase I becomes a dotless lowercase ı instead of i. The default downcase produces the wrong result for Turkish text, so the :turkic option is essential when the locale is known.

If you’re processing Turkish text with the default behavior, "ID".downcase gives you "id" which looks right but is technically wrong. Use :turkic when you know the locale.

"İSTANBUL".downcase
# => "istanbul"  (standard)

"İSTANBUL".downcase(:turkic)
# => "ıstanbul"  (Turkish correct)

The Turkish İ (dotted capital I) becomes a regular i with the standard mapping, but becomes a dotless ı with the :turkic option. This is a very common gotcha in internationalized applications that process user-generated text across multiple languages.

Combining options

"STRİNG".downcase(:turkic, :ascii)
# => "strıng"

This applies Turkish rules for the I character, but leaves other non-ASCII characters untouched.

Unicode case folding

:fold uses Unicode case folding instead of case mapping. This is primarily useful for case-insensitive matching where you need to handle characters that have multiple possible forms.

"ß".downcase
# => "ss"

"ß".downcase(:fold)
# => "ß"

Case folding is primarily intended for case-insensitive matching rather than for display purposes. Unlike regular downcasing, folding treats certain characters differently to maximize the chance that two strings compare equal after folding.

Note that :fold doesn’t normalize Unicode. A character like Å that has multiple Unicode representations may not downcase as expected if not in NFC form.

Common Patterns

Normalizing user input

def normalize_name(name)
  name.downcase.strip
end

normalize_name("  ALICE ")
# => "alice"

normalize_name("Bob")
# => "bob"

Normalizing user input by downcasing and stripping whitespace is a very common first step in form processing. This pattern collapses case differences so that “ALICE”, “Alice”, and “alice” all compare as equal.

Case-insensitive comparison

Downcasing both strings before comparison removes case as a factor.

query = "RUBY"
targets = ["ruby", "Rails", "Python"]

targets.any? { |t| t.downcase == query.downcase }
# => true

Downcasing both sides of a comparison is the simplest way to do case-insensitive matching for equality checks on string values in Ruby. For frequent lookups, consider downcasing once and storing the normalized value rather than downcasing on every individual comparison.

Safe email storage and lookup

email = "User@Example.COM"
stored_email = email.downcase

# Later, compare incoming login:
incoming = "user@example.com"
incoming.downcase == stored_email
# => true

The local part of email addresses is technically case-sensitive per the RFC specification, but in practice most email providers treat it as case-insensitive for end-user convenience and support. Downcasing before storage avoids duplicate accounts that differ only in capitalization.

Chaining with other methods

"POST".downcase.reverse
# => "stop"

Chaining downcase with other string methods like reverse or strip is safe and predictable because downcase always returns a new string object as its result. This lets you combine multiple different transformations in a single pipeline without intermediate nil checks.

Safe navigation

name = nil
name&.downcase
# => nil

name = "ALICE"
name&.downcase
# => "alice"

The safe navigation operator prevents NoMethodError when the receiver happens to be nil. If name is nil, the entire expression short-circuits to nil instead of raising an exception.

String#downcase vs String#downcase!

Ruby provides two ways to downcase a string.

MethodModifies original?Return value
downcaseNoNew string (or self if no changes)
downcase!Yesself or nil

The bang version downcase! changes the string in place and returns self if changes were made, or nil if the string already contained no uppercase characters.

s = "Hello"
s.downcase!
# => "hello"
s
# => "hello"

t = "hello"
t.downcase!
# => nil
t
# => "hello"

Use downcase when you need to preserve the original string for later use in the same method or class. Use downcase! when you want to modify in place and only care about the transformed result.

Edge Cases

Empty strings

Empty strings return themselves unchanged.

"".downcase
# => ""

No uppercase characters

Strings with no uppercase letters return a new string equal to the original. The method still allocates a new string object in most cases, even when the actual content has not changed at all from the original input value.

"abc".downcase
# => "abc"

"abc".downcase.equal?("abc")
# => false  # a different object

The return value is always a new string object when changes occurred, but may be self when no changes were needed.

The Turkish locale trap

The most common gotcha with downcase is the Turkish I problem. In standard locales, "ID" downcases to "id". But in Turkish, the capital letter I should become ı (a lowercase i without the dot).

# Standard (works for most languages):
"ID".downcase
# => "id"

# Turkish (correct for Turkish text):
"ID".downcase(:turkic)
# => "ıd"

The Turkish I problem is the most well-known locale trap with downcase. In standard behavior, “ID” becomes “id”, but in Turkish it should become “ıd”. Use the :turkic option only when you know the text is Turkish.

If you’re building systems that handle multiple languages, this matters. Use :turkic only when you know the input is Turkish—applying it blindly will give wrong results for other languages.

Lithuanian dotted i

Lithuanian has special rules for dotted and dotless i. Without the :lithuanian option, a dotted uppercase İ (U+0130) becomes a plain i, which is incorrect for Lithuanian.

# Standard (incomplete for Lithuanian):
"İ".downcase
# => "i"

# Lithuanian correct:
"İ".downcase(:lithuanian)
# => "i"  (with proper Unicode rules)

Lithuanian has special rules for dotted and dotless i characters. Without the :lithuanian option, a dotted uppercase İ becomes a plain i, which is incorrect for Lithuanian text processing.

Context-dependent Unicode mappings not handled

Ruby does not perform context-dependent Unicode case mapping. For example, in Greek, the lowercase of sigma depends on position: final form ς at the end of a word, regular σ elsewhere. Ruby always uses the non-final form.

# Greek sigma - Ruby always uses non-final form
"Σ".downcase
# => "σ"  (not context-aware)

Ruby does not perform context-dependent Unicode case mapping. Greek sigma has a final form ς at word endings and a regular form σ elsewhere, but Ruby downcase always uses the non-final form regardless of position within the text.

This is rarely a practical issue but worth knowing when working with Greek text.

See Also