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