If software with "Chhattisgarhi" translations was loaded (such as k3b), we would fail to load the language name for it, as we were not parsing the ISO-639-3 database. https://bugzilla.gnome.org/show_bug.cgi?id=641373
1159 lines
35 KiB
C
1159 lines
35 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright 2008 Red Hat, Inc,
|
|
* 2007 William Jon McCann <mccann@jhu.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Written by : William Jon McCann <mccann@jhu.edu>
|
|
* Ray Strode <rstrode@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <locale.h>
|
|
#include <langinfo.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
|
|
#include "gdm-languages.h"
|
|
|
|
#include <langinfo.h>
|
|
#ifndef __LC_LAST
|
|
#define __LC_LAST 13
|
|
#endif
|
|
#include "locarchive.h"
|
|
|
|
#define ALIASES_FILE DATADIR "/gdm/locale.alias"
|
|
#define ARCHIVE_FILE LIBLOCALEDIR "/locale-archive"
|
|
#define SYSTEM_ARCHIVE_FILE "/usr/lib/locale/locale-archive"
|
|
#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
|
|
#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
|
|
|
|
typedef struct _GdmLocale {
|
|
char *id;
|
|
char *name;
|
|
char *language_code;
|
|
char *territory_code;
|
|
char *codeset;
|
|
char *modifier;
|
|
} GdmLocale;
|
|
|
|
static GHashTable *gdm_languages_map;
|
|
static GHashTable *gdm_territories_map;
|
|
static GHashTable *gdm_available_locales_map;
|
|
|
|
static char * construct_language_name (const char *language,
|
|
const char *territory,
|
|
const char *codeset,
|
|
const char *modifier);
|
|
|
|
static gboolean language_name_is_valid (const char *language_name);
|
|
|
|
static void
|
|
gdm_locale_free (GdmLocale *locale)
|
|
{
|
|
if (locale == NULL) {
|
|
return;
|
|
}
|
|
|
|
g_free (locale->id);
|
|
g_free (locale->name);
|
|
g_free (locale->codeset);
|
|
g_free (locale->modifier);
|
|
g_free (locale);
|
|
}
|
|
|
|
static char *
|
|
normalize_codeset (const char *codeset)
|
|
{
|
|
char *normalized_codeset;
|
|
const char *p;
|
|
char *q;
|
|
|
|
normalized_codeset = g_strdup (codeset);
|
|
|
|
if (codeset != NULL) {
|
|
for (p = codeset, q = normalized_codeset;
|
|
*p != '\0'; p++) {
|
|
|
|
if (*p == '-' || *p == '_') {
|
|
continue;
|
|
}
|
|
|
|
*q = g_ascii_tolower (*p);
|
|
q++;
|
|
}
|
|
*q = '\0';
|
|
}
|
|
|
|
return normalized_codeset;
|
|
}
|
|
|
|
/*
|
|
* According to http://en.wikipedia.org/wiki/Locale
|
|
* locale names are of the form:
|
|
* [language[_territory][.codeset][@modifier]]
|
|
*/
|
|
gboolean
|
|
gdm_parse_language_name (const char *name,
|
|
char **language_codep,
|
|
char **territory_codep,
|
|
char **codesetp,
|
|
char **modifierp)
|
|
{
|
|
GRegex *re;
|
|
GMatchInfo *match_info;
|
|
gboolean res;
|
|
GError *error;
|
|
gchar *normalized_codeset = NULL;
|
|
gchar *normalized_name = NULL;
|
|
gboolean retval;
|
|
|
|
match_info = NULL;
|
|
retval = FALSE;
|
|
|
|
error = NULL;
|
|
re = g_regex_new ("^(?P<language>[^_.@[:space:]]+)"
|
|
"(_(?P<territory>[[:upper:]]+))?"
|
|
"(\\.(?P<codeset>[-_0-9a-zA-Z]+))?"
|
|
"(@(?P<modifier>[[:ascii:]]+))?$",
|
|
0, 0, &error);
|
|
if (re == NULL) {
|
|
g_warning ("%s", error->message);
|
|
goto out;
|
|
}
|
|
|
|
if (!g_regex_match (re, name, 0, &match_info) ||
|
|
g_match_info_is_partial_match (match_info)) {
|
|
g_warning ("locale %s isn't valid\n", name);
|
|
goto out;
|
|
}
|
|
|
|
res = g_match_info_matches (match_info);
|
|
if (! res) {
|
|
g_warning ("Unable to parse locale: %s", name);
|
|
goto out;
|
|
}
|
|
|
|
retval = TRUE;
|
|
|
|
if (language_codep != NULL) {
|
|
*language_codep = g_match_info_fetch_named (match_info, "language");
|
|
}
|
|
|
|
if (territory_codep != NULL) {
|
|
*territory_codep = g_match_info_fetch_named (match_info, "territory");
|
|
|
|
if (*territory_codep != NULL &&
|
|
*territory_codep[0] == '\0') {
|
|
g_free (*territory_codep);
|
|
*territory_codep = NULL;
|
|
}
|
|
}
|
|
|
|
if (codesetp != NULL) {
|
|
*codesetp = g_match_info_fetch_named (match_info, "codeset");
|
|
|
|
if (*codesetp != NULL &&
|
|
*codesetp[0] == '\0') {
|
|
g_free (*codesetp);
|
|
*codesetp = NULL;
|
|
}
|
|
}
|
|
|
|
if (modifierp != NULL) {
|
|
*modifierp = g_match_info_fetch_named (match_info, "modifier");
|
|
|
|
if (*modifierp != NULL &&
|
|
*modifierp[0] == '\0') {
|
|
g_free (*modifierp);
|
|
*modifierp = NULL;
|
|
}
|
|
}
|
|
|
|
if (codesetp != NULL && *codesetp != NULL) {
|
|
normalized_codeset = normalize_codeset (*codesetp);
|
|
normalized_name = construct_language_name (language_codep ? *language_codep : NULL,
|
|
territory_codep ? *territory_codep : NULL,
|
|
normalized_codeset,
|
|
modifierp ? *modifierp : NULL);
|
|
|
|
if (language_name_is_valid (normalized_name)) {
|
|
g_free (*codesetp);
|
|
*codesetp = normalized_codeset;
|
|
} else {
|
|
g_free (normalized_codeset);
|
|
}
|
|
g_free (normalized_name);
|
|
}
|
|
|
|
out:
|
|
g_match_info_free (match_info);
|
|
g_regex_unref (re);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static char *
|
|
construct_language_name (const char *language,
|
|
const char *territory,
|
|
const char *codeset,
|
|
const char *modifier)
|
|
{
|
|
char *name;
|
|
|
|
g_assert (language[0] != 0);
|
|
g_assert (territory == NULL || territory[0] != 0);
|
|
g_assert (codeset == NULL || codeset[0] != 0);
|
|
g_assert (modifier == NULL || modifier[0] != 0);
|
|
|
|
name = g_strdup_printf ("%s%s%s%s%s%s%s",
|
|
language,
|
|
territory != NULL? "_" : "",
|
|
territory != NULL? territory : "",
|
|
codeset != NULL? "." : "",
|
|
codeset != NULL? codeset : "",
|
|
modifier != NULL? "@" : "",
|
|
modifier != NULL? modifier : "");
|
|
|
|
return name;
|
|
}
|
|
|
|
char *
|
|
gdm_normalize_language_name (const char *name)
|
|
{
|
|
char *normalized_name;
|
|
char *language_code;
|
|
char *territory_code;
|
|
char *codeset;
|
|
char *modifier;
|
|
|
|
if (name[0] == '\0') {
|
|
return NULL;
|
|
}
|
|
|
|
gdm_parse_language_name (name,
|
|
&language_code,
|
|
&territory_code,
|
|
&codeset, &modifier);
|
|
|
|
normalized_name = construct_language_name (language_code,
|
|
territory_code,
|
|
codeset, modifier);
|
|
g_free (language_code);
|
|
g_free (territory_code);
|
|
g_free (codeset);
|
|
g_free (modifier);
|
|
|
|
return normalized_name;
|
|
}
|
|
|
|
static gboolean
|
|
language_name_is_valid (const char *language_name)
|
|
{
|
|
char *old_locale;
|
|
gboolean is_valid;
|
|
#ifdef WITH_INCOMPLETE_LOCALES
|
|
int lc_type_id = LC_CTYPE;
|
|
#else
|
|
int lc_type_id = LC_MESSAGES;
|
|
#endif
|
|
|
|
old_locale = g_strdup (setlocale (lc_type_id, NULL));
|
|
is_valid = setlocale (lc_type_id, language_name) != NULL;
|
|
setlocale (lc_type_id, old_locale);
|
|
g_free (old_locale);
|
|
|
|
return is_valid;
|
|
}
|
|
|
|
static void
|
|
language_name_get_codeset_details (const char *language_name,
|
|
char **pcodeset,
|
|
gboolean *is_utf8)
|
|
{
|
|
char *old_locale;
|
|
char *codeset;
|
|
|
|
old_locale = g_strdup (setlocale (LC_CTYPE, NULL));
|
|
|
|
if (setlocale (LC_CTYPE, language_name) == NULL) {
|
|
g_free (old_locale);
|
|
return;
|
|
}
|
|
|
|
codeset = nl_langinfo (CODESET);
|
|
|
|
if (pcodeset != NULL) {
|
|
*pcodeset = g_strdup (codeset);
|
|
}
|
|
|
|
if (is_utf8 != NULL) {
|
|
codeset = normalize_codeset (codeset);
|
|
|
|
*is_utf8 = strcmp (codeset, "utf8") == 0;
|
|
g_free (codeset);
|
|
}
|
|
|
|
setlocale (LC_CTYPE, old_locale);
|
|
g_free (old_locale);
|
|
}
|
|
|
|
static gboolean
|
|
language_name_has_translations (const char *language_name)
|
|
{
|
|
GDir *dir;
|
|
char *path;
|
|
const char *name;
|
|
gboolean has_translations;
|
|
|
|
path = g_build_filename (GNOMELOCALEDIR, language_name, "LC_MESSAGES", NULL);
|
|
|
|
has_translations = FALSE;
|
|
dir = g_dir_open (path, 0, NULL);
|
|
g_free (path);
|
|
|
|
if (dir == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
do {
|
|
name = g_dir_read_name (dir);
|
|
|
|
if (name == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (g_str_has_suffix (name, ".mo")) {
|
|
has_translations = TRUE;
|
|
break;
|
|
}
|
|
} while (name != NULL);
|
|
g_dir_close (dir);
|
|
|
|
out:
|
|
return has_translations;
|
|
}
|
|
|
|
static gboolean
|
|
add_locale (const char *language_name,
|
|
gboolean utf8_only)
|
|
{
|
|
GdmLocale *locale;
|
|
GdmLocale *old_locale;
|
|
char *name;
|
|
gboolean is_utf8;
|
|
|
|
g_return_val_if_fail (language_name != NULL, FALSE);
|
|
|
|
language_name_get_codeset_details (language_name, NULL, &is_utf8);
|
|
|
|
if (is_utf8) {
|
|
name = g_strdup (language_name);
|
|
} else if (utf8_only) {
|
|
name = g_strdup_printf ("%s.utf8", language_name);
|
|
|
|
language_name_get_codeset_details (name, NULL, &is_utf8);
|
|
if (!is_utf8) {
|
|
g_free (name);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
name = g_strdup (language_name);
|
|
}
|
|
|
|
if (!language_name_is_valid (name)) {
|
|
g_debug ("Ignoring '%s' as a locale, since it's invalid", name);
|
|
g_free (name);
|
|
return FALSE;
|
|
}
|
|
|
|
locale = g_new0 (GdmLocale, 1);
|
|
gdm_parse_language_name (name,
|
|
&locale->language_code,
|
|
&locale->territory_code,
|
|
&locale->codeset,
|
|
&locale->modifier);
|
|
g_free (name);
|
|
name = NULL;
|
|
|
|
#ifdef WITH_INCOMPLETE_LOCALES
|
|
if (utf8_only) {
|
|
if (locale->territory_code == NULL || locale->modifier) {
|
|
gdm_locale_free (locale);
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
locale->id = construct_language_name (locale->language_code, locale->territory_code,
|
|
NULL, locale->modifier);
|
|
locale->name = construct_language_name (locale->language_code, locale->territory_code,
|
|
locale->codeset, locale->modifier);
|
|
|
|
#ifndef WITH_INCOMPLETE_LOCALES
|
|
if (!language_name_has_translations (locale->name) &&
|
|
!language_name_has_translations (locale->id) &&
|
|
!language_name_has_translations (locale->language_code) &&
|
|
utf8_only) {
|
|
g_debug ("Ignoring '%s' as a locale, since it lacks translations", locale->name);
|
|
gdm_locale_free (locale);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (!utf8_only) {
|
|
g_free (locale->id);
|
|
locale->id = g_strdup (locale->name);
|
|
}
|
|
|
|
old_locale = g_hash_table_lookup (gdm_available_locales_map, locale->id);
|
|
if (old_locale != NULL) {
|
|
if (strlen (old_locale->name) > strlen (locale->name)) {
|
|
gdm_locale_free (locale);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
g_hash_table_insert (gdm_available_locales_map, g_strdup (locale->id), locale);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct nameent
|
|
{
|
|
char *name;
|
|
uint32_t locrec_offset;
|
|
};
|
|
|
|
static gboolean
|
|
collect_locales_from_archive (void)
|
|
{
|
|
GMappedFile *mapped;
|
|
GError *error;
|
|
char *addr;
|
|
struct locarhead *head;
|
|
struct namehashent *namehashtab;
|
|
struct nameent *names;
|
|
uint32_t used;
|
|
uint32_t cnt;
|
|
gsize len;
|
|
gboolean locales_collected;
|
|
|
|
error = NULL;
|
|
mapped = g_mapped_file_new (ARCHIVE_FILE, FALSE, &error);
|
|
if (mapped == NULL) {
|
|
mapped = g_mapped_file_new (SYSTEM_ARCHIVE_FILE, FALSE, NULL);
|
|
if (mapped == NULL) {
|
|
g_warning ("Mapping failed for %s: %s", ARCHIVE_FILE, error->message);
|
|
g_error_free (error);
|
|
return FALSE;
|
|
}
|
|
g_error_free (error);
|
|
}
|
|
|
|
locales_collected = FALSE;
|
|
|
|
addr = g_mapped_file_get_contents (mapped);
|
|
len = g_mapped_file_get_length (mapped);
|
|
|
|
head = (struct locarhead *) addr;
|
|
if (head->namehash_offset + head->namehash_size > len
|
|
|| head->string_offset + head->string_size > len
|
|
|| head->locrectab_offset + head->locrectab_size > len
|
|
|| head->sumhash_offset + head->sumhash_size > len) {
|
|
goto out;
|
|
}
|
|
|
|
namehashtab = (struct namehashent *) (addr + head->namehash_offset);
|
|
|
|
names = (struct nameent *) g_new0 (struct nameent, head->namehash_used);
|
|
for (cnt = used = 0; cnt < head->namehash_size; ++cnt) {
|
|
if (namehashtab[cnt].locrec_offset != 0) {
|
|
names[used].name = addr + namehashtab[cnt].name_offset;
|
|
names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
|
|
}
|
|
}
|
|
|
|
for (cnt = 0; cnt < used; ++cnt) {
|
|
add_locale (names[cnt].name, TRUE);
|
|
}
|
|
|
|
g_free (names);
|
|
|
|
locales_collected = TRUE;
|
|
out:
|
|
|
|
g_mapped_file_unref (mapped);
|
|
return locales_collected;
|
|
}
|
|
|
|
static int
|
|
select_dirs (const struct dirent *dirent)
|
|
{
|
|
int result = 0;
|
|
|
|
if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0) {
|
|
mode_t mode = 0;
|
|
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK) {
|
|
mode = DTTOIF (dirent->d_type);
|
|
} else
|
|
#endif
|
|
{
|
|
struct stat st;
|
|
char *path;
|
|
|
|
path = g_build_filename (LIBLOCALEDIR, dirent->d_name, NULL);
|
|
if (g_stat (path, &st) == 0) {
|
|
mode = st.st_mode;
|
|
}
|
|
g_free (path);
|
|
}
|
|
|
|
result = S_ISDIR (mode);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
collect_locales_from_directory (void)
|
|
{
|
|
struct dirent **dirents;
|
|
int ndirents;
|
|
int cnt;
|
|
|
|
ndirents = scandir (LIBLOCALEDIR, &dirents, select_dirs, alphasort);
|
|
|
|
for (cnt = 0; cnt < ndirents; ++cnt) {
|
|
add_locale (dirents[cnt]->d_name, TRUE);
|
|
}
|
|
|
|
if (ndirents > 0) {
|
|
free (dirents);
|
|
}
|
|
}
|
|
|
|
static void
|
|
collect_locales_from_locale_file (const char *locale_file)
|
|
{
|
|
FILE *langlist;
|
|
char curline[256];
|
|
char *getsret;
|
|
|
|
if (locale_file == NULL)
|
|
return;
|
|
|
|
langlist = fopen (locale_file, "r");
|
|
|
|
if (langlist == NULL)
|
|
return;
|
|
|
|
for (;;) {
|
|
char *name;
|
|
char *lang;
|
|
char **lang_list;
|
|
int i;
|
|
|
|
getsret = fgets (curline, sizeof (curline), langlist);
|
|
if (getsret == NULL)
|
|
break;
|
|
|
|
if (curline[0] <= ' ' ||
|
|
curline[0] == '#')
|
|
continue;
|
|
|
|
name = strtok (curline, " \t\r\n");
|
|
if (name == NULL)
|
|
continue;
|
|
|
|
lang = strtok (NULL, " \t\r\n");
|
|
if (lang == NULL)
|
|
continue;
|
|
|
|
lang_list = g_strsplit (lang, ",", -1);
|
|
if (lang_list == NULL)
|
|
continue;
|
|
|
|
lang = NULL;
|
|
for (i = 0; lang_list[i] != NULL; i++) {
|
|
if (add_locale (lang_list[i], FALSE)) {
|
|
break;
|
|
}
|
|
}
|
|
g_strfreev (lang_list);
|
|
}
|
|
|
|
fclose (langlist);
|
|
}
|
|
|
|
static void
|
|
collect_locales (void)
|
|
{
|
|
|
|
if (gdm_available_locales_map == NULL) {
|
|
gdm_available_locales_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdm_locale_free);
|
|
}
|
|
|
|
if (!collect_locales_from_archive ()) {
|
|
#ifndef WITH_INCOMPLETE_LOCALES
|
|
g_warning ("Could not read list of available locales from libc, "
|
|
"guessing possible locales from available translations, "
|
|
"but list may be incomplete!");
|
|
#endif
|
|
|
|
collect_locales_from_directory ();
|
|
}
|
|
collect_locales_from_locale_file (ALIASES_FILE);
|
|
}
|
|
|
|
static gboolean
|
|
is_fallback_language (const char *code)
|
|
{
|
|
const char *fallback_language_names[] = { "C", "POSIX", NULL };
|
|
int i;
|
|
|
|
for (i = 0; fallback_language_names[i] != NULL; i++) {
|
|
if (strcmp (code, fallback_language_names[i]) == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static const char *
|
|
get_language (const char *code)
|
|
{
|
|
const char *name;
|
|
int len;
|
|
|
|
g_assert (code != NULL);
|
|
|
|
if (is_fallback_language (code)) {
|
|
return "Unspecified";
|
|
}
|
|
|
|
len = strlen (code);
|
|
if (len != 2 && len != 3) {
|
|
return NULL;
|
|
}
|
|
|
|
name = (const char *) g_hash_table_lookup (gdm_languages_map, code);
|
|
|
|
return name;
|
|
}
|
|
|
|
static char *
|
|
get_first_item_in_semicolon_list (const char *list)
|
|
{
|
|
char **items;
|
|
char *item;
|
|
|
|
/* Some entries in iso codes have multiple values, separated
|
|
* by semicolons. Not really sure which one to pick, so
|
|
* we just arbitrarily pick the first one.
|
|
*/
|
|
items = g_strsplit (list, "; ", 2);
|
|
|
|
item = g_strdup (items[0]);
|
|
g_strfreev (items);
|
|
|
|
return item;
|
|
}
|
|
|
|
static char *
|
|
get_translated_language (const char *code,
|
|
const char *locale)
|
|
{
|
|
const char *language;
|
|
char *name;
|
|
|
|
language = get_language (code);
|
|
|
|
name = NULL;
|
|
if (language != NULL) {
|
|
const char *translated_name;
|
|
char *old_locale;
|
|
|
|
if (locale != NULL) {
|
|
old_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
|
|
setlocale (LC_MESSAGES, locale);
|
|
}
|
|
|
|
if (is_fallback_language (code)) {
|
|
name = g_strdup (_("Unspecified"));
|
|
} else {
|
|
translated_name = dgettext ("iso_639", language);
|
|
name = get_first_item_in_semicolon_list (translated_name);
|
|
}
|
|
|
|
if (locale != NULL) {
|
|
setlocale (LC_MESSAGES, old_locale);
|
|
g_free (old_locale);
|
|
}
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
static const char *
|
|
get_territory (const char *code)
|
|
{
|
|
const char *name;
|
|
int len;
|
|
|
|
g_assert (code != NULL);
|
|
|
|
len = strlen (code);
|
|
if (len != 2 && len != 3) {
|
|
return NULL;
|
|
}
|
|
|
|
name = (const char *) g_hash_table_lookup (gdm_territories_map, code);
|
|
|
|
return name;
|
|
}
|
|
|
|
static char *
|
|
get_translated_territory (const char *code,
|
|
const char *locale)
|
|
{
|
|
const char *territory;
|
|
char *name;
|
|
|
|
territory = get_territory (code);
|
|
|
|
name = NULL;
|
|
if (territory != NULL) {
|
|
const char *translated_territory;
|
|
char *old_locale;
|
|
|
|
if (locale != NULL) {
|
|
old_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
|
|
setlocale (LC_MESSAGES, locale);
|
|
}
|
|
|
|
translated_territory = dgettext ("iso_3166", territory);
|
|
name = get_first_item_in_semicolon_list (translated_territory);
|
|
|
|
if (locale != NULL) {
|
|
setlocale (LC_MESSAGES, old_locale);
|
|
g_free (old_locale);
|
|
}
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
static void
|
|
languages_parse_start_tag (GMarkupParseContext *ctx,
|
|
const char *element_name,
|
|
const char **attr_names,
|
|
const char **attr_values,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
const char *ccode_longB;
|
|
const char *ccode_longT;
|
|
const char *ccode;
|
|
const char *ccode_id;
|
|
const char *lang_name;
|
|
|
|
if (! (g_str_equal (element_name, "iso_639_entry") || g_str_equal (element_name, "iso_639_3_entry"))
|
|
|| attr_names == NULL || attr_values == NULL) {
|
|
return;
|
|
}
|
|
|
|
ccode = NULL;
|
|
ccode_longB = NULL;
|
|
ccode_longT = NULL;
|
|
ccode_id = NULL;
|
|
lang_name = NULL;
|
|
|
|
while (*attr_names && *attr_values) {
|
|
if (g_str_equal (*attr_names, "iso_639_1_code")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
if (strlen (*attr_values) != 2) {
|
|
return;
|
|
}
|
|
ccode = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "iso_639_2B_code")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
if (strlen (*attr_values) != 3) {
|
|
return;
|
|
}
|
|
ccode_longB = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "iso_639_2T_code")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
if (strlen (*attr_values) != 3) {
|
|
return;
|
|
}
|
|
ccode_longT = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "id")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
if (strlen (*attr_values) != 2 &&
|
|
strlen (*attr_values) != 3) {
|
|
return;
|
|
}
|
|
ccode_id = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "name")) {
|
|
lang_name = *attr_values;
|
|
}
|
|
|
|
++attr_names;
|
|
++attr_values;
|
|
}
|
|
|
|
if (lang_name == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (ccode != NULL) {
|
|
g_hash_table_insert (gdm_languages_map,
|
|
g_strdup (ccode),
|
|
g_strdup (lang_name));
|
|
}
|
|
if (ccode_longB != NULL) {
|
|
g_hash_table_insert (gdm_languages_map,
|
|
g_strdup (ccode_longB),
|
|
g_strdup (lang_name));
|
|
}
|
|
if (ccode_longT != NULL) {
|
|
g_hash_table_insert (gdm_languages_map,
|
|
g_strdup (ccode_longT),
|
|
g_strdup (lang_name));
|
|
}
|
|
if (ccode_id != NULL) {
|
|
g_hash_table_insert (gdm_languages_map,
|
|
g_strdup (ccode_id),
|
|
g_strdup (lang_name));
|
|
}
|
|
}
|
|
|
|
static void
|
|
territories_parse_start_tag (GMarkupParseContext *ctx,
|
|
const char *element_name,
|
|
const char **attr_names,
|
|
const char **attr_values,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
const char *acode_2;
|
|
const char *acode_3;
|
|
const char *ncode;
|
|
const char *territory_common_name;
|
|
const char *territory_name;
|
|
|
|
if (! g_str_equal (element_name, "iso_3166_entry") || attr_names == NULL || attr_values == NULL) {
|
|
return;
|
|
}
|
|
|
|
acode_2 = NULL;
|
|
acode_3 = NULL;
|
|
ncode = NULL;
|
|
territory_common_name = NULL;
|
|
territory_name = NULL;
|
|
|
|
while (*attr_names && *attr_values) {
|
|
if (g_str_equal (*attr_names, "alpha_2_code")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
if (strlen (*attr_values) != 2) {
|
|
return;
|
|
}
|
|
acode_2 = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "alpha_3_code")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
if (strlen (*attr_values) != 3) {
|
|
return;
|
|
}
|
|
acode_3 = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "numeric_code")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
if (strlen (*attr_values) != 3) {
|
|
return;
|
|
}
|
|
ncode = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "common_name")) {
|
|
/* skip if empty */
|
|
if (**attr_values) {
|
|
territory_common_name = *attr_values;
|
|
}
|
|
} else if (g_str_equal (*attr_names, "name")) {
|
|
territory_name = *attr_values;
|
|
}
|
|
|
|
++attr_names;
|
|
++attr_values;
|
|
}
|
|
|
|
if (territory_common_name != NULL) {
|
|
territory_name = territory_common_name;
|
|
}
|
|
|
|
if (territory_name == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (acode_2 != NULL) {
|
|
g_hash_table_insert (gdm_territories_map,
|
|
g_strdup (acode_2),
|
|
g_strdup (territory_name));
|
|
}
|
|
if (acode_3 != NULL) {
|
|
g_hash_table_insert (gdm_territories_map,
|
|
g_strdup (acode_3),
|
|
g_strdup (territory_name));
|
|
}
|
|
if (ncode != NULL) {
|
|
g_hash_table_insert (gdm_territories_map,
|
|
g_strdup (ncode),
|
|
g_strdup (territory_name));
|
|
}
|
|
}
|
|
|
|
static void
|
|
languages_variant_init (const char *variant)
|
|
{
|
|
GError *error;
|
|
gboolean res;
|
|
char *buf;
|
|
gsize buf_len;
|
|
char *filename;
|
|
|
|
bindtextdomain (variant, ISO_CODES_LOCALESDIR);
|
|
bind_textdomain_codeset (variant, "UTF-8");
|
|
|
|
error = NULL;
|
|
filename = g_strdup_printf (ISO_CODES_DATADIR "/%s.xml", variant);
|
|
res = g_file_get_contents (filename,
|
|
&buf,
|
|
&buf_len,
|
|
&error);
|
|
if (res) {
|
|
GMarkupParseContext *ctx;
|
|
GMarkupParser parser = { languages_parse_start_tag, NULL, NULL, NULL, NULL };
|
|
|
|
ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
|
|
|
|
error = NULL;
|
|
res = g_markup_parse_context_parse (ctx, buf, buf_len, &error);
|
|
|
|
if (! res) {
|
|
g_warning ("Failed to parse '%s': %s\n",
|
|
filename,
|
|
error->message);
|
|
g_error_free (error);
|
|
g_free (filename);
|
|
}
|
|
|
|
g_markup_parse_context_free (ctx);
|
|
g_free (buf);
|
|
} else {
|
|
g_warning ("Failed to load '%s': %s\n",
|
|
filename,
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
languages_init (void)
|
|
{
|
|
gdm_languages_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
|
|
languages_variant_init ("iso_639");
|
|
languages_variant_init ("iso_639_3");
|
|
}
|
|
|
|
static void
|
|
territories_init (void)
|
|
{
|
|
GError *error;
|
|
gboolean res;
|
|
char *buf;
|
|
gsize buf_len;
|
|
|
|
bindtextdomain ("iso_3166", ISO_CODES_LOCALESDIR);
|
|
bind_textdomain_codeset ("iso_3166", "UTF-8");
|
|
|
|
gdm_territories_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
|
|
error = NULL;
|
|
res = g_file_get_contents (ISO_CODES_DATADIR "/iso_3166.xml",
|
|
&buf,
|
|
&buf_len,
|
|
&error);
|
|
if (res) {
|
|
GMarkupParseContext *ctx;
|
|
GMarkupParser parser = { territories_parse_start_tag, NULL, NULL, NULL, NULL };
|
|
|
|
ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
|
|
|
|
error = NULL;
|
|
res = g_markup_parse_context_parse (ctx, buf, buf_len, &error);
|
|
|
|
if (! res) {
|
|
g_warning ("Failed to parse '%s': %s\n",
|
|
ISO_CODES_DATADIR "/iso_3166.xml",
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_markup_parse_context_free (ctx);
|
|
g_free (buf);
|
|
} else {
|
|
g_warning ("Failed to load '%s': %s\n",
|
|
ISO_CODES_DATADIR "/iso_3166.xml",
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
char *
|
|
gdm_get_language_from_name (const char *name,
|
|
const char *locale)
|
|
{
|
|
GString *full_language;
|
|
char *language_code;
|
|
char *territory_code;
|
|
char *codeset_code;
|
|
char *langinfo_codeset;
|
|
char *translated_language;
|
|
char *translated_territory;
|
|
gboolean is_utf8 = TRUE;
|
|
|
|
translated_territory = NULL;
|
|
translated_language = NULL;
|
|
langinfo_codeset = NULL;
|
|
|
|
full_language = g_string_new (NULL);
|
|
|
|
if (gdm_languages_map == NULL) {
|
|
languages_init ();
|
|
}
|
|
|
|
if (gdm_territories_map == NULL) {
|
|
territories_init ();
|
|
}
|
|
|
|
language_code = NULL;
|
|
territory_code = NULL;
|
|
codeset_code = NULL;
|
|
|
|
gdm_parse_language_name (name,
|
|
&language_code,
|
|
&territory_code,
|
|
&codeset_code,
|
|
NULL);
|
|
|
|
if (language_code == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
translated_language = get_translated_language (language_code, locale);
|
|
if (translated_language == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
full_language = g_string_append (full_language, translated_language);
|
|
|
|
if (territory_code != NULL) {
|
|
translated_territory = get_translated_territory (territory_code, locale);
|
|
}
|
|
if (translated_territory != NULL) {
|
|
g_string_append_printf (full_language,
|
|
" (%s)",
|
|
translated_territory);
|
|
}
|
|
|
|
language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
|
|
|
|
if (codeset_code == NULL && langinfo_codeset != NULL) {
|
|
codeset_code = g_strdup (langinfo_codeset);
|
|
}
|
|
|
|
if (!is_utf8 && codeset_code) {
|
|
g_string_append_printf (full_language,
|
|
" [%s]",
|
|
codeset_code);
|
|
}
|
|
|
|
out:
|
|
g_free (language_code);
|
|
g_free (territory_code);
|
|
g_free (codeset_code);
|
|
g_free (langinfo_codeset);
|
|
g_free (translated_language);
|
|
g_free (translated_territory);
|
|
|
|
if (full_language->len == 0) {
|
|
g_string_free (full_language, TRUE);
|
|
return NULL;
|
|
}
|
|
|
|
return g_string_free (full_language, FALSE);
|
|
}
|
|
|
|
char **
|
|
gdm_get_all_language_names (void)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
GPtrArray *array;
|
|
|
|
if (gdm_available_locales_map == NULL) {
|
|
collect_locales ();
|
|
}
|
|
|
|
array = g_ptr_array_new ();
|
|
g_hash_table_iter_init (&iter, gdm_available_locales_map);
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
GdmLocale *locale;
|
|
|
|
locale = (GdmLocale *) value;
|
|
|
|
g_ptr_array_add (array, g_strdup (locale->name));
|
|
}
|
|
g_ptr_array_add (array, NULL);
|
|
|
|
return (char **) g_ptr_array_free (array, FALSE);
|
|
}
|