Initial import of ephy (rev# 7126) from svn
[ephy-soc.git] / lib / .svn / text-base / ephy-langs.c.svn-base
blob8ef5d255648d74fb32562d5a48244c8f11985c66
1 /*
2  *  Copyright © 2003, 2004 Christian Persch
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2, or (at your option)
7  *  any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *  $Id$
19  */
21 #include "config.h"
23 #include "ephy-langs.h"
25 #include "ephy-debug.h"
27 #include <glib/gi18n.h>
29 #include <string.h>
31 #include <libxml/xmlreader.h>
33 /* If you add more language codes here, remember to also
34  * add them to data/generate-font-schemas.py .
35  */
36 static const EphyFontsLanguageInfo font_languages [] =
38         /* Translators: The text before the "|" is context to help you decide on
39          * the correct translation. You MUST OMIT it in the translated string. */
40         { N_("select fonts for|Arabic"),                                "ar" },
41         /* Translators: The text before the "|" is context to help you decide on
42          * the correct translation. You MUST OMIT it in the translated string. */
43         { N_("select fonts for|Baltic"),                                "x-baltic" },
44         /* Translators: The text before the "|" is context to help you decide on
45          * the correct translation. You MUST OMIT it in the translated string. */
46         { N_("select fonts for|Central European"),                      "x-central-euro" },
47         /* Translators: The text before the "|" is context to help you decide on
48          * the correct translation. You MUST OMIT it in the translated string. */
49         { N_("select fonts for|Cyrillic"),                              "x-cyrillic" },
50         /* Translators: The text before the "|" is context to help you decide on
51          * the correct translation. You MUST OMIT it in the translated string. */
52         { N_("select fonts for|Devanagari"),                            "x-devanagari" },
53         /* Translators: The text before the "|" is context to help you decide on
54          * the correct translation. You MUST OMIT it in the translated string. */
55         { N_("select fonts for|Greek"),                                 "el" },
56         /* Translators: The text before the "|" is context to help you decide on
57          * the correct translation. You MUST OMIT it in the translated string. */
58         { N_("select fonts for|Hebrew"),                                "he" },
59         /* Translators: The text before the "|" is context to help you decide on
60          * the correct translation. You MUST OMIT it in the translated string. */
61         { N_("select fonts for|Japanese"),                              "ja" },
62         /* Translators: The text before the "|" is context to help you decide on
63          * the correct translation. You MUST OMIT it in the translated string. */
64         { N_("select fonts for|Korean"),                                "ko" },
65         /* Translators: The text before the "|" is context to help you decide on
66          * the correct translation. You MUST OMIT it in the translated string. */
67         { N_("select fonts for|Simplified Chinese"),                    "zh-CN" },
68         /* Translators: The text before the "|" is context to help you decide on
69          * the correct translation. You MUST OMIT it in the translated string. */
70         { N_("select fonts for|Tamil"),                                 "x-tamil" },
71         /* Translators: The text before the "|" is context to help you decide on
72          * the correct translation. You MUST OMIT it in the translated string. */
73         { N_("select fonts for|Thai"),                                  "th" },
74         /* Translators: The text before the "|" is context to help you decide on
75          * the correct translation. You MUST OMIT it in the translated string. */
76         { N_("select fonts for|Traditional Chinese"),                   "zh-TW" },
77         /* Translators: The text before the "|" is context to help you decide on
78          * the correct translation. You MUST OMIT it in the translated string. */
79         { N_("select fonts for|Traditional Chinese (Hong Kong)"),       "zh-HK" },
80         /* Translators: The text before the "|" is context to help you decide on
81          * the correct translation. You MUST OMIT it in the translated string. */
82         { N_("select fonts for|Turkish"),                               "tr" },
83         /* Translators: The text before the "|" is context to help you decide on
84          * the correct translation. You MUST OMIT it in the translated string. */
85         { N_("select fonts for|Armenian"),                              "x-armn" },
86         /* Translators: The text before the "|" is context to help you decide on
87          * the correct translation. You MUST OMIT it in the translated string. */
88         { N_("select fonts for|Bengali"),                               "x-beng" },
89         /* Translators: The text before the "|" is context to help you decide on
90          * the correct translation. You MUST OMIT it in the translated string. */
91         { N_("select fonts for|Unified Canadian Syllabics"),            "x-cans" },
92         /* Translators: The text before the "|" is context to help you decide on
93          * the correct translation. You MUST OMIT it in the translated string. */
94         { N_("select fonts for|Ethiopic"),                              "x-ethi" },
95         /* Translators: The text before the "|" is context to help you decide on
96          * the correct translation. You MUST OMIT it in the translated string. */
97         { N_("select fonts for|Georgian"),                              "x-geor" },
98         /* Translators: The text before the "|" is context to help you decide on
99          * the correct translation. You MUST OMIT it in the translated string. */
100         { N_("select fonts for|Gujarati"),                              "x-gujr" },
101         /* Translators: The text before the "|" is context to help you decide on
102          * the correct translation. You MUST OMIT it in the translated string. */
103         { N_("select fonts for|Gurmukhi"),                              "x-guru" },
104         /* Translators: The text before the "|" is context to help you decide on
105          * the correct translation. You MUST OMIT it in the translated string. */
106         { N_("select fonts for|Khmer"),                                 "x-khmr" },
107         /* Translators: The text before the "|" is context to help you decide on
108          * the correct translation. You MUST OMIT it in the translated string. */
109         { N_("select fonts for|Malayalam"),                             "x-mlym" },
110         /* Translators: The text before the "|" is context to help you decide on
111          * the correct translation. You MUST OMIT it in the translated string. */
112         { N_("select fonts for|Western"),                               "x-western" },
113         /* Translators: The text before the "|" is context to help you decide on
114          * the correct translation. You MUST OMIT it in the translated string. */
115         { N_("select fonts for|Other Scripts"),                         "x-unicode" }
118 const EphyFontsLanguageInfo *
119 ephy_font_languages (void)
121         return font_languages;
124 guint                    
125 ephy_font_n_languages (void)
127         return G_N_ELEMENTS (font_languages);
130 /* sanitise the languages list according to the rules for HTTP accept-language
131  * in RFC 2616, Sect. 14.4
132  */
133 void
134 ephy_langs_sanitise (GArray *array)
136         char *lang1, *lang2;
137         int i, j;
139         /* if we have 'xy-ab' in list but not 'xy', append 'xy' */
140         for (i = 0; i < array->len; i++)
141         {
142                 gboolean found = FALSE;
143                 char *dash, *prefix;
145                 lang1 = (char *) g_array_index (array,char *, i);
147                 dash = strchr (lang1, '-');
148                 if (dash == NULL) continue;
150                 for (j = i + 1; j < array->len; j++)
151                 {
152                         lang2 = (char *) g_array_index (array, char *, j);
153                         if (strchr (lang2, '-') == NULL &&
154                             g_str_has_prefix (lang1, lang2))
155                         {
156                                 found = TRUE;
157                         }
158                 }
160                 if (found == FALSE)
161                 {
162                         prefix = g_strndup (lang1, dash - lang1);
163                         g_array_append_val (array, prefix);
164                 }
165         }
167         /* uniquify */
168         for (i = 0; i < (int) array->len - 1; i++)
169         {
170                 for (j = (int) array->len - 1; j > i; j--)
171                 {
172                         lang1 = (char *) g_array_index (array,char *, i);
173                         lang2 = (char *) g_array_index (array, char *, j);
175                         if (strcmp (lang1, lang2) == 0)
176                         {
177                                 g_array_remove_index (array, j);
178                                 g_free (lang2);
179                         }
180                 }
181         }
183         /* move 'xy' code behind all 'xy-ab' codes */
184         for (i = (int) array->len - 2; i >= 0; i--)
185         {
186                 for (j = (int) array->len - 1; j > i; j--)
187                 {
188                         lang1 = (char *) g_array_index (array, char *, i);
189                         lang2 = (char *) g_array_index (array, char *, j);
191                         if (strchr (lang1, '-') == NULL &&
192                             strchr (lang2, '-') != NULL &&
193                             g_str_has_prefix (lang2, lang1))
194                         {
195                                 g_array_insert_val (array, j + 1, lang1);
196                                 g_array_remove_index (array, i);
197                                 break;
198                         }
199                 }
200         }
203 void
204 ephy_langs_append_languages (GArray *array)
206         const char * const * languages;
207         char *lang;
208         int i;
210         languages = g_get_language_names ();
211         g_return_if_fail (languages != NULL);
213         /* FIXME: maybe just use the first, instead of all of them? */
214         for (i = 0; languages[i] != NULL; i++)
215         {
217                 if (strstr (languages[i], ".") == 0 &&
218                     strstr (languages[i], "@") == 0 &&
219                     strcmp (languages[i], "C") != 0)
220                 {
221                         /* change to lowercase and '_' to '-' */
222                         lang = g_strdelimit (g_ascii_strdown
223                                                 (languages[i], -1), "_", '-');
225                         g_array_append_val (array, lang);
226                 }
227         }
229         /* Fallback: add "en" if list is empty */
230         if (array->len == 0)
231         {
232                 lang = g_strdup ("en");
233                 g_array_append_val (array, lang);
234         }
237 char **
238 ephy_langs_get_languages (void)
240         GArray *array;
242         array = g_array_new (TRUE, FALSE, sizeof (char *));
244         ephy_langs_append_languages (array);
246         ephy_langs_sanitise (array);
248         return (char **) g_array_free (array, FALSE);
251 #define ISOCODESLOCALEDIR       ISO_CODES_PREFIX "/share/locale"
253 static void
254 ephy_langs_bind_iso_domains (void)
256 #ifdef ENABLE_NLS
257         static gboolean bound = FALSE;
259         if (bound == FALSE)
260         {
261                 bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR);
262                 bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
264                 bindtextdomain(ISO_3166_DOMAIN, ISOCODESLOCALEDIR);
265                 bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
267                 bound = TRUE;
268         }
269 #endif
272 static void
273 read_iso_639_entry (xmlTextReaderPtr reader,
274                     GHashTable *table)
276         xmlChar *code, *name;
278         code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_1_code");
279         name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
281         /* Get iso-639-2 code */
282         if (code == NULL || code[0] == '\0')
283         {
284                 xmlFree (code);
285                 /* FIXME: use the 2T or 2B code? */
286                 code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_2T_code");
287         }
289         if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
290         {
291                 g_hash_table_insert (table, code, name);
292         }
293         else
294         {
295                 xmlFree (code);
296                 xmlFree (name);
297         }
300 static void
301 read_iso_3166_entry (xmlTextReaderPtr reader,
302                      GHashTable *table)
304         xmlChar *code, *name;
306         code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "alpha_2_code");
307         name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
309         if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
310         {
311                 char *lcode;
313                 lcode = g_ascii_strdown ((char *) code, -1);
314                 xmlFree (code);
316                 g_hash_table_insert (table, lcode, name);
317         }
318         else
319         {
320                 xmlFree (code);
321                 xmlFree (name);
322         }
326 typedef enum
328         STATE_START,
329         STATE_STOP,
330         STATE_ENTRIES,
331 } ParserState;
333 static void
334 load_iso_entries (int iso,
335                   GFunc read_entry_func,
336                   gpointer user_data)
338         xmlTextReaderPtr reader;
339         ParserState state = STATE_START;
340         xmlChar iso_entries[32], iso_entry[32];
341         char *filename;
342         int ret = -1;
344         LOG ("Loading ISO-%d codes", iso);
346         START_PROFILER ("Loading ISO codes")
348         filename = g_strdup_printf (ISO_CODES_PREFIX "/share/xml/iso-codes/iso_%d.xml", iso);
349         reader = xmlNewTextReaderFilename (filename);
350         if (reader == NULL) goto out;
352         xmlStrPrintf (iso_entries, sizeof (iso_entries), (const xmlChar *)"iso_%d_entries", iso);
353         xmlStrPrintf (iso_entry, sizeof (iso_entry), (const xmlChar *)"iso_%d_entry", iso);
355         ret = xmlTextReaderRead (reader);
357         while (ret == 1)
358         {
359                 const xmlChar *tag;
360                 xmlReaderTypes type;
362                 tag = xmlTextReaderConstName (reader);
363                 type = xmlTextReaderNodeType (reader);
365                 if (state == STATE_ENTRIES &&
366                     type == XML_READER_TYPE_ELEMENT &&
367                     xmlStrEqual (tag, iso_entry))
368                 {
369                         read_entry_func (reader, user_data);
370                 }
371                 else if (state == STATE_START &&
372                          type == XML_READER_TYPE_ELEMENT &&
373                          xmlStrEqual (tag, iso_entries))
374                 {
375                         state = STATE_ENTRIES;
376                 }
377                 else if (state == STATE_ENTRIES &&
378                          type == XML_READER_TYPE_END_ELEMENT &&
379                          xmlStrEqual (tag, iso_entries))
380                 {
381                         state = STATE_STOP;
382                 }
383                 else if (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE ||
384                          type == XML_READER_TYPE_WHITESPACE ||
385                          type == XML_READER_TYPE_TEXT ||
386                          type == XML_READER_TYPE_COMMENT)
387                 {
388                         /* eat it */
389                 }
390                 else
391                 {
392                         /* ignore it */
393                 }
395                 ret = xmlTextReaderRead (reader);
396         }
398         xmlFreeTextReader (reader);
400 out:
401         if (ret < 0 || state != STATE_STOP)
402         {
403                 g_warning ("Failed to load ISO-%d codes from %s!\n",
404                            iso, filename);
405         }
407         g_free (filename);
409         STOP_PROFILER ("Loading ISO codes")
412 GHashTable *
413 ephy_langs_iso_639_table (void)
415         GHashTable *table;
417         ephy_langs_bind_iso_domains ();
418         table = g_hash_table_new_full (g_str_hash, g_str_equal,
419                                        (GDestroyNotify) xmlFree,
420                                        (GDestroyNotify) xmlFree);
422         load_iso_entries (639, (GFunc) read_iso_639_entry, table);
424         return table;
427 GHashTable *
428 ephy_langs_iso_3166_table (void)
430         GHashTable *table;
432         ephy_langs_bind_iso_domains ();
433         table = g_hash_table_new_full (g_str_hash, g_str_equal,
434                                        (GDestroyNotify) g_free,
435                                        (GDestroyNotify) xmlFree);
436         
437         load_iso_entries (3166, (GFunc) read_iso_3166_entry, table);
439         return table;