/* * Copyright (C) 2010 Robert Ancell. * Author: Robert Ancell * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #include #include #include #include #include #include "lightdm/language.h" enum { PROP_0, PROP_CODE, PROP_NAME, PROP_TERRITORY }; typedef struct { gchar *code; gchar *name; gchar *territory; } LightDMLanguagePrivate; G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT); #define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LANGUAGE, LightDMLanguagePrivate) static gboolean have_languages = FALSE; static GList *languages = NULL; static void update_languages (void) { gchar *command = "locale -a"; gchar *stdout_text = NULL, *stderr_text = NULL; gint exit_status; gboolean result; GError *error = NULL; if (have_languages) return; result = g_spawn_command_line_sync (command, &stdout_text, &stderr_text, &exit_status, &error); if (error) { g_warning ("Failed to run '%s': %s", command, error->message); g_clear_error (&error); } else if (exit_status != 0) g_warning ("Failed to get languages, '%s' returned %d", command, exit_status); else if (result) { gchar **tokens; int i; tokens = g_strsplit_set (stdout_text, "\n\r", -1); for (i = 0; tokens[i]; i++) { LightDMLanguage *language; gchar *code; code = g_strchug (tokens[i]); if (code[0] == '\0') continue; /* Ignore the non-interesting languages */ if (strcmp (command, "locale -a") == 0 && !g_strrstr (code, ".utf8")) continue; language = g_object_new (LIGHTDM_TYPE_LANGUAGE, "code", code, NULL); languages = g_list_append (languages, language); } g_strfreev (tokens); } g_free (stdout_text); g_free (stderr_text); have_languages = TRUE; } static gboolean is_utf8 (const gchar *code) { return g_strrstr (code, ".utf8") || g_strrstr (code, ".UTF-8"); } /* Get a valid locale name that can be passed to setlocale(), so we always can use nl_langinfo() to get language and country names. */ static gchar * get_locale_name (const gchar *code) { gchar *locale = NULL, *language; char *at; static gchar **avail_locales; gint i; if (is_utf8 (code)) return (gchar *) code; if ((at = strchr (code, '@'))) language = g_strndup (code, at - code); else language = g_strdup (code); if (!avail_locales) { gchar *locales; GError *error = NULL; if (g_spawn_command_line_sync ("locale -a", &locales, NULL, NULL, &error)) { avail_locales = g_strsplit (g_strchomp (locales), "\n", -1); g_free (locales); } else { g_warning ("Failed to run 'locale -a': %s", error->message); g_clear_error (&error); } } if (avail_locales) { for (i = 0; avail_locales[i]; i++) { gchar *loc = avail_locales[i]; if (!g_strrstr (loc, ".utf8")) continue; if (g_str_has_prefix (loc, language)) { locale = g_strdup (loc); break; } } } g_free (language); return locale; } /** * lightdm_get_language: * * Get the current language. * * Return value: (transfer none): The current language or #NULL if no language. **/ LightDMLanguage * lightdm_get_language (void) { const gchar *lang; GList *link; lang = g_getenv ("LANG"); if (!lang) return NULL; for (link = lightdm_get_languages (); link; link = link->next) { LightDMLanguage *language = link->data; if (lightdm_language_matches (language, lang)) return language; } return NULL; } /** * lightdm_get_languages: * * Get a list of languages to present to the user. * * Return value: (element-type LightDMLanguage) (transfer none): A list of #LightDMLanguage that should be presented to the user. **/ GList * lightdm_get_languages (void) { update_languages (); return languages; } /** * lightdm_language_get_code: * @language: A #LightDMLanguage * * Get the code of a language (e.g. "de_DE.UTF-8") * * Return value: The code of the language **/ const gchar * lightdm_language_get_code (LightDMLanguage *language) { g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL); return GET_PRIVATE (language)->code; } /** * lightdm_language_get_name: * @language: A #LightDMLanguage * * Get the name of a language. * * Return value: The name of the language **/ const gchar * lightdm_language_get_name (LightDMLanguage *language) { LightDMLanguagePrivate *priv; g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL); priv = GET_PRIVATE (language); if (!priv->name) { gchar *locale = get_locale_name (priv->code); if (locale) { gchar *current = setlocale (LC_ALL, NULL); setlocale (LC_IDENTIFICATION, locale); setlocale (LC_MESSAGES, ""); gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE); if (language_en && strlen (language_en) > 0) priv->name = g_strdup (dgettext ("iso_639_3", language_en)); setlocale (LC_ALL, current); } if (!priv->name) { gchar **tokens = g_strsplit_set (priv->code, "_.@", 2); priv->name = g_strdup (tokens[0]); g_strfreev (tokens); } } return priv->name; } /** * lightdm_language_get_territory: * @language: A #LightDMLanguage * * Get the territory the language is used in. * * Return value: The territory the language is used in. **/ const gchar * lightdm_language_get_territory (LightDMLanguage *language) { LightDMLanguagePrivate *priv; g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL); priv = GET_PRIVATE (language); if (!priv->territory && strchr (priv->code, '_')) { gchar *locale = get_locale_name (priv->code); if (locale) { gchar *current = setlocale (LC_ALL, NULL); setlocale (LC_IDENTIFICATION, locale); setlocale (LC_MESSAGES, ""); gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY); if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0) priv->territory = g_strdup (dgettext ("iso_3166", country_en)); setlocale (LC_ALL, current); } if (!priv->territory) { gchar **tokens = g_strsplit_set (priv->code, "_.@", 3); priv->territory = g_strdup (tokens[1]); g_strfreev (tokens); } } return priv->territory; } /** * lightdm_language_matches: * @language: A #LightDMLanguage * @code: A language code * * Check if a language code matches this language. * * Return value: #TRUE if the code matches this language. **/ gboolean lightdm_language_matches (LightDMLanguage *language, const gchar *code) { LightDMLanguagePrivate *priv; g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE); g_return_val_if_fail (code != NULL, FALSE); priv = GET_PRIVATE (language); /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */ if (is_utf8 (priv->code) && is_utf8 (code)) { /* Match the characters before the '.' */ int i; for (i = 0; priv->code[i] && code[i] && priv->code[i] == code[i] && code[i] != '.' ; i++); return priv->code[i] == '.' && code[i] == '.'; } return g_str_equal (priv->code, code); } static void lightdm_language_init (LightDMLanguage *language) { } static void lightdm_language_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { LightDMLanguage *self = LIGHTDM_LANGUAGE (object); LightDMLanguagePrivate *priv = GET_PRIVATE (self); switch (prop_id) { case PROP_CODE: g_free (priv->name); priv->code = g_strdup (g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void lightdm_language_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { LightDMLanguage *self; self = LIGHTDM_LANGUAGE (object); switch (prop_id) { case PROP_CODE: g_value_set_string (value, lightdm_language_get_code (self)); break; case PROP_NAME: g_value_set_string (value, lightdm_language_get_name (self)); break; case PROP_TERRITORY: g_value_set_string (value, lightdm_language_get_territory (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void lightdm_language_class_init (LightDMLanguageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate)); object_class->set_property = lightdm_language_set_property; object_class->get_property = lightdm_language_get_property; g_object_class_install_property (object_class, PROP_CODE, g_param_spec_string ("code", "code", "Language code", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", "name", "Name of the language", NULL, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_TERRITORY, g_param_spec_string ("territory", "territory", "Territory the language is from", NULL, G_PARAM_READABLE)); }