]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/language.c
Revert r2392 - it seems to have broken ABI in liblightdm-gobject
[sojka/lightdm.git] / liblightdm-gobject / language.c
1 /*
2  * Copyright (C) 2010 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; either version 2 or version 3 of the License.
8  * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
9  */
10
11 #include <string.h>
12 #include <locale.h>
13 #include <langinfo.h>
14 #include <stdio.h>
15 #include <glib/gi18n.h>
16
17 #include "lightdm/language.h"
18
19 enum {
20     PROP_CODE = 1,
21     PROP_NAME,
22     PROP_TERRITORY
23 };
24
25 typedef struct
26 {
27     gchar *code;
28     gchar *name;
29     gchar *territory;
30 } LightDMLanguagePrivate;
31
32 G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT);
33
34 #define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LANGUAGE, LightDMLanguagePrivate)
35
36 static gboolean have_languages = FALSE;
37 static GList *languages = NULL;
38
39 static void
40 update_languages (void)
41 {
42     gchar *command = "locale -a";
43     gchar *stdout_text = NULL, *stderr_text = NULL;
44     gint exit_status;
45     gboolean result;
46     GError *error = NULL;
47
48     if (have_languages)
49         return;
50
51     result = g_spawn_command_line_sync (command, &stdout_text, &stderr_text, &exit_status, &error);
52     if (error)
53     {
54         g_warning ("Failed to run '%s': %s", command, error->message);
55         g_clear_error (&error);
56     }
57     else if (exit_status != 0)
58         g_warning ("Failed to get languages, '%s' returned %d", command, exit_status);
59     else if (result)
60     {
61         gchar **tokens;
62         int i;
63
64         tokens = g_strsplit_set (stdout_text, "\n\r", -1);
65         for (i = 0; tokens[i]; i++)
66         {
67             LightDMLanguage *language;
68             gchar *code;
69
70             code = g_strchug (tokens[i]);
71             if (code[0] == '\0')
72                 continue;
73
74             /* Ignore the non-interesting languages */
75             if (strcmp (command, "locale -a") == 0 && !g_strrstr (code, ".utf8"))
76                 continue;
77
78             language = g_object_new (LIGHTDM_TYPE_LANGUAGE, "code", code, NULL);
79             languages = g_list_append (languages, language);
80         }
81
82         g_strfreev (tokens);
83     }
84
85     g_free (stdout_text);
86     g_free (stderr_text);
87
88     have_languages = TRUE;
89 }
90
91 static gboolean
92 is_utf8 (const gchar *code)
93 {
94     return g_strrstr (code, ".utf8") || g_strrstr (code, ".UTF-8");
95 }
96
97 /* Get a valid locale name that can be passed to setlocale(), so we always can use nl_langinfo() to get language and country names. */
98 static gchar *
99 get_locale_name (const gchar *code)
100 {
101     gchar *locale = NULL, *language;
102     char *at;
103     static gchar **avail_locales;
104     gint i;
105
106     if (is_utf8 (code))
107         return (gchar *) code;
108
109     if ((at = strchr (code, '@')))
110         language = g_strndup (code, at - code);
111     else
112         language = g_strdup (code);
113
114     if (!avail_locales)
115     {
116         gchar *locales;
117         GError *error = NULL;
118
119         if (g_spawn_command_line_sync ("locale -a", &locales, NULL, NULL, &error))
120         {
121             avail_locales = g_strsplit (g_strchomp (locales), "\n", -1);
122             g_free (locales);
123         }
124         else
125         {
126             g_warning ("Failed to run 'locale -a': %s", error->message);
127             g_clear_error (&error);
128         }
129     }
130
131     if (avail_locales)
132     {
133         for (i = 0; avail_locales[i]; i++)
134         {
135             gchar *loc = avail_locales[i];
136             if (!g_strrstr (loc, ".utf8"))
137                 continue;
138             if (g_str_has_prefix (loc, language))
139             {
140                 locale = g_strdup (loc);
141                 break;
142             }
143         }
144     }
145
146     g_free (language);
147
148     return locale;
149 }
150
151 /**
152  * lightdm_get_language:
153  *
154  * Get the current language.
155  *
156  * Return value: (transfer none): The current language or #NULL if no language.
157  **/
158 LightDMLanguage *
159 lightdm_get_language (void)
160 {
161     const gchar *lang;
162     GList *link;
163
164     lang = g_getenv ("LANG");
165     if (!lang)
166         return NULL;
167
168     for (link = lightdm_get_languages (); link; link = link->next)
169     {
170         LightDMLanguage *language = link->data;
171         if (lightdm_language_matches (language, lang))
172             return language;
173     }
174
175     return NULL;
176 }
177
178 /**
179  * lightdm_get_languages:
180  *
181  * Get a list of languages to present to the user.
182  *
183  * Return value: (element-type LightDMLanguage) (transfer none): A list of #LightDMLanguage that should be presented to the user.
184  **/
185 GList *
186 lightdm_get_languages (void)
187 {
188     update_languages ();
189     return languages;
190 }
191
192 /**
193  * lightdm_language_get_code:
194  * @language: A #LightDMLanguage
195  *
196  * Get the code of a language (e.g. "de_DE.UTF-8")
197  *
198  * Return value: The code of the language
199  **/
200 const gchar *
201 lightdm_language_get_code (LightDMLanguage *language)
202 {
203     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
204     return GET_PRIVATE (language)->code;
205 }
206
207 /**
208  * lightdm_language_get_name:
209  * @language: A #LightDMLanguage
210  *
211  * Get the name of a language.
212  *
213  * Return value: The name of the language
214  **/
215 const gchar *
216 lightdm_language_get_name (LightDMLanguage *language)
217 {
218     LightDMLanguagePrivate *priv;
219
220     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
221
222     priv = GET_PRIVATE (language);
223
224     if (!priv->name)
225     {
226         gchar *locale = get_locale_name (priv->code);
227         if (locale)
228         {
229             gchar *current = setlocale (LC_ALL, NULL);
230             setlocale (LC_IDENTIFICATION, locale);
231             setlocale (LC_MESSAGES, "");
232
233             gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE);
234             if (language_en && strlen (language_en) > 0)
235                 priv->name = g_strdup (dgettext ("iso_639_3", language_en));
236
237             setlocale (LC_ALL, current);
238         }
239         if (!priv->name)
240         {
241             gchar **tokens = g_strsplit_set (priv->code, "_.@", 2);
242             priv->name = g_strdup (tokens[0]);
243             g_strfreev (tokens);
244         }
245     }
246
247     return priv->name;
248 }
249
250 /**
251  * lightdm_language_get_territory:
252  * @language: A #LightDMLanguage
253  *
254  * Get the territory the language is used in.
255  *
256  * Return value: The territory the language is used in.
257  **/
258 const gchar *
259 lightdm_language_get_territory (LightDMLanguage *language)
260 {
261     LightDMLanguagePrivate *priv;
262
263     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
264
265     priv = GET_PRIVATE (language);
266
267     if (!priv->territory && strchr (priv->code, '_'))
268     {
269         gchar *locale = get_locale_name (priv->code);
270         if (locale)
271         {
272             gchar *current = setlocale (LC_ALL, NULL);
273             setlocale (LC_IDENTIFICATION, locale);
274             setlocale (LC_MESSAGES, "");
275
276             gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY);
277             if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0)
278                 priv->territory = g_strdup (dgettext ("iso_3166", country_en));
279
280             setlocale (LC_ALL, current);
281         }
282         if (!priv->territory)
283         {
284             gchar **tokens = g_strsplit_set (priv->code, "_.@", 3);
285             priv->territory = g_strdup (tokens[1]);
286             g_strfreev (tokens);
287         }
288     }
289
290     return priv->territory;
291 }
292
293 /**
294  * lightdm_language_matches:
295  * @language: A #LightDMLanguage
296  * @code: A language code
297  *
298  * Check if a language code matches this language.
299  *
300  * Return value: #TRUE if the code matches this language.
301  **/
302 gboolean
303 lightdm_language_matches (LightDMLanguage *language, const gchar *code)
304 {
305     LightDMLanguagePrivate *priv;
306
307     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE);
308     g_return_val_if_fail (code != NULL, FALSE);
309
310     priv = GET_PRIVATE (language);
311
312     /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */
313     if (is_utf8 (priv->code) && is_utf8 (code))
314     {
315         /* Match the characters before the '.' */
316         int i;
317         for (i = 0; priv->code[i] && code[i] && priv->code[i] == code[i] && code[i] != '.' ; i++);
318         return priv->code[i] == '.' && code[i] == '.';
319     }
320
321     return g_str_equal (priv->code, code);
322 }
323
324 static void
325 lightdm_language_init (LightDMLanguage *language)
326 {
327 }
328
329 static void
330 lightdm_language_set_property (GObject      *object,
331                                guint         prop_id,
332                                const GValue *value,
333                                GParamSpec   *pspec)
334 {
335     LightDMLanguage *self = LIGHTDM_LANGUAGE (object);
336     LightDMLanguagePrivate *priv = GET_PRIVATE (self);
337
338     switch (prop_id) {
339     case PROP_CODE:
340         g_free (priv->name);
341         priv->code = g_strdup (g_value_get_string (value));
342         break;
343     default:
344         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
345         break;
346     }
347 }
348
349 static void
350 lightdm_language_get_property (GObject    *object,
351                                guint       prop_id,
352                                GValue     *value,
353                                GParamSpec *pspec)
354 {
355     LightDMLanguage *self;
356
357     self = LIGHTDM_LANGUAGE (object);
358
359     switch (prop_id) {
360     case PROP_CODE:
361         g_value_set_string (value, lightdm_language_get_code (self));
362         break;
363     case PROP_NAME:
364         g_value_set_string (value, lightdm_language_get_name (self));
365         break;
366     case PROP_TERRITORY:
367         g_value_set_string (value, lightdm_language_get_territory (self));
368         break;
369     default:
370         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371         break;
372     }
373 }
374
375 static void
376 lightdm_language_class_init (LightDMLanguageClass *klass)
377 {
378     GObjectClass *object_class = G_OBJECT_CLASS (klass);
379
380     g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate));
381
382     object_class->set_property = lightdm_language_set_property;
383     object_class->get_property = lightdm_language_get_property;
384
385     g_object_class_install_property (object_class,
386                                      PROP_CODE,
387                                      g_param_spec_string ("code",
388                                                           "code",
389                                                           "Language code",
390                                                           NULL,
391                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
392     g_object_class_install_property (object_class,
393                                      PROP_NAME,
394                                      g_param_spec_string ("name",
395                                                           "name",
396                                                           "Name of the language",
397                                                           NULL,
398                                                           G_PARAM_READABLE));
399     g_object_class_install_property (object_class,
400                                      PROP_TERRITORY,
401                                      g_param_spec_string ("territory",
402                                                           "territory",
403                                                           "Territory the language is from",
404                                                           NULL,
405                                                           G_PARAM_READABLE));
406 }