Easily Create Filenames & URL Slugs From Blog Post Titles

The Problem

My workflow for naming files has never been anything to write home about. Squarespace had a nifty ‘auto-create-a-URL-slug’ feature that I sorely missed after transitioning to Camel. For the past month, I had been doing it by hand. And frankly, it was just long and tedious as you can imagine.

For instance, suppose I wanted to create the filename (and in Camel, the resultant URL slug) for this post:

Easily Create Filenames & URL Slugs From Blog Post Titles

It isn’t the longest I’ve ever dreamt up, but it certainly isn’t the shortest, either. If I wanted to create a URL-friendly filename, it would take several steps.

  1. First, I would select and copy the title’s text
  2. Next, Command-S to invoke the save dialog box
  3. Then I paste the previously selected text into the Save As field:
  4. After that, I start manually changing the uppercase letters to lowercase, removing spaces and replacing them with hyphens, and deleting URL-unfriendly characters like &:
Starting the process This takes forever
😑

There had to be a better way.

The Solution

I tried a few browser searches, but nothing came up. I decided it would be a good idea to ask the king of scripting (at least amongst my Twitter follow’s):

@ToniWonKanobi I wrote a Python function that does that as part of a blog-posting script. You could build a script/service from it.

— Dr. Drang (@drdrang) June 27, 2015

The good doctor was kind enough to send me an email with the contents of a Python script.

On June 27, 2015 at 8:34:20 AM, Dr. Drang (drdrang@gmail.com) wrote:

This takes the title as standard input and returns the hyphen-separated slug on standard output. You’ll need to install the unidecode library.


#!/usr/bin/env python 
# coding: utf8 

from sys import stdin, stdout 
from unidecode import unidecode 
from datetime import datetime 
import re 

def slugify(u): 
"Convert Unicode string into blog slug." 
u = re.sub(u'[–—…/:;,.]', '-', u) # replace separating 
punctuation 
a = unidecode(u).lower() # best ASCII substitutions, 
lowercased 
a = re.sub(r'[^a-z0-9 -]', '', a) # delete any other characters 
a = a.replace(' ', '-') # spaces to hyphens 
a = re.sub(r'-+', '-', a) # condense repeated hyphens 
a = a.strip('-') # delete leading and trailing 
hyphens 
return a 

title = stdin.read().strip() 

slug = slugify(title.decode('utf8')) 
stdout.write(slug) 

Regards, Dr. Drang

I tried getting it to work, but, alas, it was beyond my understanding.

Brett Terpstra’s suggestion ended up being the winner:

@ToniWonKanobi tons. Look up "slugify".

— Brett Terpstra (@ttscoff) June 27, 2015

Ah. Slugify. I went back and double-checked Dr. Drang’s script. It was basically leveraging Slugify.

After a bit more searching, I found this page, in which Alex Plumb shared his AppleScript, cribbed from two other AppleScripts he found elsewhere online.

After some cribbing of my own, here is the contents of my version of Alex’s script:

# What is this?
# This AppleScript takes as input the clipboard content [that is potentially unsafe-for-URLs string of text] (such as a blog post in Title Case)
# The script then creates a URL-safe version
# For example: `This Is a Title of a Post!` --> `this-is-a-title-of-a-post`
# Boomshakalaka

# This is from https://superuser.com/questions/635351/process-clipboard-content-on-mac-os

# The workflow:
# 1) Select text
# 2) Copy to clipboard
# 3) Run Slugify.workflow (as a Service in macOS `~/Library/Services`)
# 4) Paste converted text

set theclip to the clipboard contents
on normalize(the_string)
	set p_script to ¬
		"# -*- coding: utf-8 -*-
import unicodedata, sys

def normalize(x):
    normal_form_1 = 'NFKD'
    normal_form_2 = 'NFC'
    x = unicodedata.normalize(normal_form_2, x)
    x = x.lower()
    x = x.replace(u'ß', u'ss')
    x = x.replace(u'å', u'aa')
    x = unicodedata.normalize(normal_form_1, x)
    x = u''.join([c for c in x if not unicodedata.combining(c)])
    x = x.encode('utf-8')
    return x
arg = sys.argv[1].decode('utf-8')
x = normalize(arg)
print x"
	
	set p_script to quoted form of p_script
	set the_string to quoted form of the_string
	
	return (do shell script ("python -c " & p_script & " " & the_string))
end normalize
on change_case(this_text)
	set the comparison_string to "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	set the source_string to "abcdefghijklmnopqrstuvwxyz"
	set the new_text to ""
	repeat with this_char in this_text
		set x to the offset of this_char in the comparison_string
		if x is not 0 then
			set the new_text to (the new_text & character x of the source_string) as string
		else
			set the new_text to (the new_text & this_char) as string
		end if
	end repeat
	return the new_text
end change_case
on replace_chars(this_text, search_string, replacement_string)
	set AppleScript's text item delimiters to the search_string
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the replacement_string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to ""
	return this_text
end replace_chars

# Alex had a bunch of special characters getting converted to their URL-safe versions. Since my desire was to just create clean filenames, I actually wanted to remove those characters entirely
set theresult to normalize(theclip)
set theresult to replace_chars(theresult, " ", "-")
set theresult to change_case(theresult)
set theresult to replace_chars(theresult, "%", "")
set theresult to replace_chars(theresult, "<", "")
set theresult to replace_chars(theresult, ">", "")
set theresult to replace_chars(theresult, "#", "")
set theresult to replace_chars(theresult, "{", "")
set theresult to replace_chars(theresult, "}", "")
set theresult to replace_chars(theresult, "|", "")
set theresult to replace_chars(theresult, "\\", "")
set theresult to replace_chars(theresult, "^", "")
set theresult to replace_chars(theresult, "~", "")
set theresult to replace_chars(theresult, "[", "")
set theresult to replace_chars(theresult, "]", "")
set theresult to replace_chars(theresult, "`", "")
set theresult to replace_chars(theresult, ";", "")
set theresult to replace_chars(theresult, "/", "")
set theresult to replace_chars(theresult, "?", "")
set theresult to replace_chars(theresult, ":", "")
set theresult to replace_chars(theresult, "@", "")
set theresult to replace_chars(theresult, "=", "")
set theresult to replace_chars(theresult, "&", "")
set theresult to replace_chars(theresult, "$", "")
# I added the following three replacements
set theresult to replace_chars(theresult, ",", "")
set theresult to replace_chars(theresult, "'", "")
set theresult to replace_chars(theresult, "\"", "")

And here’s a screenshot of the workflow:

Slugify.workflow
Slugify.workflow

Explanation

Essentially, what my Slugify.workflow does is take selected text and automate the changes I was making to the title text previously. It makes uppercase letters lowercase, and it removes spaces and funky characters and replaces them with hyphens.

I also assigned a keyboard shortcut to the service, so that I don’t have to invoke the ‘right-click’ submenu.

Shift-Option-Command-R
Shift-Option-Command-R

This couldn’t get any easier.

Select the text And ...done.
😑 ➞ 😄

Source

You can check out my Slugify.applescript on GitHub. There are instructions there for creating an Automator service as well.