diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5aa1b19..f8e5aa5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,6 +23,32 @@ set(VERSION "0.11.0")
 set(VERSION_INFO "Release")
 set(LANGUAGE_SUPPORT_DIRECTORY ${CMAKE_INSTALL_PREFIX}/share/locale)
 
+if (NOT ISO_CODE_639_XML)
+  find_path(ISOCODES_DIRECTORY NAMES iso_639.xml PATHS ${CMAKE_INSTALL_PREFIX} /usr/share/xml/iso-codes)
+  if (ISOCODES_DIRECTORY)
+    set(ISO_CODE_639_XML ${ISOCODES_DIRECTORY}/iso_639.xml)
+  else ()
+    message(WARNING "File iso_639.xml not found. Please specify it manually using cmake -DISO_CODE_639_XML=/path/to/iso_639.xml")
+  endif ()
+else ()
+  if (NOT EXISTS ${ISO_CODE_639_XML})
+    message(WARNING "The path to iso_639.xml specified in ISO_CODE_639_XML is not valid.")
+  endif ()
+endif ()
+
+if (NOT ISO_CODE_3166_XML)
+  find_path(ISOCODES_DIRECTORY NAMES iso_3166.xml PATHS ${CMAKE_INSTALL_PREFIX} /usr/share/xml/iso-codes)
+  if (ISOCODES_DIRECTORY)
+    set(ISO_CODE_3166_XML ${ISOCODES_DIRECTORY}/iso_3166.xml)
+  else ()
+    message(WARNING "File iso_3166.xml not found. Please specify it manually using cmake -DISO_CODE_3166_XML=/path/to/iso_3166.xml")
+  endif ()
+else ()
+  if (NOT EXISTS ${ISO_CODE_3166_XML})
+    message(WARNING "The path to iso_3166.xml specified in ISO_CODE_3166_XML is not valid.")
+  endif ()
+endif ()
+
 # Packaging filenamesnames.
 set(ARCHIVE_BASE_NAME ${CMAKE_PROJECT_NAME}-${VERSION})
 set(ARCHIVE_FULL_NAME ${ARCHIVE_BASE_NAME}.tar.xz)
@@ -58,6 +84,17 @@ find_package(PkgConfig)
 pkg_check_modules(LIBUNITY QUIET unity>=5.12.0)
 pkg_check_modules(LIBMESSAGINGMENU QUIET messaging-menu>=12.10.2)
 
+# CMake's range-checking syntax doesn't allow for a bare less-than, so using
+# improbably high version numbers to indicate maximum
+pkg_check_modules(WEBKITGTK24X QUIET webkitgtk-3.0>=2.3.0)
+pkg_check_modules(WEBKITGTK22X QUIET webkitgtk-3.0>=2.2.0 webkitgtk-3.0<=2.2.100)
+pkg_check_modules(WEBKITGTK20X QUIET webkitgtk-3.0>=2.0.0 webkitgtk-3.0<=2.0.100)
+pkg_check_modules(WEBKITGTK110X QUIET webkitgtk-3.0>=1.10.0 webkitgtk-3.0<=1.10.2)
+
+pkg_check_modules(GTK312X QUIET gtk+-3.0>=3.12.0)
+
+pkg_check_modules(ENCHANT QUIET enchant)
+
 pkg_check_modules(SQLITE311 QUIET sqlite3>=3.11.0)
 pkg_check_modules(SQLITE312 QUIET sqlite3>=3.12.0)
 if (SQLITE311_FOUND AND NOT SQLITE312_FOUND)
@@ -75,6 +112,7 @@ endif ()
 # GResources
 include(GlibCompileResourcesSupport)
 
+
 #
 # Uninstall target
 #
