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"
24 #include <glib/gi18n-lib.h>
27 #define ISO_639_DOMAIN "iso_639"
28 #define ISO_3166_DOMAIN "iso_3166"
30 struct _GtkhtmlSpellLanguage
{
36 static GHashTable
*iso_639_table
= NULL
;
37 static GHashTable
*iso_3166_table
= NULL
;
41 #define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
48 static HMODULE hmodule
;
51 DllMain (HINSTANCE hinstDLL
,
56 DllMain (HINSTANCE hinstDLL
,
62 case DLL_PROCESS_ATTACH
:
71 _get_iso_codes_prefix (void)
73 static gchar retval
[1000];
74 static gint beenhere
= 0;
80 if (!(temp_dir
= g_win32_get_package_installation_directory_of_module ((gpointer
) hmodule
))) {
81 strcpy (retval
, ISO_CODES_PREFIX
);
85 strcpy (retval
, temp_dir
);
92 _get_isocodeslocaledir (void)
94 static gchar retval
[1000];
95 static gint beenhere
= 0;
100 strcpy (retval
, _get_iso_codes_prefix ());
101 strcat (retval
, "\\share\\locale");
106 #undef ISO_CODES_PREFIX
107 #define ISO_CODES_PREFIX _get_iso_codes_prefix ()
109 #undef ISOCODESLOCALEDIR
110 #define ISOCODESLOCALEDIR _get_isocodeslocaledir ()
115 iso_639_start_element (GMarkupParseContext
*context
,
116 const gchar
*element_name
,
117 const gchar
**attribute_names
,
118 const gchar
**attribute_values
,
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
;
129 if (strcmp (element_name
, "iso_639_entry") != 0)
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
)));
150 iso_3166_start_element (GMarkupParseContext
*context
,
151 const gchar
*element_name
,
152 const gchar
**attribute_names
,
153 const gchar
**attribute_values
,
157 GHashTable
*hash_table
= data
;
158 const gchar
*name
= NULL
;
159 const gchar
*code
= NULL
;
162 if (strcmp (element_name
, "iso_3166_entry") != 0)
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
189 iso_codes_parse (const GMarkupParser
*parser
,
190 const gchar
*basename
,
191 GHashTable
*hash_table
)
193 GMappedFile
*mapped_file
;
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
);
203 if (mapped_file
!= NULL
) {
204 GMarkupParseContext
*context
;
205 const gchar
*contents
;
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
);
218 g_mapped_file_free (mapped_file
);
223 g_warning ("%s: %s", basename
, error
->message
);
224 g_error_free (error
);
228 #endif /* HAVE_ISO_CODES */
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
,
237 const gchar
*iso_639_name
;
238 const gchar
*iso_3166_name
;
239 gchar
*language_name
;
243 /* Split language code into lowercase tokens. */
244 lowercase
= g_ascii_strdown (language_code
, -1);
245 tokens
= g_strsplit (lowercase
, "_", -1);
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
);
259 if (g_strv_length (tokens
) < 2) {
260 language_name
= g_strdup (iso_639_name
);
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
);
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]);
280 g_tree_replace (tree
, g_strdup (language_code
), language_name
);
283 static const GtkhtmlSpellLanguage
*
284 spell_language_copy (const GtkhtmlSpellLanguage
*language
)
290 spell_language_free (const GtkhtmlSpellLanguage
*language
)
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)
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
;
321 spell_language_traverse_cb (const gchar
*code
,
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
);
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
);
354 gtkhtml_spell_language_get_available (void)
356 static gboolean initialized
= FALSE
;
357 static GList
*available_languages
= NULL
;
358 EnchantBroker
*broker
;
362 return available_languages
;
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");
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
);
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
);
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
;
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
;
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 ();
447 gtkhtml_spell_language_get_code (const GtkhtmlSpellLanguage
*language
)
449 g_return_val_if_fail (language
!= NULL
, NULL
);
451 return language
->code
;
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
;
466 gtkhtml_spell_language_compare (const GtkhtmlSpellLanguage
*language_a
,
467 const GtkhtmlSpellLanguage
*language_b
)
469 return strcmp (language_a
->ckey
, language_b
->ckey
);