]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blobdiff - liblightdm-gobject/language.c
Revert r2392 - it seems to have broken ABI in liblightdm-gobject
[sojka/lightdm.git] / liblightdm-gobject / language.c
index a75985808f5dbb788f63ae573c330f39dca88e22..ac17645cd3c142f88a8acfa2bbcbc04cbf98c746 100644 (file)
@@ -1,22 +1,23 @@
 /*
  * Copyright (C) 2010 Robert Ancell.
  * Author: Robert Ancell <robert.ancell@canonical.com>
- * 
+ *
  * This library is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option) any
- * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
- * license.
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
  */
 
+#include <string.h>
 #include <locale.h>
 #include <langinfo.h>
+#include <stdio.h>
+#include <glib/gi18n.h>
 
 #include "lightdm/language.h"
 
 enum {
-    PROP_0,
-    PROP_CODE,
+    PROP_CODE = 1,
     PROP_NAME,
     PROP_TERRITORY
 };
@@ -26,126 +27,284 @@ typedef struct
     gchar *code;
     gchar *name;
     gchar *territory;
-} LdmLanguagePrivate;
+} LightDMLanguagePrivate;
+
+G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LANGUAGE, LightDMLanguagePrivate)
 
-G_DEFINE_TYPE (LdmLanguage, ldm_language, G_TYPE_OBJECT);
+static gboolean have_languages = FALSE;
+static GList *languages = NULL;
 
-#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LDM_TYPE_LANGUAGE, LdmLanguagePrivate)
+static void
+update_languages (void)
+{
+    gchar *command = "locale -a";
+    gchar *stdout_text = NULL, *stderr_text = NULL;
+    gint exit_status;
+    gboolean result;
+    GError *error = NULL;
+
+    if (have_languages)
+        return;
+
+    result = g_spawn_command_line_sync (command, &stdout_text, &stderr_text, &exit_status, &error);
+    if (error)
+    {
+        g_warning ("Failed to run '%s': %s", command, error->message);
+        g_clear_error (&error);
+    }
+    else if (exit_status != 0)
+        g_warning ("Failed to get languages, '%s' returned %d", command, exit_status);
+    else if (result)
+    {
+        gchar **tokens;
+        int i;
+
+        tokens = g_strsplit_set (stdout_text, "\n\r", -1);
+        for (i = 0; tokens[i]; i++)
+        {
+            LightDMLanguage *language;
+            gchar *code;
+
+            code = g_strchug (tokens[i]);
+            if (code[0] == '\0')
+                continue;
+
+            /* Ignore the non-interesting languages */
+            if (strcmp (command, "locale -a") == 0 && !g_strrstr (code, ".utf8"))
+                continue;
+
+            language = g_object_new (LIGHTDM_TYPE_LANGUAGE, "code", code, NULL);
+            languages = g_list_append (languages, language);
+        }
+
+        g_strfreev (tokens);
+    }
+
+    g_free (stdout_text);
+    g_free (stderr_text);
+
+    have_languages = TRUE;
+}
+
+static gboolean
+is_utf8 (const gchar *code)
+{
+    return g_strrstr (code, ".utf8") || g_strrstr (code, ".UTF-8");
+}
+
+/* Get a valid locale name that can be passed to setlocale(), so we always can use nl_langinfo() to get language and country names. */
+static gchar *
+get_locale_name (const gchar *code)
+{
+    gchar *locale = NULL, *language;
+    char *at;
+    static gchar **avail_locales;
+    gint i;
+
+    if (is_utf8 (code))
+        return (gchar *) code;
+
+    if ((at = strchr (code, '@')))
+        language = g_strndup (code, at - code);
+    else
+        language = g_strdup (code);
+
+    if (!avail_locales)
+    {
+        gchar *locales;
+        GError *error = NULL;
+
+        if (g_spawn_command_line_sync ("locale -a", &locales, NULL, NULL, &error))
+        {
+            avail_locales = g_strsplit (g_strchomp (locales), "\n", -1);
+            g_free (locales);
+        }
+        else
+        {
+            g_warning ("Failed to run 'locale -a': %s", error->message);
+            g_clear_error (&error);
+        }
+    }
+
+    if (avail_locales)
+    {
+        for (i = 0; avail_locales[i]; i++)
+        {
+            gchar *loc = avail_locales[i];
+            if (!g_strrstr (loc, ".utf8"))
+                continue;
+            if (g_str_has_prefix (loc, language))
+            {
+                locale = g_strdup (loc);
+                break;
+            }
+        }
+    }
+
+    g_free (language);
+
+    return locale;
+}
+
+/**
+ * lightdm_get_language:
+ *
+ * Get the current language.
+ *
+ * Return value: (transfer none): The current language or #NULL if no language.
+ **/
+LightDMLanguage *
+lightdm_get_language (void)
+{
+    const gchar *lang;
+    GList *link;
+
+    lang = g_getenv ("LANG");
+    if (!lang)
+        return NULL;
+
+    for (link = lightdm_get_languages (); link; link = link->next)
+    {
+        LightDMLanguage *language = link->data;
+        if (lightdm_language_matches (language, lang))
+            return language;
+    }
+
+    return NULL;
+}
 
 /**
- * ldm_language_new:
- * 
- * Create a new language.
- * @code: The language code
- * 
- * Return value: the new #LdmLanguage
+ * lightdm_get_languages:
+ *
+ * Get a list of languages to present to the user.
+ *
+ * Return value: (element-type LightDMLanguage) (transfer none): A list of #LightDMLanguage that should be presented to the user.
  **/
