Write a program that takes in a string and spells that word out using the NATO Phonetic Alphabet.

The mapping is as follows:

'A' -> 'Alfa'
'B' -> 'Bravo'
'C' -> 'Charlie'
'D' -> 'Delta'
'E' -> 'Echo'
'F' -> 'Foxtrot'
'G' -> 'Golf'
'H' -> 'Hotel'
'I' -> 'India'
'J' -> 'Juliett'
'K' -> 'Kilo'
'L' -> 'Lima'
'M' -> 'Mike'
'N' -> 'November'
'O' -> 'Oscar'
'P' -> 'Papa'
'Q' -> 'Quebec'
'R' -> 'Romeo'
'S' -> 'Sierra'
'T' -> 'Tango'
'U' -> 'Uniform'
'V' -> 'Victor'
'W' -> 'Whiskey'
'X' -> 'Xray'
'Y' -> 'Yankee'
'Z' -> 'Zulu'


'Hello World' -> ['Hotel', 'Echo', 'Lima', 'Lima', 'Oscar', 'Whisky', 'Oscar', 'Romeo', 'Lima', 'Delta']

The input can be any string, but will always be comprised of only letters and spaces. Case is irrelevant in the output, but the input may contain letters in uppercase, lowercase, or both. Spaces should be ignored in the output.

You can output in any reasonable format, but it must be a delimited set of NATO callsigns.

  • 4
    \$\begingroup\$ Welcome to PPCG! This question has already been asked. But it probably would have been closed anyway for several other reasons: 1) Unless there's a good reason to do so, asking to answer in a specific way (a function) in a specific language is usually frowned upon. 2) Challenges must be self-contained: you should explain what the NATO phonetic alphabet exactly is within the body of the question. 3) In its current form, it looks like a homework assignment. If it is, you might find help on Stack Overflow, provided that you include what you've done so far and explain where you're stuck. \$\endgroup\$ – Arnauld Jan 4 at 12:26
  • 4
    \$\begingroup\$ I'd be in favour of reopening this if the points @Arnauld raised above were addressed as it doesn't have the source restriction or input validation of the linked challenge. \$\endgroup\$ – Shaggy Jan 4 at 12:41
  • 1
    \$\begingroup\$ I've edited this to possibly be re-openable, since it doesn't have the restriction on characters. Are all my changes appropriate? Especially check the input restrictions. \$\endgroup\$ – Riker Jan 4 at 16:59
  • 1
    \$\begingroup\$ @Riker, personally, I'd say ditch the - in X-ray and require the output be in title case. \$\endgroup\$ – Shaggy Jan 4 at 17:07
  • 4
    \$\begingroup\$ This is the ICAO alphabet; the NATO alphabet uses Alpha and Juliet instead. \$\endgroup\$ – Neil Jan 5 at 11:01

13 Answers 13


sfk, 78 59 57 bytes

+spell -nato
+xed _ph_f_ _et_ett_ _-__ "*: [keep]""

Try it online!

Just use the right tool.

Output is the phonetics separated by one or more spaces.


IBM PC DOS 8088 assembly, 208 204 197 194 192 bytes

be80 00ad 8ac8 ac51 24df 8ad0 2c40 3c1b 7321 8af0 b024 b18b 9090 bf37 01f2 aefe
ce75 fab4 02cd 218b d7b4 09cd 21b2 20b4 02cd 2159 e2d0 c324 6c66 6124 7261 766f
2468 6172 6c69 6524 656c 7461 2463 686f 246f 7874 726f 7424 6f6c 6624 6f74 656c
246e 6469 6124 756c 6965 7474 2469 6c6f 2469 6d61 2469 6b65 246f 7665 6d62 6572
2473 6361 7224 6170 6124 7565 6265 6324 6f6d 656f 2469 6572 7261 2461 6e67 6f24
6e69 666f 726d 2469 6374 6f72 2468 6973 6b65 7924 7261 7924 616e 6b65 6524 756c

Download the DOS NATO.COM executable:

Try it offline! (in DOSBox, etc.)

    MOV  SI, 80H            ; point SI to DOS PSP
    LODSW                   ; load arg length into AL, advance SI to 82H
    MOV  CL, AL             ; set up loop counter
    LODSB                   ; load next char from DS:SI into AL, advance SI 
    PUSH CX                 ; save outer loop position
    AND  AL, 0DFH           ; uppercase the input letter
    MOV  DL, AL             ; save for output
    SUB  AL, 'A'-1          ; convert letter to one-based index (A=1, Z=26, etc)
    CMP  AL, 27             ; if greater than 26, not a valid char
    JNC  NOTFOUND           ; if not, move to next
    MOV  DH, AL             ; DH is loop counter
    MOV  AL, '$'            ; search for string delimiter
    MOV  CL, LNATO          ; repeat search through length of word data
    MOV  DI, OFFSET NATO    ; re-point SCASB to beginning of word data
    REPNZ SCASB             ; search until delimiter in AL is found ES:DI, advance DI
    DEC  DH                 ; delimiter found, decrement counter
    JNZ  SCANLOOP           ; if counter reached 0, index has been found
    MOV  AH, 02H            ; display first char
    INT  21H
    MOV  DX, DI             ; put found string memory location to DX for display
    MOV  AH, 09H            ; display string function
    INT  21H
    MOV  DL, ' '            ; display a space between words
    MOV  AH, 02H
    INT  21H
    POP  CX                 ; restore outer loop counter
    LOOP SEARCH             ; move to next char in input
NATO    DB  '$lfa$ravo$harlie$elta$cho$oxtrot$olf$otel$ndia$'
        DB  'uliett$ilo$ima$ike$ovember$scar$apa$uebec$omeo$'
        DB  'ierra$ango$niform$ictor$hiskey$ray$ankee$ulu$'

Test output:

A>NATO abc aaa
Alfa Bravo Charlie Alfa Alfa Alfa 
Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett 
A>NATO Alfa Bravo!
Alfa Lima Foxtrot Alfa Bravo Romeo Alfa Victor Oscar 

Updated to remove first char from word list array (though only saves 11 bytes due to additional required code).

*Note: MASM adds two NOP instructions before the REPNZ SCASB for alignment purposes so the code is really 192 bytes, but ends up being 194 assembled.


JavaScript (ES6), 181 189 bytes


Since output case doesn't matter, we can save bytes by running words together:

... GolFoxtroTangO ...

Try it online!


Jelly,  80  77 bytes


Try it online! (The footer formats the list by joining with spaces to avoid implicit smashing print when run as a full program)


Python 3, 250 191 bytes

-47 bytes thanks to @Jo King, -2 more thanks to @Jonathan Allen

It goes through all non-space characters of the input, and for each of them it selects the relevant phrase for the letter, which can be reduced a bit because the first letter of each phrase is the character itself. Splits a string instead of storing the phrases as an array to save bytes from the unnecessary 's and ,s.

lambda s:[c+"lfa ravo harlie elta cho oxtrot olf otel ndia uliett ilo ima ike ovember scar apa uebec omeo ierra ango niform ictor hiskey ray ankee ulu".split()[ord(c)%32-1]for c in s if' '<c]

Try it online!

Original solution

lambda s:[c+['lfa','ravo','harlie','elta','cho','oxtrot','olf','otel','ndia','uliett','ilo','ima','ike','ovember','scar','apa','uebec','omeo','ierra','ango','niform','ictor','hiskey','ray','ankee','ulu'][ord(c)-65]for c in s.replace(" ", "").upper()]

Try it online!

  • \$\begingroup\$ 191 with if' '<c \$\endgroup\$ – Jonathan Allan Jan 4 at 23:41

05AB1E, 102 96 95 bytes

álSA”AlfaІvo¼¯¤œ®È¨›trotŠˆƒ‹Š™ÈŸtt Kilo´àma—……ÍЗŽêpa¼°«Äoµ†Çâgo¸šÉµ Whiskey Xrayµ‹nkeeâ¸lu”#‡

Output is a list of Titlecased NATO words.

Try it online.


á              # Only leave the letters of the (implicit) input
 l             # Convert it to lowercase
  S            # Split it to a list of characters
   A           # Push the alphabet
    ”...”      # Push all the NATO words in titlecase and space-delimited
         #     # Split the string by spaces
          ‡    # Transliterate; map all letters in the lowercase input with this
               # list at the same indices (and output the resulting list implicitly)

