Tagged 0.7.5
[scuttle] / includes / php-gettext / gettext.inc
index 21a41cced353eb594164121e568f6cedde234e25..399a0f24b42207f2904c1751eaff1a13fea48096 100644 (file)
@@ -1,9 +1,10 @@
 <?php
 /*
    Copyright (c) 2005 Steven Armstrong <sa at c-area dot ch>
-   
+   Copyright (c) 2009 Danilo Segan <danilo@kvota.net>
+
    Drop in replacement for native gettext.
-   
+
    This file is part of PHP-gettext.
 
    PHP-gettext is free software; you can redistribute it and/or modify
 
 */
 /*
-LC_CTYPE               0
-LC_NUMERIC     1
-LC_TIME                        2
-LC_COLLATE     3
-LC_MONETARY    4
-LC_MESSAGES    5
-LC_ALL                 6
+LC_CTYPE        0
+LC_NUMERIC      1
+LC_TIME         2
+LC_COLLATE      3
+LC_MONETARY     4
+LC_MESSAGES     5
+LC_ALL          6
 */
 
-require(dirname(__FILE__) .'/streams.php');
-require(dirname(__FILE__) .'/gettext.php');
+
+// LC_MESSAGES is not available if php-gettext is not loaded
+// while the other constants are already available from session extension.
+if (!defined('LC_MESSAGES')) {
+  define('LC_MESSAGES',        5);
+}
+
+require('streams.php');
+require('gettext.php');
+
 
 // Variables
 
@@ -43,29 +52,96 @@ $LC_CATEGORIES = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MO
 $EMULATEGETTEXT = 0;
 $CURRENTLOCALE = '';
 
+/* Class to hold a single domain included in $text_domains. */
+class domain {
+  var $l10n;
+  var $path;
+  var $codeset;
+}
 
 // Utility functions
 
+/**
+ * Return a list of locales to try for any POSIX-style locale specification.
+ */
+function get_list_of_locales($locale) {
+  /* Figure out all possible locale names and start with the most
+   * specific ones.  I.e. for sr_CS.UTF-8@latin, look through all of
+   * sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr.
+   */
+  $locale_names = array();
+  $lang = NULL;
+  $country = NULL;
+  $charset = NULL;
+  $modifier = NULL;
+  if ($locale) {
+    if (preg_match("/^(?P<lang>[a-z]{2,3})"              // language code
+                   ."(?:_(?P<country>[A-Z]{2}))?"           // country code
+                   ."(?:\.(?P<charset>[-A-Za-z0-9_]+))?"    // charset
+                   ."(?:@(?P<modifier>[-A-Za-z0-9_]+))?$/",  // @ modifier
+                   $locale, $matches)) {
+
+      if (isset($matches["lang"])) $lang = $matches["lang"];
+      if (isset($matches["country"])) $country = $matches["country"];
+      if (isset($matches["charset"])) $charset = $matches["charset"];
+      if (isset($matches["modifier"])) $modifier = $matches["modifier"];
+
+      if ($modifier) {
+        if ($country) {
+          if ($charset)
+            array_push($locale_names, "${lang}_$country.$charset@$modifier");
+          array_push($locale_names, "${lang}_$country@$modifier");
+        } elseif ($charset)
+            array_push($locale_names, "${lang}.$charset@$modifier");
+        array_push($locale_names, "$lang@$modifier");
+      }
+      if ($country) {
+        if ($charset)
+          array_push($locale_names, "${lang}_$country.$charset");
+        array_push($locale_names, "${lang}_$country");
+      } elseif ($charset)
+          array_push($locale_names, "${lang}.$charset");
+      array_push($locale_names, $lang);
+    }
+
+    // If the locale name doesn't match POSIX style, just include it as-is.
+    if (!in_array($locale, $locale_names))
+      array_push($locale_names, $locale);
+  }
+  return $locale_names;
+}
+
 /**
  * Utility function to get a StreamReader for the given text domain.
  */
 function _get_reader($domain=null, $category=5, $enable_cache=true) {
-       global $text_domains, $default_domain, $LC_CATEGORIES;
-       if (!isset($domain)) $domain = $default_domain;
-       if (!isset($text_domains[$domain]->l10n)) {
-               // get the current locale
-               $locale = _setlocale(LC_MESSAGES, 0);
-               $p = isset($text_domains[$domain]->path) ? $text_domains[$domain]->path : './';
-               $path = $p . "$locale/". $LC_CATEGORIES[$category] ."/$domain.mo";
-               if (file_exists($path)) {
-                       $input = new FileReader($path);
-               }
-               else {
-                       $input = null;
-               }
-               $text_domains[$domain]->l10n = new gettext_reader($input, $enable_cache);
-       }
-       return $text_domains[$domain]->l10n;
+    global $text_domains, $default_domain, $LC_CATEGORIES;
+    if (!isset($domain)) $domain = $default_domain;
+    if (!isset($text_domains[$domain]->l10n)) {
+        // get the current locale
+        $locale = _setlocale(LC_MESSAGES, 0);
+        $bound_path = isset($text_domains[$domain]->path) ?
+          $text_domains[$domain]->path : './';
+        $subpath = $LC_CATEGORIES[$category] ."/$domain.mo";
+
+        $locale_names = get_list_of_locales($locale);
+        $input = null;
+        foreach ($locale_names as $locale) {
+          $full_path = $bound_path . $locale . "/" . $subpath;
+          if (file_exists($full_path)) {
+            $input = new FileReader($full_path);
+            break;
+          }
+        }
+
+        if (!array_key_exists($domain, $text_domains)) {
+          // Initialize an empty domain object.
+          $text_domains[$domain] = new domain();
+        }
+        $text_domains[$domain]->l10n = new gettext_reader($input,
+                                                          $enable_cache);
+    }
+    return $text_domains[$domain]->l10n;
 }
 
 /**
@@ -79,8 +155,10 @@ function locale_emulation() {
 /**
  * Checks if the current locale is supported on this system.
  */