-LdmLanguage *
-ldm_language_new (const gchar *code)
+GList *
+lightdm_get_languages (void)
 {
-    return g_object_new (LDM_TYPE_LANGUAGE, "code", code, NULL);
+    update_languages ();
+    return languages;
 }
 
 /**
- * ldm_language_get_code:
- * @language: A #LdmLanguage
- * 
- * Get the code of a language.
- * 
+ * lightdm_language_get_code:
+ * @language: A #LightDMLanguage
+ *
+ * Get the code of a language (e.g. "de_DE.UTF-8")
+ *
  * Return value: The code of the language
  **/
 const gchar *
-ldm_language_get_code (LdmLanguage *language)
+lightdm_language_get_code (LightDMLanguage *language)
 {
-    g_return_val_if_fail (LDM_IS_LANGUAGE (language), NULL);
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
     return GET_PRIVATE (language)->code;
 }
 
 /**
- * ldm_language_get_name:
- * @language: A #LdmLanguage
- * 
+ * lightdm_language_get_name:
+ * @language: A #LightDMLanguage
+ *
  * Get the name of a language.
  *
  * Return value: The name of the language
  **/
 const gchar *
-ldm_language_get_name (LdmLanguage *language)
+lightdm_language_get_name (LightDMLanguage *language)
 {
-    LdmLanguagePrivate *priv;
+    LightDMLanguagePrivate *priv;
 
-    g_return_val_if_fail (LDM_IS_LANGUAGE (language), NULL);
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
 
     priv = GET_PRIVATE (language);
 
     if (!priv->name)
     {
-        char *current = setlocale(LC_ALL, NULL);
-        setlocale(LC_ALL, priv->code);
-#ifdef _NL_IDENTIFICATION_LANGUAGE
-        priv->name = g_strdup (nl_langinfo (_NL_IDENTIFICATION_LANGUAGE));
-#else
-        priv->name = g_strdup ("Unknown");
-#endif
-        setlocale(LC_ALL, current);
+        gchar *locale = get_locale_name (priv->code);
+        if (locale)
+        {
+            gchar *current = setlocale (LC_ALL, NULL);
+            setlocale (LC_IDENTIFICATION, locale);
+            setlocale (LC_MESSAGES, "");
+
+            gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE);
+            if (language_en && strlen (language_en) > 0)
+                priv->name = g_strdup (dgettext ("iso_639_3", language_en));
+
+            setlocale (LC_ALL, current);
+        }
+        if (!priv->name)
+        {
+            gchar **tokens = g_strsplit_set (priv->code, "_.@", 2);
+            priv->name = g_strdup (tokens[0]);
+            g_strfreev (tokens);
+        }
     }
 
     return priv->name;
 }
 
 /**
- * ldm_language_get_territory:
- * @language: A #LdmLanguage
- * 
+ * lightdm_language_get_territory:
+ * @language: A #LightDMLanguage
+ *
  * Get the territory the language is used in.
- * 
+ *
  * Return value: The territory the language is used in.
  **/
 const gchar *
-ldm_language_get_territory (LdmLanguage *language)
+lightdm_language_get_territory (LightDMLanguage *language)
 {
-    LdmLanguagePrivate *priv;
+    LightDMLanguagePrivate *priv;
 
-    g_return_val_if_fail (LDM_IS_LANGUAGE (language), NULL);
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
 
     priv = GET_PRIVATE (language);
 
-    if (!priv->territory)
+    if (!priv->territory && strchr (priv->code, '_'))
     {
-        char *current = setlocale(LC_ALL, NULL);
-        setlocale(LC_ALL, priv->code);
-#ifdef _NL_IDENTIFICATION_TERRITORY
-        priv->territory = g_strdup (nl_langinfo (_NL_IDENTIFICATION_TERRITORY));
-#else
-        priv->territory = g_strdup ("Unknown");
-#endif
-        setlocale(LC_ALL, current);
+        gchar *locale = get_locale_name (priv->code);
+        if (locale)
+        {
+            gchar *current = setlocale (LC_ALL, NULL);
+            setlocale (LC_IDENTIFICATION, locale);
+            setlocale (LC_MESSAGES, "");
+
+            gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY);
+            if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0)
+                priv->territory = g_strdup (dgettext ("iso_3166", country_en));
+
+            setlocale (LC_ALL, current);
+        }
+        if (!priv->territory)
+        {
+            gchar **tokens = g_strsplit_set (priv->code, "_.@", 3);
+            priv->territory = g_strdup (tokens[1]);
+            g_strfreev (tokens);
+        }
     }
 
     return priv->territory;
 }
 
