Back to index

lightdm  1.3.2
language.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Robert Ancell.
00003  * Author: Robert Ancell <robert.ancell@canonical.com>
00004  * 
00005  * This library is free software; you can redistribute it and/or modify it under
00006  * the terms of the GNU Lesser General Public License as published by the Free
00007  * Software Foundation; either version 3 of the License, or (at your option) any
00008  * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
00009  * license.
00010  */
00011 
00012 #include <string.h>
00013 #include <locale.h>
00014 #include <langinfo.h>
00015 #include <stdio.h>
00016 #include <glib/gi18n.h>
00017 
00018 #include "lightdm/language.h"
00019 
00020 enum {
00021     PROP_0,
00022     PROP_CODE,
00023     PROP_NAME,
00024     PROP_TERRITORY
00025 };
00026 
00027 typedef struct
00028 {
00029     gchar *code;
00030     gchar *name;
00031     gchar *territory;
00032 } LightDMLanguagePrivate;
00033 
00034 G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT);
00035 
00036 #define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LANGUAGE, LightDMLanguagePrivate)
00037 
00038 static gboolean have_languages = FALSE;
00039 static GList *languages = NULL;
00040 
00041 static void
00042 update_languages (void)
00043 {
00044     gchar *command = "locale -a";
00045     gchar *stdout_text = NULL, *stderr_text = NULL;
00046     gint exit_status;
00047     gboolean result;
00048     GError *error = NULL;
00049 
00050     if (have_languages)
00051         return;
00052 
00053     result = g_spawn_command_line_sync (command, &stdout_text, &stderr_text, &exit_status, &error);
00054     if (error)
00055     {
00056         g_warning ("Failed to run '%s': %s", command, error->message);
00057         g_clear_error (&error);
00058     }
00059     else if (exit_status != 0)
00060         g_warning ("Failed to get languages, '%s' returned %d", command, exit_status);
00061     else if (result)
00062     {
00063         gchar **tokens;
00064         int i;
00065 
00066         tokens = g_strsplit_set (stdout_text, "\n\r", -1);
00067         for (i = 0; tokens[i]; i++)
00068         {
00069             LightDMLanguage *language;
00070             gchar *code;
00071 
00072             code = g_strchug (tokens[i]);
00073             if (code[0] == '\0')
00074                 continue;
00075 
00076             /* Ignore the non-interesting languages */
00077             if (strcmp (command, "locale -a") == 0 && !g_strrstr (code, ".utf8"))
00078                 continue;
00079 
00080             language = g_object_new (LIGHTDM_TYPE_LANGUAGE, "code", code, NULL);
00081             languages = g_list_append (languages, language);
00082         }
00083 
00084         g_strfreev (tokens);
00085     }
00086 
00087     g_free (stdout_text);
00088     g_free (stderr_text);
00089 
00090     have_languages = TRUE;
00091 }
00092 
00093 static gboolean
00094 is_utf8 (const gchar *code)
00095 {
00096     return g_strrstr (code, ".utf8") || g_strrstr (code, ".UTF-8");
00097 }
00098 
00099 /* Get a valid locale name that can be passed to setlocale(), so we always can use nl_langinfo() to get language and country names. */
00100 static gchar *
00101 get_locale_name (const gchar *code)
00102 {
00103     gchar *locale = NULL, *language;
00104     char *at;
00105     static gchar **avail_locales;
00106     gint i;
00107 
00108     if (is_utf8 (code))
00109         return (gchar *) code;
00110 
00111     if ((at = strchr (code, '@')))
00112         language = g_strndup (code, at - code);
00113     else
00114         language = g_strdup (code);
00115 
00116     if (!avail_locales)
00117     {
00118         gchar *locales;
00119         GError *error = NULL;
00120 
00121         if (g_spawn_command_line_sync ("locale -a", &locales, NULL, NULL, &error))
00122         {
00123             avail_locales = g_strsplit (g_strchomp (locales), "\n", -1);
00124             g_free (locales);
00125         }
00126         else
00127         {
00128             g_warning ("Failed to run 'locale -a': %s", error->message);
00129             g_clear_error (&error);
00130         }
00131     }
00132 
00133     if (avail_locales)
00134     {
00135         for (i = 0; avail_locales[i]; i++)
00136         {
00137             gchar *loc = avail_locales[i];
00138             if (!g_strrstr (loc, ".utf8"))
00139                 continue;
00140             if (g_str_has_prefix (loc, language))
00141             {
00142                 locale = g_strdup (loc);
00143                 break;
00144             }
00145         }
00146     }
00147 
00148     g_free (language);
00149 
00150     return locale;
00151 }
00152 
00160 LightDMLanguage *
00161 lightdm_get_language (void)
00162 {
00163     const gchar *lang;
00164     GList *link;
00165 
00166     lang = g_getenv ("LANG");
00167     if (!lang)
00168         return NULL;
00169 
00170     for (link = lightdm_get_languages (); link; link = link->next)
00171     {
00172         LightDMLanguage *language = link->data;
00173         if (lightdm_language_matches (language, lang))
00174             return language;
00175     }
00176 
00177     return NULL;
00178 }
00179 
00187 GList *
00188 lightdm_get_languages (void)
00189 {
00190     update_languages ();
00191     return languages;
00192 }
00193 
00202 const gchar *
00203 lightdm_language_get_code (LightDMLanguage *language)
00204 {
00205     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
00206     return GET_PRIVATE (language)->code;
00207 }
00208 
00217 const gchar *
00218 lightdm_language_get_name (LightDMLanguage *language)
00219 {
00220     LightDMLanguagePrivate *priv;
00221 
00222     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
00223 
00224     priv = GET_PRIVATE (language);
00225 
00226     if (!priv->name)
00227     {
00228         gchar *locale = get_locale_name (priv->code);
00229         if (locale)
00230         {
00231             gchar *current = setlocale (LC_ALL, NULL);
00232             setlocale (LC_IDENTIFICATION, locale);
00233             setlocale (LC_MESSAGES, "");
00234 
00235             gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE);
00236             if (language_en && strlen (language_en) > 0)
00237                 priv->name = g_strdup (dgettext ("iso_639_3", language_en));
00238 
00239             setlocale (LC_ALL, current);
00240         }
00241         if (!priv->name)
00242         {
00243             gchar **tokens = g_strsplit_set (priv->code, "_.@", 2);
00244             priv->name = g_strdup (tokens[0]);
00245             g_strfreev (tokens);
00246         }
00247     }
00248 
00249     return priv->name;
00250 }
00251 
00260 const gchar *
00261 lightdm_language_get_territory (LightDMLanguage *language)
00262 {
00263     LightDMLanguagePrivate *priv;
00264 
00265     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
00266 
00267     priv = GET_PRIVATE (language);
00268 
00269     if (!priv->territory && strchr (priv->code, '_'))
00270     {
00271         gchar *locale = get_locale_name (priv->code);
00272         if (locale)
00273         {
00274             gchar *current = setlocale (LC_ALL, NULL);
00275             setlocale (LC_IDENTIFICATION, locale);
00276             setlocale (LC_MESSAGES, "");
00277 
00278             gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY);
00279             if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0)
00280                 priv->territory = g_strdup (dgettext ("iso_3166", country_en));
00281 
00282             setlocale (LC_ALL, current);
00283         }
00284         if (!priv->territory)
00285         {
00286             gchar **tokens = g_strsplit_set (priv->code, "_.@", 3);
00287             priv->territory = g_strdup (tokens[1]);
00288             g_strfreev (tokens);
00289         }
00290     }
00291 
00292     return priv->territory;
00293 }
00294 
00304 gboolean
00305 lightdm_language_matches (LightDMLanguage *language, const gchar *code)
00306 {
00307     LightDMLanguagePrivate *priv;
00308 
00309     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE);
00310     g_return_val_if_fail (code != NULL, FALSE);
00311 
00312     priv = GET_PRIVATE (language);
00313 
00314     /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */
00315     if (is_utf8 (priv->code) && is_utf8 (code))
00316     {
00317         /* Match the characters before the '.' */
00318         int i;
00319         for (i = 0; priv->code[i] && code[i] && priv->code[i] == code[i] && code[i] != '.' ; i++);
00320         return priv->code[i] == '.' && code[i] == '.';
00321     }
00322 
00323     return g_str_equal (priv->code, code);
00324 }
00325 
00326 static void
00327 lightdm_language_init (LightDMLanguage *language)
00328 {
00329 }
00330 
00331 static void
00332 lightdm_language_set_property (GObject      *object,
00333                            guint         prop_id,
00334                            const GValue *value,
00335                            GParamSpec   *pspec)
00336 {
00337     LightDMLanguage *self = LIGHTDM_LANGUAGE (object);
00338     LightDMLanguagePrivate *priv = GET_PRIVATE (self);
00339 
00340     switch (prop_id) {
00341     case PROP_CODE:
00342         g_free (priv->name);
00343         priv->code = g_strdup (g_value_get_string (value));
00344         break;
00345     default:
00346         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00347         break;
00348     }
00349 }
00350 
00351 static void
00352 lightdm_language_get_property (GObject    *object,
00353                            guint       prop_id,
00354                            GValue     *value,
00355                            GParamSpec *pspec)
00356 {
00357     LightDMLanguage *self;
00358 
00359     self = LIGHTDM_LANGUAGE (object);
00360 
00361     switch (prop_id) {
00362     case PROP_CODE:
00363         g_value_set_string (value, lightdm_language_get_code (self));
00364         break;
00365     case PROP_NAME:
00366         g_value_set_string (value, lightdm_language_get_name (self));
00367         break;
00368     case PROP_TERRITORY:
00369         g_value_set_string (value, lightdm_language_get_territory (self));
00370         break;
00371     default:
00372         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00373         break;
00374     }
00375 }
00376 
00377 static void
00378 lightdm_language_class_init (LightDMLanguageClass *klass)
00379 {
00380     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00381   
00382     g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate));
00383 
00384     object_class->set_property = lightdm_language_set_property;
00385     object_class->get_property = lightdm_language_get_property;
00386 
00387     g_object_class_install_property (object_class,
00388                                      PROP_CODE,
00389                                      g_param_spec_string ("code",
00390                                                           "code",
00391                                                           "Language code",
00392                                                           NULL,
00393                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
00394     g_object_class_install_property (object_class,
00395                                      PROP_NAME,
00396                                      g_param_spec_string ("name",
00397                                                           "name",
00398                                                           "Name of the language",
00399                                                           NULL,
00400                                                           G_PARAM_READABLE));
00401     g_object_class_install_property (object_class,
00402                                      PROP_TERRITORY,
00403                                      g_param_spec_string ("territory",
00404                                                           "territory",
00405                                                           "Territory the language is from",
00406                                                           NULL,
00407                                                           G_PARAM_READABLE));
00408 }