diff options
Diffstat (limited to 'plugins/spell/pluma-spell-checker-language.c')
-rwxr-xr-x | plugins/spell/pluma-spell-checker-language.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/plugins/spell/pluma-spell-checker-language.c b/plugins/spell/pluma-spell-checker-language.c new file mode 100755 index 00000000..042e7706 --- /dev/null +++ b/plugins/spell/pluma-spell-checker-language.c @@ -0,0 +1,439 @@ +/* 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, 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 <config.h> +#endif + +#include <string.h> + +#include <enchant.h> + +#include <glib/gi18n.h> +#include <libxml/xmlreader.h> + +#include "pluma-spell-checker-language.h" + +#include <pluma/pluma-debug.h> + +#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 xmlChar *)"iso_%d_entries", iso); + xmlStrPrintf (iso_entry, sizeof (iso_entry), (const xmlChar *)"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; +} |