diff --git a/libgnome-desktop/Makefile.am b/libgnome-desktop/Makefile.am
index 28b71fe..19b91a2 100644
--- a/libgnome-desktop/Makefile.am
+++ b/libgnome-desktop/Makefile.am
@@ -45,6 +45,7 @@ libgnome_desktop_3_la_SOURCES = 	\
 	gnome-datetime-source.h		\
 	gnome-datetime-source.c		\
 	gnome-rr-private.h		\
+	default-input-sources.h		\
 	edid.h				\
 	locarchive.h			\
 	private.h
diff --git a/libgnome-desktop/default-input-sources.h b/libgnome-desktop/default-input-sources.h
new file mode 100644
index 0000000..b055cbe
--- /dev/null
+++ b/libgnome-desktop/default-input-sources.h
@@ -0,0 +1,52 @@
+typedef struct
+{
+  const gchar const *locale;
+  const gchar const *type;
+  const gchar const *id;
+} DefaultInputSource;
+
+static DefaultInputSource default_input_sources[] =
+{
+  { "ar_DZ",    "xkb",          "ara+azerty" },
+  { "as_IN",    "ibus",         "m17n:as:phonetic" },
+  { "ast_ES",   "xkb",          "es+ast" },
+  { "az_AZ",    "xkb",          "az" },
+  { "be_BY",    "xkb",          "by" },
+  { "bn_IN",    "ibus",         "m17n:bn:inscript" },
+  { "cat_ES",   "xkb",          "es+cat" },
+  { "cs_CZ",    "xkb",          "cz" },
+  { "de_DE",    "xkb",          "de" },
+  { "el_CY",    "xkb",          "gr" },
+  { "el_GR",    "xkb",          "gr" },
+  { "en_GB",    "xkb",          "gb" },
+  { "en_US",    "xkb",          "us" },
+  { "es_ES",    "xkb",          "es" },
+  { "fr_FR",    "xkb",          "fr+oss" },
+  { "gl_ES",    "xkb",          "es" },
+  { "gu_IN",    "ibus",         "m17n:gu:inscript" },
+  { "he_IL",    "xkb",          "il" },
+  { "hi_IN",    "ibus",         "m17n:hi:inscript" },
+  { "it_IT",    "xkb",          "it" },
+  { "ja_JP",    "ibus",         "anthy" },
+  { "kn_IN",    "ibus",         "m17n:kn:kgp" },
+  { "ko_KR",    "ibus",         "hangul" },
+  { "mai_IN",   "ibus",         "m17n:mai:inscript" },
+  { "ml_IN",    "ibus",         "m17n:ml:inscript" },
+  { "mr_IN",    "ibus",         "m17n:mr:inscript" },
+  { "nl_NL",    "xkb",          "us+altgr-intl" },
+  { "or_IN",    "ibus",         "m17n:or:inscript" },
+  { "pa_IN",    "ibus",         "m17n:pa:inscript" },
+  { "pl_PL",    "xkb",          "pl" },
+  { "pt_BR",    "xkb",          "br" },
+  { "pt_PT",    "xkb",          "pt" },
+  { "ru_RU",    "xkb",          "ru" },
+  { "sd_IN",    "ibus",         "m17n:sd:inscript" },
+  { "sk_SK",    "xkb",          "sk" },
+  { "ta_IN",    "ibus",         "m17n:ta:tamil99" },
+  { "te_IN",    "ibus",         "m17n:te:inscript" },
+  { "ur_IN",    "ibus",         "m17n:ur:phonetic" },
+  { "zh_CN",    "ibus",         "libpinyin" },
+  { "zh_HK",    "ibus",         "cangjie" },
+  { "zh_TW",    "ibus",         "chewing" },
+  { NULL,       NULL,           NULL }
+};
diff --git a/libgnome-desktop/gnome-languages.c b/libgnome-desktop/gnome-languages.c
index 7b2b592..18e3705 100644
--- a/libgnome-desktop/gnome-languages.c
+++ b/libgnome-desktop/gnome-languages.c
@@ -51,6 +51,8 @@
 #define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
 #define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
 
+#include "default-input-sources.h"
+
 typedef struct _GnomeLocale {
         char *id;
         char *name;
@@ -113,22 +115,36 @@ normalize_codeset (const char *codeset)
         return normalized_codeset;
 }
 
-/*
- * According to http://en.wikipedia.org/wiki/Locale
- * locale names are of the form:
- * [language[_territory][.codeset][@modifier]]
+/**
+ * gnome_parse_locale:
+ * @locale: a locale string
+ * @language_codep: (out) (allow-none) (transfer full): location to
+ * store the language code, or %NULL
+ * @country_codep: (out) (allow-none) (transfer full): location to
+ * store the country code, or %NULL
+ * @codesetp: (out) (allow-none) (transfer full): location to
+ * store the codeset, or %NULL
+ * @modifierp: (out) (allow-none) (transfer full): location to
+ * store the modifier, or %NULL
+ *
+ * Extracts the various components of a locale string of the form
+ * [language[_country][.codeset][@modifier]]. See
+ * http://en.wikipedia.org/wiki/Locale.
+ *
+ * Return value: %TRUE if parsing was successful.
+ *
+ * Since: 3.8
  */
 gboolean