-function _check_locale() {
+function _check_locale_and_function($function=false) {
     global $EMULATEGETTEXT;
+    if ($function and !function_exists($function))
+        return false;
     return !$EMULATEGETTEXT;
 }
 
@@ -88,56 +166,70 @@ function _check_locale() {
  * Get the codeset for the given domain.
  */
 function _get_codeset($domain=null) {
-       global $text_domains, $default_domain, $LC_CATEGORIES;
-       if (!isset($domain)) $domain = $default_domain;
-       return (isset($text_domains[$domain]->codeset))? $text_domains[$domain]->codeset : ini_get('mbstring.internal_encoding');
+    global $text_domains, $default_domain, $LC_CATEGORIES;
+    if (!isset($domain)) $domain = $default_domain;
+    return (isset($text_domains[$domain]->codeset))? $text_domains[$domain]->codeset : ini_get('mbstring.internal_encoding');
 }
 
 /**
  * Convert the given string to the encoding set by bind_textdomain_codeset.
  */
 function _encode($text) {
-       $source_encoding = mb_detect_encoding($text);
-       $target_encoding = _get_codeset();
-       if ($source_encoding != $target_encoding) {
-               return mb_convert_encoding($text, $target_encoding, $source_encoding);
-       }
-       else {
-               return $text;
-       }
+    $source_encoding = mb_detect_encoding($text);
+    $target_encoding = _get_codeset();
+    if ($source_encoding != $target_encoding) {
+        return mb_convert_encoding($text, $target_encoding, $source_encoding);
+    }
+    else {
+        return $text;
+    }
 }
 
 
-
-
 // Custom implementation of the standard gettext related functions
 
+/**
+ * Returns passed in $locale, or environment variable $LANG if $locale == ''.
+ */
+function _get_default_locale($locale) {
+  if ($locale == '') // emulate variable support
+    return getenv('LANG');
+  else
+    return $locale;
+}
+
 /**
  * Sets a requested locale, if needed emulates it.
  */
 function _setlocale($category, $locale) {
     global $CURRENTLOCALE, $EMULATEGETTEXT;
     if ($locale === 0) { // use === to differentiate between string "0"
-        if ($CURRENTLOCALE != '') 
+        if ($CURRENTLOCALE != '')
             return $CURRENTLOCALE;
-        else 
+        else
             // obey LANG variable, maybe extend to support all of LC_* vars
             // even if we tried to read locale without setting it first
             return _setlocale($category, $CURRENTLOCALE);
     } else {
-        $ret = 0;
-        if (function_exists('setlocale')) // I don't know if this ever happens ;)
-           $ret = setlocale($category, $locale);
-        if (($ret and $locale == '') or ($ret == $locale)) {
-            $EMULATEGETTEXT = 0;
+        if (function_exists('setlocale')) {
+          $ret = setlocale($category, $locale);
+          if (($locale == '' and !$ret) or // failed setting it by env
+              ($locale != '' and $ret != $locale)) { // failed setting it
+            // Failed setting it according to environment.
+            $CURRENTLOCALE = _get_default_locale($locale);
+            $EMULATEGETTEXT = 1;
+          } else {
             $CURRENTLOCALE = $ret;
+            $EMULATEGETTEXT = 0;
+          }
         } else {
-           if ($locale == '') // emulate variable support
-               $CURRENTLOCALE = getenv('LANG');
-           else
-               $CURRENTLOCALE = $locale;
-            $EMULATEGETTEXT = 1;
+          // No function setlocale(), emulate it all.
+          $CURRENTLOCALE = _get_default_locale($locale);
+          $EMULATEGETTEXT = 1;
         }
+        // Allow locale to be changed on the go for one translation domain.
+        global $text_domains, $default_domain;
+        unset($text_domains[$default_domain]->l10n);
         return $CURRENTLOCALE;
     }
 }
@@ -146,135 +238,240 @@ function _setlocale($category, $locale) {
  * Sets the path for a domain.
  */
 function _bindtextdomain($domain, $path) {
-       global $text_domains;
-       // ensure $path ends with a slash
-       if ($path[strlen($path) - 1] != '/') $path .= '/';
-       elseif ($path[strlen($path) - 1] != '\\') $path .= '\\';
-       $text_domains[$domain]->path = $path;
+    global $text_domains;
+    // ensure $path ends with a slash ('/' should work for both, but lets still play nice)
+    if (substr(php_uname(), 0, 7) == "Windows") {
+      if ($path[strlen($path)-1] != '\\' and $path[strlen($path)-1] != '/')
+        $path .= '\\';
+    } else {
+      if ($path[strlen($path)-1] != '/')
+        $path .= '/';
+    }
+    if (!array_key_exists($domain, $text_domains)) {
+      // Initialize an empty domain object.
+      $text_domains[$domain] = new domain();
+    }
+    $text_domains[$domain]->path = $path;
 }
 
 /**
  * Specify the character encoding in which the messages from the DOMAIN message catalog will be returned.
  */
 function _bind_textdomain_codeset($domain, $codeset) {
-       global $text_domains;
-       $text_domains[$domain]->codeset = $codeset;
+    global $text_domains;
+    $text_domains[$domain]->codeset = $codeset;
 }
 
 /**
  * Sets the default domain.
  */
 function _textdomain($domain) {
-       global $default_domain;
-       $default_domain = $domain;
+    global $default_domain;
+    $default_domain = $domain;
 }
 
 /**
  * Lookup a message in the current domain.
  */
 function _gettext($msgid) {
-       $l10n = _get_reader();
-       //return $l10n->translate($msgid);
-       return _encode($l10n->translate($msgid));
+    $l10n = _get_reader();
+    return _encode($l10n->translate($msgid));
 }
+
 /**
  * Alias for gettext.
  */
 function __($msgid) {
-       return _gettext($msgid);
+    return _gettext($msgid);
 }
+
 /**
  * Plural version of gettext.
  */
 function _ngettext($single, $plural, $number) {
-       $l10n = _get_reader();
-       //return $l10n->ngettext($single, $plural, $number);
-       return _encode($l10n->ngettext($single, $plural, $number));
+    $l10n = _get_reader();
+    return _encode($l10n->ngettext($single, $plural, $number));
 }
 
 /**
  * Override the current domain.
  */
 function _dgettext($domain, $msgid) {
-       $l10n = _get_reader($domain);
-       //return $l10n->translate($msgid);
-       return _encode($l10n->translate($msgid));
+    $l10n = _get_reader($domain);
+    return _encode($l10n->translate($msgid));
 }
+
 /**
  * Plural version of dgettext.
  */
 function _dngettext($domain, $single, $plural, $number) {
-       $l10n = _get_reader($domain);
-       //return $l10n->ngettext($single, $plural, $number);
-       return _encode($l10n->ngettext($single, $plural, $number));
+    $l10n = _get_reader($domain);
+    return _encode($l10n->ngettext($single, $plural, $number));
 }
 
 /**
  * Overrides the domain and category for a single lookup.
  */
 function _dcgettext($domain, $msgid, $category) {
-       $l10n = _get_reader($domain, $category);
-       //return $l10n->translate($msgid);
-       return _encode($l10n->translate($msgid));
+    $l10n = _get_reader($domain, $category);
+    return _encode($l10n->translate($msgid));
 }
 /**
  * Plural version of dcgettext.
  */
 function _dcngettext($domain, $single, $plural, $number, $category) {
-       $l10n = _get_reader($domain, $category);
-       //return $l10n->ngettext($single, $plural, $number);
-       return _encode($l10n->ngettext($single, $plural, $number));
+    $l10n = _get_reader($domain, $category);
+    return _encode($l10n->ngettext($single, $plural, $number));
 }
 
+/**
+ * Context version of gettext.
+ */
+function _pgettext($context, $msgid) {
+    $l10n = _get_reader();
+    return _encode($l10n->pgettext($context, $msgid));
+}
 
+/**
+ * Override the current domain in a context gettext call.
+ */
+function _dpgettext($domain, $context, $msgid) {
+    $l10n = _get_reader($domain);
+    return _encode($l10n->pgettext($context, $msgid));
+}
 
-// Wrappers to use if the standard gettext functions are available, but the current locale is not supported by the system.
-// Use the standard impl if the current locale is supported, use the custom impl otherwise.
+/**
+ * Overrides the domain and category for a single context-based lookup.
+ */
+function _dcpgettext($domain, $context, $msgid, $category) {
+    $l10n = _get_reader($domain, $category);
+    return _encode($l10n->pgettext($context, $msgid));
+}
+
+/**
+ * Context version of ngettext.
+ */
+function _npgettext($context, $singular, $plural) {
+    $l10n = _get_reader();
+    return _encode($l10n->npgettext($context, $singular, $plural));
+}
+
+/**
+ * Override the current domain in a context ngettext call.
+ */
+function _dnpgettext($domain, $context, $singular, $plural) {
+    $l10n = _get_reader($domain);
+    return _encode($l10n->npgettext($context, $singular, $plural));
+}
+
+/**
+ * Overrides the domain and category for a plural context-based lookup.
+ */
+function _dcnpgettext($domain, $context, $singular, $plural, $category) {
+    $l10n = _get_reader($domain, $category);
+    return _encode($l10n->npgettext($context, $singular, $plural));
+}
+
+
+
+// Wrappers to use if the standard gettext functions are available,
+// but the current locale is not supported by the system.
+// Use the standard impl if the current locale is supported, use the
+// custom impl otherwise.
 
 function T_setlocale($category, $locale) {
     return _setlocale($category, $locale);
 }
 
 function T_bindtextdomain($domain, $path) {
-       if (_check_locale()) return bindtextdomain($domain, $path);
-       else return _bindtextdomain($domain, $path);
+    if (_check_locale_and_function()) return bindtextdomain($domain, $path);
+    else return _bindtextdomain($domain, $path);
 }
 function T_bind_textdomain_codeset($domain, $codeset) {
     // bind_textdomain_codeset is available only in PHP 4.2.0+
-       if (_check_locale() and function_exists('bind_textdomain_codeset')) return bind_textdomain_codeset($domain, $codeset);
-       else return _bind_textdomain_codeset($domain, $codeset);
+    if (_check_locale_and_function('bind_textdomain_codeset'))
+        return bind_textdomain_codeset($domain, $codeset);
+    else return _bind_textdomain_codeset($domain, $codeset);
 }
 function T_textdomain($domain) {
-       if (_check_locale()) return textdomain($domain);
-       else return _textdomain($domain);
+    if (_check_locale_and_function()) return textdomain($domain);
+    else return _textdomain($domain);
 }
 function T_gettext($msgid) {
-       if (_check_locale()) return gettext($msgid);
-       else return _gettext($msgid);
+    if (_check_locale_and_function()) return gettext($msgid);
+    else return _gettext($msgid);
 }
 function T_($msgid) {
-       if (_check_locale()) return _($msgid);
-       return __($msgid);
+    if (_check_locale_and_function()) return _($msgid);
+    return __($msgid);
 }
 function T_ngettext($single, $plural, $number) {
-       if (_check_locale()) return ngettext($single, $plural, $number);
-       else return _ngettext($single, $plural, $number);
+    if (_check_locale_and_function())
+        return ngettext($single, $plural, $number);
+    else return _ngettext($single, $plural, $number);
 }
 function T_dgettext($domain, $msgid) {
-       if (_check_locale()) return dgettext($domain, $msgid);
-       else return _dgettext($domain, $msgid);
+    if (_check_locale_and_function()) return dgettext($domain, $msgid);
+    else return _dgettext($domain, $msgid);
 }
 function T_dngettext($domain, $single, $plural, $number) {
-       if (_check_locale()) return dngettext($domain, $single, $plural, $number);
-       else return _dngettext($domain, $single, $plural, $number);
+    if (_check_locale_and_function())
+        return dngettext($domain, $single, $plural, $number);
+    else return _dngettext($domain, $single, $plural, $number);
 }
 function T_dcgettext($domain, $msgid, $category) {
-       if (_check_locale()) return dcgettext($domain, $msgid, $category);
-       else return _dcgettext($domain, $msgid, $category);
+    if (_check_locale_and_function())
+        return dcgettext($domain, $msgid, $category);
+    else return _dcgettext($domain, $msgid, $category);
 }
 function T_dcngettext($domain, $single, $plural, $number, $category) {
-       if (_check_locale()) return dcngettext($domain, $single, $plural, $number, $category);
-       else return _dcngettext($domain, $single, $plural, $number, $category);
+    if (_check_locale_and_function())
+      return dcngettext($domain, $single, $plural, $number, $category);
+    else return _dcngettext($domain, $single, $plural, $number, $category);
+}
+
+function T_pgettext($context, $msgid) {
+  if (_check_locale_and_function('pgettext'))
+      return pgettext($context, $msgid);
+  else
+      return _pgettext($context, $msgid);
+}
+
+function T_dpgettext($domain, $context, $msgid) {
+  if (_check_locale_and_function('dpgettext'))
+      return dpgettext($domain, $context, $msgid);
+  else
+      return _dpgettext($domain, $context, $msgid);
+}
+
+function T_dcpgettext($domain, $context, $msgid, $category) {
+  if (_check_locale_and_function('dcpgettext'))
+      return dcpgettext($domain, $context, $msgid, $category);
+  else
+      return _dcpgettext($domain, $context, $msgid, $category);
+}
+
+function T_npgettext($context, $singular, $plural) {
+    if (_check_locale_and_function('npgettext'))
+        return npgettext($context, $single, $plural, $number);
+    else
+        return _npgettext($context, $single, $plural, $number);
+}
+
+function T_dnpgettext($domain, $context, $singular, $plural) {
+  if (_check_locale_and_function('dnpgettext'))
+      return dnpgettext($domain, $context, $single, $plural, $number);
+  else
+      return _dnpgettext($domain, $context, $single, $plural, $number);
+}
+
+function T_dcnpgettext($domain, $context, $singular, $plural, $category) {
+    if (_check_locale_and_function('dcnpgettext'))
+        return dcnpgettext($domain, $context, $single,
+                           $plural, $number, $category);
+    else
+        return _dcnpgettext($domain, $context, $single,
+                            $plural, $number, $category);
 }
 
 
@@ -282,36 +479,56 @@ function T_dcngettext($domain, $single, $plural, $number, $category) {
 // Wrappers used as a drop in replacement for the standard gettext functions
 
 if (!function_exists('gettext')) {
-       function bindtextdomain($domain, $path) {
-               return _bindtextdomain($domain, $path);
-       }
-       function bind_textdomain_codeset($domain, $codeset) {
-               return _bind_textdomain_codeset($domain, $codeset);
-       }
-       function textdomain($domain) {
-               return _textdomain($domain);
-       }
-       function gettext($msgid) {
-               return _gettext($msgid);
-       }
-       function _($msgid) {
-               return __($msgid);
-       }
-       function ngettext($single, $plural, $number) {
-               return _ngettext($single, $plural, $number);
-       }
-       function dgettext($domain, $msgid) {
-               return _dgettext($domain, $msgid);
-       }
-       function dngettext($domain, $single, $plural, $number) {
-               return _dngettext($domain, $single, $plural, $number);
-       }
-       function dcgettext($domain, $msgid, $category) {
-               return _dcgettext($domain, $msgid, $category);
-       }
-       function dcngettext($domain, $single, $plural, $number, $category) {
-               return _dcngettext($domain, $single, $plural, $number, $category);
-       }
-}
-
-?>
\ No newline at end of file
+    function bindtextdomain($domain, $path) {
+        return _bindtextdomain($domain, $path);
+    }
+    function bind_textdomain_codeset($domain, $codeset) {
+        return _bind_textdomain_codeset($domain, $codeset);
+    }
+    function textdomain($domain) {
+        return _textdomain($domain);
+    }
+    function gettext($msgid) {
+        return _gettext($msgid);
+    }
+    function _($msgid) {
+        return __($msgid);
+    }
+    function ngettext($single, $plural, $number) {
+        return _ngettext($single, $plural, $number);
+    }
+    function dgettext($domain, $msgid) {
+        return _dgettext($domain, $msgid);
+    }
+    function dngettext($domain, $single, $plural, $number) {
+        return _dngettext($domain, $single, $plural, $number);
+    }
+    function dcgettext($domain, $msgid, $category) {
+        return _dcgettext($domain, $msgid, $category);
+    }
+    function dcngettext($domain, $single, $plural, $number, $category) {
+        return _dcngettext($domain, $single, $plural, $number, $category);
+    }
+    function pgettext($context, $msgid) {
+        return _pgettext($context, $msgid);
+    }
+    function npgettext($context, $single, $plural, $number) {
+        return _npgettext($context, $single, $plural, $number);
+    }
+    function dpgettext($domain, $context, $msgid) {
+        return _dpgettext($domain, $context, $msgid);
+    }
+    function dnpgettext($domain, $context, $single, $plural, $number) {
+        return _dnpgettext($domain, $context, $single, $plural, $number);
+    }
+    function dcpgettext($domain, $context, $msgid, $category) {
+        return _dcpgettext($domain, $context, $msgid, $category);
+    }
+    function dcnpgettext($domain, $context, $single, $plural,
+                         $number, $category) {
+      return _dcnpgettext($domain, $context, $single, $plural,
+                          $number, $category);
+    }
+}
+
+?>

Benjamin Mako Hill || Want to submit a patch?