-static gboolean
-is_utf8 (const gchar *code)
-{
-    return g_str_has_suffix (code, ".utf8") || g_str_has_suffix (code, ".UTF-8");
-}
-
 /**
- * ldm_language_matches:
- * @language: A #LdmLanguage
+ * lightdm_language_matches:
+ * @language: A #LightDMLanguage
  * @code: A language code
- * 
+ *
  * Check if a language code matches this language.
- * 
+ *
  * Return value: #TRUE if the code matches this language.
  **/
 gboolean
-ldm_language_matches (LdmLanguage *language, const gchar *code)
+lightdm_language_matches (LightDMLanguage *language, const gchar *code)
 {
-    LdmLanguagePrivate *priv;
+    LightDMLanguagePrivate *priv;
 
-    g_return_val_if_fail (LDM_IS_LANGUAGE (language), FALSE);
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE);
     g_return_val_if_fail (code != NULL, FALSE);
 
     priv = GET_PRIVATE (language);
@@ -163,18 +322,18 @@ ldm_language_matches (LdmLanguage *language, const gchar *code)
 }
 
 static void
-ldm_language_init (LdmLanguage *language)
+lightdm_language_init (LightDMLanguage *language)
 {
 }
 
 static void
-ldm_language_set_property (GObject      *object,
-                           guint         prop_id,
-                           const GValue *value,
-                           GParamSpec   *pspec)
+lightdm_language_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
 {
-    LdmLanguage *self = LDM_LANGUAGE (object);
-    LdmLanguagePrivate *priv = GET_PRIVATE (self);
+    LightDMLanguage *self = LIGHTDM_LANGUAGE (object);
+    LightDMLanguagePrivate *priv = GET_PRIVATE (self);
 
     switch (prop_id) {
     case PROP_CODE:
@@ -188,24 +347,24 @@ ldm_language_set_property (GObject      *object,
 }
 
 static void
-ldm_language_get_property (GObject    *object,
-                           guint       prop_id,
-                           GValue     *value,
-                           GParamSpec *pspec)
+lightdm_language_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
 {
-    LdmLanguage *self;
+    LightDMLanguage *self;
 
-    self = LDM_LANGUAGE (object);
+    self = LIGHTDM_LANGUAGE (object);
 
     switch (prop_id) {
     case PROP_CODE:
-        g_value_set_string (value, ldm_language_get_code (self));
+        g_value_set_string (value, lightdm_language_get_code (self));
         break;
     case PROP_NAME:
-        g_value_set_string (value, ldm_language_get_name (self));
+        g_value_set_string (value, lightdm_language_get_name (self));
         break;
     case PROP_TERRITORY:
-        g_value_set_string (value, ldm_language_get_territory (self));
+        g_value_set_string (value, lightdm_language_get_territory (self));
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -214,34 +373,34 @@ ldm_language_get_property (GObject    *object,
 }
 
 static void
-ldm_language_class_init (LdmLanguageClass *klass)
+lightdm_language_class_init (LightDMLanguageClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  
-    g_type_class_add_private (klass, sizeof (LdmLanguagePrivate));
-
-    object_class->set_property = ldm_language_set_property;
-    object_class->get_property = ldm_language_get_property;
-
-    g_object_class_install_property(object_class,
-                                    PROP_CODE,
-                                    g_param_spec_string("code",
-                                                        "code",
-                                                        "Language code",
-                                                        NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-    g_object_class_install_property(object_class,
-                                    PROP_NAME,
-                                    g_param_spec_string("name",
-                                                        "name",
-                                                        "Name of the language",
-                                                        NULL,
-                                                        G_PARAM_READABLE));
-    g_object_class_install_property(object_class,
-                                    PROP_TERRITORY,
-                                    g_param_spec_string("territory",
-                                                        "territory",
-                                                        "Territory the language is from",
-                                                        NULL,
-                                                        G_PARAM_READABLE));
+
+    g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate));
+
+    object_class->set_property = lightdm_language_set_property;
+    object_class->get_property = lightdm_language_get_property;
+
+    g_object_class_install_property (object_class,
+                                     PROP_CODE,
+                                     g_param_spec_string ("code",
+                                                          "code",
+                                                          "Language code",
+                                                          NULL,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+    g_object_class_install_property (object_class,
+                                     PROP_NAME,
+                                     g_param_spec_string ("name",
+                                                          "name",
+                                                          "Name of the language",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     PROP_TERRITORY,
+                                     g_param_spec_string ("territory",
+                                                          "territory",
+                                                          "Territory the language is from",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
 }