/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * pluma-spell-checker-language.c * This file is part of pluma * * Copyright (C) 2006 Paolo Maggi * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* * Modified by the pluma Team, 2006. See the AUTHORS file for a * list of people on the pluma Team. * See the ChangeLog files for a list of changes. */ /* Part of the code taked from Epiphany. * * Copyright (C) 2003, 2004 Christian Persch */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "pluma-spell-checker-language.h" #include #define ISO_639_DOMAIN "iso_639" #define ISO_3166_DOMAIN "iso_3166" #define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale" struct _PlumaSpellCheckerLanguage { gchar *abrev; gchar *name; }; static gboolean available_languages_initialized = FALSE; static GSList *available_languages = NULL; static GHashTable *iso_639_table = NULL; static GHashTable *iso_3166_table = NULL; static void bind_iso_domains (void) { static gboolean bound = FALSE; if (bound == FALSE) { bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR); bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8"); bindtextdomain(ISO_3166_DOMAIN, ISOCODESLOCALEDIR); bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8"); bound = TRUE; } } static void read_iso_639_entry (xmlTextReaderPtr reader, GHashTable *table) { xmlChar *code, *name; code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_1_code"); name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name"); /* Get iso-639-2 code */ if (code == NULL || code[0] == '\0') { xmlFree (code); /* FIXME: use the 2T or 2B code? */ code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_2T_code"); } if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0') { g_hash_table_insert (table, code, name); } else { xmlFree (code); xmlFree (name); } } static void read_iso_3166_entry (xmlTextReaderPtr reader, GHashTable *table) { xmlChar *code, *name; code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "alpha_2_code"); name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name"); if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0') { char *lcode; lcode = g_ascii_strdown ((char *) code, -1); xmlFree (code); /* g_print ("%s -> %s\n", lcode, name); */ g_hash_table_insert (table, lcode, name); } else { xmlFree (code); xmlFree (name); } } typedef enum { STATE_START, STATE_STOP, STATE_ENTRIES, } ParserState; static void load_iso_entries (int iso, GFunc read_entry_func, gpointer user_data) { xmlTextReaderPtr reader; ParserState state = STATE_START; xmlChar iso_entries[32], iso_entry[32]; char *filename; int ret = -1; pluma_debug_message (DEBUG_PLUGINS, "Loading ISO-%d codes", iso); filename = g_strdup_printf (ISO_CODES_PREFIX "/share/xml/iso-codes/iso_%d.xml", iso); reader = xmlNewTextReaderFilename (filename); if (reader == NULL) goto out; xmlStrPrintf (iso_entries, sizeof (iso_entries), (const char *)"iso_%d_entries", iso); xmlStrPrintf (iso_entry, sizeof (iso_entry), (const char *)"iso_%d_entry", iso); ret = xmlTextReaderRead (reader); while (ret == 1) { const xmlChar *tag; xmlReaderTypes type; tag = xmlTextReaderConstName (reader); type = xmlTextReaderNodeType (reader); if (state == STATE_ENTRIES && type == XML_READER_TYPE_ELEMENT && xmlStrEqual (tag, iso_entry)) { read_entry_func (reader, user_data); } else if (state == STATE_START && type == XML_READER_TYPE_ELEMENT && xmlStrEqual (tag, iso_entries)) { state = STATE_ENTRIES; } else if (state == STATE_ENTRIES && type == XML_READER_TYPE_END_ELEMENT && xmlStrEqual (tag, iso_entries)) { state = STATE_STOP; } else if (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE || type == XML_READER_TYPE_WHITESPACE || type == XML_READER_TYPE_TEXT || type == XML_READER_TYPE_COMMENT) { /* eat it */ } else { /* ignore it */ } ret = xmlTextReaderRead (reader); } xmlFreeTextReader (reader); out: if (ret < 0 || state != STATE_STOP) { g_warning ("Failed to load ISO-%d codes from %s!\n", iso, filename); } g_free (filename); } static GHashTable * create_iso_639_table (void) { GHashTable *table; bind_iso_domains (); table = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) xmlFree, (GDestroyNotify) xmlFree); load_iso_entries (639, (GFunc) read_iso_639_entry, table); return table; } static GHashTable * create_iso_3166_table (void) { GHashTable *table; bind_iso_domains (); table = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) xmlFree); load_iso_entries (3166, (GFunc) read_iso_3166_entry, table); return table; } static char * create_name_for_language (const char *code) { char **str; char *name = NULL; const char *langname, *localename; int len; g_return_val_if_fail (iso_639_table != NULL, NULL); g_return_val_if_fail (iso_3166_table != NULL, NULL); str = g_strsplit (code, "_", -1); len = g_strv_length (str); g_return_val_if_fail (len != 0, NULL); langname = (const char *) g_hash_table_lookup (iso_639_table, str[0]); if (len == 1 && langname != NULL) { name = g_strdup (dgettext (ISO_639_DOMAIN, langname)); } else if (len == 2 && langname != NULL) { gchar *locale_code = g_ascii_strdown (str[1], -1); localename = (const char *) g_hash_table_lookup (iso_3166_table, locale_code); g_free (locale_code); if (localename != NULL) { /* Translators: the first %s is the language name, and * the second %s is the locale name. Example: * "French (France)" */ name = g_strdup_printf (C_("language", "%s (%s)"), dgettext (ISO_639_DOMAIN, langname), dgettext (ISO_3166_DOMAIN, localename)); } else { name = g_strdup_printf (C_("language", "%s (%s)"), dgettext (ISO_639_DOMAIN, langname), str[1]); } } else { /* Translators: this refers to an unknown language code * (one which isn't in our built-in list). */ name = g_strdup_printf (C_("language", "Unknown (%s)"), code); } g_strfreev (str); return name; } static void enumerate_dicts (const char * const lang_tag, const char * const provider_name, const char * const provider_desc, const char * const provider_file, void * user_data) { gchar *lang_name; GTree *dicts = (GTree *)user_data; lang_name = create_name_for_language (lang_tag); g_return_if_fail (lang_name != NULL); /* g_print ("%s - %s\n", lang_tag, lang_name); */ g_tree_replace (dicts, g_strdup (lang_tag), lang_name); } static gint key_cmp (gconstpointer a, gconstpointer b, gpointer user_data) { return strcmp (a, b); } static gint lang_cmp (const PlumaSpellCheckerLanguage *a, const PlumaSpellCheckerLanguage *b) { return g_utf8_collate (a->name, b->name); } static gboolean build_langs_list (const gchar *key, const gchar *value, gpointer data) { PlumaSpellCheckerLanguage *lang = g_new (PlumaSpellCheckerLanguage, 1); lang->abrev = g_strdup (key); lang->name = g_strdup (value); available_languages = g_slist_insert_sorted (available_languages, lang, (GCompareFunc)lang_cmp); return FALSE; } const GSList * pluma_spell_checker_get_available_languages (void) { EnchantBroker *broker; GTree *dicts; if (available_languages_initialized) return available_languages; g_return_val_if_fail (available_languages == NULL, NULL); available_languages_initialized = TRUE; broker = enchant_broker_init (); g_return_val_if_fail (broker != NULL, NULL); /* Use a GTree to efficiently remove duplicates while building the list */ dicts = g_tree_new_full (key_cmp, NULL, (GDestroyNotify)g_free, (GDestroyNotify)g_free); iso_639_table = create_iso_639_table (); iso_3166_table = create_iso_3166_table (); enchant_broker_list_dicts (broker, enumerate_dicts, dicts); enchant_broker_free (broker); g_hash_table_destroy (iso_639_table); g_hash_table_destroy (iso_3166_table); iso_639_table = NULL; iso_3166_table = NULL; g_tree_foreach (dicts, (GTraverseFunc)build_langs_list, NULL); g_tree_destroy (dicts); return available_languages; } const gchar * pluma_spell_checker_language_to_string (const PlumaSpellCheckerLanguage *lang) { if (lang == NULL) /* Translators: this refers the Default language used by the * spell checker */ return C_("language", "Default"); return lang->name; } const gchar * pluma_spell_checker_language_to_key (const PlumaSpellCheckerLanguage *lang) { g_return_val_if_fail (lang != NULL, NULL); return lang->abrev; } const PlumaSpellCheckerLanguage * pluma_spell_checker_language_from_key (const gchar *key) { const GSList *langs; g_return_val_if_fail (key != NULL, NULL); langs = pluma_spell_checker_get_available_languages (); while (langs != NULL) { const PlumaSpellCheckerLanguage *l = (const PlumaSpellCheckerLanguage *)langs->data; if (g_ascii_strcasecmp (key, l->abrev) == 0) return l; langs = g_slist_next (langs); } return NULL; }