Updated Esperanto translation
[gtkhtml.git] / components / editor / gtkhtml-spell-language.c
blobbc9d8ef4a2c4e4bf1f9d1871aa2bfe3c59046de8
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gtkhtml-editor-spell-language.c
4 * Copyright (C) 2008 Novell, Inc.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include "gtkhtml-spell-language.h"
23 #include <string.h>
24 #include <glib/gi18n-lib.h>
25 #include <enchant.h>
27 #define ISO_639_DOMAIN "iso_639"
28 #define ISO_3166_DOMAIN "iso_3166"
30 struct _GtkhtmlSpellLanguage {
31 gchar *code;
32 gchar *name;
33 gchar *ckey;
36 static GHashTable *iso_639_table = NULL;
37 static GHashTable *iso_3166_table = NULL;
39 #ifdef HAVE_ISO_CODES
41 #define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
43 #ifdef G_OS_WIN32
44 #ifdef DATADIR
45 #undef DATADIR
46 #endif
47 #include <shlobj.h>
48 static HMODULE hmodule;
50 BOOL WINAPI
51 DllMain (HINSTANCE hinstDLL,
52 DWORD fdwReason,
53 LPVOID lpvReserved);
55 BOOL WINAPI
56 DllMain (HINSTANCE hinstDLL,
57 DWORD fdwReason,
58 LPVOID lpvReserved)
60 switch (fdwReason)
62 case DLL_PROCESS_ATTACH:
63 hmodule = hinstDLL;
64 break;
67 return TRUE;
70 static gchar *
71 _get_iso_codes_prefix (void)
73 static gchar retval[1000];
74 static gint beenhere = 0;
75 gchar *temp_dir = 0;
77 if (beenhere)
78 return retval;
80 if (!(temp_dir = g_win32_get_package_installation_directory_of_module ((gpointer) hmodule))) {
81 strcpy (retval, ISO_CODES_PREFIX);
82 return retval;
85 strcpy (retval, temp_dir);
86 g_free (temp_dir);
87 beenhere = 1;
88 return retval;
91 static gchar *
92 _get_isocodeslocaledir (void)
94 static gchar retval[1000];
95 static gint beenhere = 0;
97 if (beenhere)
98 return retval;
100 strcpy (retval, _get_iso_codes_prefix ());
101 strcat (retval, "\\share\\locale");
102 beenhere = 1;
103 return retval;
106 #undef ISO_CODES_PREFIX
107 #define ISO_CODES_PREFIX _get_iso_codes_prefix ()
109 #undef ISOCODESLOCALEDIR
110 #define ISOCODESLOCALEDIR _get_isocodeslocaledir ()
112 #endif
114 static void
115 iso_639_start_element (GMarkupParseContext *context,
116 const gchar *element_name,
117 const gchar **attribute_names,
118 const gchar **attribute_values,
119 gpointer data,
120 GError **error)
122 GHashTable *hash_table = data;
123 const gchar *iso_639_1_code = NULL;
124 const gchar *iso_639_2_code = NULL;
125 const gchar *name = NULL;
126 const gchar *code = NULL;
127 gint ii;
129 if (strcmp (element_name, "iso_639_entry") != 0)
130 return;
132 for (ii = 0; attribute_names[ii] != NULL; ii++) {
133 if (strcmp (attribute_names[ii], "name") == 0)
134 name = attribute_values[ii];
135 else if (strcmp (attribute_names[ii], "iso_639_1_code") == 0)
136 iso_639_1_code = attribute_values[ii];
137 else if (strcmp (attribute_names[ii], "iso_639_2T_code") == 0)
138 iso_639_2_code = attribute_values[ii];
141 code = (iso_639_1_code != NULL) ? iso_639_1_code : iso_639_2_code;
143 if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
144 g_hash_table_insert (
145 hash_table, g_strdup (code),
146 g_strdup (dgettext (ISO_639_DOMAIN, name)));
149 static void
150 iso_3166_start_element (GMarkupParseContext *context,
151 const gchar *element_name,
152 const gchar **attribute_names,
153 const gchar **attribute_values,
154 gpointer data,
155 GError **error)
157 GHashTable *hash_table = data;
158 const gchar *name = NULL;
159 const gchar *code = NULL;
160 gint ii;
162 if (strcmp (element_name, "iso_3166_entry") != 0)
163 return;
165 for (ii = 0; attribute_names[ii] != NULL; ii++) {
166 if (strcmp (attribute_names[ii], "name") == 0)
167 name = attribute_values[ii];
168 else if (strcmp (attribute_names[ii], "alpha_2_code") == 0)
169 code = attribute_values[ii];
172 if (code != NULL && *code != '\0' && name != NULL && *name != '\0')
173 g_hash_table_insert (
174 hash_table, g_ascii_strdown (code, -1),
175 g_strdup (dgettext (ISO_3166_DOMAIN, name)));
178 static GMarkupParser iso_639_parser = {
179 iso_639_start_element,
180 NULL, NULL, NULL, NULL
183 static GMarkupParser iso_3166_parser = {
184 iso_3166_start_element,
185 NULL, NULL, NULL, NULL
188 static void
189 iso_codes_parse (const GMarkupParser *parser,
190 const gchar *basename,
191 GHashTable *hash_table)
193 GMappedFile *mapped_file;
194 gchar *filename;
195 GError *error = NULL;
197 filename = g_build_filename (
198 ISO_CODES_PREFIX, "share", "xml",
199 "iso-codes", basename, NULL);
200 mapped_file = g_mapped_file_new (filename, FALSE, &error);
201 g_free (filename);
203 if (mapped_file != NULL) {
204 GMarkupParseContext *context;
205 const gchar *contents;
206 gsize length;
208 context = g_markup_parse_context_new (
209 parser, 0, hash_table, NULL);
210 contents = g_mapped_file_get_contents (mapped_file);
211 length = g_mapped_file_get_length (mapped_file);
212 g_markup_parse_context_parse (
213 context, contents, length, &error);
214 g_markup_parse_context_free (context);
215 #if GLIB_CHECK_VERSION(2,21,3)
216 g_mapped_file_unref (mapped_file);
217 #else
218 g_mapped_file_free (mapped_file);
219 #endif
222 if (error != NULL) {
223 g_warning ("%s: %s", basename, error->message);
224 g_error_free (error);
228 #endif /* HAVE_ISO_CODES */
230 static void
231 spell_language_dict_describe_cb (const gchar * const language_code,
232 const gchar * const provider_name,
233 const gchar * const provider_desc,
234 const gchar * const provider_file,
235 GTree *tree)
237 const gchar *iso_639_name;
238 const gchar *iso_3166_name;
239 gchar *language_name;
240 gchar *lowercase;
241 gchar **tokens;
243 /* Split language code into lowercase tokens. */
244 lowercase = g_ascii_strdown (language_code, -1);
245 tokens = g_strsplit (lowercase, "_", -1);
246 g_free (lowercase);
248 g_return_if_fail (tokens != NULL);
250 iso_639_name = g_hash_table_lookup (iso_639_table, tokens[0]);
252 if (iso_639_name == NULL) {
253 language_name = g_strdup_printf (
254 /* Translators: %s is the language ISO code. */
255 C_("language", "Unknown (%s)"), language_code);
256 goto exit;
259 if (g_strv_length (tokens) < 2) {
260 language_name = g_strdup (iso_639_name);
261 goto exit;
264 iso_3166_name = g_hash_table_lookup (iso_3166_table, tokens[1]);
266 if (iso_3166_name != NULL)
267 language_name = g_strdup_printf (
268 /* Translators: The first %s is the language name, and the
269 * second is the country name. Example: "French (France)" */
270 C_("language", "%s (%s)"), iso_639_name, iso_3166_name);
271 else
272 language_name = g_strdup_printf (
273 /* Translators: The first %s is the language name, and the
274 * second is the country name. Example: "French (France)" */
275 C_("language", "%s (%s)"), iso_639_name, tokens[1]);
277 exit:
278 g_strfreev (tokens);
280 g_tree_replace (tree, g_strdup (language_code), language_name);
283 static const GtkhtmlSpellLanguage *
284 spell_language_copy (const GtkhtmlSpellLanguage *language)
286 return language;
289 static void
290 spell_language_free (const GtkhtmlSpellLanguage *language)
292 /* do nothing */
295 static const GtkhtmlSpellLanguage *
296 spell_language_lookup (const gchar *language_code)
298 const GtkhtmlSpellLanguage *closest_match = NULL;
299 const GList *available_languages;
301 available_languages = gtkhtml_spell_language_get_available ();
303 while (available_languages != NULL && language_code != NULL) {
304 GtkhtmlSpellLanguage *language = available_languages->data;
305 const gchar *code = language->code;
306 gsize length = strlen (code);
308 if (g_ascii_strcasecmp (language_code, code) == 0)
309 return language;
311 if (g_ascii_strncasecmp (language_code, code, length) == 0)
312 closest_match = language;
314 available_languages = g_list_next (available_languages);
317 return closest_match;
320 static gboolean
321 spell_language_traverse_cb (const gchar *code,
322 const gchar *name,
323 GList **available_languages)
325 GtkhtmlSpellLanguage *language;
327 language = g_slice_new (GtkhtmlSpellLanguage);
328 language->code = g_strdup (code);
329 language->name = g_strdup (name);
330 language->ckey = g_utf8_collate_key (name, -1);
332 *available_languages = g_list_insert_sorted (
333 *available_languages, language,
334 (GCompareFunc) gtkhtml_spell_language_compare);
336 return FALSE;
339 GType
340 gtkhtml_spell_language_get_type (void)
342 static GType type = 0;
344 if (G_UNLIKELY (type == 0))
345 type = g_boxed_type_register_static (
346 "GtkhtmlSpellLanguage",
347 (GBoxedCopyFunc) spell_language_copy,
348 (GBoxedFreeFunc) spell_language_free);
350 return type;
353 const GList *
354 gtkhtml_spell_language_get_available (void)
356 static gboolean initialized = FALSE;
357 static GList *available_languages = NULL;
358 EnchantBroker *broker;
359 GTree *tree;
361 if (initialized)
362 return available_languages;
364 initialized = TRUE;
366 #if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES)
367 bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR);
368 bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
370 bindtextdomain (ISO_3166_DOMAIN, ISOCODESLOCALEDIR);
371 bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
372 #endif
374 iso_639_table = g_hash_table_new_full (
375 g_str_hash, g_str_equal,
376 (GDestroyNotify) g_free,
377 (GDestroyNotify) g_free);
379 iso_3166_table = g_hash_table_new_full (
380 g_str_hash, g_str_equal,
381 (GDestroyNotify) g_free,
382 (GDestroyNotify) g_free);
384 #ifdef HAVE_ISO_CODES
385 iso_codes_parse (&iso_639_parser, "iso_639.xml", iso_639_table);
386 iso_codes_parse (&iso_3166_parser, "iso_3166.xml", iso_3166_table);
387 #endif
389 tree = g_tree_new_full (
390 (GCompareDataFunc) strcmp, NULL,
391 (GDestroyNotify) g_free,
392 (GDestroyNotify) g_free);
394 broker = enchant_broker_init ();
395 enchant_broker_list_dicts (
396 broker, (EnchantDictDescribeFn)
397 spell_language_dict_describe_cb, tree);
398 enchant_broker_free (broker);
400 g_tree_foreach (
401 tree, (GTraverseFunc)
402 spell_language_traverse_cb,
403 &available_languages);
405 g_tree_destroy (tree);
407 return available_languages;
410 static const GtkhtmlSpellLanguage *
411 spell_language_pick_default (void)
413 const GtkhtmlSpellLanguage *language = NULL;
414 const gchar * const *language_names;
415 const GList *available_languages;
416 gint ii;
418 language_names = g_get_language_names ();
419 available_languages = gtkhtml_spell_language_get_available ();
421 for (ii = 0; language == NULL && language_names[ii] != NULL; ii++)
422 language = spell_language_lookup (language_names[ii]);
424 if (language == NULL)
425 language = spell_language_lookup ("en_US");
427 if (language == NULL && available_languages != NULL)
428 language = available_languages->data;
430 return language;
433 const GtkhtmlSpellLanguage *
434 gtkhtml_spell_language_lookup (const gchar *language_code)
436 const GtkhtmlSpellLanguage *language = NULL;
438 language = spell_language_lookup (language_code);
440 if (language == NULL)
441 language = spell_language_pick_default ();
443 return language;
446 const gchar *
447 gtkhtml_spell_language_get_code (const GtkhtmlSpellLanguage *language)
449 g_return_val_if_fail (language != NULL, NULL);
451 return language->code;
454 const gchar *
455 gtkhtml_spell_language_get_name (const GtkhtmlSpellLanguage *language)
457 if (language == NULL)
458 /* Translators: This refers to the default language used
459 * by the spell checker. */
460 return C_("language", "Default");
462 return language->name;
465 gint
466 gtkhtml_spell_language_compare (const GtkhtmlSpellLanguage *language_a,
467 const GtkhtmlSpellLanguage *language_b)
469 return strcmp (language_a->ckey, language_b->ckey);