Creating emoji flags in Emacs Lisp and Bash

Vexil is an Emacs package.
Flaggify is a Bash package.

They can both convert territory and subdivision codes to their corresponding flags.

A territory is typically a "country", with some exceptions (e.g. EU → 🇪🇺).

There're two approaches for conversion:

With lookups

The first approach: dictionary lookups.

It's straightforward in any language:

  1. Copy all territory codes
  2. Copy all territory flag emojis
  3. Put those pairs in some key–value structure
  4. Write a lookup function

And done.

Behold:

cat flag.csv
k,v
AC,🇦🇨
AD,🇦🇩
..... 250+ other pairs here .....
ZA,🇿🇦
ZM,🇿🇲
ZW,🇿🇼

In Bash:

code2flag() { look "$1" flag.csv | cut -d, -f2 ;}
code2flag AQ  #⇒ 🇦🇶

In Emacs Lisp, either with xht:

(defun code2flag (code)
  (-> "flag.csv"  h-read-csv  h<-csv  (h-get* code "v")))

or with native functions:

(defun code2flag (code)
  (with-temp-buffer
    (insert-file-contents "flag.csv")
    (when (re-search-forward (format "\n%s,\\(.*\\)" code) nil t 1)
      (match-string 1))))

you'll get the same:

(code2flag "AQ") => "🇦🇶"

Without lookups

Here we'll calculate the flag. We only need letters and some math.

Territories

We need to convert each of the two letters to something called a regional indicator, which looks like this:

A ⇒ 🇦

A regional indicator is just a unicode character.

And it turns out that the difference between it and its corresponding "normal letter" is constant.

Emacs Lisp

In Vexil, this is accomplished by this internal function:

(defun vexil--convert-str-territ-code->flag (code)
  "Convert territory two-letter CODE to flag."
  (let ((Δ (- ?🇦 ?A)))
    (with-output-to-string
      (dotimes (i 2)
        (princ (string (+ (aref code i) Δ)))))))
(vexil--convert-str-territ-code->flag "AQ") => "🇦🇶"

But you should use the public vexil-flag instead.
It uses the above, but can also deal with symbols and errors:

(vexil-flag "AQ")  => "🇦🇶"
(vexil-flag 'AQ)   => '🇦🇶
(vexil-flag 'aq)  !!> error   ; must be two uppercase letters

There are no lookups, and any two letters will do — so we could work out the flag of a non-existing country.

Behold the flag of Flandrewnia:

(vexil-flag 'FL) => '🇫🇱

Since FL is not assigned, there's no flag.

Depending on your fonts, you'll probably see either:

  • a question mark on a white flag
  • or two regional indicators: 🇫​🇱

Bash

Flaggify was initially conceived to use only lookups.

The goal was to find country codes in text strings, replacing only those of existing countries. Lookups allow for conservative replacements, since it includes only 250 or so flags, instead of the possible 676 (26 × 26) combinations:

flaggify r "Next week you'll go to JP, OK?"
#⇒ Next week you'll go to 🇯🇵, OK?

So the "OK" has been preserved, which is good.

But I also wanted Bash to be able to convert without lookups — so I wrote this:

f-territ()
while read -N1 -r c
do : "$(printf "$c" | od -An -t x1)"
   : "$(printf "%X" "$((0xa5 + 0x${_// }))")"
   printf "\U1F1$_"
done < <(printf "$1")

While not as readable as vexil--convert-str-territ-code->flag, it's short and it works.

And flaggify t is simply a call to f-territ. So:

flaggify t AQ  #⇒ 🇦🇶

Subdivisions

Flags of country subdivisions can also be calculated from codes.

Subdivision codes come in two flavors. Example for Quebec, Canada:

  • ISO 3166-2: CA-QC
  • Unicode CLDR: caqc

To get from CLDR to flag, we need to:

  • start with a waving black flag: 🏴
  • follow it with each character converted to its respective tag letter
  • end with a cancel tag

And it turns out that the difference between a letter and its corresponding tag letter is constant.

Emacs Lisp

In Vexil, this is accomplished by these three internal functions:

(defun vexil--convert-str-subdiv-code->flag (code)
  "Convert subdivision CODE to flag."
  (let ((wbf #x1f3f4)                         ; WAVING BLACK FLAG
        (stl (vexil--subdiv-str->tags code))  ; TAG CHARS (lowercase, list)
        (ctg #xe007f))                        ; CANCEL TAG
    (concat `(,wbf ,@stl ,ctg))))

(defun vexil--subdiv-str->tags (str)
  "From string STR to list of their corresponding lowercase tag chars."
  (mapcar #'vexil--subdiv-char->tag
          (replace-regexp-in-string "-" "" str)))

(defun vexil--subdiv-char->tag (char)
  "From character CHAR to its lowercase tag equivalent."
  (+ (downcase char) (- #xe0061 ?a)))

And vexil-flag uses vexil--convert-str-subdiv-code->flag behind the scenes. So:

(vexil-flag 'gbsct) => '🏴󠁧󠁢󠁳󠁣󠁴󠁿  ; Scotland, United Kingdom
(vexil-flag 'US-SC) => '🏴󠁵󠁳󠁳󠁣󠁿  ; South Carolina, USA
(vexil-flag 'CA-QC) => '🏴󠁣󠁡󠁱󠁣󠁿  ; Quebec, Canada

Bash

Flaggify can also deal with subdivision flags.

Here's my short solution:

taggify()
while read -N1 -r c
do : "$(printf "$c" | od -An -t x1)"
   printf "\UE00${_// }"
done < <(printf "${1,,}" | tr -d -)

f-subdiv() { printf "\U1F3F4$(taggify "$1")\UE007F" ;}

And flaggify s is simply a call to f-subdiv. So:

flaggify s gbsct  #⇒ 🏴󠁧󠁢󠁳󠁣󠁴󠁿
flaggify s US-SC  #⇒ 🏴󠁵󠁳󠁳󠁣󠁿
flaggify s CA-QC  #⇒ 🏴󠁣󠁡󠁱󠁣󠁿

Enjoy flags!

📆 2025-11-02