See this 05AB1E tip of mine (section How to use the dictionary?) to understand why ”AlfaІvo¼¯¤œ®È¨›trotŠˆƒ‹Š™ÈŸtt Kilo´àma—……ÍЗŽêpa¼°«Äoµ†Çâgo¸šÉµ Whiskey Xrayµ‹nkeeâ¸lu” is "Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xray Yankee Zulu". Credit of this compressed dictionary string goes to @ErikTheGolfer in this comment (with an added t for Juliett instead of Juliet).


Red, 210 193 bytes

func[s][foreach c trim/all s[prin c print pick[:lfa:ravo:harlie:elta:cho:oxtrot:olf:otel:ndia:uliett:ilo:ima:ike:ovember:scar:apa:uebec:omeo:ierra:ango:niform:ictor:hiskey:ray:ankee:ulu]c% 32]]

Try it online!


foreach iterates over the string after all whitespaces are removed by trim/all. prin prints the character (no newline). print prints a symbol, picked from the list of get-word!s (symbols) using the character mapped to the range 1..26 as an index.


Clean, 218 bytes

import StdEnv
$s=[takeWhile((<=)c)(dropWhile((<)c)['ZuluYankeeXrayWhiskeyVictorUniformTangoSierraRomeoQuebecPapaOscarNovemberMikeLimaKiloJuliettIndiaHotelGolfFoxtrotEchoDeltaCharlieBravoAlfa'])\\c<-map toUpper s|c>' ']

Try it online!


Perl 6, 176 170 166 bytes

*.comb>>.&{$_~:128[q`>$RbD[Orlo~Q1nX,OVq8x9'6%h'1.I$83ua7	vsD=s-{W}{>iQ:Js37py)hNN,i{Pt\~#f4<>`.ords].base(35).split('J')[.ord%32]}.words

Try it online!

Outputs in uppercases with the first letter in the original case. Compresses the string, which only saves 6 bytes over the simpler plain text:

*.comb>>.&{$_~ <lfa ravo harlie elta cho oxtrot olf otel ndia uliett ilo ima ike ovember scar apa uebec omeo ierra ango niform ictor hiskey ray ankee ulu>[.ord%32-1]if ' 'ne$_}

Try it online!


*.comb>>.&{                 }         # Map each letter to
           $_~                        # The letter plus
              <...>[.ord%32]          # The letter indexed into the list of words
                             .words   # And remove the extra spaces

Japt, 108 106 bytes

¸®¬Ë+u cg`ovem¼rws¯r°pawue¼cÙ o±ØǯfmØtØkeyÙ°nkeewªuwlfaæ1ÃÉr¦e³ltawÖoxÉwolfÙ*lÙAawªieâ-¹µ±ke`qw

Try it

The backticks contains the compressed string:


Charcoal, 99 bytes


Try it online! Link is to verbose version of code. Outputs in proper case. Explanation:

   S                    Input string
  ↥                     Uppercased
 Φ                      Filtered where
     α                  Predefined uppercase alphabet
    №                   Contains
      ι                 Current character
E                       Mapped over characters
        ι               Current character
       ⁺                Concatenated with
           ”...”        Compressed string
          ⪪             Split on
                j       Literal string `j`
         §              Indexed by
                 ⌕      Index of
                   ι    Current character
                  α     In uppercase alphabet
                        Implicitly print each word on its own line

C# (Visual C# Interactive Compiler), 218 bytes


Try it online!

Alternate version using Split(), 194 bytes


Try it online!


TSQL, 313 bytes


DECLARE @ varchar(max)='Hello World' 

DECLARE @x INT=len(@)WHILE @x>0SELECT @=stuff(@,@x,1,substring(@,@x,1)+choose(ascii(substring(@,@x,1))%32,'lfa','eta','harlie','elta','cho','oxtrot','olf','otel','ndia','uliett','ilo','ima','ike','ovember','scar','apa','uebec','omeo','ierra','ango','niform','ictor','hiskey','ray','ankee','ulu')+';'),@x-=1PRINT @


DECLARE @ varchar(max)='Hello World' 

DECLARE @x INT=len(@)
WHILE @x>0
  SELECT @=stuff(@,@x,1,substring(@,@x,1)+choose(ascii(substring(@,@x,1))%32,


Try it out

Output ends with semicolon


Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.