When normalizing user input like titles or descriptions, itβs common to lowercase everything and capitalize the first word. But what if you want to preserve specific acronyms or names like API
, CSS
, or Elixir
?
In this post, weβll build a small Elixir utility to:
- Lowercase and normalize input
- Capitalize the first word of each sentence
- Preserve casing for special words like
API
,HTML
,MCP
, etc.
Example
input = "i built this using elixir, html and css. then deployed the api to phoenix."
TitleCaser.smart_case(input)
# => "I built this using Elixir, HTML and CSS. Then deployed the API to Phoenix."
Implementation
defmodule TitleCaser do
@special_words ~w(
I CSS MCP API HTTP HTML XML Elixir Phoenix Go Golang JavaScript TypeScript JSON
)
def smart_case(text) do
text
|> split_sentences()
|> Enum.map(&process_sentence/1)
|> Enum.join(" ")
end
defp split_sentences(text) do
Regex.split(~r/(?<=[.!?])\s+/, text)
end
defp process_sentence(sentence) do
sentence
|> String.downcase()
|> String.trim()
|> String.split(~r/\s+/, trim: true)
|> case do
[] -> ""
[first | rest] ->
[String.capitalize(first) | rest]
|> Enum.map(&restore_special_word/1)
|> Enum.join(" ")
end
end
defp restore_special_word(word) do
{base, punctuation} = split_word_and_punctuation(word)
restored =
Enum.find(@special_words, fn w ->
String.downcase(w) == String.downcase(base)
end) || base
restored <> punctuation
end
defp split_word_and_punctuation(word) do
case Regex.run(~r/^(.+?)([.,!?;:]*)$/, word, capture: :all_but_first) do
[base, punctuation] -> {base, punctuation}
_ -> {word, ""}
end
end
end
Tests
Use ExUnit
to verify correct behavior:
test "preserves casing for acronyms" do
input = "mcp and json are used in the api. i wrote it in go."
expected = "MCP and JSON are used in the API. I wrote it in Go."
assert TitleCaser.smart_case(input) == expected
end
Tip: make special words configurable
To make this reusable, consider passing @special_words
as a config or module attribute override.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.