Add a function to return a list of applicable locale names. (#95587,
authorMatthias Clasen <mclasen@redhat.com>
Tue, 7 Sep 2004 18:37:10 +0000 (18:37 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Tue, 7 Sep 2004 18:37:10 +0000 (18:37 +0000)
2004-09-07  Matthias Clasen  <mclasen@redhat.com>

* glib/gutils.h:
* glib/gutils.c (g_get_language_names): Add a function to
return a list of applicable locale names.  (#95587,
Hidetoshi Tajima)
(guess_category_value, compute_locale_variants):
(explode_locale, unalias_lang, read_aliases): Helper
functions for g_get_language_names()

* tests/testglib.c (main): Show the results of
g_get_language_names()

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-6
ChangeLog.pre-2-8
docs/reference/ChangeLog
docs/reference/glib/glib-sections.txt
glib/gutils.c
glib/gutils.h
tests/testglib.c

index 25ad832..e4a470c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2004-09-07  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/gutils.h: 
+       * glib/gutils.c (g_get_language_names): Add a function to
+       return a list of applicable locale names.  (#95587, 
+       Hidetoshi Tajima)
+       (guess_category_value, compute_locale_variants):
+       (explode_locale, unalias_lang, read_aliases): Helper 
+       functions for g_get_language_names()
+
+       * tests/testglib.c (main): Show the results of 
+       g_get_language_names()
+
 Sun Sep  5 01:46:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * glib/glib.def: 
index 25ad832..e4a470c 100644 (file)
@@ -1,3 +1,16 @@
+2004-09-07  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/gutils.h: 
+       * glib/gutils.c (g_get_language_names): Add a function to
+       return a list of applicable locale names.  (#95587, 
+       Hidetoshi Tajima)
+       (guess_category_value, compute_locale_variants):
+       (explode_locale, unalias_lang, read_aliases): Helper 
+       functions for g_get_language_names()
+
+       * tests/testglib.c (main): Show the results of 
+       g_get_language_names()
+
 Sun Sep  5 01:46:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * glib/glib.def: 
index 25ad832..e4a470c 100644 (file)
@@ -1,3 +1,16 @@
+2004-09-07  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/gutils.h: 
+       * glib/gutils.c (g_get_language_names): Add a function to
+       return a list of applicable locale names.  (#95587, 
+       Hidetoshi Tajima)
+       (guess_category_value, compute_locale_variants):
+       (explode_locale, unalias_lang, read_aliases): Helper 
+       functions for g_get_language_names()
+
+       * tests/testglib.c (main): Show the results of 
+       g_get_language_names()
+
 Sun Sep  5 01:46:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * glib/glib.def: 
index 25ad832..e4a470c 100644 (file)
@@ -1,3 +1,16 @@
+2004-09-07  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/gutils.h: 
+       * glib/gutils.c (g_get_language_names): Add a function to
+       return a list of applicable locale names.  (#95587, 
+       Hidetoshi Tajima)
+       (guess_category_value, compute_locale_variants):
+       (explode_locale, unalias_lang, read_aliases): Helper 
+       functions for g_get_language_names()
+
+       * tests/testglib.c (main): Show the results of 
+       g_get_language_names()
+
 Sun Sep  5 01:46:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * glib/glib.def: 
index 25ad832..e4a470c 100644 (file)
@@ -1,3 +1,16 @@
+2004-09-07  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/gutils.h: 
+       * glib/gutils.c (g_get_language_names): Add a function to
+       return a list of applicable locale names.  (#95587, 
+       Hidetoshi Tajima)
+       (guess_category_value, compute_locale_variants):
+       (explode_locale, unalias_lang, read_aliases): Helper 
+       functions for g_get_language_names()
+
+       * tests/testglib.c (main): Show the results of 
+       g_get_language_names()
+
 Sun Sep  5 01:46:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * glib/glib.def: 
index 34bd0a2..43f7cbd 100644 (file)
@@ -1,3 +1,7 @@
+2004-09-07  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/glib-sections.txt: Add g_get_language_names().
+
 Mon Sep  6 01:56:13 2004  Matthias Clasen  <maclas@gmx.de>
 
        * glib/tmpl/messages.sgml: Remove excess markup and fix a markup
index f5eb2c2..89557b7 100644 (file)
@@ -2096,5 +2096,6 @@ _
 Q_
 N_
 g_strip_context
-
+<SUBSECTION>
+g_get_language_names
 </SECTION>
index 398dc75..2afa8d2 100644 (file)
@@ -38,6 +38,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <locale.h>
 #include <string.h>
 #include <errno.h>
 #ifdef HAVE_PWD_H
@@ -1520,6 +1521,296 @@ g_get_system_config_dirs (void)
   return (G_CONST_RETURN gchar * G_CONST_RETURN *) conf_dir_vector;
 }
 
+static GHashTable *alias_table = NULL;
+
+/* read an alias file for the locales */
+static void
+read_aliases (gchar *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, *q;
+
+      g_strstrip (buf);
+
+      /* Line is a comment */
+      if ((buf[0] == '#') || (buf[0] == '\0'))
+       continue;
+
+      /* Reads first column */
+      for (p = buf, q = NULL; *p; p++) {
+       if ((*p == '\t') || (*p == ' ') || (*p == ':')) {
+         *p = '\0';
+         q = p+1;
+         while ((*q == '\t') || (*q == ' ')) {
+           q++;
+         }
+         break;
+       }
+      }
+      /* The line only had one column */
+      if (!q || *q == '\0')
+       continue;
+      
+      /* Read second column */
+      for (p = q; *p; p++) {
+       if ((*p == '\t') || (*p == ' ')) {
+         *p = '\0';
+         break;
+       }
+      }
+
+      /* Add to alias table if necessary */
+      if (!g_hash_table_lookup (alias_table, buf)) {
+       g_hash_table_insert (alias_table, g_strdup (buf), g_strdup (q));
+      }
+    }
+  fclose (fp);
+}
+
+static char *
+unalias_lang (char *lang)
+{
+  char *p;
+  int i;
+
+  if (!alias_table)
+    read_aliases ("/usr/share/locale/locale.alias");
+
+  i = 0;
+  while ((p = g_hash_table_lookup (alias_table, lang)) && (strcmp (p, lang) != 0))
+    {
+      lang = p;
+      if (i++ == 30)
+        {
+          static gboolean said_before = FALSE;
+         if (!said_before)
+            g_warning ("Too many alias levels for a locale, "
+                      "may indicate a loop");
+         said_before = TRUE;
+         return lang;
+       }
+    }
+  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_strndup (dot_pos, at_pos - dot_pos);
+    }
+  else
+    dot_pos = at_pos;
+
+  if (uscore_pos)
+    {
+      mask |= COMPONENT_TERRITORY;
+      *territory = g_strndup (uscore_pos, dot_pos - uscore_pos);
+    }
+  else
+    uscore_pos = dot_pos;
+
+  *language = g_strndup (locale, uscore_pos - locale);
+
+  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 GSList *
+compute_locale_variants (const gchar *locale)
+{
+  GSList *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_slist_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 *category_name)
+{
+  const gchar *retval;
+
+  /* The highest priority value is the `LANGUAGE' environment
+     variable.  This is a GNU extension.  */
+  retval = g_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 = g_getenv ("LC_ALL");  
+  if ((retval != NULL) && (retval[0] != '\0'))
+    return retval;
+
+  /* Next comes the name of the desired category.  */
+  retval = g_getenv (category_name);
+  if ((retval != NULL) && (retval[0] != '\0'))
+    return retval;
+
+  /* Last possibility is the LANG environment variable.  */
+  retval = g_getenv ("LANG");
+  if ((retval != NULL) && (retval[0] != '\0'))
+    return retval;
+
+  return NULL;
+}
+
+static gchar **languages = NULL;
+
+/**
+ * g_get_language_names:
+ * 
+ * Computes a list of applicable locale names, which can be used to 
+ * e.g. construct locale-dependent filenames or search paths. The returned 
+ * list is sorted from most desirable to least desirable and always contains 
+ * the default locale "C".
+ *
+ * For example, if LANGUAGE=de:en_US, then the returned list is
+ * "de", "en_US", "en", "C".
+ *
+ * This function consults the environment variables <envar>LANGUAGE</envar>, 
+ * <envar>LC_ALL</envar>, <envar>LC_MESSAGES</envar> and <envar>LANG</envar> 
+ * to find the list of locales specified by the user.
+ * 
+ * Return value: a %NULL-terminated array of strings owned by GLib 
+ *    that must not be modified or freed.
+ *
+ * Since: 2.6
+ **/
+G_CONST_RETURN gchar * G_CONST_RETURN * 
+g_get_language_names ()
+{
+  G_LOCK (g_utils_global);
+
+  if (!languages)
+    {
+      const gchar *value;
+      gchar **alist, **a;
+      GSList *list, *l;
+      gint i;
+
+      value = guess_category_value ("LC_MESSAGES");
+      if (!value)
+       value = "C";
+
+      alist = g_strsplit (value, ":", 0);
+      list = NULL;
+      for (a = alist; *a; a++)
+       {
+         gchar *b = unalias_lang (*a);
+         list = g_slist_concat (list, compute_locale_variants (b));
+       }
+      g_strfreev (alist);
+      list = g_slist_append (list, "C");
+
+      languages = g_new (gchar *, g_slist_length (list) + 1);
+      for (l = list, i = 0; l; l = l->next, i++)
+       languages[i] = l->data;
+      languages[i] = NULL;
+
+      g_slist_free (list);
+    }
+
+  G_UNLOCK (g_utils_global);
+
+  return (G_CONST_RETURN gchar * G_CONST_RETURN *) languages;
+}
+
 guint
 g_direct_hash (gconstpointer v)
 {
index 73084af..c8ba172 100644 (file)
@@ -128,6 +128,8 @@ G_CONST_RETURN gchar*    g_get_user_cache_dir     (void);
 G_CONST_RETURN gchar * G_CONST_RETURN * g_get_system_data_dirs   (void);
 G_CONST_RETURN gchar * G_CONST_RETURN * g_get_system_config_dirs (void);
 
+G_CONST_RETURN gchar * G_CONST_RETURN * g_get_language_names (void);
+
 typedef struct _GDebugKey      GDebugKey;
 struct _GDebugKey
 {
index 83ee2cf..e8f9f8a 100644 (file)
@@ -451,6 +451,8 @@ main (int   argc,
   sv = (gchar **) g_get_system_config_dirs ();
   g_print ("system_config: %s\n", s ? g_strjoinv (G_SEARCHPATH_SEPARATOR_S, sv) : "NULL!");
   g_print ("tmp-dir: %s\n", g_get_tmp_dir ());
+  sv = (gchar **) g_get_language_names ();
+  g_print ("languages: %s\n", s ? g_strjoinv (":", sv) : "NULL!");
 
   /* type sizes */
   g_print ("checking size of gint8: %"    G_GSIZE_FORMAT, sizeof (gint8));