#include "intl.h" #include #include #include #ifndef GNOME /* taken from gnome-libs/libgnome/gnome-i18n.c ... */ static GHashTable *alias_table = NULL; /*read an alias file for the locales*/ static void read_aliases (char *file) { FILE *fp; char buf[256]; if (!alias_table) alias_table = g_hash_table_new (g_str_hash, g_str_equal); fp = fopen (file,"r"); if (!fp) return; while (fgets (buf,256,fp)) { char *p; g_strstrip(buf); if(buf[0]=='#' || buf[0]=='\0') continue; p = strtok(buf,"\t "); if(!p) continue; p = strtok(NULL,"\t "); if(!p) continue; g_hash_table_insert (alias_table, g_strdup(buf), g_strdup(p)); } fclose (fp); } static void free_alias_cb(gpointer key, gpointer value, gpointer user_data) { g_free(key); g_free(value); } static void free_alias_table(void) { if (!alias_table) return; g_hash_table_foreach(alias_table, free_alias_cb, NULL); g_hash_table_destroy(alias_table); alias_table = NULL; } /*return the un-aliased language as a newly allocated string*/ static char * unalias_lang (char *lang) { char *p; if(!alias_table) { read_aliases ("/usr/share/locale/locale.alias"); read_aliases ("/usr/local/share/locale/locale.alias"); read_aliases ("/usr/lib/X11/locale/locale.alias"); read_aliases ("/usr/openwin/lib/locale/locale.alias"); } while((p=g_hash_table_lookup(alias_table,lang)) && strcmp(p, lang)) lang = p; return lang; } /* Mask for components of locale spec. The ordering here is from * least significant to most significant */ enum { COMPONENT_CODESET = 1 << 0, COMPONENT_TERRITORY = 1 << 1, COMPONENT_MODIFIER = 1 << 2 }; /* Break an X/Open style locale specification into components */ static guint explode_locale (const gchar *locale, gchar **language, gchar **territory, gchar **codeset, gchar **modifier) { const gchar *uscore_pos; const gchar *at_pos; const gchar *dot_pos; guint mask = 0; uscore_pos = strchr (locale, '_'); dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.'); at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@'); if (at_pos) { mask |= COMPONENT_MODIFIER; *modifier = g_strdup (at_pos); } else at_pos = locale + strlen (locale); if (dot_pos) { mask |= COMPONENT_CODESET; *codeset = g_new (gchar, 1 + at_pos - dot_pos); strncpy (*codeset, dot_pos, at_pos - dot_pos); (*codeset)[at_pos - dot_pos] = '\0'; } else dot_pos = at_pos; if (uscore_pos) { mask |= COMPONENT_TERRITORY; *territory = g_new (gchar, 1 + dot_pos - uscore_pos); strncpy (*territory, uscore_pos, dot_pos - uscore_pos); (*territory)[dot_pos - uscore_pos] = '\0'; } else uscore_pos = dot_pos; *language = g_new (gchar, 1 + uscore_pos - locale); strncpy (*language, locale, uscore_pos - locale); (*language)[uscore_pos - locale] = '\0'; return mask; } /* * Compute all interesting variants for a given locale name - * by stripping off different components of the value. * * For simplicity, we assume that the locale is in * X/Open format: language[_territory][.codeset][@modifier] * * TODO: Extend this to handle the CEN format (see the GNUlibc docs) * as well. We could just copy the code from glibc wholesale * but it is big, ugly, and complicated, so I'm reluctant * to do so when this should handle 99% of the time... */ static GList * compute_locale_variants (const gchar *locale) { GList *retval = NULL; gchar *language; gchar *territory; gchar *codeset; gchar *modifier; guint mask; guint i; g_return_val_if_fail (locale != NULL, NULL); mask = explode_locale (locale, &language, &territory, &codeset, &modifier); /* Iterate through all possible combinations, from least attractive * to most attractive. */ for (i=0; i<=mask; i++) if ((i & ~mask) == 0) { gchar *val = g_strconcat(language, (i & COMPONENT_TERRITORY) ? territory : "", (i & COMPONENT_CODESET) ? codeset : "", (i & COMPONENT_MODIFIER) ? modifier : "", NULL); retval = g_list_prepend (retval, val); } g_free (language); if (mask & COMPONENT_CODESET) g_free (codeset); if (mask & COMPONENT_TERRITORY) g_free (territory); if (mask & COMPONENT_MODIFIER) g_free (modifier); return retval; } /* The following is (partly) taken from the gettext package. Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. */ static const gchar * guess_category_value (const gchar *categoryname) { const gchar *retval; /* The highest priority value is the `LANGUAGE' environment variable. This is a GNU extension. */ retval = getenv ("LANGUAGE"); if (retval != NULL && retval[0] != '\0') return retval; /* `LANGUAGE' is not set. So we have to proceed with the POSIX methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some systems this can be done by the `setlocale' function itself. */ /* Setting of LC_ALL overwrites all other. */ retval = getenv ("LC_ALL"); if (retval != NULL && retval[0] != '\0') return retval; /* Next comes the name of the desired category. */ retval = getenv (categoryname); if (retval != NULL && retval[0] != '\0') return retval; /* Last possibility is the LANG environment variable. */ retval = getenv ("LANG"); if (retval != NULL && retval[0] != '\0') return retval; return NULL; } static GList * get_language_list(const gchar *category_name) { GList *list = NULL; gint c_locale_defined= FALSE; const gchar *category_value; gchar *category_memory, *orig_category_memory; if (!category_name) category_name= "LC_ALL"; category_value = guess_category_value (category_name); if (! category_value) category_value = "C"; orig_category_memory = category_memory = g_malloc (strlen (category_value)+1); while (category_value[0] != '\0') { while (category_value[0] != '\0' && category_value[0] == ':') ++category_value; if (category_value[0] != '\0') { char *cp= category_memory; while (category_value[0] != '\0' && category_value[0] != ':') *category_memory++= *category_value++; category_memory[0]= '\0'; category_memory++; cp = unalias_lang(cp); if (strcmp (cp, "C") == 0) c_locale_defined= TRUE; list= g_list_concat (list, compute_locale_variants (cp)); } } g_free (orig_category_memory); if (!c_locale_defined) list= g_list_append (list, "C"); return list; } #endif GList * intl_get_language_list(void) { static GList *list = NULL; if (!list) { #ifdef GNOME list = gnome_i18n_get_language_list("LC_MESSAGES"); #else list = get_language_list("LC_MESSAGES"); free_alias_table(); #endif } return list; } /* low numbers are better */ int intl_score_locale(const gchar *locale) { GList *list = intl_get_language_list(); GList *tmp; int i; /* NULL is same as C locale */ if (!locale) return g_list_length(list) - 1; for (tmp = list, i = 0; tmp; tmp = tmp->next, i++) if (!strcmp((char *)tmp->data, locale)) break; if (!tmp) /* not found */ i = G_MAXINT; return i; }