diff options
Diffstat (limited to 'modules/gallery/libraries/Gallery_I18n.php')
| -rw-r--r-- | modules/gallery/libraries/Gallery_I18n.php | 430 | 
1 files changed, 430 insertions, 0 deletions
| diff --git a/modules/gallery/libraries/Gallery_I18n.php b/modules/gallery/libraries/Gallery_I18n.php new file mode 100644 index 00000000..9a5e7dc1 --- /dev/null +++ b/modules/gallery/libraries/Gallery_I18n.php @@ -0,0 +1,430 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA. + */ + +/** + * Translates a localizable message. + * @param $message String The message to be translated. E.g. "Hello world" + * @param $options array (optional) Options array for key value pairs which are used + *        for pluralization and interpolation. Special key: "locale" to override the + *        currently configured locale. + * @return String The translated message string. + */ +function t($message, $options=array()) { +  return Gallery_I18n::instance()->translate($message, $options); +} + +/** + * Translates a localizable message with plural forms. + * @param $singular String The message to be translated. E.g. "There is one album." + * @param $plural String The plural message to be translated. E.g. + *        "There are %count albums." + * @param $count Number The number which is inserted for the %count placeholder and + *        which is used to select the proper plural form ($singular or $plural). + * @param $options array (optional) Options array for key value pairs which are used + *        for pluralization and interpolation. Special key: "locale" to override the + *        currently configured locale. + * @return String The translated message string. + */ +function t2($singular, $plural, $count, $options=array()) { +  return Gallery_I18n::instance()->translate(array("one" => $singular, "other" => $plural), +                                     array_merge($options, array("count" => $count))); +} + +class Gallery_I18n_Core { +  private static $_instance; +  private $_config = array(); +  private $_call_log = array(); +  private $_cache = array(); + +  private function __construct($config) { +    $this->_config = $config; +    $this->locale($config['default_locale']); +  } + +  public static function instance($config=null) { +    if (self::$_instance == NULL || isset($config)) { +      $config = isset($config) ? $config : Kohana::config('locale'); +      if (empty($config['default_locale'])) { +        $config['default_locale'] = module::get_var('gallery', 'default_locale'); +      } +      self::$_instance = new Gallery_I18n_Core($config); +    } + +    return self::$_instance; +  } + +  public function locale($locale=null) { +    if ($locale) { +      $this->_config['default_locale'] = $locale; +      // Attempt to set PHP's locale as well (for number formatting, collation, etc.) +      // TODO: See G2 for better fallack code. +      $locale_prefs = array($locale); +      $locale_prefs[] = 'en_US'; +      $new_locale = setlocale(LC_ALL, $locale_prefs); +      if (is_string($new_locale) && strpos($new_locale, 'tr') === 0) { +        // Make PHP 5 work with Turkish (the localization results are mixed though). +        // Hack for http://bugs.php.net/18556 +        setlocale(LC_CTYPE, 'C'); +      } +    } +    return $this->_config['default_locale']; +  } + +  /** +   * Translates a localizable message. +   * +   * Security: +   * The returned string is safe for use in HTML (it contains a safe subset of HTML and +   * interpolation parameters are converted to HTML entities). +   * For use in JavaScript, please call ->for_js() on it. +   * +   * @param $message String|array The message to be translated. E.g. "Hello world" +   *                 or array("one" => "One album", "other" => "%count albums") +   * @param $options array (optional) Options array for key value pairs which are used +   *        for pluralization and interpolation. Special keys are "count" and "locale", +   *        the latter to override the currently configured locale. +   * @return String The translated message string. +   */ +  public function translate($message, $options=array()) { +    $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale']; +    $count = isset($options['count']) ? $options['count'] : null; +    $values = $options; +    unset($values['locale']); +    $this->log($message, $options); + +    $entry = $this->lookup($locale, $message); + +    if (null === $entry) { +      // Default to the root locale. +      $entry = $message; +      $locale = $this->_config['root_locale']; +    } + +    $entry = $this->pluralize($locale, $entry, $count); + +    $entry = $this->interpolate($locale, $entry, $values); + +    return SafeString::of_safe_html($entry); +  } + +  private function lookup($locale, $message) { +    if (!isset($this->_cache[$locale])) { +      $this->_cache[$locale] = array(); +      // TODO: Load data from locale file instead of the DB. +      foreach (db::build() +               ->select("key", "translation") +               ->from("incoming_translations") +               ->where("locale", "=", $locale) +               ->execute() +               ->as_array() as $row) { +        $this->_cache[$locale][$row->key] = unserialize($row->translation); +      } + +      // Override incoming with outgoing... +      foreach (db::build() +               ->select("key", "translation") +               ->from("outgoing_translations") +               ->where("locale", "=", $locale) +               ->execute() +               ->as_array() as $row) { +        $this->_cache[$locale][$row->key] = unserialize($row->translation); +      } +    } + +    $key = self::get_message_key($message); + +    if (isset($this->_cache[$locale][$key])) { +      return $this->_cache[$locale][$key]; +    } else { +      return null; +    } +  } + +  public function has_translation($message, $options=null) { +    $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale']; + +    $entry = $this->lookup($locale, $message); + +    if (null === $entry) { +      return false; +    } else if (!is_array($message)) { +      return $entry !== ''; +    } else { +      if (!is_array($entry) || empty($entry)) { +        return false; +      } +      // It would be better to verify that all the locale's plural forms have a non-empty +      // translation, but this is fine for now. +      foreach ($entry as $value) { +        if ($value === '') { +          return false; +        } +      } +      return true; +    } +  } + +  static function get_message_key($message) { +    $as_string = is_array($message) ? implode('|', $message) : $message; +    return md5($as_string); +  } + +  static function is_plural_message($message) { +    return is_array($message); +  } + +  private function interpolate($locale, $string, $key_values) { +    // TODO: Handle locale specific number formatting. + +    // Replace x_y before replacing x. +    krsort($key_values, SORT_STRING); + +    $keys = array(); +    $values = array(); +    foreach ($key_values as $key => $value) { +      $keys[] = "%$key"; +      $values[] = new SafeString($value); +    } +    return str_replace($keys, $values, $string); +  } + +  private function pluralize($locale, $entry, $count) { +    if (!is_array($entry)) { +      return $entry; +    } + +    $plural_key = self::get_plural_key($locale, $count); +    if (!isset($entry[$plural_key])) { +      // Fallback to the default plural form. +      $plural_key = 'other'; +    } + +    if (isset($entry[$plural_key])) { +      return $entry[$plural_key]; +    } else { +      // Fallback to just any plural form. +      list ($plural_key, $string) = each($entry); +      return $string; +    } +  } + +  private function log($message, $options) { +    $key = self::get_message_key($message); +    isset($this->_call_log[$key]) or $this->_call_log[$key] = array($message, $options); +  } + +  public function call_log() { +    return $this->_call_log; +  } + +  private static function get_plural_key($locale, $count) { +    $parts = explode('_', $locale); +    $language = $parts[0]; + +    // Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml). +    // Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html +    switch ($language) { +      case 'az': +      case 'fa': +      case 'hu': +      case 'ja': +      case 'ko': +      case 'my': +      case 'to': +      case 'tr': +      case 'vi': +      case 'yo': +      case 'zh': +      case 'bo': +      case 'dz': +      case 'id': +      case 'jv': +      case 'ka': +      case 'km': +      case 'kn': +      case 'ms': +      case 'th': +        return 'other'; + +      case 'ar': +        if ($count == 0) { +          return 'zero'; +        } else if ($count == 1) { +          return 'one'; +        } else if ($count == 2) { +          return 'two'; +        } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 10) { +          return 'few'; +        } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 99) { +          return 'many'; +        } else { +          return 'other'; +        } + +      case 'pt': +      case 'am': +      case 'bh': +      case 'fil': +      case 'tl': +      case 'guw': +      case 'hi': +      case 'ln': +      case 'mg': +      case 'nso': +      case 'ti': +      case 'wa': +        if ($count == 0 || $count == 1) { +          return 'one'; +        } else { +          return 'other'; +        } + +      case 'fr': +        if ($count >= 0 and $count < 2) { +          return 'one'; +        } else { +          return 'other'; +        } + +      case 'lv': +        if ($count == 0) { +          return 'zero'; +        } else if ($count % 10 == 1 && $count % 100 != 11) { +          return 'one'; +        } else { +          return 'other'; +        } + +      case 'ga': +      case 'se': +      case 'sma': +      case 'smi': +      case 'smj': +      case 'smn': +      case 'sms': +        if ($count == 1) { +          return 'one'; +        } else if ($count == 2) { +          return 'two'; +        } else { +          return 'other'; +        } + +      case 'ro': +      case 'mo': +        if ($count == 1) { +          return 'one'; +        } else if (is_int($count) && $count == 0 && ($i = $count % 100) >= 1 && $i <= 19) { +          return 'few'; +        } else { +          return 'other'; +        } + +      case 'lt': +        if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) { +          return 'one'; +        } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 9 && ($i = $count % 100) < 11 && $i > 19) { +          return 'few'; +        } else { +          return 'other'; +        } + +      case 'hr': +      case 'ru': +      case 'sr': +      case 'uk': +      case 'be': +      case 'bs': +      case 'sh': +        if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) { +          return 'one'; +        } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && ($i = $count % 100) < 12 && $i > 14) { +          return 'few'; +        } else if (is_int($count) && ($count % 10 == 0 || (($i = $count % 10) >= 5 && $i <= 9) || (($i = $count % 100) >= 11 && $i <= 14))) { +          return 'many'; +        } else { +          return 'other'; +        } + +      case 'cs': +      case 'sk': +        if ($count == 1) { +          return 'one'; +        } else if (is_int($count) && $count >= 2 && $count <= 4) { +          return 'few'; +        } else { +          return 'other'; +        } + +      case 'pl': +        if ($count == 1) { +          return 'one'; +        } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && +                   ($i = $count % 100) < 12 && $i > 14 && ($i = $count % 100) < 22 && $i > 24) { +          return 'few'; +        } else { +          return 'other'; +        } + +      case 'sl': +        if ($count % 100 == 1) { +          return 'one'; +        } else if ($count % 100 == 2) { +          return 'two'; +        } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 4) { +          return 'few'; +        } else { +          return 'other'; +        } + +      case 'mt': +        if ($count == 1) { +          return 'one'; +        } else if ($count == 0 || is_int($count) && ($i = $count % 100) >= 2 && $i <= 10) { +          return 'few'; +        } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 19) { +          return 'many'; +        } else { +          return 'other'; +        } + +      case 'mk': +        if ($count % 10 == 1) { +          return 'one'; +        } else { +          return 'other'; +        } + +      case 'cy': +        if ($count == 1) { +          return 'one'; +        } else if ($count == 2) { +          return 'two'; +        } else if ($count == 8 || $count == 11) { +          return 'many'; +        } else { +          return 'other'; +        } + +      default: // en, de, etc. +        return $count == 1 ? 'one' : 'other'; +    } +  } +} | 
