Editor

Morse Code Translator in Python

Beginner · Text and words · about 30 minutes to build · 88 lines

Type a sentence to see it in dots and dashes, or paste Morse code to read it as text.

The program

Press Run and answer its questions in the box that appears under the code.

morse_code_translator.py
"""Morse code translator: text to Morse code, and Morse code back to text."""
# International Morse code for letters, digits and common punctuation.
TEXT_TO_MORSE = {
"A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", "F": "..-.",
"G": "--.", "H": "....", "I": "..", "J": ".---", "K": "-.-", "L": ".-..",
"M": "--", "N": "-.", "O": "---", "P": ".--.", "Q": "--.-", "R": ".-.",
"S": "...", "T": "-", "U": "..-", "V": "...-", "W": ".--", "X": "-..-",
"Y": "-.--", "Z": "--..",
"0": "-----", "1": ".----", "2": "..---", "3": "...--", "4": "....-",
"5": ".....", "6": "-....", "7": "--...", "8": "---..", "9": "----.",
".": ".-.-.-", ",": "--..--", "?": "..--..", "'": ".----.", "!": "-.-.--",
"/": "-..-.", "(": "-.--.", ")": "-.--.-", ":": "---...", "=": "-...-",
"+": ".-.-.", "-": "-....-", "@": ".--.-.",
}
# The same table turned around, built with a dictionary comprehension.
MORSE_TO_TEXT = {code: char for char, code in TEXT_TO_MORSE.items()}
WORD_GAP = " / " # letters are split by a space, words by a slash
MORSE_SYMBOLS = set(".-/ ")
def text_to_morse(text):
"""Encode text, returning the Morse code and any characters it skipped."""
words, skipped = [], []
for word in text.upper().split():
codes = []
for char in word:
if char in TEXT_TO_MORSE:
codes.append(TEXT_TO_MORSE[char])
else:
skipped.append(char)
if codes:
words.append(" ".join(codes))
return WORD_GAP.join(words), skipped
def morse_to_text(morse):
"""Decode Morse code, returning the text and any codes it did not know."""
words, unknown = [], []
for word in morse.split("/"):
letters = []
for code in word.split():
if code in MORSE_TO_TEXT:
letters.append(MORSE_TO_TEXT[code])
else:
letters.append("#")
unknown.append(code)
if letters:
words.append("".join(letters))
return " ".join(words), unknown
def ask_morse():
"""Ask for Morse code until it has only dots, dashes, spaces and /."""
while True:
morse = input("Morse code (spaces between letters, / between words): ")
morse = morse.strip()
if morse and set(morse) <= MORSE_SYMBOLS:
return morse
print("Use only dots, dashes, spaces and slashes, like ... --- ...")
def main():
"""Show the menu and translate in either direction until the user quits."""
print("Morse code translator")
while True:
print("\n1) Text to Morse 2) Morse to text 3) Quit")
choice = input("Choose 1-3: ").strip()
if choice == "1":
text = input("Text: ")
morse, skipped = text_to_morse(text)
print("Morse:", morse if morse else "(nothing to translate)")
if skipped:
print("Skipped (no Morse code for these):", " ".join(skipped))
elif choice == "2":
text, unknown = morse_to_text(ask_morse())
print("Text:", text if text else "(nothing to translate)")
if unknown:
print("Not Morse code (shown as #):", " ".join(unknown))
elif choice == "3":
print("Goodbye.")
break
else:
print("Please type 1, 2 or 3.")
if __name__ == "__main__":
main()

Open it in the full editor to save it, share it and keep building.

Sample run

What the program printed, and what was typed (in yellow).

Morse code translator

1) Text to Morse  2) Morse to text  3) Quit
Choose 1-3: 1
Text: Hello, World
Morse: .... . .-.. .-.. --- --..-- / .-- --- .-. .-.. -..

