2018-01-03 04:46:37 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2018-03-28 03:59:11 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_UNISTD_H
|
2018-01-03 04:46:37 +00:00
|
|
|
#include <unistd.h>
|
2018-03-28 03:59:11 +00:00
|
|
|
#endif
|
|
|
|
|
2018-01-03 04:46:37 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
#include <glib/gi18n.h>
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <hb-ot.h>
|
|
|
|
|
|
|
|
#include "language-names.h"
|
|
|
|
|
2022-01-10 06:49:42 +00:00
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <windows.h>
|
|
|
|
#else
|
2019-07-24 12:02:07 +00:00
|
|
|
#ifndef ISO_CODES_PREFIX
|
|
|
|
#define ISO_CODES_PREFIX "/usr"
|
|
|
|
#endif
|
|
|
|
|
2018-01-03 04:46:37 +00:00
|
|
|
#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
|
|
|
|
#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
|
2022-01-10 06:49:42 +00:00
|
|
|
#endif
|
2018-01-03 04:46:37 +00:00
|
|
|
|
|
|
|
static GHashTable *language_map;
|
|
|
|
|
2022-01-10 06:49:42 +00:00
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
/* if we are using native Windows use native Windows API for language names */
|
|
|
|
static BOOL CALLBACK
|
|
|
|
get_win32_all_locales_scripts (LPWSTR locale_w, DWORD flags, LPARAM param)
|
|
|
|
{
|
|
|
|
wchar_t *langname_w = NULL;
|
|
|
|
wchar_t locale_abbrev_w[9];
|
|
|
|
gchar *langname, *locale_abbrev, *locale, *p;
|
|
|
|
gint i;
|
|
|
|
const LCTYPE iso639_lctypes[] = { LOCALE_SISO639LANGNAME, LOCALE_SISO639LANGNAME2 };
|
|
|
|
GHashTable *ht_scripts_langs = (GHashTable *) param;
|
|
|
|
PangoLanguage *lang;
|
|
|
|
|
|
|
|
gint langname_size, locale_abbrev_size;
|
|
|
|
langname_size = GetLocaleInfoEx (locale_w, LOCALE_SLOCALIZEDDISPLAYNAME, langname_w, 0);
|
|
|
|
if (langname_size == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
langname_w = g_new0 (wchar_t, langname_size);
|
|
|
|
|
|
|
|
if (langname_size == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
GetLocaleInfoEx (locale_w, LOCALE_SLOCALIZEDDISPLAYNAME, langname_w, langname_size);
|
|
|
|
langname = g_utf16_to_utf8 (langname_w, -1, NULL, NULL, NULL);
|
|
|
|
locale = g_utf16_to_utf8 (locale_w, -1, NULL, NULL, NULL);
|
|
|
|
p = strchr (locale, '-');
|
|
|
|
lang = pango_language_from_string (locale);
|
|
|
|
if (g_hash_table_lookup (ht_scripts_langs, lang) == NULL)
|
|
|
|
g_hash_table_insert (ht_scripts_langs, lang, langname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Track 3+-letter ISO639-2/3 language codes as well (these have a max length of 9 including terminating NUL)
|
|
|
|
* ISO639-2: iso639_lctypes[0] = LOCALE_SISO639LANGNAME
|
|
|
|
* ISO639-3: iso639_lctypes[1] = LOCALE_SISO639LANGNAME2
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
locale_abbrev_size = GetLocaleInfoEx (locale_w, iso639_lctypes[i], locale_abbrev_w, 0);
|
|
|
|
if (locale_abbrev_size > 0)
|
|
|
|
{
|
|
|
|
GetLocaleInfoEx (locale_w, iso639_lctypes[i], locale_abbrev_w, locale_abbrev_size);
|
|
|
|
|
|
|
|
locale_abbrev = g_utf16_to_utf8 (locale_abbrev_w, -1, NULL, NULL, NULL);
|
|
|
|
lang = pango_language_from_string (locale_abbrev);
|
|
|
|
if (g_hash_table_lookup (ht_scripts_langs, lang) == NULL)
|
|
|
|
g_hash_table_insert (ht_scripts_langs, lang, langname);
|
|
|
|
|
|
|
|
g_free (locale_abbrev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (locale);
|
|
|
|
g_free (langname_w);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* non-Windows */
|
|
|
|
|
2018-01-03 04:46:37 +00:00
|
|
|
static char *
|
|
|
|
get_first_item_in_semicolon_list (const char *list)
|
|
|
|
{
|
|
|
|
char **items;
|
|
|
|
char *item;
|
|
|
|
|
|
|
|
items = g_strsplit (list, "; ", 2);
|
|
|
|
|
|
|
|
item = g_strdup (items[0]);
|
|
|
|
g_strfreev (items);
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
capitalize_utf8_string (const char *str)
|
|
|
|
{
|
|
|
|
char first[8] = { 0 };
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
g_unichar_to_utf8 (g_unichar_totitle (g_utf8_get_char (str)), first);
|
|
|
|
|
|
|
|
return g_strconcat (first, g_utf8_offset_to_pointer (str, 1), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
get_display_name (const char *language)
|
|
|
|
{
|
|
|
|
const char *translated;
|
|
|
|
char *tmp;
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
translated = dgettext ("iso_639", language);
|
|
|
|
|
|
|
|
tmp = get_first_item_in_semicolon_list (translated);
|
|
|
|
name = capitalize_utf8_string (tmp);
|
|
|
|
g_free (tmp);
|
|
|
|
|
|
|
|
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;
|
|
|
|
char *display_name;
|
2020-09-19 13:45:17 +00:00
|
|
|
const char *long_names[] = {
|
|
|
|
"Dogri",
|
|
|
|
"Greek, Modern",
|
|
|
|
"Interlingua",
|
|
|
|
"Konkani",
|
|
|
|
"Tonga",
|
|
|
|
"Turkish, Ottoman",
|
|
|
|
};
|
|
|
|
int i;
|
2018-01-03 04:46:37 +00:00
|
|
|
|
|
|
|
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"))
|
|
|
|
{
|
|
|
|
if (**attr_values)
|
|
|
|
{
|
|
|
|
if (strlen (*attr_values) != 2)
|
|
|
|
return;
|
|
|
|
ccode = *attr_values;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (g_str_equal (*attr_names, "iso_639_2B_code"))
|
|
|
|
{
|
|
|
|
if (**attr_values)
|
|
|
|
{
|
|
|
|
if (strlen (*attr_values) != 3)
|
|
|
|
return;
|
|
|
|
ccode_longB = *attr_values;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (g_str_equal (*attr_names, "iso_639_2T_code"))
|
|
|
|
{
|
|
|
|
if (**attr_values)
|
|
|
|
{
|
|
|
|
if (strlen (*attr_values) != 3)
|
|
|
|
return;
|
|
|
|
ccode_longT = *attr_values;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (g_str_equal (*attr_names, "id"))
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
display_name = get_display_name (lang_name);
|
|
|
|
|
2020-09-19 13:45:17 +00:00
|
|
|
/* Fix up some egregious names */
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (long_names); i++)
|
|
|
|
{
|
|
|
|
if (g_str_has_prefix (display_name, long_names[i]))
|
2021-01-22 18:56:04 +00:00
|
|
|
display_name[strlen (long_names[i])] = '\0';
|
2020-09-19 13:45:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-03 04:46:37 +00:00
|
|
|
if (ccode != NULL)
|
|
|
|
g_hash_table_insert (language_map,
|
|
|
|
pango_language_from_string (ccode),
|
|
|
|
g_strdup (display_name));
|
|
|
|
|
|
|
|
if (ccode_longB != NULL)
|
|
|
|
g_hash_table_insert (language_map,
|
|
|
|
pango_language_from_string (ccode_longB),
|
|
|
|
g_strdup (display_name));
|
|
|
|
|
|
|
|
if (ccode_longT != NULL)
|
|
|
|
g_hash_table_insert (language_map,
|
|
|
|
pango_language_from_string (ccode_longT),
|
|
|
|
g_strdup (display_name));
|
|
|
|
|
|
|
|
if (ccode_id != NULL)
|
|
|
|
g_hash_table_insert (language_map,
|
|
|
|
pango_language_from_string (ccode_id),
|
|
|
|
g_strdup (display_name));
|
|
|
|
|
|
|
|
g_free (display_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
languages_variant_init (const char *variant)
|
|
|
|
{
|
|
|
|
gboolean res;
|
|
|
|
gsize buf_len;
|
2020-09-17 23:34:03 +00:00
|
|
|
char *buf;
|
|
|
|
char *filename;
|
|
|
|
GError *error;
|
2018-01-03 04:46:37 +00:00
|
|
|
|
|
|
|
bindtextdomain (variant, ISO_CODES_LOCALESDIR);
|
|
|
|
bind_textdomain_codeset (variant, "UTF-8");
|
|
|
|
|
|
|
|
error = NULL;
|
|
|
|
filename = g_strconcat (ISO_CODES_DATADIR, "/", variant, ".xml", NULL);
|
|
|
|
res = g_file_get_contents (filename, &buf, &buf_len, &error);
|
|
|
|
if (res)
|
|
|
|
{
|
2018-03-28 03:59:11 +00:00
|
|
|
GMarkupParseContext *ctx = NULL;
|
2018-01-03 04:46:37 +00:00
|
|
|
GMarkupParser parser = { languages_parse_start_tag, NULL, NULL, NULL, NULL };
|
|
|
|
|
|
|
|
ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
|
|
|
|
|
|
|
|
res = g_markup_parse_context_parse (ctx, buf, buf_len, &error);
|
2018-03-28 03:59:11 +00:00
|
|
|
g_free (ctx);
|
2018-01-03 04:46:37 +00:00
|
|
|
|
|
|
|
if (!res)
|
2020-09-17 23:34:03 +00:00
|
|
|
{
|
|
|
|
g_warning ("Failed to parse '%s': %s\n", filename, error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
}
|
2018-01-03 04:46:37 +00:00
|
|
|
}
|
|
|
|
else
|
2020-09-17 23:34:03 +00:00
|
|
|
{
|
|
|
|
g_warning ("Failed to load '%s': %s\n", filename, error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
}
|
2018-03-28 03:59:11 +00:00
|
|
|
|
|
|
|
g_free (filename);
|
|
|
|
g_free (buf);
|
2018-01-03 04:46:37 +00:00
|
|
|
}
|
|
|
|
|
2022-01-10 06:49:42 +00:00
|
|
|
#endif
|
|
|
|
|
2018-01-03 04:46:37 +00:00
|
|
|
static void
|
|
|
|
languages_init (void)
|
|
|
|
{
|
|
|
|
if (language_map)
|
|
|
|
return;
|
|
|
|
|
|
|
|
language_map = g_hash_table_new_full (NULL, NULL, NULL, g_free);
|
2022-01-10 06:49:42 +00:00
|
|
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
g_return_if_fail (EnumSystemLocalesEx (&get_win32_all_locales_scripts, LOCALE_ALL, (LPARAM) language_map, NULL));
|
|
|
|
#else
|
2018-01-03 04:46:37 +00:00
|
|
|
languages_variant_init ("iso_639");
|
|
|
|
languages_variant_init ("iso_639_3");
|
2022-01-10 06:49:42 +00:00
|
|
|
#endif
|
2018-01-03 04:46:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
get_language_name (PangoLanguage *language)
|
|
|
|
{
|
|
|
|
languages_init ();
|
|
|
|
|
|
|
|
return (const char *) g_hash_table_lookup (language_map, language);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
get_language_name_for_tag (guint32 tag)
|
|
|
|
{
|
|
|
|
hb_language_t lang;
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
lang = hb_ot_tag_to_language (tag);
|
|
|
|
s = hb_language_to_string (lang);
|
|
|
|
|
|
|
|
return get_language_name (pango_language_from_string (s));
|
|
|
|
}
|