diff --git a/bindings/vapi/enchant.vapi b/bindings/vapi/enchant.vapi
new file mode 100644
index 0000000..c4a030f
--- /dev/null
+++ b/bindings/vapi/enchant.vapi
@@ -0,0 +1,34 @@
+[CCode (cheader_filename = "enchant.h")]
+namespace Enchant {
+	public delegate void BrokerDescribeFn (string provider_name, string provider_desc, string provider_dll_file);
+	public delegate void DictDescribeFn (string lang_tag, string provider_name, string provider_desc, string provider_file);
+
+	[Compact]
+	[CCode (free_function = "enchant_broker_free")]
+	public class Broker {
+		[CCode (cname = "enchant_broker_init")]
+		public Broker ();
+
+		public unowned Dict request_dict (string tag);
+		public unowned Dict request_pwl_dict (string pwl);
+		public void free_dict (Dict dict);
+		public int dict_exists (string tag);
+		public void set_ordering (string tag, string ordering);
+		public void describe (BrokerDescribeFn fn);
+		public void list_dicts (DictDescribeFn fn);
+		public unowned string get_error ();
+	}
+
+	[Compact]
+	public class Dict {
+		public int check (string word, long len = -1);
+		public unowned string[] suggest (string word, long len = -1);
+		public void free_string_list ([CCode (array_length = false)] string[] string_list);
+		public void add_to_session (string word, long len = -1);
+		public int is_in_session (string word, long len = -1);
+		public void store_replacement ( string mis, long mis_len, string cor, long cor_len);
+		public void add_to_pwl ( string word, long len = -1);
+		public void describe (DictDescribeFn fn);
+		public unowned string get_error ();
+	}
+}
diff --git a/desktop/org.yorba.geary.gschema.xml b/desktop/org.yorba.geary.gschema.xml
index 2c435b3..76d4e6b 100644
--- a/desktop/org.yorba.geary.gschema.xml
+++ b/desktop/org.yorba.geary.gschema.xml
@@ -67,12 +67,25 @@
         <summary>display message previews</summary>
         <description>True if we should display a short preview of each message.</description>
     </key>
-    
+
     <key name="spell-check" type="b">
         <default>true</default>
         <summary>enable inline spell checking</summary>
         <description>True to spell check while typing.</description>
     </key>
+
+    <key name="spell-check-languages" type="as">
+         <default>[]</default>
+         <summary>Languages that shall be used in the spell checker</summary>
+         <description>List of the languages to use in the spell checker</description>
+    </key>
+
+    <key name="spell-check-visible-languages" type="as">
+        <default>[]</default>
+        <summary>Languages that are displayed in the spell checker popover.</summary>
+        <description>List of languages that are always displayed in the popover of the spell checker.</description>
+    </key>
+
     <key name="play-sounds" type="b">
         <default>true</default>
         <summary>enable notification sounds</summary>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d0e494c..ad1b34f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -355,6 +355,7 @@ client/composer/contact-entry-completion.vala
 client/composer/contact-list-store.vala
 client/composer/email-entry.vala
 client/composer/scrollable-overlay.vala
+client/composer/spell-check-popover.vala
 client/composer/webview-edit-fixer.vala
 
 client/conversation-list/conversation-list-cell-renderer.vala
@@ -495,6 +496,7 @@ pkg_check_modules(DEPS REQUIRED
     gcr-3>=3.10.1
     gobject-introspection-1.0
     webkitgtk-3.0>=2.4.0
+    enchant>=1.6
     ${EXTRA_CLIENT_PKG_CONFIG}
 )
 
