This site is a work-in-progress. Some of the information is incomplete and may not work as described. See the homepage for details.

Liquid doesn’t have a title case filter. They do have a capitalize filter that will turn “this is my title” into “This is my title.” But what if you want it to be “This Is My Title?”

You could split the string into it’s individual words and map Ruby’s capitalize method to each of those.

def title_case(words)
  return words.split(%r{[\s-]+}).map(&:capitalize).join(' ')
end

But what happens when you have a title about SSL or an iPhone?

irb> "title about SSL or an iPhone?".split(%r{[\s-]+}).map(&:capitalize).join(' ')
=> "Title About Ssl Or An Iphone?"

That’s less than ideal. We would want SSL to stay uppercase and iPhone to be unchanged.

Let’s try again using gsub to check if the word starts lowercase and if so, capitalize it.

irb> "title about SSL or an iPhone?".gsub(/\b('?[a-z])/) { $1.capitalize }
=> "Title About SSL Or An IPhone?"

That looks much better, though not perfect. Title Case is a difficult problem and the rules vary depending on the style guide you prefer. The more detailed you need to be, the more conditionals you’re going to need. For example, the gsub option above will not work if you have an uppercase letter in the middle of the word (e.g. iPhone).

Here’s how I solve it, a complete Jekyll plugin.

module Jekyll
  module Titleize
    def titleize(title)
      # Array of words I don't want to capitalize
      stop_words = ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'for', 'from', 'in', 'into', 'like', 'nor', 'of', 'off', 'on', 'onto', 'or', 'per', 'so', 'than', 'the', 'to', 'up', 'via', 'with', 'without', 'yet']
      # Break the words and punctuation into an array
      words = title.scan(/[\w'-]+|[[:punct:]]+/)
      # Create a variable to hole the new title
      title_case = ''
      # Loop each word in `words`
      words.each do |word|
        # Only change the words if not in the stop_words array
        unless stop_words.include? word
          # Only capitalize words that don't already start with a capital
          word = word.gsub(/\b('?[a-z])/) { $1.capitalize }
        end
        # Add word to string
        title_case << word + ' '
      end
      # Fix spacing issue with punctuation
      title_case.gsub(/ \./, '.').gsub(/ ,/, ',').gsub(/ \?/, '?').gsub(/ \!/, '!')
    end
  end
end

Liquid::Template.register_filter(Jekyll::Titleize)

Because this is a filter plugin, you will use it by calling something like this:

{{ page.title | titleize }}

This doesn’t solve the problem completely. It will turn “Vincent van Gogh” into “Vincent Van Gogh” and “example.com” into “Example. Com.” But it works for about 95% of my use cases and I think it’s a good example of how to build a Jekyll plugin.