]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/language.c
8234f0985d5a256a75e6454105e0fa0cdbadde96
[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 struct _LightDMLanguage
26 {
27     GObject parent_instance;
28
29     gchar *code;
30     gchar *name;
31     gchar *territory;
32 };
33
34 G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT);
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 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     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
219
220     if (!language->name)
221     {
222         gchar *locale = get_locale_name (language->code);
223         if (locale)
224         {
225             gchar *current = setlocale (LC_ALL, NULL);
226             setlocale (LC_IDENTIFICATION, locale);
227             setlocale (LC_MESSAGES, "");
228
229             gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE);
230             if (language_en && strlen (language_en) > 0)
231                 language->name = g_strdup (dgettext ("iso_639_3", language_en));
232
233             setlocale (LC_ALL, current);
234         }
235         if (!language->name)
236         {
237             gchar **tokens = g_strsplit_set (language->code, "_.@", 2);
238             language->name = g_strdup (tokens[0]);
239             g_strfreev (tokens);
240         }
241     }
242
243     return language->name;
244 }
245
246 /**
247  * lightdm_language_get_territory:
248  * @language: A #LightDMLanguage
249  *
250  * Get the territory the language is used in.
251  *
252  * Return value: The territory the language is used in.
253  **/
254 const gchar *
255 lightdm_language_get_territory (LightDMLanguage *language)
256 {
257     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
258
259     if (!language->territory && strchr (language->code, '_'))
260     {
261         gchar *locale = get_locale_name (language->code);
262         if (locale)
263         {
264             gchar *current = setlocale (LC_ALL, NULL);
265             setlocale (LC_IDENTIFICATION, locale);
266             setlocale (LC_MESSAGES, "");
267
268             gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY);
269             if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0)
270                 language->territory = g_strdup (dgettext ("iso_3166", country_en));
271
272             setlocale (LC_ALL, current);
273         }
274         if (!language->territory)
275         {
276             gchar **tokens = g_strsplit_set (language->code, "_.@", 3);
277             language->territory = g_strdup (tokens[1]);
278             g_strfreev (tokens);
279         }
280     }
281
282     return language->territory;
283 }
284
285 /**
286  * lightdm_language_matches:
287  * @language: A #LightDMLanguage
288  * @code: A language code
289  *
290  * Check if a language code matches this language.
291  *
292  * Return value: #TRUE if the code matches this language.
293  **/
294 gboolean
295 lightdm_language_matches (LightDMLanguage *language, const gchar *code)
296 {
297     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE);
298     g_return_val_if_fail (code != NULL, FALSE);
299
300     /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */
301     if (is_utf8 (language->code) && is_utf8 (code))
302     {
303         /* Match the characters before the '.' */
304         int i;
305         for (i = 0; language->code[i] && code[i] && language->code[i] == code[i] && code[i] != '.' ; i++);
306         return language->code[i] == '.' && code[i] == '.';
307     }
308
309     return g_str_equal (language->code, code);
310 }
311
312 static void
313 lightdm_language_init (LightDMLanguage *language)
314 {
315 }
316
317 static void
318 lightdm_language_set_property (GObject      *object,
319                                guint         prop_id,
320                                const GValue *value,
321                                GParamSpec   *pspec)
322 {
323     LightDMLanguage *self = LIGHTDM_LANGUAGE (object);
324
325     switch (prop_id) {
326     case PROP_CODE:
327         g_free (self->name);
328         self->code = g_strdup (g_value_get_string (value));
329         break;
330     default:
331         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332         break;
333     }
334 }
335
336 static void
337 lightdm_language_get_property (GObject    *object,
338                                guint       prop_id,
339                                GValue     *value,
340                                GParamSpec *pspec)
341 {
342     LightDMLanguage *self;
343
344     self = LIGHTDM_LANGUAGE (object);
345
346     switch (prop_id) {
347     case PROP_CODE:
348         g_value_set_string (value, lightdm_language_get_code (self));
349         break;
350     case PROP_NAME:
351         g_value_set_string (value, lightdm_language_get_name (self));
352         break;
353     case PROP_TERRITORY:
354         g_value_set_string (value, lightdm_language_get_territory (self));
355         break;
356     default:
357         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358         break;
359     }
360 }
361
362 static void
363 lightdm_language_class_init (LightDMLanguageClass *klass)
364 {
365     GObjectClass *object_class = G_OBJECT_CLASS (klass);
366
367     object_class->set_property = lightdm_language_set_property;
368     object_class->get_property = lightdm_language_get_property;
369
370     g_object_class_install_property (object_class,
371                                      PROP_CODE,
372                                      g_param_spec_string ("code",
373                                                           "code",
374                                                           "Language code",
375                                                           NULL,
376                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
377     g_object_class_install_property (object_class,
378                                      PROP_NAME,
379                                      g_param_spec_string ("name",
380                                                           "name",
381                                                           "Name of the language",
382                                                           NULL,
383                                                           G_PARAM_READABLE));
384     g_object_class_install_property (object_class,
385                                      PROP_TERRITORY,
386                                      g_param_spec_string ("territory",
387                                                           "territory",
388                                                           "Territory the language is from",
389                                                           NULL,
390                                                           G_PARAM_READABLE));
391 }