@@ -505,7 +507,7 @@ set(ENGINE_PACKAGES
 # webkitgtk-3.0 is listed as a custom VAPI (below) to ensure it's treated as a dependency and
 # built before compilation
 set(CLIENT_PACKAGES
-    gtk+-3.0 libsecret-1 libsoup-2.4 libnotify libcanberra gcr-3 ${EXTRA_CLIENT_PACKAGES}
+    gtk+-3.0 libsecret-1 libsoup-2.4 libnotify libcanberra gcr-3 enchant ${EXTRA_CLIENT_PACKAGES}
 )
 
 set(CONSOLE_PACKAGES
@@ -523,6 +525,8 @@ set(CFLAGS
     -D_GSETTINGS_DIR=\"${CMAKE_BINARY_DIR}/gsettings\"
     -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\"
     -DLANGUAGE_SUPPORT_DIRECTORY=\"${LANGUAGE_SUPPORT_DIRECTORY}\"
+    -DISO_CODE_639_XML=\"${ISO_CODE_639_XML}\"
+    -DISO_CODE_3166_XML=\"${ISO_CODE_3166_XML}\"
     -DGCR_API_SUBJECT_TO_CHANGE
     -g
 )
diff --git a/src/client/application/geary-config.vala b/src/client/application/geary-config.vala
index e79fe26..c8cfa56 100644
--- a/src/client/application/geary-config.vala
+++ b/src/client/application/geary-config.vala
@@ -23,10 +23,12 @@ public class Configuration {
     public const string STARTUP_NOTIFICATIONS_KEY = "startup-notifications";
     public const string ASK_OPEN_ATTACHMENT_KEY = "ask-open-attachment";
     public const string COMPOSE_AS_HTML_KEY = "compose-as-html";
+    public const string SPELL_CHECK_VISIBLE_LANGUAGES = "spell-check-visible-languages";
+    public const string SPELL_CHECK_LANGUAGES = "spell-check-languages";
 
     public Settings settings { get; private set; }
     public Settings gnome_interface;
-
+    
     public int window_width {
         get { return settings.get_int(WINDOW_WIDTH_KEY); }
     }
@@ -77,6 +79,26 @@ public class Configuration {
         get { return settings.get_boolean(SPELL_CHECK_KEY); }
     }
 
+    public string[] spell_check_languages {
+        owned get {
+	    string[] langs = settings.get_strv(SPELL_CHECK_LANGUAGES);
+	    return langs;
+	}
+        set { settings.set_strv(SPELL_CHECK_LANGUAGES, value); }
+    }
+
+    public string[] spell_check_visible_languages {
+	owned get {
+	    string[] langs = settings.get_strv(SPELL_CHECK_VISIBLE_LANGUAGES);
+	    if (langs.length == 0) {
+		langs = International.get_user_preferred_languages();
+		settings.set_strv(SPELL_CHECK_VISIBLE_LANGUAGES, langs);
+	    }
+	    return langs;
+	}
+	set { settings.set_strv(SPELL_CHECK_VISIBLE_LANGUAGES, value); }
+    }
+
     public bool play_sounds {
         get { return settings.get_boolean(PLAY_SOUNDS_KEY); }
     }
diff --git a/src/client/composer/composer-toolbar.vala b/src/client/composer/composer-toolbar.vala
index 8e36c77..f080e0d 100644
--- a/src/client/composer/composer-toolbar.vala
+++ b/src/client/composer/composer-toolbar.vala
@@ -7,6 +7,8 @@
 public class ComposerToolbar : PillToolbar {
     
     public string label_text { get; set; }
+
+    public Gtk.Button select_dictionary_button;
     
     public ComposerToolbar(Gtk.ActionGroup toolbar_action_group, Gtk.Menu menu) {
         base(toolbar_action_group);
@@ -36,6 +38,12 @@ public class ComposerToolbar : PillToolbar {
         insert.add(create_toolbar_button(null, ComposerWidget.ACTION_REMOVE_FORMAT));
         add_start(create_pill_buttons(insert));
         
+        // Select dictionary
+        insert.clear();
+        select_dictionary_button = create_toolbar_button(null, ComposerWidget.ACTION_SELECT_DICTIONARY);
+        insert.add(select_dictionary_button);
+        add_start(create_pill_buttons(insert));
+
         // Menu.
         insert.clear();
         insert.add(create_menu_button(null, menu, ComposerWidget.ACTION_MENU));
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index a60da4c..d367b5b 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -68,6 +68,7 @@ public class ComposerWidget : Gtk.EventBox {
     public const string ACTION_SEND = "send";
     public const string ACTION_ADD_ATTACHMENT = "add attachment";
     public const string ACTION_ADD_ORIGINAL_ATTACHMENTS = "add original attachments";
+    public const string ACTION_SELECT_DICTIONARY = "select dictionary";
     
     private const string DRAFT_SAVED_TEXT = _("Saved");
     private const string DRAFT_SAVING_TEXT = _("Saving");
@@ -250,7 +251,9 @@ public class ComposerWidget : Gtk.EventBox {
     private Gtk.MenuItem html_item2;
     private Gtk.MenuItem extended_item;
     
+    private ComposerToolbar composer_toolbar;
     private Gtk.ActionGroup actions;
+    private SpellCheckPopover? spell_check_popover = null;
     private string? hover_url = null;
     private bool action_flag = false;
     private bool is_attachment_overlay_visible = false;
@@ -405,7 +408,7 @@ public class ComposerWidget : Gtk.EventBox {
             actions.get_action(ACTION_OUTDENT).icon_name = "format-indent-less-symbolic";
         }
         
-        ComposerToolbar composer_toolbar = new ComposerToolbar(actions, menu);
+        composer_toolbar = new ComposerToolbar(actions, menu);
         Gtk.Alignment toolbar_area = (Gtk.Alignment) builder.get_object("toolbar area");
         toolbar_area.add(composer_toolbar);
         bind_property("toolbar-text", composer_toolbar, "label-text", BindingFlags.SYNC_CREATE);
@@ -447,6 +450,7 @@ public class ComposerWidget : Gtk.EventBox {
         actions.get_action(ACTION_SEND).activate.connect(on_send);
         actions.get_action(ACTION_ADD_ATTACHMENT).activate.connect(on_add_attachment_button_clicked);
         actions.get_action(ACTION_ADD_ORIGINAL_ATTACHMENTS).activate.connect(on_pending_attachments_button_clicked);
+        actions.get_action(ACTION_SELECT_DICTIONARY).activate.connect(on_select_dictionary_clicked);
         
         ui = new Gtk.UIManager();
         ui.insert_action_group(actions, 0);
@@ -578,6 +582,8 @@ public class ComposerWidget : Gtk.EventBox {
         
         WebKit.WebSettings s = editor.settings;
         s.enable_spell_checking = GearyApplication.instance.config.spell_check;
+        s.spell_checking_languages = string.joinv(",",
+			GearyApplication.instance.config.spell_check_languages);
         s.auto_load_images = false;
         s.enable_scripts = false;
         s.enable_java_applet = false;
@@ -2158,14 +2164,25 @@ public class ComposerWidget : Gtk.EventBox {
         Gtk.MenuItem select_all_item = new Gtk.MenuItem.with_mnemonic(Stock.SELECT__ALL);
         select_all_item.activate.connect(on_select_all);
         context_menu.append(select_all_item);
-        
+
         context_menu.show_all();
         
         update_actions();
         
         return false;
     }
-    
+
+    private void on_select_dictionary_clicked() {
+	if (spell_check_popover == null) {
+	    spell_check_popover = new SpellCheckPopover(composer_toolbar.select_dictionary_button);
+	    spell_check_popover.selection_changed.connect((active_langs) => {
+		    editor.settings.spell_checking_languages = string.joinv(",", active_langs);
+		    GearyApplication.instance.config.spell_check_languages = active_langs;
+		});
+	}
+	spell_check_popover.toggle();
+    }
+
     private bool on_editor_key_press(Gdk.EventKey event) {
         // widget's keypress override doesn't receive non-modifier keys when the editor processes
         // them, regardless if true or false is called; this deals with that issue (specifically
diff --git a/src/client/composer/spell-check-popover.vala b/src/client/composer/spell-check-popover.vala
new file mode 100644
index 0000000..5a027c3
--- /dev/null
+++ b/src/client/composer/spell-check-popover.vala
@@ -0,0 +1,287 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+public class SpellCheckPopover {
+
+    /**
+     * This signal is emitted then the selection of rows changes.
+     *
+     * @param active_langs The new set of active dictionaries after the
+     *                     selection has changed.
+     */
+    public signal void selection_changed(string[] active_langs);
+
+    private Gtk.Popover? popover = null;
+    private GLib.GenericSet<string> selected_rows;
+    private bool is_expanded = false;
+    private Gtk.ListBox langs_list;
+    private Gtk.Entry search_box;
+    private Gtk.ScrolledWindow view;
+    private Gtk.Box content;
+
+    private enum SpellCheckStatus {
+	INACTIVE,
+	DEACTIVATING,
+	ACTIVE
+    }
+
+    private class SpellCheckLangRow : Gtk.ListBoxRow {
+
+	/**
+	 * This signal is emitted then the user activates the row.
+	 *
+	 * @param lang_code The language code associated to this row (such as en_US).
+	 * @param status true if the associated dictionary should be enabled, false if it should be
+	 *               disabled.
+	 */
+	public signal void toggled (string lang_code, bool status);
+
+	private string lang_code;
+	private string lang_name;
+	private string country_name;
+	private bool is_lang_visible;
+	private Gtk.Image current_image;
+	private SpellCheckStatus lang_active = SpellCheckStatus.INACTIVE;
+	private uint timer_id = 0;
+
+	public SpellCheckLangRow (string lang_code) {
+	    this.lang_code = lang_code;
+	    Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
+
+	    lang_name = International.language_name_from_locale(lang_code);
+	    country_name = International.country_name_from_locale(lang_code);
+
+	    string label_text = lang_name;
+	    if (country_name != null)
+		label_text += " (" + country_name + ")";
+	    Gtk.Label label = new Gtk.Label(label_text);
+	    label.set_xalign(0.0f);
+	    label.set_size_request(-1, 24);
+
+	    box.pack_start(label, true, true);
+
+	    current_image = new Gtk.Image();
+	    box.pack_start(current_image, false, true);
+
+	    is_lang_visible = false;
+	    foreach (string visible_lang in GearyApplication.instance.config.spell_check_visible_languages) {
+		if (visible_lang == lang_code)
+		    is_lang_visible = true;
+	    }
+
+	    foreach (string active_lang in GearyApplication.instance.config.spell_check_languages) {
+		if (active_lang == lang_code)
+		    lang_active = SpellCheckStatus.ACTIVE;
+	    }
+
+	    update_image();
+	    add(box);
+	}
+
+	public bool is_lang_active() {
+	    return lang_active == SpellCheckStatus.ACTIVE;
+	}
+
+	private void update_image() {
+	    Gtk.IconSize sz = Gtk.IconSize.SMALL_TOOLBAR;
+
+	    switch (lang_active) {
+	    case SpellCheckStatus.ACTIVE:
+		current_image.set_from_icon_name("object-select-symbolic", sz);
+		break;
+	    case SpellCheckStatus.DEACTIVATING:
+		current_image.set_from_icon_name("list-remove-symbolic", sz);
+		break;
+	    case SpellCheckStatus.INACTIVE:
+		current_image.clear();
+		break;
+	    }
+	}
+
+	public bool match_filter(string filter) {
+	    string filter_down = filter.down();
+	    return (filter_down in lang_name.down() || filter_down in country_name.down());
+	}
+
+	private void set_lang_active(SpellCheckStatus active) {
+	    lang_active = active;
+
+	    switch (active) {
+	    case SpellCheckStatus.ACTIVE:
+		// If the lang is not visible make it visible now
+		if (!is_lang_visible) {
+		    string[] visible_langs = GearyApplication.instance.config.spell_check_visible_languages;
+		    visible_langs += lang_code;
+		    GearyApplication.instance.config.spell_check_visible_languages = visible_langs;
+		    is_lang_visible = true;
+		}
+		break;
+	    case SpellCheckStatus.DEACTIVATING:
+		// Make it switch automatically to INACTIVE in 5 seconds
+		timer_id = GLib.Timeout.add(5000, this.on_automatic_deactivation, 0);
+		break;
+	    case SpellCheckStatus.INACTIVE:
+		// Reset the timer counter, this has either been disabled or has expired.
+		if (timer_id > 0) {
+		    timer_id = 0;
+		}
+		break;
+	    }
+
+	    update_image();
+	    this.toggled(lang_code, active == SpellCheckStatus.ACTIVE);
+	}
+
+	private bool on_automatic_deactivation() {
+	    set_lang_active(SpellCheckStatus.INACTIVE);
+	    return false;
+	}
+
+	public void handle_activation(SpellCheckPopover spell_check_popover) {
+	    switch (lang_active) {
+	    case SpellCheckStatus.ACTIVE:
+		set_lang_active(SpellCheckStatus.DEACTIVATING);
+		break;
+	    case SpellCheckStatus.DEACTIVATING:
+		GLib.Source.remove(timer_id);
+
+		// Since the user has selected the button before the automatic timeout,
+		// we shall remove it from the quicklist.
+		is_lang_visible = false;
+		string[] visible_langs = {};
+		foreach (string visible_lang in GearyApplication.instance.config.spell_check_visible_languages) {
+		    if (visible_lang != lang_code)
+			visible_langs += visible_lang;
+		}
+		GearyApplication.instance.config.spell_check_visible_languages = visible_langs;
+		set_lang_active(SpellCheckStatus.INACTIVE);
+		break;
+	    case SpellCheckStatus.INACTIVE:
+		set_lang_active(SpellCheckStatus.ACTIVE);
+		break;
+	    }
+	}
+
+	public bool is_row_visible(bool is_expanded) {
+	    return is_lang_visible || is_expanded;
+	}
+    }
+
+    public SpellCheckPopover(Gtk.Widget button) {
+	popover = new Gtk.Popover(button);
+	selected_rows = new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
+	setup_popover();
+    }
+
+    private bool filter_function (Gtk.ListBoxRow row) {
+	string text = search_box.get_text();
+	SpellCheckLangRow r = row as SpellCheckLangRow;
+	return (r.is_row_visible(is_expanded) && r.match_filter(text));
+    }
+
+    private void setup_popover() {
+	// We populate the popover with the list of languages that the user wants to see
+	string[] languages = International.get_available_dictionaries();
+
+	content = new Gtk.Box(Gtk.Orientation.VERTICAL, 6);
+	search_box = new Gtk.Entry();
+	search_box.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, "edit-find-symbolic");
+	search_box.set_placeholder_text(_("Search for more languages"));
+	search_box.changed.connect(on_search_box_changed);
+	search_box.grab_focus.connect(on_search_box_grab_focus);
+	content.pack_start(search_box, false, true);
+
+	view = new Gtk.ScrolledWindow(null, null);
+	view.set_shadow_type(Gtk.ShadowType.IN);
+	view.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+
+	langs_list = new Gtk.ListBox();
+	langs_list.set_selection_mode(Gtk.SelectionMode.NONE);
+	foreach (string lang in languages) {
+	    SpellCheckLangRow row = new SpellCheckLangRow(lang);
+	    langs_list.add(row);
+
+	    if (row.is_lang_active())
+		selected_rows.add(lang);
+
+	    row.toggled.connect(this.on_row_toggled);
+	}
+	langs_list.row_activated.connect(on_row_activated);
+	view.add(langs_list);
+
+	content.pack_start(view, true, true);
+
+	langs_list.set_filter_func(this.filter_function);
+
+	view.set_size_request(350, 300);
+	popover.add(content);
+
+	// Make sure that the search box does not get the focus first. We want it to have it only
+	// if the user wants to perform an extended search.
+	content.set_focus_child(view);
+	content.set_margin_start(6);
+	content.set_margin_end(6);
+	content.set_margin_top(6);
+	content.set_margin_bottom(6);
+    }
+
+    private void on_row_activated(Gtk.ListBoxRow row) {
+	SpellCheckLangRow r = row as SpellCheckLangRow;
+	r.handle_activation(this);
+
+	// Make sure that we update the visible languages based on the
+	// possibly updated is_lang_visible_properties.
+	langs_list.invalidate_filter();
+    }
+
+    private void on_search_box_changed() {
+	langs_list.invalidate_filter();
+    }
+
+    private void on_search_box_grab_focus() {
+	set_expanded(true);
+    }
+
+    private void set_expanded(bool expanded) {
+	is_expanded = expanded;
+	langs_list.invalidate_filter();
+    }
+
+    /*
+     * Toggle the visibility of the popover, and return the final status.
+     *
+     * @return true if the Popover is visible after the call, false otherwise.
+     */
+    public bool toggle() {
+	if (popover.get_visible()) {
+	    popover.hide();
+	}
+	else {
+	    // Make sure that when the box is shown the list is not expanded anymore.
+	    search_box.set_text("");
+	    content.set_focus_child(view);
+	    is_expanded = false;
+	    langs_list.invalidate_filter();
+
+	    popover.show_all();
+	}
+
+	return popover.get_visible();
+    }
+
+    private void on_row_toggled(string lang_code, bool active) {
+	if (active)
+	    selected_rows.add(lang_code);
+	else
+	    selected_rows.remove(lang_code);
+
+	// Signal that the selection has changed
+	string[] active_langs = {};
+	selected_rows.foreach((lang) => active_langs += lang);
+	this.selection_changed(active_langs);
+    }
+
+}
diff --git a/src/client/util/util-international.vala b/src/client/util/util-international.vala
index d0d9005..8eadc57 100644
--- a/src/client/util/util-international.vala
+++ b/src/client/util/util-international.vala
@@ -5,10 +5,15 @@
  */
 
 extern const string LANGUAGE_SUPPORT_DIRECTORY;
+extern const string ISO_CODE_639_XML;
+extern const string ISO_CODE_3166_XML;
 public const string TRANSLATABLE = "translatable";
 
 namespace International {
 
+private GLib.HashTable<string, string> language_names = null;
+private GLib.HashTable<string, string> country_names = null;
+
 public const string SYSTEM_LOCALE = "";
 
 void init(string package_name, string program_path, string locale = SYSTEM_LOCALE) {
@@ -23,5 +28,188 @@ private string get_langpack_dir_path(string program_path) {
     return LANGUAGE_SUPPORT_DIRECTORY;
 }
 
+public string[] get_available_dictionaries() {
+    string[] dictionaries = {};
+
+    Enchant.Broker broker = new Enchant.Broker();
+    broker.list_dicts((lang_tag, provider_name, provider_desc, provider_file) => {
+	    dictionaries += lang_tag;
+	});
+
+    // Whenever regional variants of the dictionaries are available use them
+    // in place of the generic ones, e.g., discard en if en_US, en_GB, ...
+    // are installed on the system.
+    GLib.GenericSet<string> regional_dictionaries =
+	new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
+    foreach (string dic in dictionaries)
+	if ("_" in dic) {
+	    int underscore = dic.index_of_char('_');
+	    regional_dictionaries.add(dic.substring(0, underscore));
+	}
+
+    GLib.List<string> filtered_dictionaries = new GLib.List<string>();
+    foreach (string dic in dictionaries)
+	if ("_" in dic || ! regional_dictionaries.contains(dic))
+	    filtered_dictionaries.append(dic);
+
+    filtered_dictionaries.sort((dic_a, dic_b) => (dic_a < dic_b) ? -1 : 1);
+
+    dictionaries = {};
+    foreach (string dic in filtered_dictionaries)
+	dictionaries += dic;
+
+    return dictionaries;
+}
+
+public string[] get_available_locales() {
+    string[] locales = {};
+
+    try {
+	 string? output = null;
+	 GLib.Subprocess p = new GLib.Subprocess.newv({ "locale", "-a" },
+						      GLib.SubprocessFlags.STDOUT_PIPE);
+	 p.communicate_utf8(null, null, out output, null);
+
+	 foreach (string l in output.split("\n")) {
+	     locales += l;
+	 }
+    } catch (GLib.Error e) {
+	return locales;
+    }
+
+    return locales;
+}
+
+/*
+ * Strip the information about the encoding from the locale.
+ *
+ * That is, en_US.UTF-8 is mapped to en_US, while en_GB remains
+ * unchanged.
+ */
+public string strip_encoding(string locale) {
+    int dot = locale.index_of_char('.');
+    return locale.substring(0, dot);
+}
+
+public string[] get_user_preferred_languages() {
+    GLib.GenericSet<string> dicts = new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
+    foreach (string dict in get_available_dictionaries())
+	dicts.add(dict);
+
+    GLib.GenericSet<string> locales = new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
+    foreach (string locale in get_available_locales())
+	locales.add(strip_encoding(locale));
+
+    string[] output = {};
+    unowned string[] language_names = GLib.Intl.get_language_names();
+    foreach (string lang in language_names) {
+	// Check if we have the associated locale and the dictionary installed before actually
+	//  considering this language.
+	if (lang != "C" && dicts.contains(lang) && locales.contains(lang)) {
+	    output += lang;
+	}
+    }
+    return output;
+}
+
+public string? language_name_from_locale (string locale) {
+    if (language_names == null) {
+	language_names = new HashTable<string, string>(GLib.str_hash, GLib.str_equal);
+
+	unowned Xml.Doc doc = Xml.Parser.parse_file(ISO_CODE_639_XML);
+	if (doc == null) {
+	    return null;
+	}
+	else {
+	    unowned Xml.Node root = doc.get_root_element();
+	    for (unowned Xml.Node entry = root.children; entry != null; entry = entry.next) {
+		if (entry.type == Xml.ElementType.ELEMENT_NODE) {
+		    string? iso_639_1 = null;
+		    string? language_name = null;
+
+		    for (unowned Xml.Attr a = entry.properties; a != null; a = a.next) {
+			switch (a.name) {
+			case "iso_639_1_code":
+			    iso_639_1 = a.children->content;
+			    break;
+			case "name":
+			    language_name = a.children->content;
+			    break;
+			default:
+			    break;
+			}
+
+			if (language_name != null) {
+			    if (iso_639_1 != null) {
+				language_names.insert(iso_639_1, language_name);
+			    }
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    // Look for the name of language matching only the part before the _
+    int pos = -1;
+    if ("_" in locale) {
+	pos = locale.index_of_char('_');
+    }
+
+    // Return a translated version of the language.
+    string language_name = GLib.dgettext("iso_639", language_names.get(locale.substring(0, pos)));
+
+    return language_name;
 }
 
+public string? country_name_from_locale(string locale) {
+    if (country_names == null) {
+	country_names = new HashTable<string, string>(GLib.str_hash, GLib.str_equal);
+
+	unowned Xml.Doc doc = Xml.Parser.parse_file(ISO_CODE_3166_XML);
+
+	if (doc == null) {
+	    return null;
+	}
+	else {
+	    unowned Xml.Node root = doc.get_root_element();
+	    for (unowned Xml.Node entry = root.children; entry != null; entry = entry.next) {
+		if (entry.type == Xml.ElementType.ELEMENT_NODE) {
+		    string? iso_3166 = null;
+		    string? country_name = null;
+
+		    for (unowned Xml.Attr a = entry.properties; a != null; a = a.next) {
+			switch (a.name) {
+			case "alpha_2_code":
+			    iso_3166 = a.children->content;
+			    break;
+			case "name":
+			    country_name = a.children->content;
+			     break;
+			default:
+			    break;
+			}
+
+			if (country_name != null) {
+			    if (iso_3166 != null) {
+				country_names.insert(iso_3166, country_name);
+			    }
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    // Look for the name of language matching only the part before the _
+    int pos = -1;
+    if ("_" in locale) {
+	pos = locale.index_of_char('_');
+    }
+
+    string country_name  = GLib.dgettext("iso_3166", country_names.get(locale.substring(pos+1)));
+
+    return country_name;
+}
+
+}
diff --git a/ui/composer.glade b/ui/composer.glade
index 192360f..2a94e16 100644
--- a/ui/composer.glade
+++ b/ui/composer.glade
@@ -237,6 +237,13 @@
         <property name="icon_name">edit-copy-symbolic</property>
       </object>
     </child>
+    <child>
+      <object class="GtkAction" id="select dictionary">
+        <property name="label" translatable="yes">Select spell checking language</property>
+        <property name="short_label" translatable="yes">Spelling language</property>
+        <property name="icon_name">accessories-dictionary-symbolic</property>
+      </object>
+    </child>
   </object>
   <object class="GtkBox" id="composer">
     <property name="visible">True</property>
@@ -274,18 +281,17 @@
                 <property name="margin_start">6</property>
                 <property name="margin_end">6</property>
                 <property name="margin_top">6</property>
-                <property name="row_spacing">0</property>
                 <property name="column_spacing">6</property>
                 <child>
                   <object class="GtkLabel" id="to label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="xalign">1</property>
+                    <property name="margin_top">6</property>
                     <property name="label" translatable="yes" comments="Address(es) e-mail is to be sent to">_To</property>
                     <property name="use_underline">True</property>
                     <property name="justify">right</property>
                     <property name="mnemonic_widget">to</property>
-                    <property name="margin_top">6</property>
+                    <property name="xalign">1</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -293,20 +299,18 @@
                   <packing>
                     <property name="left_attach">0</property>
                     <property name="top_attach">1</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkLabel" id="cc label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="xalign">1</property>
+                    <property name="margin_top">6</property>
                     <property name="label" translatable="yes">_Cc</property>
                     <property name="use_underline">True</property>
                     <property name="justify">right</property>
                     <property name="mnemonic_widget">to</property>
-                    <property name="margin_top">6</property>
+                    <property name="xalign">1</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -314,8 +318,6 @@
                   <packing>
                     <property name="left_attach">0</property>
                     <property name="top_attach">2</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
@@ -327,8 +329,6 @@
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="top_attach">1</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
@@ -340,36 +340,31 @@
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="top_attach">2</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkEntry" id="subject">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
+                    <property name="margin_top">6</property>
                     <property name="hexpand">True</property>
                     <property name="invisible_char">•</property>
-                    <property name="invisible_char_set">True</property>
-                    <property name="margin_top">6</property>
                   </object>
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="top_attach">5</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkLabel" id="subject label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="xalign">1</property>
+                    <property name="margin_top">6</property>
                     <property name="label" translatable="yes">_Subject</property>
                     <property name="use_underline">True</property>
                     <property name="justify">right</property>
                     <property name="mnemonic_widget">subject</property>
-                    <property name="margin_top">6</property>
+                    <property name="xalign">1</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -377,20 +372,18 @@
                   <packing>
                     <property name="left_attach">0</property>
                     <property name="top_attach">5</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkLabel" id="bcc label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="xalign">1</property>
+                    <property name="margin_top">6</property>
                     <property name="label" translatable="yes">_Bcc</property>
                     <property name="use_underline">True</property>
                     <property name="justify">right</property>
                     <property name="mnemonic_widget">to</property>
-                    <property name="margin_top">6</property>
+                    <property name="xalign">1</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -398,8 +391,6 @@
                   <packing>
                     <property name="left_attach">0</property>
                     <property name="top_attach">3</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
@@ -411,20 +402,18 @@
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="top_attach">3</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkLabel" id="reply to label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="xalign">1</property>
+                    <property name="margin_top">6</property>
                     <property name="label" translatable="yes">_Reply-To</property>
                     <property name="use_underline">True</property>
                     <property name="justify">right</property>
                     <property name="mnemonic_widget">to</property>
-                    <property name="margin_top">6</property>
+                    <property name="xalign">1</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -432,8 +421,6 @@
                   <packing>
                     <property name="left_attach">0</property>
                     <property name="top_attach">4</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
@@ -445,18 +432,16 @@
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="top_attach">4</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
                   <object class="GtkLabel" id="from label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="xalign">1</property>
                     <property name="label" translatable="yes" comments="Geary account mail will be sent from">From</property>
                     <property name="justify">right</property>
                     <property name="mnemonic_widget">to</property>
+                    <property name="xalign">1</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -464,8 +449,6 @@
                   <packing>
                     <property name="left_attach">0</property>
                     <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
@@ -489,8 +472,6 @@
                       <object class="GtkComboBoxText" id="from_multiple">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="entry_text_column">0</property>
-                        <property name="id_column">1</property>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -502,8 +483,6 @@
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
                   </packing>
                 </child>
               </object>
