169

The upcase method capitalizes the entire string, but I need to capitalize only the first letter.

Also, I need to support several popular languages, like German and Russian.

How do I do it?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
AntonAL
  • 16,692
  • 21
  • 80
  • 114
  • 4
    Be aware that some languages have different ideas about what the first letter to be capitalized is. In Irish, you do things like "i mBaile Átha Cliath" ("in Dublin") - lower-case 'm', upper-case 'B'. (See http://en.wikipedia.org/wiki/Consonant_mutation#Celtic_languages if you're curious about why Irish would do that and why it makes sense.) – James Moore Feb 09 '12 at 23:19
  • 4
    And also be aware that #capitalize will downcase all letters which aren't the first letter...which is not always what you want. `['space', 'UFO', 'NASA'].collect{|w| w.capitalize} #=> ['Space', 'Ufo', 'Nasa']` – Huliax Nov 09 '15 at 20:03

10 Answers10

295

It depends on which Ruby version you use:

Ruby 2.4 and higher:

It just works, as since Ruby v2.4.0 supports Unicode case mapping:

"мария".capitalize #=> Мария

Ruby 2.3 and lower:

"maria".capitalize #=> "Maria"
"мария".capitalize #=> мария

The problem is, it just doesn't do what you want it to, it outputs мария instead of Мария.

If you're using Rails there's an easy workaround:

"мария".mb_chars.capitalize.to_s # requires ActiveSupport::Multibyte

Otherwise, you'll have to install the unicode gem and use it like this:

require 'unicode'

Unicode::capitalize("мария") #=> Мария

Ruby 1.8:

Be sure to use the coding magic comment:

#!/usr/bin/env ruby

puts "мария".capitalize

gives invalid multibyte char (US-ASCII), while:

#!/usr/bin/env ruby
#coding: utf-8

puts "мария".capitalize

works without errors, but also see the "Ruby 2.3 and lower" section for real capitalization.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Alberto Santini
  • 6,425
  • 1
  • 26
  • 37
  • 38
    Note that apparently `"my API is great".capitalize` will produce `My api is great` which probably is undesired behavior. So this answer doesn't really answer the question as he only wants the FIRST letter turned to upper case and others untouched. – Daniel A. R. Werner Jul 29 '16 at 16:18
74

capitalize first letter of first word of string

"kirk douglas".capitalize
#=> "Kirk douglas"

capitalize first letter of each word

In rails:

"kirk douglas".titleize
=> "Kirk Douglas"

OR

"kirk_douglas".titleize
=> "Kirk Douglas"    

In ruby:

"kirk douglas".split(/ |\_|\-/).map(&:capitalize).join(" ") 
#=> "Kirk Douglas"

OR

require 'active_support/core_ext'
"kirk douglas".titleize
boulder_ruby
  • 38,457
  • 9
  • 79
  • 100
  • 1
    Beware of the side effect that "kirk-douglas".titleize gives "Kirk Douglas" and removes the hyphen. Think I'll go for the upcase_first method as mentioned in the Rails 5+ post, to keep the hyphen. – folium Sep 16 '22 at 08:36
24

Rails 5+

As of Active Support and Rails 5.0.0.beta4 you can use one of both methods: String#upcase_first or ActiveSupport::Inflector#upcase_first.

"my API is great".upcase_first #=> "My API is great"
"мария".upcase_first           #=> "Мария"
"мария".upcase_first           #=> "Мария"
"NASA".upcase_first            #=> "NASA"
"MHz".upcase_first             #=> "MHz"
"sputnik".upcase_first         #=> "Sputnik"

Check "Rails 5: New upcase_first Method" for more info.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
user1519240
  • 2,186
  • 1
  • 22
  • 20
21

Well, just so we know how to capitalize only the first letter and leave the rest of them alone, because sometimes that is what is desired:

['NASA', 'MHz', 'sputnik'].collect do |word|
  letters = word.split('')
  letters.first.upcase!
  letters.join
end

 => ["NASA", "MHz", "Sputnik"]

Calling capitalize would result in ["Nasa", "Mhz", "Sputnik"].

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Huliax
  • 1,489
  • 3
  • 15
  • 27
  • Thank you just what I was looking for, useful for converting headings to 'sentence case' – Good Lux Sep 23 '17 at 11:12
  • 3
    `word[0] = word[0].upcase` – David May 12 '19 at 16:53
  • @David. NO! That changes the values of the words in the array that #collect is called on. That is a bad side-effect. – Huliax May 14 '19 at 17:36
  • I was showing a simpler way for capitalizing the first letter of a word, replacing the inner 3 lines of this solution, which I made clear by using the `word` variable. Of course, if you have more words, just call them on all of them! ;) `words.map{|word| word[0] = word[0].upcase}` – David May 14 '19 at 21:52
  • @David. Your code amounts to `#capitalize!` and not `#capitalize`. The latter returns a new String while the former modifies the receiver of the method (in this case the receiver is `word` and the method is `#[]`). If you used your code inside of a #collect block then you'd end up with two different arrays with the same String objects in each of them (and the Strings would have been modified). That is not something you'd normally want to do. Even if you're aware of this, other readers should understand this. – Huliax May 15 '19 at 23:49
