diff --git a/bootstrap.php b/bootstrap.php index 8e11f0e9818b0c88b16939dda14f955c172b9690..712f5bc4536f13b530103abe2ff0736a44c03019 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -266,17 +266,6 @@ class Bootstrap { } if (extension_loaded('iconv')) iconv_set_encoding('internal_encoding', 'UTF-8'); - - if ($use_php_gettext == true && function_exists('mb_detect_encoding')) { - $f = fopen(INCLUDE_DIR.'locale/'.$language.'/LC_MESSAGES/messages.mo', 'r'); - $meta = stream_get_meta_data($f); - if ($meta['mode'] == NULL) { - $sysnotice='The translation file "include/locale/'.$language.'/LC_MESSAGES/messages.mo" isn\'t readable, check permissions.'; - } - else { - fclose($f); - } - } } function croak($message) { diff --git a/include/class.i18n.php b/include/class.i18n.php index ccfdff34460752bb58e212545d592a40f3e49139..35f68f337e038c17745267f14bc69fd565563120 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -285,48 +285,37 @@ class Internationalization { return $best_match_langcode; } - static function bootstrap($user) { - global $cfg; - - #if (!extension_loaded('gettext')) - require_once INCLUDE_DIR . 'gettext/gettext.inc'; - - if ($user && method_exists($user, 'getLanguage')) - $lang = $user->getLanguage(); - else - $lang = Internationalization::getDefaultLanguage(); + static function bootstrap() { - $packs = Internationalization::availableLanguages(); + require_once INCLUDE_DIR . 'class.translation.php'; $domain = 'messages'; + TextDomain::setDefaultDomain($domain); + TextDomain::lookup()->setPath(I18N_DIR); // User-specific translations - putenv('LC_ALL=' . $lang); - setlocale(LC_ALL, $lang); - bindtextdomain($domain, $packs[$lang]['path']); - textdomain($domain); - - // User-specific translations - if (!function_exists('__')) { - function __($text) { return _($text); } - } - function _N($msgid, $plural, $count) { - return ngettext($msgid, $plural, $count); + return TextDomain::lookup()->getTranslation(LC_MESSAGES, $locale) + ->ngettext($msgid, $plural, $count); } // System-specific translations function _S($msgid) { global $cfg; + return __($msgid); } function _SN($msgid, $plural, $count) { global $cfg; } // Language-specific translations - function _L($msgid, $lang) { + function _L($msgid, $locale) { + return TextDomain::lookup()->getTranslation($locale) + ->translate($msgid); } - function _LN($msgid, $plural, $count, $lang) { + function _LN($msgid, $plural, $count, $locale) { + return TextDomain::lookup()->getTranslation($locale) + ->ngettext($msgid); } } } diff --git a/include/class.osticket.php b/include/class.osticket.php index 88a055504ead22b6d6ebc9118d2c04ab51c3569f..9f8870547699b8b2ac8d67fa4fc933921fe24c32 100644 --- a/include/class.osticket.php +++ b/include/class.osticket.php @@ -444,6 +444,8 @@ class osTicket { /**** static functions ****/ function start() { + // Prep basic translation support + Internationalization::bootstrap(); if(!($ost = new osTicket())) return null; diff --git a/include/class.translation.php b/include/class.translation.php new file mode 100644 index 0000000000000000000000000000000000000000..cc33b8a1a73a8b75b058b2733cebc666037950d1 --- /dev/null +++ b/include/class.translation.php @@ -0,0 +1,838 @@ +<?php +/********************************************************************* + class.gettext.php + + This implements a `Translation` class that is loosely based on the PHP + gettext pure-php module. + + This extension to the PHP gettext extension using a specially crafted MO + file which is a PHP serialized hash array. The file can be built using a + utility method in this class. + + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2014 osTicket + http://www.osticket.com + + PHP gettext extension is copyrighted separately: + --------------- + Copyright (c) 2003, 2009 Danilo Segan <danilo@kvota.net>. + Copyright (c) 2005 Nico Kaiser <nico@siriux.net> + + This file is part of PHP-gettext. + + PHP-gettext 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. + + PHP-gettext 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 PHP-gettext; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + --------------- + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +/** + * Provides a simple gettext replacement that works independently from + * the system's gettext abilities. + * It can read MO files and use them for translating strings. + * The files are passed to gettext_reader as a Stream (see streams.php) + * + * This version has the ability to cache all strings and translations to + * speed up the string lookup. + * While the cache is enabled by default, it can be switched off with the + * second parameter in the constructor (e.g. whenusing very large MO files + * that you don't want to keep in memory) + */ +class gettext_reader { + //public: + var $error = 0; // public variable that holds error code (0 if no error) + + //private: + var $BYTEORDER = 0; // 0: low endian, 1: big endian + var $STREAM = NULL; + var $short_circuit = false; + var $enable_cache = false; + var $originals = NULL; // offset of original table + var $translations = NULL; // offset of translation table + var $pluralheader = NULL; // cache header field for plural forms + var $total = 0; // total string count + var $table_originals = NULL; // table for original strings (offsets) + var $table_translations = NULL; // table for translated strings (offsets) + var $cache_translations = NULL; // original -> translation mapping + + + /* Methods */ + + + /** + * Reads a 32bit Integer from the Stream + * + * @access private + * @return Integer from the Stream + */ + function readint() { + if ($this->BYTEORDER == 0) { + // low endian + $input=unpack('V', $this->STREAM->read(4)); + return array_shift($input); + } else { + // big endian + $input=unpack('N', $this->STREAM->read(4)); + return array_shift($input); + } + } + + function read($bytes) { + return $this->STREAM->read($bytes); + } + + /** + * Reads an array of Integers from the Stream + * + * @param int count How many elements should be read + * @return Array of Integers + */ + function readintarray($count) { + if ($this->BYTEORDER == 0) { + // low endian + return unpack('V'.$count, $this->STREAM->read(4 * $count)); + } else { + // big endian + return unpack('N'.$count, $this->STREAM->read(4 * $count)); + } + } + + /** + * Constructor + * + * @param object Reader the StreamReader object + * @param boolean enable_cache Enable or disable caching of strings (default on) + */ + function gettext_reader($Reader, $enable_cache = true) { + // If there isn't a StreamReader, turn on short circuit mode. + if (! $Reader || isset($Reader->error) ) { + $this->short_circuit = true; + return; + } + + // Caching can be turned off + $this->enable_cache = $enable_cache; + + $MAGIC1 = "\x95\x04\x12\xde"; + $MAGIC2 = "\xde\x12\x04\x95"; + + $this->STREAM = $Reader; + $magic = $this->read(4); + if ($magic == $MAGIC1) { + $this->BYTEORDER = 1; + } elseif ($magic == $MAGIC2) { + $this->BYTEORDER = 0; + } else { + $this->error = 1; // not MO file + return false; + } + + // FIXME: Do we care about revision? We should. + $this->revision = $this->readint(); + + $this->total = $this->readint(); + $this->originals = $this->readint(); + $this->translations = $this->readint(); + } + + /** + * Loads the translation tables from the MO file into the cache + * If caching is enabled, also loads all strings into a cache + * to speed up translation lookups + * + * @access private + */ + function load_tables() { + if (is_array($this->cache_translations) && + is_array($this->table_originals) && + is_array($this->table_translations)) + return; + + /* get original and translations tables */ + if (!is_array($this->table_originals)) { + $this->STREAM->seekto($this->originals); + $this->table_originals = $this->readintarray($this->total * 2); + } + if (!is_array($this->table_translations)) { + $this->STREAM->seekto($this->translations); + $this->table_translations = $this->readintarray($this->total * 2); + } + + if ($this->enable_cache) { + $this->cache_translations = array (); + /* read all strings in the cache */ + for ($i = 0; $i < $this->total; $i++) { + $this->STREAM->seekto($this->table_originals[$i * 2 + 2]); + $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]); + $this->STREAM->seekto($this->table_translations[$i * 2 + 2]); + $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]); + $this->cache_translations[$original] = $translation; + } + } + } + + /** + * Returns a string from the "originals" table + * + * @access private + * @param int num Offset number of original string + * @return string Requested string if found, otherwise '' + */ + function get_original_string($num) { + $length = $this->table_originals[$num * 2 + 1]; + $offset = $this->table_originals[$num * 2 + 2]; + if (! $length) + return ''; + $this->STREAM->seekto($offset); + $data = $this->STREAM->read($length); + return (string)$data; + } + + /** + * Returns a string from the "translations" table + * + * @access private + * @param int num Offset number of original string + * @return string Requested string if found, otherwise '' + */ + function get_translation_string($num) { + $length = $this->table_translations[$num * 2 + 1]; + $offset = $this->table_translations[$num * 2 + 2]; + if (! $length) + return ''; + $this->STREAM->seekto($offset); + $data = $this->STREAM->read($length); + return (string)$data; + } + + /** + * Binary search for string + * + * @access private + * @param string string + * @param int start (internally used in recursive function) + * @param int end (internally used in recursive function) + * @return int string number (offset in originals table) + */ + function find_string($string, $start = -1, $end = -1) { + if (($start == -1) or ($end == -1)) { + // find_string is called with only one parameter, set start end end + $start = 0; + $end = $this->total; + } + if (abs($start - $end) <= 1) { + // We're done, now we either found the string, or it doesn't exist + $txt = $this->get_original_string($start); + if ($string == $txt) + return $start; + else + return -1; + } else if ($start > $end) { + // start > end -> turn around and start over + return $this->find_string($string, $end, $start); + } else { + // Divide table in two parts + $half = (int)(($start + $end) / 2); + $cmp = strcmp($string, $this->get_original_string($half)); + if ($cmp == 0) + // string is exactly in the middle => return it + return $half; + else if ($cmp < 0) + // The string is in the upper half + return $this->find_string($string, $start, $half); + else + // The string is in the lower half + return $this->find_string($string, $half, $end); + } + } + + /** + * Translates a string + * + * @access public + * @param string string to be translated + * @return string translated string (or original, if not found) + */ + function translate($string) { + if ($this->short_circuit) + return $string; + $this->load_tables(); + + if ($this->enable_cache) { + // Caching enabled, get translated string from cache + if (array_key_exists($string, $this->cache_translations)) + return $this->cache_translations[$string]; + else + return $string; + } else { + // Caching not enabled, try to find string + $num = $this->find_string($string); + if ($num == -1) + return $string; + else + return $this->get_translation_string($num); + } + } + + /** + * Sanitize plural form expression for use in PHP eval call. + * + * @access private + * @return string sanitized plural form expression + */ + function sanitize_plural_expression($expr) { + // Get rid of disallowed characters. + $expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr); + + // Add parenthesis for tertiary '?' operator. + $expr .= ';'; + $res = ''; + $p = 0; + for ($i = 0; $i < strlen($expr); $i++) { + $ch = $expr[$i]; + switch ($ch) { + case '?': + $res .= ' ? ('; + $p++; + break; + case ':': + $res .= ') : ('; + break; + case ';': + $res .= str_repeat( ')', $p) . ';'; + $p = 0; + break; + default: + $res .= $ch; + } + } + return $res; + } + + /** + * Parse full PO header and extract only plural forms line. + * + * @access private + * @return string verbatim plural form header field + */ + function extract_plural_forms_header_from_po_header($header) { + if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs)) + $expr = $regs[2]; + else + $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; + return $expr; + } + + /** + * Get possible plural forms from MO header + * + * @access private + * @return string plural form header + */ + function get_plural_forms() { + // lets assume message number 0 is header + // this is true, right? + $this->load_tables(); + + // cache header field for plural forms + if (! is_string($this->pluralheader)) { + if ($this->enable_cache) { + $header = $this->cache_translations[""]; + } else { + $header = $this->get_translation_string(0); + } + $expr = $this->extract_plural_forms_header_from_po_header($header); + $this->pluralheader = $this->sanitize_plural_expression($expr); + } + return $this->pluralheader; + } + + /** + * Detects which plural form to take + * + * @access private + * @param n count + * @return int array index of the right plural form + */ + function select_string($n) { + $string = $this->get_plural_forms(); + $string = str_replace('nplurals',"\$total",$string); + $string = str_replace("n",$n,$string); + $string = str_replace('plural',"\$plural",$string); + + $total = 0; + $plural = 0; + + eval("$string"); + if ($plural >= $total) $plural = $total - 1; + return $plural; + } + + /** + * Plural version of gettext + * + * @access public + * @param string single + * @param string plural + * @param string number + * @return translated plural form + */ + function ngettext($single, $plural, $number) { + if ($this->short_circuit) { + if ($number != 1) + return $plural; + else + return $single; + } + + // find out the appropriate form + $select = $this->select_string($number); + + // this should contains all strings separated by NULLs + $key = $single . chr(0) . $plural; + + + if ($this->enable_cache) { + if (! array_key_exists($key, $this->cache_translations)) { + return ($number != 1) ? $plural : $single; + } else { + $result = $this->cache_translations[$key]; + $list = explode(chr(0), $result); + return $list[$select]; + } + } else { + $num = $this->find_string($key); + if ($num == -1) { + return ($number != 1) ? $plural : $single; + } else { + $result = $this->get_translation_string($num); + $list = explode(chr(0), $result); + return $list[$select]; + } + } + } + + function pgettext($context, $msgid) { + $key = $context . chr(4) . $msgid; + $ret = $this->translate($key); + if (strpos($ret, "\004") !== FALSE) { + return $msgid; + } else { + return $ret; + } + } + + function npgettext($context, $singular, $plural, $number) { + $key = $context . chr(4) . $singular; + $ret = $this->ngettext($key, $plural, $number); + if (strpos($ret, "\004") !== FALSE) { + return $singular; + } else { + return $ret; + } + + } +} + +class FileReader { + var $_pos; + var $_fd; + var $_length; + + function FileReader($filename) { + if (file_exists($filename)) { + + $this->_length=filesize($filename); + $this->_pos = 0; + $this->_fd = fopen($filename,'rb'); + if (!$this->_fd) { + $this->error = 3; // Cannot read file, probably permissions + return false; + } + } else { + $this->error = 2; // File doesn't exist + return false; + } + } + + function read($bytes) { + if ($bytes) { + fseek($this->_fd, $this->_pos); + + // PHP 5.1.1 does not read more than 8192 bytes in one fread() + // the discussions at PHP Bugs suggest it's the intended behaviour + $data = ''; + while ($bytes > 0) { + $chunk = fread($this->_fd, $bytes); + $data .= $chunk; + $bytes -= strlen($chunk); + } + $this->_pos = ftell($this->_fd); + + return $data; + } else return ''; + } + + function seekto($pos) { + fseek($this->_fd, $pos); + $this->_pos = ftell($this->_fd); + return $this->_pos; + } + + function currentpos() { + return $this->_pos; + } + + function length() { + return $this->_length; + } + + function close() { + fclose($this->_fd); + } + +} + +/** + * Class: Translation + * + * This class is strongly based on the gettext_reader class. It makes use of + * a few simple optimizations for the context of osTicket + * + * * The language packs are pre-compiled and distributed (which means + * they can be customized). + * * The MO file will always be processed by PHP code + * * osTicket uses utf-8 output exclusively (for web traffic anyway) + * + * These allow us to optimize the MO file for the osTicket project + * specifically and make enough of an optimization to allow using a pure-PHP + * source gettext library implementation which should be roughly the same + * performance as the libc gettext library. + */ +class Translation extends gettext_reader { + + var $charset; + + function __construct($reader, $charset=false) { + if (!$reader || $reader->error) + return parent::__construct($reader); + + // Just load the cache + $this->STREAM = $reader; + $this->enable_cache = true; + $this->charset = $charset; + $this->encode = $charset && strcasecmp($charset, 'utf-8') !== 0; + } + + function load_tables() { + if (is_array($this->cache_translations)) + return; + + $this->STREAM->seekto(0); + $this->cache_translations = + unserialize($this->STREAM->read($this->STREAM->length())); + } + + function translate($string) { + $translation = parent::translate($string); + if (!$this->encode) + return $translation; + else + return Format::encode($translation, 'utf-8', $this->charset); + } + + static function buildHashFile($mofile, $outfile=false) { + if (!$outfile) { + $stream = fopen('php://stdout', 'w'); + } + elseif (is_string($outfile)) { + $stream = fopen($outfile, 'w'); + } + elseif (is_resource($outfile)) { + $stream = $outfile; + } + + if (!$stream) + throw new InvalidArgumentException( + 'Expected a filename or valid resource'); + + if (!$mofile instanceof FileReader) + $mofile = new FileReader($mofile); + + $reader = new parent($mofile, true); + + if ($reader->short_circuit || $reader->error) + throw new Exception('Unable to initialize MO input file'); + + $reader->load_tables(); + + // Get basic table + if (!($table = $reader->cache_translations)) + throw new Exception('Unable to read translations from file'); + + // Transcode the table to UTF-8 + $header = $table[""]; + $info = array(); + preg_match('/^content-type: (.*)$/im', $header, $info); + $charset = false; + if ($content_type = $info[1]) { + // Find the charset property + $settings = explode(';', $content_type); + foreach ($settings as $v) { + @list($prop, $value) = explode('=', trim($v), 2); + if (strtolower($prop) == 'charset') { + $charset = trim($value); + break; + } + } + } + if ($charset && strcasecmp($charset, 'utf-8') !== 0) { + foreach ($table as $orig=>$trans) { + // Format::encode defaults to UTF-8 output + $table[Format::encode($orig, $charset)] = + Format::encode($trans, $charset); + unset($table[$orig]); + } + } + + // Add in some meta-data + $table[0] = array( + 'Revision' => $reader->revision, // From the MO + 'Total-Strings' => $reader->total, // From the MO + 'Table-Size' => count($table), // Sanity check for later + 'Build-Timestamp' => gmdate(DATE_RFC822), + 'Format-Version' => 'A', // Support future formats + 'Encoding' => 'UTF-8', + ); + + // Serialize the PHP array and write to output + fwrite($stream, serialize($table)); + } +} + +if (!defined('LC_MESSAGES')) { + define('LC_MESSAGES', 6); +} + +/* Class to hold a single domain included in $text_domains. */ +class TextDomain { + var $l10n = array(); + var $path; + var $codeset; + var $domain; + + static $registry; + static $default_domain = 'messages'; + static $current_locale = ''; + static $LC_CATEGORIES = array('LC_ALL', 'LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', + 'LC_COLLATE', 'LC_MONETARY', 'LC_MESSAGES'); + + function __construct($domain) { + $this->domain = $domain; + } + + function getTranslation($category=LC_MESSAGES, $locale=false) { + $locale = $locale ?: self::setlocale(LC_MESSAGES, 0); + if (!isset($this->l10n[$locale])) { + // get the current locale + $bound_path = @$this->path ?: './'; + $subpath = self::$LC_CATEGORIES[$category] . + '/'.$this->domain.'.mo.php'; + + $locale_names = self::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; + } + $phar_path = 'phar://' . $bound_path . $locale . ".phar/" . $subpath; + if (file_exists($full_path)) { + $input = new FileReader($phar_path); + break; + } + } + $this->l10n[$locale] = new Translation($input, $charset); + } + return $this->l10n[$locale]; + } + + function setPath($path) { + $this->path = $path; + } + + static function configureForUser($user) { + if ($user && method_exists($user, 'getLanguage')) + $lang = $user->getLanguage(); + else + $lang = Internationalization::getDefaultLanguage(); + + // User-specific translations + putenv('LC_ALL=' . $lang); + self::setLocale(LC_ALL, $lang); + } + + static function setDefaultDomain($domain) { + static::$default_domain = $domain; + } + + /** + * Returns passed in $locale, or environment variable $LANG if $locale == ''. + */ + static function get_default_locale($locale) { + if ($locale == '') // emulate variable support + return getenv('LANG'); + else + return $locale; + } + + static 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 = $m = array(); + $lang = 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, $m) + ) { + + if ($m['modifier']) { + // TODO: Confirm if Crowdin uses the modifer flags + if ($m['country']) { + $locale_names[] = "{$m['lang']}_{$m['country']}@{$m['modifier']}"; + } + $locale_names[] = "{$m['lang']}@{$m['modifier']}"; + } + if ($country) { + $locale_names[] = "{$m['lang']}_{$m['country']}"; + } + $locale_names[] = $m['lang']; + } + + // If the locale name doesn't match POSIX style, just include it as-is. + if (!in_array($locale, $locale_names)) + $locale_names[] = $locale; + } + return array_filter($locale_names); + } + + static function setLocale($category, $locale) { + if ($locale === 0) { // use === to differentiate between string "0" + if (static::$current_locale != '') + return static::$current_locale; + else + // obey LANG variable, maybe extend to support all of LC_* vars + // even if we tried to read locale without setting it first + return self::setlocale($category, static::$current_locale); + } else { + 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. + static::$current_locale = self::get_default_locale($locale); + } else { + static::$current_locale = $ret; + } + } else { + // No function setlocale(), emulate it all. + static::$current_locale = self::get_default_locale($locale); + } + return static::$current_locale; + } + } + + static function lookup($domain=null) { + if (!isset($domain)) + $domain = self::$default_domain; + if (!isset(static::$registry[$domain])) { + static::$registry[$domain] = new TextDomain($domain); + } + return static::$registry[$domain]; + } +} + +// Functions for gettext library. Since the gettext extension for PHP is not +// used as a fallback, there is no detection and compat funciton +// installation for the gettext library function calls. + +function _gettext($msgid) { + return TextDomain::lookup()->getTranslation()->translate($msgid); +} +function __($msgid) { + return _gettext($msgid); +} +function _ngettext($singular, $plural, $number) { + return TextDomain::lookup()->getTranslation() + ->ngettext($singular, $plural, $number); +} +function _dgettext($domain, $msgid) { + return TextDomain::lookup($domain)->getTranslation() + ->translate($msgid); +} +function _dngettext($domain, $singular, $plural, $number) { + return TextDomain::lookup($domain)->getTranslation() + ->ngettext($singular, $plural, $number); +} +function _dcgettext($domain, $msgid, $category) { + return TextDomain::lookup($domain)->getTranslation($category) + ->translate($msgid); +} +function _dcngettext($domain, $singular, $plural, $number, $category) { + return TextDomain::lookup($domain)->getTranslation($category) + ->ngettext($singular, $plural, $number); +} +function _pgettext($context, $msgid) { + return TextDomain::lookup()->getTranslation() + ->pgettext($context, $msgid); +} +function _dpgettext($domain, $context, $msgid) { + return TextDomain::lookup($domain)->getTranslation() + ->pgettext($context, $msgid); +} +function _dcpgettext($domain, $context, $msgid, $category) { + return TextDomain::lookup($domain)->getTranslation($category) + ->pgettext($context, $msgid); +} +function _npgettext($context, $singular, $plural) { + return TextDomain::lookup()->getTranslation() + ->npgettext($context, $singular, $plural); +} +function _dnpgettext($domain, $context, $singular, $plural) { + return TextDomain::lookup($domain)->getTranslation() + ->npgettext($context, $singular, $plural); +} +function _dcnpgettext($domain, $context, $singular, $plural, $category) { + return TextDomain::lookup($domain)->getTranslation($category) + ->npgettext($context, $singular, $plural); +} + + +do { + if (PHP_SAPI != 'cli') break; + if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; + if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], basename(__FILE__)) ) break; + $file = $argv[1]; + quick_gettext_reader::buildHashFile($file); +} while (0); diff --git a/include/gettext/gettext.inc b/include/gettext/gettext.inc deleted file mode 100644 index 00b966692cc7d16e1acc19ed21bd0e1fd3afa46c..0000000000000000000000000000000000000000 --- a/include/gettext/gettext.inc +++ /dev/null @@ -1,536 +0,0 @@ -<?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 - 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. - - PHP-gettext 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 PHP-gettext; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ -/* -LC_CTYPE 0 -LC_NUMERIC 1 -LC_TIME 2 -LC_COLLATE 3 -LC_MONETARY 4 -LC_MESSAGES 5 -LC_ALL 6 -*/ - -// 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 - -global $text_domains, $default_domain, $LC_CATEGORIES, $EMULATEGETTEXT, $CURRENTLOCALE; -$text_domains = array(); -$default_domain = 'messages'; -$LC_CATEGORIES = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MONETARY', 'LC_MESSAGES', 'LC_ALL'); -$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); - $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; -} - -/** - * Returns whether we are using our emulated gettext API or PHP built-in one. - */ -function locale_emulation() { - global $EMULATEGETTEXT; - return $EMULATEGETTEXT; -} - -/** - * Checks if the current locale is supported on this system. - */ -function _check_locale_and_function($function=false) { - global $EMULATEGETTEXT; - if ($function and !function_exists($function)) - return false; - return !$EMULATEGETTEXT; -} - -/** - * 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'); -} - -/** - * 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; - } -} - - -// 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 != '') - return $CURRENTLOCALE; - 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 { - 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 { - // 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; - if (array_key_exists($default_domain, $text_domains)) { - unset($text_domains[$default_domain]->l10n); - } - return $CURRENTLOCALE; - } -} - -/** - * Sets the path for a domain. - */ -function _bindtextdomain($domain, $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; -} - -/** - * Sets the default domain. - */ -function _textdomain($domain) { - global $default_domain; - $default_domain = $domain; -} - -/** - * Lookup a message in the current domain. - */ -function _gettext($msgid) { - $l10n = _get_reader(); - return _encode($l10n->translate($msgid)); -} - -/** - * Alias for gettext. - */ -function __($msgid) { - return _gettext($msgid); -} - -/** - * Plural version of gettext. - */ -function _ngettext($singular, $plural, $number) { - $l10n = _get_reader(); - return _encode($l10n->ngettext($singular, $plural, $number)); -} - -/** - * Override the current domain. - */ -function _dgettext($domain, $msgid) { - $l10n = _get_reader($domain); - return _encode($l10n->translate($msgid)); -} - -/** - * Plural version of dgettext. - */ -function _dngettext($domain, $singular, $plural, $number) { - $l10n = _get_reader($domain); - return _encode($l10n->ngettext($singular, $plural, $number)); -} - -/** - * Overrides the domain and category for a single lookup. - */ -function _dcgettext($domain, $msgid, $category) { - $l10n = _get_reader($domain, $category); - return _encode($l10n->translate($msgid)); -} -/** - * Plural version of dcgettext. - */ -function _dcngettext($domain, $singular, $plural, $number, $category) { - $l10n = _get_reader($domain, $category); - return _encode($l10n->ngettext($singular, $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)); -} - -/** - * 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_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('bind_textdomain_codeset')) - return bind_textdomain_codeset($domain, $codeset); - else return _bind_textdomain_codeset($domain, $codeset); -} -function T_textdomain($domain) { - if (_check_locale_and_function()) return textdomain($domain); - else return _textdomain($domain); -} -function T_gettext($msgid) { - if (_check_locale_and_function()) return gettext($msgid); - else return _gettext($msgid); -} -function T_($msgid) { - if (_check_locale_and_function()) return _($msgid); - return __($msgid); -} -function T_ngettext($singular, $plural, $number) { - if (_check_locale_and_function()) - return ngettext($singular, $plural, $number); - else return _ngettext($singular, $plural, $number); -} -function T_dgettext($domain, $msgid) { - if (_check_locale_and_function()) return dgettext($domain, $msgid); - else return _dgettext($domain, $msgid); -} -function T_dngettext($domain, $singular, $plural, $number) { - if (_check_locale_and_function()) - return dngettext($domain, $singular, $plural, $number); - else return _dngettext($domain, $singular, $plural, $number); -} -function T_dcgettext($domain, $msgid, $category) { - if (_check_locale_and_function()) - return dcgettext($domain, $msgid, $category); - else return _dcgettext($domain, $msgid, $category); -} -function T_dcngettext($domain, $singular, $plural, $number, $category) { - if (_check_locale_and_function()) - return dcngettext($domain, $singular, $plural, $number, $category); - else return _dcngettext($domain, $singular, $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, $number) { - if (_check_locale_and_function('npgettext')) - return npgettext($context, $singular, $plural, $number); - else - return _npgettext($context, $singular, $plural, $number); -} - -function T_dnpgettext($domain, $context, $singular, $plural, $number) { - if (_check_locale_and_function('dnpgettext')) - return dnpgettext($domain, $context, $singular, $plural, $number); - else - return _dnpgettext($domain, $context, $singular, $plural, $number); -} - -function T_dcnpgettext($domain, $context, $singular, $plural, - $number, $category) { - if (_check_locale_and_function('dcnpgettext')) - return dcnpgettext($domain, $context, $singular, - $plural, $number, $category); - else - return _dcnpgettext($domain, $context, $singular, - $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($singular, $plural, $number) { - return _ngettext($singular, $plural, $number); - } - function dgettext($domain, $msgid) { - return _dgettext($domain, $msgid); - } - function dngettext($domain, $singular, $plural, $number) { - return _dngettext($domain, $singular, $plural, $number); - } - function dcgettext($domain, $msgid, $category) { - return _dcgettext($domain, $msgid, $category); - } - function dcngettext($domain, $singular, $plural, $number, $category) { - return _dcngettext($domain, $singular, $plural, $number, $category); - } - function pgettext($context, $msgid) { - return _pgettext($context, $msgid); - } - function npgettext($context, $singular, $plural, $number) { - return _npgettext($context, $singular, $plural, $number); - } - function dpgettext($domain, $context, $msgid) { - return _dpgettext($domain, $context, $msgid); - } - function dnpgettext($domain, $context, $singular, $plural, $number) { - return _dnpgettext($domain, $context, $singular, $plural, $number); - } - function dcpgettext($domain, $context, $msgid, $category) { - return _dcpgettext($domain, $context, $msgid, $category); - } - function dcnpgettext($domain, $context, $singular, $plural, - $number, $category) { - return _dcnpgettext($domain, $context, $singular, $plural, - $number, $category); - } -} - -?> diff --git a/include/gettext/gettext.php b/include/gettext/gettext.php deleted file mode 100644 index 5064047cbd2427248c2a058e8ac09640bcb6c503..0000000000000000000000000000000000000000 --- a/include/gettext/gettext.php +++ /dev/null @@ -1,432 +0,0 @@ -<?php -/* - Copyright (c) 2003, 2009 Danilo Segan <danilo@kvota.net>. - Copyright (c) 2005 Nico Kaiser <nico@siriux.net> - - This file is part of PHP-gettext. - - PHP-gettext 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. - - PHP-gettext 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 PHP-gettext; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -/** - * Provides a simple gettext replacement that works independently from - * the system's gettext abilities. - * It can read MO files and use them for translating strings. - * The files are passed to gettext_reader as a Stream (see streams.php) - * - * This version has the ability to cache all strings and translations to - * speed up the string lookup. - * While the cache is enabled by default, it can be switched off with the - * second parameter in the constructor (e.g. whenusing very large MO files - * that you don't want to keep in memory) - */ -class gettext_reader { - //public: - var $error = 0; // public variable that holds error code (0 if no error) - - //private: - var $BYTEORDER = 0; // 0: low endian, 1: big endian - var $STREAM = NULL; - var $short_circuit = false; - var $enable_cache = false; - var $originals = NULL; // offset of original table - var $translations = NULL; // offset of translation table - var $pluralheader = NULL; // cache header field for plural forms - var $total = 0; // total string count - var $table_originals = NULL; // table for original strings (offsets) - var $table_translations = NULL; // table for translated strings (offsets) - var $cache_translations = NULL; // original -> translation mapping - - - /* Methods */ - - - /** - * Reads a 32bit Integer from the Stream - * - * @access private - * @return Integer from the Stream - */ - function readint() { - if ($this->BYTEORDER == 0) { - // low endian - $input=unpack('V', $this->STREAM->read(4)); - return array_shift($input); - } else { - // big endian - $input=unpack('N', $this->STREAM->read(4)); - return array_shift($input); - } - } - - function read($bytes) { - return $this->STREAM->read($bytes); - } - - /** - * Reads an array of Integers from the Stream - * - * @param int count How many elements should be read - * @return Array of Integers - */ - function readintarray($count) { - if ($this->BYTEORDER == 0) { - // low endian - return unpack('V'.$count, $this->STREAM->read(4 * $count)); - } else { - // big endian - return unpack('N'.$count, $this->STREAM->read(4 * $count)); - } - } - - /** - * Constructor - * - * @param object Reader the StreamReader object - * @param boolean enable_cache Enable or disable caching of strings (default on) - */ - function gettext_reader($Reader, $enable_cache = true) { - // If there isn't a StreamReader, turn on short circuit mode. - if (! $Reader || isset($Reader->error) ) { - $this->short_circuit = true; - return; - } - - // Caching can be turned off - $this->enable_cache = $enable_cache; - - $MAGIC1 = "\x95\x04\x12\xde"; - $MAGIC2 = "\xde\x12\x04\x95"; - - $this->STREAM = $Reader; - $magic = $this->read(4); - if ($magic == $MAGIC1) { - $this->BYTEORDER = 1; - } elseif ($magic == $MAGIC2) { - $this->BYTEORDER = 0; - } else { - $this->error = 1; // not MO file - return false; - } - - // FIXME: Do we care about revision? We should. - $revision = $this->readint(); - - $this->total = $this->readint(); - $this->originals = $this->readint(); - $this->translations = $this->readint(); - } - - /** - * Loads the translation tables from the MO file into the cache - * If caching is enabled, also loads all strings into a cache - * to speed up translation lookups - * - * @access private - */ - function load_tables() { - if (is_array($this->cache_translations) && - is_array($this->table_originals) && - is_array($this->table_translations)) - return; - - /* get original and translations tables */ - if (!is_array($this->table_originals)) { - $this->STREAM->seekto($this->originals); - $this->table_originals = $this->readintarray($this->total * 2); - } - if (!is_array($this->table_translations)) { - $this->STREAM->seekto($this->translations); - $this->table_translations = $this->readintarray($this->total * 2); - } - - if ($this->enable_cache) { - $this->cache_translations = array (); - /* read all strings in the cache */ - for ($i = 0; $i < $this->total; $i++) { - $this->STREAM->seekto($this->table_originals[$i * 2 + 2]); - $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]); - $this->STREAM->seekto($this->table_translations[$i * 2 + 2]); - $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]); - $this->cache_translations[$original] = $translation; - } - } - } - - /** - * Returns a string from the "originals" table - * - * @access private - * @param int num Offset number of original string - * @return string Requested string if found, otherwise '' - */ - function get_original_string($num) { - $length = $this->table_originals[$num * 2 + 1]; - $offset = $this->table_originals[$num * 2 + 2]; - if (! $length) - return ''; - $this->STREAM->seekto($offset); - $data = $this->STREAM->read($length); - return (string)$data; - } - - /** - * Returns a string from the "translations" table - * - * @access private - * @param int num Offset number of original string - * @return string Requested string if found, otherwise '' - */ - function get_translation_string($num) { - $length = $this->table_translations[$num * 2 + 1]; - $offset = $this->table_translations[$num * 2 + 2]; - if (! $length) - return ''; - $this->STREAM->seekto($offset); - $data = $this->STREAM->read($length); - return (string)$data; - } - - /** - * Binary search for string - * - * @access private - * @param string string - * @param int start (internally used in recursive function) - * @param int end (internally used in recursive function) - * @return int string number (offset in originals table) - */ - function find_string($string, $start = -1, $end = -1) { - if (($start == -1) or ($end == -1)) { - // find_string is called with only one parameter, set start end end - $start = 0; - $end = $this->total; - } - if (abs($start - $end) <= 1) { - // We're done, now we either found the string, or it doesn't exist - $txt = $this->get_original_string($start); - if ($string == $txt) - return $start; - else - return -1; - } else if ($start > $end) { - // start > end -> turn around and start over - return $this->find_string($string, $end, $start); - } else { - // Divide table in two parts - $half = (int)(($start + $end) / 2); - $cmp = strcmp($string, $this->get_original_string($half)); - if ($cmp == 0) - // string is exactly in the middle => return it - return $half; - else if ($cmp < 0) - // The string is in the upper half - return $this->find_string($string, $start, $half); - else - // The string is in the lower half - return $this->find_string($string, $half, $end); - } - } - - /** - * Translates a string - * - * @access public - * @param string string to be translated - * @return string translated string (or original, if not found) - */ - function translate($string) { - if ($this->short_circuit) - return $string; - $this->load_tables(); - - if ($this->enable_cache) { - // Caching enabled, get translated string from cache - if (array_key_exists($string, $this->cache_translations)) - return $this->cache_translations[$string]; - else - return $string; - } else { - // Caching not enabled, try to find string - $num = $this->find_string($string); - if ($num == -1) - return $string; - else - return $this->get_translation_string($num); - } - } - - /** - * Sanitize plural form expression for use in PHP eval call. - * - * @access private - * @return string sanitized plural form expression - */ - function sanitize_plural_expression($expr) { - // Get rid of disallowed characters. - $expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr); - - // Add parenthesis for tertiary '?' operator. - $expr .= ';'; - $res = ''; - $p = 0; - for ($i = 0; $i < strlen($expr); $i++) { - $ch = $expr[$i]; - switch ($ch) { - case '?': - $res .= ' ? ('; - $p++; - break; - case ':': - $res .= ') : ('; - break; - case ';': - $res .= str_repeat( ')', $p) . ';'; - $p = 0; - break; - default: - $res .= $ch; - } - } - return $res; - } - - /** - * Parse full PO header and extract only plural forms line. - * - * @access private - * @return string verbatim plural form header field - */ - function extract_plural_forms_header_from_po_header($header) { - if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs)) - $expr = $regs[2]; - else - $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; - return $expr; - } - - /** - * Get possible plural forms from MO header - * - * @access private - * @return string plural form header - */ - function get_plural_forms() { - // lets assume message number 0 is header - // this is true, right? - $this->load_tables(); - - // cache header field for plural forms - if (! is_string($this->pluralheader)) { - if ($this->enable_cache) { - $header = $this->cache_translations[""]; - } else { - $header = $this->get_translation_string(0); - } - $expr = $this->extract_plural_forms_header_from_po_header($header); - $this->pluralheader = $this->sanitize_plural_expression($expr); - } - return $this->pluralheader; - } - - /** - * Detects which plural form to take - * - * @access private - * @param n count - * @return int array index of the right plural form - */ - function select_string($n) { - $string = $this->get_plural_forms(); - $string = str_replace('nplurals',"\$total",$string); - $string = str_replace("n",$n,$string); - $string = str_replace('plural',"\$plural",$string); - - $total = 0; - $plural = 0; - - eval("$string"); - if ($plural >= $total) $plural = $total - 1; - return $plural; - } - - /** - * Plural version of gettext - * - * @access public - * @param string single - * @param string plural - * @param string number - * @return translated plural form - */ - function ngettext($single, $plural, $number) { - if ($this->short_circuit) { - if ($number != 1) - return $plural; - else - return $single; - } - - // find out the appropriate form - $select = $this->select_string($number); - - // this should contains all strings separated by NULLs - $key = $single . chr(0) . $plural; - - - if ($this->enable_cache) { - if (! array_key_exists($key, $this->cache_translations)) { - return ($number != 1) ? $plural : $single; - } else { - $result = $this->cache_translations[$key]; - $list = explode(chr(0), $result); - return $list[$select]; - } - } else { - $num = $this->find_string($key); - if ($num == -1) { - return ($number != 1) ? $plural : $single; - } else { - $result = $this->get_translation_string($num); - $list = explode(chr(0), $result); - return $list[$select]; - } - } - } - - function pgettext($context, $msgid) { - $key = $context . chr(4) . $msgid; - $ret = $this->translate($key); - if (strpos($ret, "\004") !== FALSE) { - return $msgid; - } else { - return $ret; - } - } - - function npgettext($context, $singular, $plural, $number) { - $key = $context . chr(4) . $singular; - $ret = $this->ngettext($key, $plural, $number); - if (strpos($ret, "\004") !== FALSE) { - return $singular; - } else { - return $ret; - } - - } -} - -?> diff --git a/include/gettext/streams.php b/include/gettext/streams.php deleted file mode 100644 index 3cdc1584e1fa57451596c59a55f1dd03dc1a7939..0000000000000000000000000000000000000000 --- a/include/gettext/streams.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php -/* - Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>. - - This file is part of PHP-gettext. - - PHP-gettext 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. - - PHP-gettext 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 PHP-gettext; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - - - // Simple class to wrap file streams, string streams, etc. - // seek is essential, and it should be byte stream -class StreamReader { - // should return a string [FIXME: perhaps return array of bytes?] - function read($bytes) { - return false; - } - - // should return new position - function seekto($position) { - return false; - } - - // returns current position - function currentpos() { - return false; - } - - // returns length of entire stream (limit for seekto()s) - function length() { - return false; - } -}; - -class StringReader { - var $_pos; - var $_str; - - function StringReader($str='') { - $this->_str = $str; - $this->_pos = 0; - } - - function read($bytes) { - $data = substr($this->_str, $this->_pos, $bytes); - $this->_pos += $bytes; - if (strlen($this->_str)<$this->_pos) - $this->_pos = strlen($this->_str); - - return $data; - } - - function seekto($pos) { - $this->_pos = $pos; - if (strlen($this->_str)<$this->_pos) - $this->_pos = strlen($this->_str); - return $this->_pos; - } - - function currentpos() { - return $this->_pos; - } - - function length() { - return strlen($this->_str); - } - -}; - - -class FileReader { - var $_pos; - var $_fd; - var $_length; - - function FileReader($filename) { - if (file_exists($filename)) { - - $this->_length=filesize($filename); - $this->_pos = 0; - $this->_fd = fopen($filename,'rb'); - if (!$this->_fd) { - $this->error = 3; // Cannot read file, probably permissions - return false; - } - } else { - $this->error = 2; // File doesn't exist - return false; - } - } - - function read($bytes) { - if ($bytes) { - fseek($this->_fd, $this->_pos); - - // PHP 5.1.1 does not read more than 8192 bytes in one fread() - // the discussions at PHP Bugs suggest it's the intended behaviour - $data = ''; - while ($bytes > 0) { - $chunk = fread($this->_fd, $bytes); - $data .= $chunk; - $bytes -= strlen($chunk); - } - $this->_pos = ftell($this->_fd); - - return $data; - } else return ''; - } - - function seekto($pos) { - fseek($this->_fd, $pos); - $this->_pos = ftell($this->_fd); - return $this->_pos; - } - - function currentpos() { - return $this->_pos; - } - - function length() { - return $this->_length; - } - - function close() { - fclose($this->_fd); - } - -}; - -// Preloads entire file in memory first, then creates a StringReader -// over it (it assumes knowledge of StringReader internals) -class CachedFileReader extends StringReader { - function CachedFileReader($filename) { - if (file_exists($filename)) { - - $length=filesize($filename); - $fd = fopen($filename,'rb'); - - if (!$fd) { - $this->error = 3; // Cannot read file, probably permissions - return false; - } - $this->_str = fread($fd, $length); - fclose($fd); - - } else { - $this->error = 2; // File doesn't exist - return false; - } - } -}; - - -?> diff --git a/include/staff/login.tpl.php b/include/staff/login.tpl.php index 51f1913236c52dec568f4f112e5405fe76dd3a05..f3f9ace7b9e22368afcd495fa66f7f2186cdc106 100644 --- a/include/staff/login.tpl.php +++ b/include/staff/login.tpl.php @@ -33,7 +33,7 @@ if (count($ext_bks)) { ?> } } ?> </div> -<div id="copyRights"><?php echo _('Copyright'); ?> © +<div id="copyRights"><?php echo __('Copyright'); ?> © <a href='http://www.osticket.com' target="_blank">osTicket.com</a></div> </body> </html> diff --git a/scp/staff.inc.php b/scp/staff.inc.php index 62dd91855bcd5cecfc557eb4efb7edd562455a90..9330717e3dc86bc495d8b22e0b0c2066001247a9 100644 --- a/scp/staff.inc.php +++ b/scp/staff.inc.php @@ -60,7 +60,7 @@ $thisstaff = StaffAuthenticationBackend::getUser(); // Bootstrap gettext translations as early as possible, but after attempting // to sign on the agent -Internationalization::bootstrap($thisstaff); +TextDomain::configureForUser($thisstaff); //1) is the user Logged in for real && is staff. if (!$thisstaff || !$thisstaff->getId() || !$thisstaff->isValid()) {