-gnome_parse_language_name (const char *name,
-                           char      **language_codep,
-                           char      **territory_codep,
-                           char      **codesetp,
-                           char      **modifierp)
+gnome_parse_locale (const char *locale,
+                    char      **language_codep,
+                    char      **country_codep,
+                    char      **codesetp,
+                    char      **modifierp)
 {
-        GRegex     *re;
+        static GRegex *re = NULL;
         GMatchInfo *match_info;
         gboolean    res;
-        GError     *error;
         gchar      *normalized_codeset = NULL;
         gchar      *normalized_name = NULL;
         gboolean    retval;
@@ -136,26 +152,29 @@ gnome_parse_language_name (const char *name,
         match_info = NULL;
         retval = FALSE;
 
-        error = NULL;
-        re = g_regex_new ("^(?P<language>[^_.@[:space:]]+)"
-                          "(_(?P<territory>[[:upper:]]+))?"
-                          "(\\.(?P<codeset>[-_0-9a-zA-Z]+))?"
-                          "(@(?P<modifier>[[:ascii:]]+))?$",
-                          0, 0, &error);
         if (re == NULL) {
-                g_warning ("%s", error->message);
-                goto out;
+                GError *error = NULL;
+                re = g_regex_new ("^(?P<language>[^_.@[:space:]]+)"
+                                  "(_(?P<territory>[[:upper:]]+))?"
+                                  "(\\.(?P<codeset>[-_0-9a-zA-Z]+))?"
+                                  "(@(?P<modifier>[[:ascii:]]+))?$",
+                                  0, 0, &error);
+                if (re == NULL) {
+                        g_warning ("%s", error->message);
+                        g_error_free (error);
+                        goto out;
+                }
         }
 
-        if (!g_regex_match (re, name, 0, &match_info) ||
+        if (!g_regex_match (re, locale, 0, &match_info) ||
             g_match_info_is_partial_match (match_info)) {
-                g_warning ("locale '%s' isn't valid\n", name);
+                g_warning ("locale '%s' isn't valid\n", locale);
                 goto out;
         }
 
         res = g_match_info_matches (match_info);
         if (! res) {
-                g_warning ("Unable to parse locale: %s", name);
+                g_warning ("Unable to parse locale: %s", locale);
                 goto out;
         }
 
@@ -165,13 +184,13 @@ gnome_parse_language_name (const char *name,
                 *language_codep = g_match_info_fetch_named (match_info, "language");
         }
 
-        if (territory_codep != NULL) {
-                *territory_codep = g_match_info_fetch_named (match_info, "territory");
+        if (country_codep != NULL) {
+                *country_codep = g_match_info_fetch_named (match_info, "territory");
 
-                if (*territory_codep != NULL &&
-                    *territory_codep[0] == '\0') {
-                        g_free (*territory_codep);
-                        *territory_codep = NULL;
+                if (*country_codep != NULL &&
+                    *country_codep[0] == '\0') {
+                        g_free (*country_codep);
+                        *country_codep = NULL;
                 }
         }
 
@@ -198,7 +217,7 @@ gnome_parse_language_name (const char *name,
         if (codesetp != NULL && *codesetp != NULL) {
                 normalized_codeset = normalize_codeset (*codesetp);
                 normalized_name = construct_language_name (language_codep ? *language_codep : NULL,
-                                                           territory_codep ? *territory_codep : NULL,
+                                                           country_codep ? *country_codep : NULL,
                                                            normalized_codeset,
                                                            modifierp ? *modifierp : NULL);
 
@@ -213,7 +232,6 @@ gnome_parse_language_name (const char *name,
 
  out:
         g_match_info_free (match_info);
-        g_regex_unref (re);
 
         return retval;
 }
@@ -243,23 +261,38 @@ construct_language_name (const char *language,
         return name;
 }
 
+/**
+ * gnome_normalize_locale:
+ * @locale: a locale string
+ *
+ * Gets the normalized locale string in the form
+ * [language[_country][.codeset][@modifier]] for @name.
+ *
+ * Return value: (transfer full): normalized locale string. Caller
+ * takes ownership.
+ *
+ * Since: 3.8
+ */
 char *
-gnome_normalize_language_name (const char *name)
+gnome_normalize_locale (const char *locale)
 {
         char *normalized_name;
         char *language_code;
         char *territory_code;
         char *codeset;
         char *modifier;
+        gboolean valid;
 
-        if (name[0] == '\0') {
+        if (locale[0] == '\0') {
                 return NULL;
         }
 
-        gnome_parse_language_name (name,
-                                   &language_code,
-                                   &territory_code,
-                                   &codeset, &modifier);
+        valid = gnome_parse_locale (locale,
+                                    &language_code,
+                                    &territory_code,
+                                    &codeset, &modifier);
+        if (!valid)
+                return NULL;
 
         normalized_name = construct_language_name (language_code,
                                                    territory_code,
@@ -323,15 +356,25 @@ language_name_get_codeset_details (const char  *language_name,
         g_free (old_locale);
 }
 
+/**
+ * gnome_language_has_translations:
+ * @code: an ISO 639 code string
+ *
+ * Returns %TRUE if there are translations for language @code.
+ *
+ * Return value: %TRUE if there are translations for language @code.
+ *
+ * Since: 3.8
+ */
 gboolean
-gnome_language_has_translations (const char *language_name)
+gnome_language_has_translations (const char *code)
 {
         GDir        *dir;
         char        *path;
         const char  *name;
         gboolean     has_translations;
 
-        path = g_build_filename (GNOMELOCALEDIR, language_name, "LC_MESSAGES", NULL);
+        path = g_build_filename (GNOMELOCALEDIR, code, "LC_MESSAGES", NULL);
 
         has_translations = FALSE;
         dir = g_dir_open (path, 0, NULL);
@@ -394,11 +437,11 @@ add_locale (const char *language_name,
         }
 
         locale = g_new0 (GnomeLocale, 1);
-        gnome_parse_language_name (name,
-                                   &locale->language_code,
-                                   &locale->territory_code,
-                                   &locale->codeset,
-                                   &locale->modifier);
+        gnome_parse_locale (name,
+                            &locale->language_code,
+                            &locale->territory_code,
+                            &locale->codeset,
+                            &locale->modifier);
         g_free (name);
         name = NULL;
 
@@ -1051,6 +1094,9 @@ languages_variant_init (const char *variant)
 static void
 languages_init (void)
 {
+        if (gnome_languages_map)
+                return;
+
         gnome_languages_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
         languages_variant_init ("iso_639");
@@ -1065,6 +1111,9 @@ territories_init (void)
         char    *buf;
         gsize    buf_len;
 
+        if (gnome_territories_map)
+                return;
+
         bindtextdomain ("iso_3166", ISO_CODES_LOCALESDIR);
         bind_textdomain_codeset ("iso_3166", "UTF-8");
 
@@ -1101,9 +1150,22 @@ territories_init (void)
         }
 }
 
+/**
+ * gnome_get_language_from_locale:
+ * @locale: a locale string
+ * @translation: (allow-none): a locale string
+ *
+ * Gets the language description for @locale. If @translation is
+ * provided the returned string is translated accordingly.
+ *
+ * Return value: (transfer full): the language description. Caller
+ * takes ownership.
+ *
+ * Since: 3.8
+ */
 char *
-gnome_get_language_from_name (const char *name,
-                              const char *locale)
+gnome_get_language_from_locale (const char *locale,
+                                const char *translation)
 {
         GString *full_language;
         char *language_code;
@@ -1114,8 +1176,8 @@ gnome_get_language_from_name (const char *name,
         char *translated_territory;
         gboolean is_utf8 = TRUE;
 
-        g_return_val_if_fail (name != NULL, NULL);
-        g_return_val_if_fail (*name != '\0', NULL);
+        g_return_val_if_fail (locale != NULL, NULL);
+        g_return_val_if_fail (*locale != '\0', NULL);
 
         translated_territory = NULL;
         translated_language = NULL;
@@ -1123,29 +1185,24 @@ gnome_get_language_from_name (const char *name,
 
         full_language = g_string_new (NULL);
 
-        if (gnome_languages_map == NULL) {
-                languages_init ();
-        }
-
-        if (gnome_territories_map == NULL) {
-                territories_init ();
-        }
+        languages_init ();
+        territories_init ();
 
         language_code = NULL;
         territory_code = NULL;
         codeset_code = NULL;
 
-        gnome_parse_language_name (name,
-                                   &language_code,
-                                   &territory_code,
-                                   &codeset_code,
-                                   NULL);
+        gnome_parse_locale (locale,
+                            &language_code,
+                            &territory_code,
+                            &codeset_code,
+                            NULL);
 
         if (language_code == NULL) {
                 goto out;
         }
 
-        translated_language = get_translated_language (language_code, locale);
+        translated_language = get_translated_language (language_code, translation);
         if (translated_language == NULL) {
                 goto out;
         }
@@ -1157,7 +1214,7 @@ gnome_get_language_from_name (const char *name,
 	}
 
         if (territory_code != NULL) {
-                translated_territory = get_translated_territory (territory_code, locale);
+                translated_territory = get_translated_territory (territory_code, translation);
         }
         if (translated_territory != NULL) {
                 g_string_append_printf (full_language,
@@ -1165,7 +1222,7 @@ gnome_get_language_from_name (const char *name,
                                         translated_territory);
         }
 
-        language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
+        language_name_get_codeset_details (locale, &langinfo_codeset, &is_utf8);
 
         if (codeset_code == NULL && langinfo_codeset != NULL) {
                 codeset_code = g_strdup (langinfo_codeset);
@@ -1193,9 +1250,22 @@ gnome_get_language_from_name (const char *name,
         return g_string_free (full_language, FALSE);
 }
 
+/**
+ * gnome_get_country_from_locale:
+ * @locale: a locale string
+ * @translation: (allow-none): a locale string
+ *
+ * Gets the country description for @locale. If @translation is
+ * provided the returned string is translated accordingly.
+ *
+ * Return value: (transfer full): the country description. Caller
+ * takes ownership.
+ *
+ * Since: 3.8
+ */
 char *
-gnome_get_region_from_name (const char *name,
-                            const char *locale)
+gnome_get_country_from_locale (const char *locale,
+                               const char *translation)
 {
         GString *full_name;
         char *language_code;
@@ -1206,8 +1276,8 @@ gnome_get_region_from_name (const char *name,
         char *translated_territory;
         gboolean is_utf8 = TRUE;
 
-        g_return_val_if_fail (name != NULL, NULL);
-        g_return_val_if_fail (*name != '\0', NULL);
+        g_return_val_if_fail (locale != NULL, NULL);
+        g_return_val_if_fail (*locale != '\0', NULL);
 
         translated_territory = NULL;
         translated_language = NULL;
@@ -1215,29 +1285,24 @@ gnome_get_region_from_name (const char *name,
 
         full_name = g_string_new (NULL);
 
-        if (gnome_languages_map == NULL) {
-                languages_init ();
-        }
-
-        if (gnome_territories_map == NULL) {
-                territories_init ();
-        }
+        languages_init ();
+        territories_init ();
 
         language_code = NULL;
         territory_code = NULL;
         codeset_code = NULL;
 
-        gnome_parse_language_name (name,
-                                   &language_code,
-                                   &territory_code,
-                                   &codeset_code,
-                                   NULL);
+        gnome_parse_locale (locale,
+                            &language_code,
+                            &territory_code,
+                            &codeset_code,
+                            NULL);
 
         if (territory_code == NULL) {
                 goto out;
         }
 
-        translated_territory = get_translated_territory (territory_code, locale);
+        translated_territory = get_translated_territory (territory_code, translation);
         g_string_append (full_name, translated_territory);
 
 	if (is_unique_territory (territory_code)) {
@@ -1245,7 +1310,7 @@ gnome_get_region_from_name (const char *name,
 	}
 
         if (language_code != NULL) {
-                translated_language = get_translated_language (language_code, locale);
+                translated_language = get_translated_language (language_code, translation);
         }
         if (translated_language != NULL) {
                 g_string_append_printf (full_name,
@@ -1253,7 +1318,7 @@ gnome_get_region_from_name (const char *name,
                                         translated_language);
         }
 
-        language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
+        language_name_get_codeset_details (translation, &langinfo_codeset, &is_utf8);
 
         if (codeset_code == NULL && langinfo_codeset != NULL) {
                 codeset_code = g_strdup (langinfo_codeset);
@@ -1281,8 +1346,19 @@ gnome_get_region_from_name (const char *name,
         return g_string_free (full_name, FALSE);
 }
 
+/**
+ * gnome_get_all_locales:
+ *
+ * Gets all locales.
+ *
+ * Return value: (array zero-terminated=1) (element-type utf8) (transfer full):
+ *   a newly allocated %NULL-terminated string array containing the
+ *   all locales. Free with g_strfreev().
+ *
+ * Since: 3.8
+ */
 char **
-gnome_get_all_language_names (void)
+gnome_get_all_locales (void)
 {
         GHashTableIter iter;
         gpointer key, value;
@@ -1305,3 +1381,107 @@ gnome_get_all_language_names (void)
 
         return (char **) g_ptr_array_free (array, FALSE);
 }
+
+/**
+ * gnome_get_language_from_code:
+ * @code: an ISO 639 code string
+ * @translation: (allow-none): a locale string
+ *
+ * Gets the language name for @code. If @locale is provided the
+ * returned string is translated accordingly.
+ *
+ * Return value: (transfer full): the language name. Caller takes
+ * ownership.
+ *
+ * Since: 3.8
+ */
+char *
+gnome_get_language_from_code (const char *code,
+                              const char *translation)
+{
+        g_return_val_if_fail (code != NULL, NULL);
+
+        languages_init ();
+
+        return get_translated_language (code, translation);
+}
+
+/**
+ * gnome_get_country_from_code:
+ * @code: an ISO 3166 code string
+ * @translation: (allow-none): a locale string
+ *
+ * Gets the country name for @code. If @locale is provided the
+ * returned string is translated accordingly.
+ *
+ * Return value: (transfer full): the country name. Caller takes
+ * ownership.
+ *
+ * Since: 3.8
+ */
+char *
+gnome_get_country_from_code (const char *code,
+                             const char *translation)
+{
+        g_return_val_if_fail (code != NULL, NULL);
+
+        territories_init ();
+
+        return get_translated_territory (code, translation);
+}
+
+/**
+ * gnome_get_input_source_from_locale:
+ * @locale: a locale string
+ * @type: (out) (transfer none): location to store the input source
+ * type
+ * @id: (out) (transfer none): location to store the input source
+ * identifier
+ *
+ * Gets the default input source's type and identifier for a given
+ * locale.
+ *
+ * Return value: %TRUE if a input source exists or %FALSE otherwise.
+ *
+ * Since: 3.8
+ */
+gboolean
+gnome_get_input_source_from_locale (const char  *locale,
+                                    const char **type,
+                                    const char **id)
+{
+        static GHashTable *table = NULL;
+        DefaultInputSource *dis;
+        gchar *l_code, *c_code;
+        gchar *key;
+        gint i;
+
+        g_return_val_if_fail (locale != NULL, FALSE);
+        g_return_val_if_fail (type != NULL, FALSE);
+        g_return_val_if_fail (id != NULL, FALSE);
+
+        if (!table) {
+                table = g_hash_table_new (g_str_hash, g_str_equal);
+                for (i = 0; default_input_sources[i].id; ++i) {
+                        dis = &default_input_sources[i];
+                        g_hash_table_insert (table, (gpointer) dis->locale, dis);
+                }
+        }
+
+        if (!gnome_parse_locale (locale, &l_code, &c_code, NULL, NULL))
+                return FALSE;
+
+        key = g_strconcat (l_code, "_", c_code, NULL);
+
+        dis = g_hash_table_lookup (table, key);
+        if (dis) {
+                *type = dis->type;
+                *id = dis->id;
+        }
+
+        g_free (l_code);
+        g_free (c_code);
+        g_free (key);
+
+        return dis != NULL;
+}
diff --git a/libgnome-desktop/gnome-languages.h b/libgnome-desktop/gnome-languages.h
index 2a05dfd..948d554 100644
--- a/libgnome-desktop/gnome-languages.h
+++ b/libgnome-desktop/gnome-languages.h
@@ -30,18 +30,25 @@
 
 G_BEGIN_DECLS
 
-char *        gnome_get_language_from_name  (const char *name,
-                                             const char *locale);
-char *        gnome_get_region_from_name    (const char *name,
-                                             const char *locale);
-char **       gnome_get_all_language_names  (void);
-gboolean      gnome_parse_language_name     (const char *name,
-                                             char      **language_codep,
-                                             char      **territory_codep,
-                                             char      **codesetp,
-                                             char      **modifierp);
-char *        gnome_normalize_language_name (const char *name);
-gboolean      gnome_language_has_translations (const char *language_name);
+char *        gnome_get_language_from_locale    (const char *locale,
+                                                 const char *translation);
+char *        gnome_get_country_from_locale     (const char *locale,
+                                                 const char *translation);
+char **       gnome_get_all_locales             (void);
+gboolean      gnome_parse_locale                (const char *locale,
+                                                 char      **language_codep,
+                                                 char      **country_codep,
+                                                 char      **codesetp,
+                                                 char      **modifierp);
+char *        gnome_normalize_locale            (const char *locale);
+gboolean      gnome_language_has_translations   (const char *code);
+char *        gnome_get_language_from_code      (const char *code,
+                                                 const char *translation);
+char *        gnome_get_country_from_code       (const char *code,
+                                                 const char *translation);
+gboolean      gnome_get_input_source_from_locale (const char  *locale,
+                                                  const char **type,
+                                                  const char **id);
 
 G_END_DECLS
 
diff --git a/libgnome-desktop/gnome-xkb-info.c b/libgnome-desktop/gnome-xkb-info.c
index aa3105e..cc43f91 100644
--- a/libgnome-desktop/gnome-xkb-info.c
+++ b/libgnome-desktop/gnome-xkb-info.c
@@ -287,6 +287,8 @@ parse_start_element (GMarkupParseContext  *context,
     }
   else if (strcmp (element_name, "variant") == 0)
     {
+      Layout *layout;
+
       if (priv->current_parser_variant)
         {
           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
@@ -301,9 +303,20 @@ parse_start_element (GMarkupParseContext  *context,
           return;
         }
 
+      if (!priv->current_parser_layout->xkb_name)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must be inside named 'layout' elements");
+          return;
+        }
+
+      layout = g_hash_table_lookup (priv->layouts_table, priv->current_parser_layout->xkb_name);
+      if (!layout)
+        layout = priv->current_parser_layout;
+
       priv->current_parser_variant = g_slice_new0 (Layout);
       priv->current_parser_variant->is_variant = TRUE;
-      priv->current_parser_variant->main_layout = priv->current_parser_layout;
+      priv->current_parser_variant->main_layout = layout;
     }
   else if (strcmp (element_name, "group") == 0)
     {
@@ -352,40 +365,15 @@ add_layout_to_table (GHashTable  *table,
                      const gchar *key,
                      Layout      *layout)
 {
-  GSequence *seq;
+  GHashTable *set;
 
-  seq = g_hash_table_lookup (table, key);
-  if (!seq)
+  set = g_hash_table_lookup (table, key);
+  if (!set)
     {
-      seq = g_sequence_new (NULL);
-      g_hash_table_replace (table, g_strdup (key), seq);
+      set = g_hash_table_new (NULL, NULL);
+      g_hash_table_replace (table, g_strdup (key), set);
     }
-  g_sequence_append (seq, layout);
-}
-
-static gint
-layout_compare (Layout   *a,
-                Layout   *b,
-                gpointer  data)
-{
-  return g_strcmp0 (a->id, b->id);
-}
-
-static void
-sort_sequence (gpointer key,
-               gpointer value,
-               gpointer data)
-{
-  g_sequence_sort ((GSequence *) value, (GCompareDataFunc) layout_compare, NULL);
-}
-
-static void
-sort_sequences (GnomeXkbInfo *self)
-{
-  GnomeXkbInfoPrivate *priv = self->priv;
-
-  g_hash_table_foreach (priv->layouts_by_country, sort_sequence, NULL);
-  g_hash_table_foreach (priv->layouts_by_language, sort_sequence, NULL);
+  g_hash_table_add (set, layout);
 }
 
 static void
@@ -407,6 +395,14 @@ parse_end_element (GMarkupParseContext  *context,
 
       priv->current_parser_layout->id = g_strdup (priv->current_parser_layout->xkb_name);
 
+      if (g_hash_table_contains (priv->layouts_table, priv->current_parser_layout->id))
+        {
+          g_hash_table_remove (priv->layouts_by_country, priv->current_parser_layout);
+          g_hash_table_remove (priv->layouts_by_language, priv->current_parser_layout);
+          g_clear_pointer (&priv->current_parser_layout, free_layout);
+          return;
+        }
+
       g_hash_table_replace (priv->layouts_table,
                             priv->current_parser_layout->id,
                             priv->current_parser_layout);
@@ -442,7 +438,7 @@ parse_end_element (GMarkupParseContext  *context,
           return;
         }
 
-      language = gnome_get_language_from_name (priv->current_parser_iso639Id, NULL);
+      language = gnome_get_language_from_code (priv->current_parser_iso639Id, NULL);
       if (language)
         {
           if (priv->current_parser_variant)
@@ -466,7 +462,7 @@ parse_end_element (GMarkupParseContext  *context,
           return;
         }
 
-      country = gnome_get_region_from_name (priv->current_parser_iso3166Id, NULL);
+      country = gnome_get_country_from_code (priv->current_parser_iso3166Id, NULL);
       if (country)
         {
           if (priv->current_parser_variant)
@@ -586,16 +582,16 @@ parse_rules (GnomeXkbInfo *self)
      XkbOptionGroup structs. */
   priv->option_groups_table = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                      NULL, free_option_group);
-  /* Maps country strings to a GSequence which is a set of Layout
+  /* Maps country strings to a GHashTable which is a set of Layout
      struct pointers into the Layout structs stored in
      layouts_table. */
   priv->layouts_by_country = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                    g_free, (GDestroyNotify) g_sequence_free);
-  /* Maps language strings to a GSequence which is a set of Layout
+                                                    g_free, (GDestroyNotify) g_hash_table_destroy);
+  /* Maps language strings to a GHashTable which is a set of Layout
      struct pointers into the Layout structs stored in
      layouts_table. */
   priv->layouts_by_language = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                     g_free, (GDestroyNotify) g_sequence_free);
+                                                     g_free, (GDestroyNotify) g_hash_table_destroy);
   /* Maps layout ids to Layout structs. Owns the Layout structs. */
   priv->layouts_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_layout);
 
@@ -605,8 +601,6 @@ parse_rules (GnomeXkbInfo *self)
     goto cleanup;
   g_free (file_path);
 
-  sort_sequences (self);
-
   settings = g_settings_new ("org.gnome.desktop.input-sources");
   show_all_sources = g_settings_get_boolean (settings, "show-all-sources");
   g_object_unref (settings);
@@ -620,8 +614,6 @@ parse_rules (GnomeXkbInfo *self)
     goto cleanup;
   g_free (file_path);
 
-  sort_sequences (self);
-
   return;
 
  cleanup:
@@ -932,156 +924,99 @@ gnome_xkb_info_get_layout_info (GnomeXkbInfo *self,
   return TRUE;
 }
 
-static Layout *
-find_first_layout_in_both (GSequence *seq_a,
-                           GSequence *seq_b)
+static void
+collect_layout_ids (gpointer key,
+                    gpointer value,
+                    gpointer data)
 {
-  Layout *layout;
-  GSequenceIter *iter_a, *iter_b;
-
-  layout = NULL;
+  Layout *layout = value;
+  GList **list = data;
 
-  iter_a = g_sequence_get_begin_iter (seq_a);
-  while (!g_sequence_iter_is_end (iter_a))
-    {
-      iter_b = g_sequence_lookup (seq_b, g_sequence_get (iter_a),
-                                  (GCompareDataFunc) layout_compare, NULL);
-      if (!g_sequence_iter_is_end (iter_b))
-        {
-          layout = g_sequence_get (iter_b);
-          break;
-        }
-
-      iter_a = g_sequence_iter_next (iter_a);
-    }
-
-  return layout;
+  *list = g_list_prepend (*list, layout->id);
 }
 
-static gboolean
-find_best_layout_for_locale (GnomeXkbInfo  *self,
-                             const gchar   *locale,
-                             Layout       **layout)
+/**
+ * gnome_xkb_info_get_layouts_for_language:
+ * @self: a #GnomeXkbInfo
+ * @language_code: an ISO 639 code string
+ *
+ * Returns a list of all layout identifiers we know about for
+ * @language_code.
+ *
+ * Return value: (transfer container) (element-type utf8): the list
+ * of layout ids. The caller takes ownership of the #GList but not of
+ * the strings themselves, those are internally allocated and must not
+ * be modified.
+ *
+ * Since: 3.8
+ */
+GList *
+gnome_xkb_info_get_layouts_for_language (GnomeXkbInfo *self,
+                                         const gchar  *language_code)
 {
-  GnomeXkbInfoPrivate *priv = self->priv;
-  gchar *language, *l_code;
-  gchar *country, *c_code;
-  GSequence *layouts_for_language;
-  GSequence *layouts_for_country;
-
-  if (!gnome_parse_language_name (locale, &l_code, &c_code, NULL, NULL))
-    return FALSE;
-
-  language = gnome_get_language_from_name (l_code, NULL);
-  country = gnome_get_region_from_name (c_code, NULL);
+  GnomeXkbInfoPrivate *priv;
+  GHashTable *layouts_for_language;
+  gchar *language;
+  GList *list;
 
-  g_free (l_code);
-  g_free (c_code);
+  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
-  if (country)
-    layouts_for_country = g_hash_table_lookup (priv->layouts_by_country, country);
-  else
-    layouts_for_country = NULL;
+  priv = self->priv;
 
-  if (language)
-    layouts_for_language = g_hash_table_lookup (priv->layouts_by_language, language);
-  else
-    layouts_for_language = NULL;
+  if (!ensure_rules_are_parsed (self))
+    return NULL;
 
-  g_free (country);
-  g_free (language);
+  language = gnome_get_language_from_code (language_code, NULL);
 
-  *layout = NULL;
+  layouts_for_language = g_hash_table_lookup (priv->layouts_by_language, language);
+  if (!layouts_for_language)
+    return NULL;
 
-  if (layouts_for_country && layouts_for_language)
-    *layout = find_first_layout_in_both (layouts_for_country, layouts_for_language);
-  else if (layouts_for_country)
-    *layout = g_sequence_get (g_sequence_get_begin_iter (layouts_for_country));
-  else if (layouts_for_language)
-    *layout = g_sequence_get (g_sequence_get_begin_iter (layouts_for_language));
+  list = NULL;
+  g_hash_table_foreach (layouts_for_language, collect_layout_ids, &list);
 
-  return *layout != NULL;
+  return list;
 }
 
 /**
- * gnome_xkb_info_get_layout_info_for_locale:
+ * gnome_xkb_info_get_layouts_for_country:
  * @self: a #GnomeXkbInfo
- * @locale: a locale string
- * @id: (out) (allow-none) (transfer none): location to store the
- * layout's indentifier, or %NULL
- * @display_name: (out) (allow-none) (transfer none): location to store
- * the layout's display name, or %NULL
- * @short_name: (out) (allow-none) (transfer none): location to store
- * the layout's short name, or %NULL
- * @xkb_layout: (out) (allow-none) (transfer none): location to store
- * the layout's XKB name, or %NULL
- * @xkb_variant: (out) (allow-none) (transfer none): location to store
- * the layout's XKB variant, or %NULL
+ * @country_code: an ISO 3166 code string
  *
- * Retrieves the layout that better fits @locale. It also fetches
- * information about that layout like gnome_xkb_info_get_layout_info().
+ * Returns a list of all layout identifiers we know about for
+ * @country_code.
  *
- * If a layout can't be found the return value is %FALSE and all the
- * (out) parameters are set to %NULL.
- *
- * Return value: %TRUE if a layout exists or %FALSE otherwise.
+ * Return value: (transfer container) (element-type utf8): the list
+ * of layout ids. The caller takes ownership of the #GList but not of
+ * the strings themselves, those are internally allocated and must not
+ * be modified.
  *
  * Since: 3.8
  */
-gboolean
-gnome_xkb_info_get_layout_info_for_locale (GnomeXkbInfo *self,
-                                           const gchar  *locale,
-                                           const gchar **id,
-                                           const gchar **display_name,
-                                           const gchar **short_name,
-                                           const gchar **xkb_layout,
-                                           const gchar **xkb_variant)
+GList *
+gnome_xkb_info_get_layouts_for_country (GnomeXkbInfo *self,
+                                        const gchar  *country_code)
 {
-  Layout *layout;
+  GnomeXkbInfoPrivate *priv;
+  GHashTable *layouts_for_country;
+  gchar *country;
+  GList *list;
 
-  if (id)
-    *id = NULL;
-  if (display_name)
-    *display_name = NULL;
-  if (short_name)
-    *short_name = NULL;
-  if (xkb_layout)
-    *xkb_layout = NULL;
-  if (xkb_variant)
-    *xkb_variant = NULL;
+  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
-  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), FALSE);
+  priv = self->priv;
 
   if (!ensure_rules_are_parsed (self))
-    return FALSE;
+    return NULL;
 
-  if (!find_best_layout_for_locale (self, locale, &layout))
-    return FALSE;
+  country = gnome_get_country_from_code (country_code, NULL);
 
-  if (id)
-    *id = layout->id;
-  if (display_name)
-    *display_name = XKEYBOARD_CONFIG_(layout->description);
+  layouts_for_country = g_hash_table_lookup (priv->layouts_by_country, country);
+  if (!layouts_for_country)
+    return NULL;
 
-  if (!layout->is_variant)
-    {
-      if (short_name)
-        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc : "");
-      if (xkb_layout)
-        *xkb_layout = layout->xkb_name;
-      if (xkb_variant)
-        *xkb_variant = "";
-    }
-  else
-    {
-      if (short_name)
-        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc :
-                        layout->main_layout->short_desc ? layout->main_layout->short_desc : "");
-      if (xkb_layout)
-        *xkb_layout = layout->main_layout->xkb_name;
-      if (xkb_variant)
-        *xkb_variant = layout->xkb_name;
-    }
+  list = NULL;
+  g_hash_table_foreach (layouts_for_country, collect_layout_ids, &list);
 
-  return TRUE;
+  return list;
 }
diff --git a/libgnome-desktop/gnome-xkb-info.h b/libgnome-desktop/gnome-xkb-info.h
index 68d5c47..4abb6ef 100644
--- a/libgnome-desktop/gnome-xkb-info.h
+++ b/libgnome-desktop/gnome-xkb-info.h
@@ -67,13 +67,6 @@ gboolean        gnome_xkb_info_get_layout_info                  (GnomeXkbInfo *s
                                                                  const gchar **short_name,
                                                                  const gchar **xkb_layout,
                                                                  const gchar **xkb_variant);
-gboolean        gnome_xkb_info_get_layout_info_for_locale       (GnomeXkbInfo *self,
-                                                                 const gchar  *locale,
-                                                                 const gchar **id,
-                                                                 const gchar **display_name,
-                                                                 const gchar **short_name,
-                                                                 const gchar **xkb_layout,
-                                                                 const gchar **xkb_variant);
 GList          *gnome_xkb_info_get_all_option_groups            (GnomeXkbInfo *self);
 const gchar    *gnome_xkb_info_description_for_group            (GnomeXkbInfo *self,
                                                                  const gchar  *group_id);
@@ -82,6 +75,10 @@ GList          *gnome_xkb_info_get_options_for_group            (GnomeXkbInfo *s
 const gchar    *gnome_xkb_info_description_for_option           (GnomeXkbInfo *self,
                                                                  const gchar  *group_id,
                                                                  const gchar  *id);
+GList          *gnome_xkb_info_get_layouts_for_language         (GnomeXkbInfo *self,
+                                                                 const gchar  *language_code);
+GList          *gnome_xkb_info_get_layouts_for_country          (GnomeXkbInfo *self,
+                                                                 const gchar  *country_code);
 
 void            gnome_xkb_info_get_var_defs                     (gchar            **rules,
                                                                  XkbRF_VarDefsRec **var_defs);
diff --git a/libgnome-desktop/test-languages.c b/libgnome-desktop/test-languages.c
index 0420832..76f9cc0 100644
--- a/libgnome-desktop/test-languages.c
+++ b/libgnome-desktop/test-languages.c
@@ -9,39 +9,43 @@
 
 int main (int argc, char **argv)
 {
-        char **langs;
+        char **locales;
         guint i;
 
-        setlocale (LC_ALL, NULL);
+        setlocale (LC_ALL, "");
         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
         if (argc > 1) {
                 guint i;
                 for (i = 1; i < argc; i++) {
-                        char *lang, *norm;
-                        norm = gnome_normalize_language_name (argv[i]);
-                        lang = gnome_get_language_from_name (norm, NULL);
-                        g_print ("%s (norm: %s) == %s\n", argv[i], norm, lang);
+                        char *lang, *country, *norm;
+                        norm = gnome_normalize_locale (argv[i]);
+                        lang = gnome_get_language_from_locale (norm, NULL);
+                        country = gnome_get_country_from_locale (norm, NULL);
+                        g_print ("%s (norm: %s) == %s -- %s\n", argv[i], norm, lang, country);
                         g_free (norm);
                         g_free (lang);
+                        g_free (country);
                 }
                 return 0;
         }
 
-        langs = gnome_get_all_language_names ();
-        if (langs == NULL) {
-                g_warning ("No languages found");
+        locales = gnome_get_all_locales ();
+        if (locales == NULL) {
+                g_warning ("No locales found");
                 return 1;
         }
 
-        for (i = 0; langs[i] != NULL; i++) {
-                char *lang;
-                lang = gnome_get_language_from_name (langs[i], NULL);
-                g_print ("%s == %s\n", langs[i], lang);
+        for (i = 0; locales[i] != NULL; i++) {
+                char *lang, *country;
+                lang = gnome_get_language_from_locale (locales[i], NULL);
+                country = gnome_get_country_from_locale (locales[i], NULL);
+                g_print ("%s == %s -- %s\n", locales[i], lang, country);
                 g_free (lang);
+                g_free (country);
         }
 
-        g_strfreev (langs);
+        g_strfreev (locales);
 
         return 0;
 }