20

Unfortunately, it is impossible for a machine to upcase/downcase/capitalize properly. It needs way too much contextual information for a computer to understand.

That's why Ruby's String class only supports capitalization for ASCII characters, because there it's at least somewhat well-defined.

What do I mean by "contextual information"?

For example, to capitalize i properly, you need to know which language the text is in. English, for example, has only two is: capital I without a dot and small i with a dot. But Turkish has four is: capital I without a dot, capital İ with a dot, small ı without a dot, small i with a dot. So, in English 'i'.upcase # => 'I' and in Turkish 'i'.upcase # => 'İ'. In other words: since 'i'.upcase can return two different results, depending on the language, it is obviously impossible to correctly capitalize a word without knowing its language.

But Ruby doesn't know the language, it only knows the encoding. Therefore it is impossible to properly capitalize a string with Ruby's built-in functionality.

It gets worse: even with knowing the language, it is sometimes impossible to do capitalization properly. For example, in German, 'Maße'.upcase # => 'MASSE' (Maße is the plural of Maß meaning measurement). However, 'Masse'.upcase # => 'MASSE' (meaning mass). So, what is 'MASSE'.capitalize? In other words: correctly capitalizing requires a full-blown Artificial Intelligence.

So, instead of sometimes giving the wrong answer, Ruby chooses to sometimes give no answer at all, which is why non-ASCII characters simply get ignored in downcase/upcase/capitalize operations. (Which of course also reads to wrong results, but at least it's easy to check.)

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 4
    Sorry, but your argumentation doesn't hold water. It is not true that Ruby chooses not to give an answer at all, Ruby always gives an answer, which often is wrong - e.g. "мария".upcase should never return "мария", that is not correct in any context. And your digressions about the need for AI is not relevant at all - there is nothing that prevents upcase returing an array, say ['I', 'İ'] for 'i'.upcase, and letting the caller decide which capitalization is relevant in a given situation. Currently the Ruby's handling of conversion between upper- and lowercase is broken, and that's it. – kxmh42 Nov 11 '12 at 03:12
  • 2
    -1 because there is a capital [Eszett](https://en.wikipedia.org/wiki/Capital_%E1%BA%9E). Using some non completely formalized area can not serve as proof of that solution is possible with AI only. – Mike Aug 13 '15 at 14:41
6

Use capitalize. From the String documentation:

Returns a copy of str with the first character converted to uppercase and the remainder to lowercase.

"hello".capitalize    #=> "Hello"
"HELLO".capitalize    #=> "Hello"
"123ABC".capitalize   #=> "123abc"
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
jhwist
  • 15,201
  • 3
  • 40
  • 47
  • Only use the exclamation point if you want the original string to be changed. – Magnar Sep 16 '10 at 08:28
  • 5
    -1. The OP *explicitly* mentions German and Russian text, which implies non-ASCII characters. `String#upcase` (and also `String#downcase`) are only defined for ASCII characters. – Jörg W Mittag Sep 16 '10 at 11:47
  • 2
    Using Ruby 2.5.0 today and `String#upcase` seems to work fine on non-ASCII characters. `2.5.0 :001 > "мария".upcase => "МАРИЯ"` – Huliax Feb 19 '18 at 22:27
  • 1
    @Huliax As mentioned in the accepted answer, that has only been the case since Ruby 2.4.0 (which was released in 2016). – nisetama Feb 25 '19 at 00:25
3

You can use mb_chars. This respects umlaute:

class String

  # Only capitalize first letter of a string
  def capitalize_first
    self[0] = self[0].mb_chars.upcase
    self
  end

end

Example:

"ümlaute".capitalize_first
#=> "Ümlaute"
phlegx
  • 2,618
  • 3
  • 35
  • 39
3

My version:

class String
    def upcase_first
        return self if empty?
        dup.tap {|s| s[0] = s[0].upcase }
    end
    def upcase_first!
        replace upcase_first
    end
end

['NASA title', 'MHz', 'sputnik'].map &:upcase_first  #=> ["NASA title", "MHz", "Sputnik"]

Check also:
https://www.rubydoc.info/gems/activesupport/5.0.0.1/String%3Aupcase_first
https://www.rubydoc.info/gems/activesupport/5.0.0.1/ActiveSupport/Inflector#upcase_first-instance_method

Inversion
  • 1,131
  • 13
  • 19
2

Below is another way to capitalize each word in a string. \w doesn't match Cyrillic characters or Latin characters with diacritics but [[:word:]] does. upcase, downcase, capitalize, and swapcase didn't apply to non-ASCII characters until Ruby 2.4.0 which was released in 2016.

"aAa-BBB ä мария _a a_a".gsub(/\w+/,&:capitalize)
=> "Aaa-Bbb ä мария _a A_a"
"aAa-BBB ä мария _a a_a".gsub(/[[:word:]]+/,&:capitalize)
=> "Aaa-Bbb Ä Мария _a A_a"

[[:word:]] matches characters in these categories:

Ll (Letter, Lowercase)
Lu (Letter, Uppercase)
Lt (Letter, Titlecase)
Lo (Letter, Other)
Lm (Letter, Modifier)
Nd (Number, Decimal Digit)
Pc (Punctuation, Connector)

[[:word:]] matches all 10 of the characters in the "Punctuation, Connector" (Pc) category:

005F _ LOW LINE
203F ‿ UNDERTIE
2040 ⁀ CHARACTER TIE
2054 ⁔ INVERTED UNDERTIE
FE33 ︳ PRESENTATION FORM FOR VERTICAL LOW LINE
FE34 ︴ PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
FE4D ﹍ DASHED LOW LINE
FE4E ﹎ CENTRELINE LOW LINE
FE4F ﹏ WAVY LOW LINE
FF3F _ FULLWIDTH LOW LINE

This is another way to only convert the first character of a string to uppercase:

"striNG".sub(/./,&:upcase)
=> "StriNG"
nisetama
  • 7,764
  • 1
  • 34
  • 21
0

If you want to capitalize only the first character in a string in ruby, as the author requested, you should just do exactly that:

word[0] = word[0].upcase

This works for all languages and only touches the first letter of the entire string.

word = "мария"
# => "мария"
word[0] = word[0].upcase
# => "М"
word
# => "Мария"

It does not require Rails or any other libary.

q9f
  • 11,293
  • 8
  • 57
  • 96