2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / intl.c
blob83402ca0b011eedca7f63557ae4318a69fbf91d7
1 #include <config.h>
3 #include "intl.h"
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
9 #ifndef GNOME
10 /* taken from gnome-libs/libgnome/gnome-i18n.c ... */
11 /* FIXME: is this still necessary, or has gtk2.0 swallowed this ? */
13 static GHashTable *alias_table = NULL;
15 /*read an alias file for the locales*/
16 static void
17 read_aliases (char *file)
19 FILE *fp;
20 char buf[256];
22 if (!alias_table)
23 alias_table = g_hash_table_new_full (g_str_hash, g_str_equal,
24 g_free, g_free);
25 fp = fopen (file,"r");
26 if (!fp)
27 return;
28 while (fgets (buf,256,fp)) {
29 char *p;
30 g_strstrip(buf);
31 if(buf[0]=='#' || buf[0]=='\0')
32 continue;
33 p = strtok(buf,"\t ");
34 if(!p) continue;
35 p = strtok(NULL,"\t ");
36 if(!p) continue;
37 g_hash_table_insert (alias_table, g_strdup(buf), g_strdup(p));
39 fclose (fp);
42 static void
43 free_alias_table(void)
45 if (!alias_table)
46 return;
47 g_hash_table_destroy(alias_table);
48 alias_table = NULL;
51 /*return the un-aliased language as a newly allocated string*/
52 static char *
53 unalias_lang (char *lang)
55 char *p;
57 if(!alias_table) {
58 read_aliases ("/usr/share/locale/locale.alias");
59 read_aliases ("/usr/local/share/locale/locale.alias");
60 read_aliases ("/usr/lib/X11/locale/locale.alias");
61 read_aliases ("/usr/openwin/lib/locale/locale.alias");
63 while((p=g_hash_table_lookup(alias_table,lang)) && strcmp(p, lang))
64 lang = p;
65 return lang;
68 /* Mask for components of locale spec. The ordering here is from
69 * least significant to most significant
71 enum
73 COMPONENT_CODESET = 1 << 0,
74 COMPONENT_TERRITORY = 1 << 1,
75 COMPONENT_MODIFIER = 1 << 2
78 /* Break an X/Open style locale specification into components
80 static guint
81 explode_locale (const gchar *locale,
82 gchar **language,
83 gchar **territory,
84 gchar **codeset,
85 gchar **modifier)
87 const gchar *uscore_pos;
88 const gchar *at_pos;
89 const gchar *dot_pos;
91 guint mask = 0;
93 uscore_pos = strchr (locale, '_');
94 dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
95 at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
97 if (at_pos) {
98 mask |= COMPONENT_MODIFIER;
99 *modifier = g_strdup (at_pos);
100 } else
101 at_pos = locale + strlen (locale);
103 if (dot_pos) {
104 mask |= COMPONENT_CODESET;
105 *codeset = g_new (gchar, 1 + at_pos - dot_pos);
106 strncpy (*codeset, dot_pos, at_pos - dot_pos);
107 (*codeset)[at_pos - dot_pos] = '\0';
108 } else
109 dot_pos = at_pos;
111 if (uscore_pos) {
112 mask |= COMPONENT_TERRITORY;
113 *territory = g_new (gchar, 1 + dot_pos - uscore_pos);
114 strncpy (*territory, uscore_pos, dot_pos - uscore_pos);
115 (*territory)[dot_pos - uscore_pos] = '\0';
116 } else
117 uscore_pos = dot_pos;
119 *language = g_new (gchar, 1 + uscore_pos - locale);
120 strncpy (*language, locale, uscore_pos - locale);
121 (*language)[uscore_pos - locale] = '\0';
123 return mask;
127 * Compute all interesting variants for a given locale name -
128 * by stripping off different components of the value.
130 * For simplicity, we assume that the locale is in
131 * X/Open format: language[_territory][.codeset][@modifier]
133 * TODO: Extend this to handle the CEN format (see the GNUlibc docs)
134 * as well. We could just copy the code from glibc wholesale
135 * but it is big, ugly, and complicated, so I'm reluctant
136 * to do so when this should handle 99% of the time...
138 static GList *
139 compute_locale_variants (const gchar *locale)
141 GList *retval = NULL;
143 gchar *language;
144 gchar *territory;
145 gchar *codeset;
146 gchar *modifier;
148 guint mask;
149 guint i;
151 g_return_val_if_fail (locale != NULL, NULL);
153 mask = explode_locale (locale, &language, &territory, &codeset, &modifier);
155 /* Iterate through all possible combinations, from least attractive
156 * to most attractive.
158 for (i=0; i<=mask; i++)
159 if ((i & ~mask) == 0)
161 gchar *val = g_strconcat(language,
162 (i & COMPONENT_TERRITORY) ? territory : "",
163 (i & COMPONENT_CODESET) ? codeset : "",
164 (i & COMPONENT_MODIFIER) ? modifier : "",
165 NULL);
166 retval = g_list_prepend (retval, val);
169 g_free (language);
170 if (mask & COMPONENT_CODESET)
171 g_free (codeset);
172 if (mask & COMPONENT_TERRITORY)
173 g_free (territory);
174 if (mask & COMPONENT_MODIFIER)
175 g_free (modifier);
177 return retval;
180 /* The following is (partly) taken from the gettext package.
181 Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. */
183 static const gchar *
184 guess_category_value (const gchar *categoryname)
186 const gchar *retval;
188 /* The highest priority value is the `LANGUAGE' environment
189 variable. This is a GNU extension. */
190 retval = getenv ("LANGUAGE");
191 if (retval != NULL && retval[0] != '\0')
192 return retval;
194 /* `LANGUAGE' is not set. So we have to proceed with the POSIX
195 methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
196 systems this can be done by the `setlocale' function itself. */
198 /* Setting of LC_ALL overwrites all other. */
199 retval = getenv ("LC_ALL");
200 if (retval != NULL && retval[0] != '\0')
201 return retval;
203 /* Next comes the name of the desired category. */
204 retval = getenv (categoryname);
205 if (retval != NULL && retval[0] != '\0')
206 return retval;
208 /* Last possibility is the LANG environment variable. */
209 retval = getenv ("LANG");
210 if (retval != NULL && retval[0] != '\0')
211 return retval;
213 return NULL;
216 static GList *
217 get_language_list(const gchar *category_name)
219 GList *list = NULL;
220 gint c_locale_defined= FALSE;
222 const gchar *category_value;
223 gchar *category_memory, *orig_category_memory;
225 if (!category_name)
226 category_name= "LC_ALL";
228 category_value = guess_category_value (category_name);
229 if (! category_value)
230 category_value = "C";
231 orig_category_memory = category_memory =
232 g_malloc (strlen (category_value)+1);
234 while (category_value[0] != '\0') {
235 while (category_value[0] != '\0' && category_value[0] == ':')
236 ++category_value;
238 if (category_value[0] != '\0') {
239 char *cp= category_memory;
241 while (category_value[0] != '\0' && category_value[0] != ':')
242 *category_memory++= *category_value++;
244 category_memory[0]= '\0';
245 category_memory++;
247 cp = unalias_lang(cp);
249 if (strcmp (cp, "C") == 0)
250 c_locale_defined= TRUE;
252 list= g_list_concat (list, compute_locale_variants (cp));
256 g_free (orig_category_memory);
258 if (!c_locale_defined)
259 list= g_list_append (list, "C");
261 return list;
264 #endif
266 const GList *
267 intl_get_language_list(void)
269 static const GList *list = NULL;
271 if (!list) {
272 #ifdef GNOME
273 list = gnome_i18n_get_language_list("LC_MESSAGES");
274 #else
275 list = get_language_list("LC_MESSAGES");
276 free_alias_table();
277 #endif
279 return list;
282 /* low numbers are better */
284 intl_score_locale(const gchar *locale)
286 const GList *list = intl_get_language_list();
287 const GList *tmp;
288 int i;
290 /* NULL is same as C locale */
291 if (!locale)
292 return g_list_length((GList*)list) - 1;
293 for (tmp = list, i = 0; tmp; tmp = tmp->next, i++)
294 if (!strcmp((const char *)tmp->data, locale))
295 break;
296 if (!tmp) /* not found */
297 i = G_MAXINT;
298 return i;