Prepending an Indefinite Article (a or an) in Front of a Word or Phrase
Today, I had to whip up a Django template tag to add “a” or “an” in front of a phrase.
It started off pretty easy — just use “an” when the first letter of the phrase is a vowel and otherwise use “a.” And then I remembered about abbreviations (“an HR department”), silent H words (“an honorable man”), and exceptions among a few O and U words (“a one-time offer” or “a union representative”), and suddenly it wasn’t so easy.
Fortunately I managed to find an excellent database of English words that includes pronunciations, and in no time at all I was able to generate a list of exceptions.
Here’s the final result, implemented as a custom django template tag (in my use case, I needed to able to wrap the original phrase in an HTML tag, so I couldn’t use a filter tag). This could also easily be a standalone Python function as well (literally by removing the @register.simple_tag decorator).
import re
@register.simple_tag
def with_indefinite_article(phrase, before_phrase="", after_phrase=""):
silent_h_words = ["homage", "hour", "herb", "heir", "honest", "honor"]
letters_with_initial_vowel_sound = ["a", "e", "f", "h", "i", "l", "m", "n", "o", "r", "s", "x"]
vowels = ["a", "e", "i", "o", "u"]
o_exceptions = ["once","one","oneness","ones","oneself","onetime"]
u_exceptions = ["ubiquitous","uganda","ugandan","ukraine","ukrainian","ukulele","ukuleles","unanimous","unanimously","unicellular","unicorn","unicorns","unicycle","unicycles","unification","unified","uniform","uniformed","uniformly","uniforms","unify","unifying","unilateral","unilateralism","unilaterally","union","unionist","unionists","unionization","unionized","unionizing","unions","unique","uniquely","uniqueness","unisex","unison","unisons","unit","unitarian","unitary","unite","united","uniting","units","unity","universal","universally","universe","universes","universities","university","universitys","unix","uranium","urinalysis","urinary","urinate","urinating","urine","urologist","urologists","urology","uruguay","uruguayan","uruguays","usable","usage","usages","use","used","useful","usefully","usefulness","useless","usenet","user","users","uses","usual","usually","usurp","usurpation","usurped","usurper","usurping","usury","utah","utahn","utahns","utahs","utensil","utensils","uterine","utero","uterus","utilitarian","utilities","utility","utilitys","utilization","utilize","utilized","utilizes","utilizing","utopia","utopian","utopians","utopias"]
# get rid of everything but letters and numbers and a few characters
first_word = re.sub(r"[^A-Za-z0-9\-]", "", phrase.split()[0])
first_letter = first_word[0].lower()
# default to 'a'
article = "a"
if first_word.isupper() or (len(first_word) == 1):
# if all caps or a single letter, we're going to assume it's an abbreviation
if first_letter in letters_with_initial_vowel_sound:
article = "an"
elif first_letter == "h" and any( [re.match(r'(?i)%s' % w, first_word) for w in silent_h_words] ):
article = "an"
elif first_letter in vowels:
if first_letter == 'e':
article = "a" if first_word.lower().split('-')[0] == 'ewe' else "an"
elif first_letter == 'o':
article = "a" if first_word.lower().split('-')[0] in o_exceptions else "an"
elif first_letter == 'u':
article = "a" if first_word.lower().split('-')[0] in u_exceptions else "an"
else:
article = "an"
return "%s %s%s%s" % (article, before_phrase, phrase, after_phrase)
Hopefully this will save someone who stumbles upon this some extra work down the road. If you find any bugs, please let me know in the comments.

1 comment
Saved me a lot of time. Very much appreciated. Thanks for sharing!