1) Text to Morse  2) Morse to text  3) Quit
Choose 1-3: 2
Morse code (spaces between letters, / between words): Python
Use only dots, dashes, spaces and slashes, like ... --- ...
Morse code (spaces between letters, / between words): .--. -.-- - .... --- -. / .. ... / ..-. ..- -.
Text: PYTHON IS FUN

1) Text to Morse  2) Morse to text  3) Quit
Choose 1-3: 1
Text: Tea & cake
Morse: - . .- / -.-. .- -.- .
Skipped (no Morse code for these): &

1) Text to Morse  2) Morse to text  3) Quit
Choose 1-3: 3
Goodbye.

How it is built

1. One table, both directions

Only one table is typed out by hand. MORSE_TO_TEXT is built from it with a dictionary comprehension that swaps each key and value, so the two directions can never disagree.

# The same table turned around, built with a dictionary comprehension.
MORSE_TO_TEXT = {code: char for char, code in TEXT_TO_MORSE.items()}
WORD_GAP = " / "  # letters are split by a space, words by a slash
MORSE_SYMBOLS = set(".-/ ")

2. Text to Morse

The text is upper-cased and split into words, and each character is looked up in the table. Characters with no code are collected in skipped so the user hears about them. Letters are joined with spaces and words with / .

def text_to_morse(text):
    """Encode text, returning the Morse code and any characters it skipped."""
    words, skipped = [], []
    for word in text.upper().split():
        codes = []
        for char in word:
            if char in TEXT_TO_MORSE:
                codes.append(TEXT_TO_MORSE[char])
            else:
                skipped.append(char)
        if codes:
            words.append(" ".join(codes))
    return WORD_GAP.join(words), skipped

3. Morse to text

Decoding runs the steps backwards: split at / into words, split each word at spaces into codes, and look each code up. A code that is not in the table becomes # and is reported, instead of stopping the program with a KeyError.

def morse_to_text(morse):
    """Decode Morse code, returning the text and any codes it did not know."""
    words, unknown = [], []
    for word in morse.split("/"):
        letters = []
        for code in word.split():
            if code in MORSE_TO_TEXT:
                letters.append(MORSE_TO_TEXT[code])
            else:
                letters.append("#")
                unknown.append(code)
        if letters:
            words.append("".join(letters))
    return " ".join(words), unknown

4. Checking the input

set(morse) <= MORSE_SYMBOLS is true when every character typed is a dot, a dash, a space or a slash. Anything else, like plain letters, gets a hint and another try.

def ask_morse():
    """Ask for Morse code until it has only dots, dashes, spaces and /."""
    while True:
        morse = input("Morse code (spaces between letters, / between words): ")
        morse = morse.strip()
        if morse and set(morse) <= MORSE_SYMBOLS:
            return morse
        print("Use only dots, dashes, spaces and slashes, like ... --- ...")

5. The menu

text_to_morse returns two values, which main unpacks into morse and skipped in one line.

def main():
    """Show the menu and translate in either direction until the user quits."""
    print("Morse code translator")
    while True:
        print("\n1) Text to Morse  2) Morse to text  3) Quit")
        choice = input("Choose 1-3: ").strip()
        if choice == "1":
            text = input("Text: ")
            morse, skipped = text_to_morse(text)
            print("Morse:", morse if morse else "(nothing to translate)")
            if skipped:
                print("Skipped (no Morse code for these):", " ".join(skipped))
        elif choice == "2":
            text, unknown = morse_to_text(ask_morse())
            print("Text:", text if text else "(nothing to translate)")
            if unknown:
                print("Not Morse code (shown as #):", " ".join(unknown))
        elif choice == "3":
            print("Goodbye.")
            break
        else:
            print("Please type 1, 2 or 3.")

Make it bigger

  • Play the code as beeps by writing a WAV file with the wave module.
  • Add a practice mode that shows the code for a random letter and asks which letter it is.
  • Work out the direction by itself: input made only of dots, dashes and slashes is decoded, anything else encoded.
  • Accept underscores as dashes, since some people type Morse code that way.

What it practises