]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/language.c
Clarify form of language strings.
[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 = "locale -a";
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
165     lang = g_getenv ("LANG");
166     if (!lang)
167         return NULL;
168
169     for (link = lightdm_get_languages (); link; link = link->next)
170     {
171         LightDMLanguage *language = link->data;
172         if (lightdm_language_matches (language, lang))
173             return language;
174     }
175
176     return NULL;
177 }
178
179 /**
180  * lightdm_get_languages:
181  *
182  * Get a list of languages to present to the user.
183  *
184  * Return value: (element-type LightDMLanguage) (transfer none): A list of #LightDMLanguage that should be presented to the user.
185  **/
186 GList *
187 lightdm_get_languages (void)
188 {
189     update_languages ();
190     return languages;
191 }
192
193 /**
194  * lightdm_language_get_code:
195  * @language: A #LightDMLanguage
196  * 
197  * Get the code of a language (e.g. "de_DE.UTF-8")
198  * 
199  * Return value: The code of the language
200  **/
201 const gchar *
202 lightdm_language_get_code (LightDMLanguage *language)
203 {
204     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
205     return GET_PRIVATE (language)->code;
206 }
207
208 /**
209  * lightdm_language_get_name:
210  * @language: A #LightDMLanguage
211  * 
212  * Get the name of a language.
213  *
214  * Return value: The name of the language
215  **/
216 const gchar *
217 lightdm_language_get_name (LightDMLanguage *language)
218 {
219     LightDMLanguagePrivate *priv;
220
221     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
222
223     priv = GET_PRIVATE (language);
224
225     if (!priv->name)
226     {
227         gchar *locale = get_locale_name (priv->code);
228         if (locale)
229         {
230             gchar *current = setlocale (LC_ALL, NULL);
231             setlocale (LC_IDENTIFICATION, locale);
232             setlocale (LC_MESSAGES, "");
233
234             gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE);
235             if (language_en && strlen (language_en) > 0)
236                 priv->name = g_strdup (dgettext ("iso_639_3", language_en));
237
238             setlocale (LC_ALL, current);
239         }
240         if (!priv->name)
241         {
242             gchar **tokens = g_strsplit_set (priv->code, "_.@", 2);
243             priv->name = g_strdup (tokens[0]);
244             g_strfreev (tokens);
245         }
246     }
247
248     return priv->name;
249 }
250
251 /**
252  * lightdm_language_get_territory:
253  * @language: A #LightDMLanguage
254  * 
255  * Get the territory the language is used in.
256  * 
257  * Return value: The territory the language is used in.
258  **/
259 const gchar *
260 lightdm_language_get_territory (LightDMLanguage *language)
261 {
262     LightDMLanguagePrivate *priv;
263
264     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
265
266     priv = GET_PRIVATE (language);
267
268     if (!priv->territory && strchr (priv->code, '_'))
269     {
270         gchar *locale = get_locale_name (priv->code);
271         if (locale)
272         {
273             gchar *current = setlocale (LC_ALL, NULL);
274             setlocale (LC_IDENTIFICATION, locale);
275             setlocale (LC_MESSAGES, "");
276
277             gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY);
278             if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0)
279                 priv->territory = g_strdup (dgettext ("iso_3166", country_en));
280
281             setlocale (LC_ALL, current);
282         }
283         if (!priv->territory)
284         {
285             gchar **tokens = g_strsplit_set (priv->code, "_.@", 3);
286             priv->territory = g_strdup (tokens[1]);
287             g_strfreev (tokens);
288         }
289     }
290
291     return priv->territory;
292 }
293
294 /**
295  * lightdm_language_matches:
296  * @language: A #LightDMLanguage
297  * @code: A language code
298  * 
299  * Check if a language code matches this language.
300  * 
301  * Return value: #TRUE if the code matches this language.
302  **/
303 gboolean
304 lightdm_language_matches (LightDMLanguage *language, const gchar *code)
305 {
306     LightDMLanguagePrivate *priv;
307
308     g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE);
309     g_return_val_if_fail (code != NULL, FALSE);
310
311     priv = GET_PRIVATE (language);
312
313     /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */
314     if (is_utf8 (priv->code) && is_utf8 (code))
315     {
316         /* Match the characters before the '.' */
317         int i;
318         for (i = 0; priv->code[i] && code[i] && priv->code[i] == code[i] && code[i] != '.' ; i++);
319         return priv->code[i] == '.' && code[i] == '.';
320     }
321
322     return g_str_equal (priv->code, code);
323 }
324
325 static void
326 lightdm_language_init (LightDMLanguage *language)
327 {
328 }
329
330 static void
331 lightdm_language_set_property (GObject      *object,
332                            guint         prop_id,
333                            const GValue *value,
334                            GParamSpec   *pspec)
335 {
336     LightDMLanguage *self = LIGHTDM_LANGUAGE (object);
337     LightDMLanguagePrivate *priv = GET_PRIVATE (self);
338
339     switch (prop_id) {
340     case PROP_CODE:
341         g_free (priv->name);
342         priv->code = g_strdup (g_value_get_string (value));
343         break;
344     default:
345         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346         break;
347     }
348 }
349
350 static void
351 lightdm_language_get_property (GObject    *object,
352                            guint       prop_id,
353                            GValue     *value,
354                            GParamSpec *pspec)
355 {
356     LightDMLanguage *self;
357
358     self = LIGHTDM_LANGUAGE (object);
359
360     switch (prop_id) {
361     case PROP_CODE:
362         g_value_set_string (value, lightdm_language_get_code (self));
363         break;
364     case PROP_NAME:
365         g_value_set_string (value, lightdm_language_get_name (self));
366         break;
367     case PROP_TERRITORY:
368         g_value_set_string (value, lightdm_language_get_territory (self));
369         break;
370     default:
371         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372         break;
373     }
374 }
375
376 static void
377 lightdm_language_class_init (LightDMLanguageClass *klass)
378 {
379     GObjectClass *object_class = G_OBJECT_CLASS (klass);
380   
381     g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate));
382
383     object_class->set_property = lightdm_language_set_property;
384     object_class->get_property = lightdm_language_get_property;
385
386     g_object_class_install_property (object_class,
387                                      PROP_CODE,
388                                      g_param_spec_string ("code",
389                                                           "code",
390                                                           "Language code",
391                                                           NULL,
392                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
393     g_object_class_install_property (object_class,
394                                      PROP_NAME,
395                                      g_param_spec_string ("name",
396                                                           "name",
397                                                           "Name of the language",
398                                                           NULL,
399                                                           G_PARAM_READABLE));
400     g_object_class_install_property (object_class,
401                                      PROP_TERRITORY,
402                                      g_param_spec_string ("territory",
403                                                           "territory",
404                                                           "Territory the language is from",
405                                                           NULL,
406                                                           G_PARAM_READABLE));
407 }