/classes/Translate.php
PHP | 349 lines | 206 code | 30 blank | 113 comment | 42 complexity | c090073fce3a378c2dd4f1d7db14847a MD5 | raw file
- <?php
- /**
- *
- *
- * @url $URL$
- * @date $Date$
- * @version $Revision$
- * @author $Author$
- * @license GPL
- *
- * @package MythWeb
- * @subpackage Classes
- *
- /**/
- class Translate extends MythBase {
- // Cache for 24 hours
- public $cacheLifetime = 86400;
- // Define the languages that mythweb can translate into. Each hash entry should
- // point to an array containing first the user-visible name of the language, the
- // php locale for printing and finally the matching language and charset codes.
- public static $Languages = array(
- 'Catalan' => array('Català', 'ca_ES.UTF-8', 'ca_ES'),
- 'Dutch' => array('Nederlands', 'nl_NL', array('nl_NL', 'nl_BE')),
- 'English' => array('English', 'en_US.UTF-8', 'en_US.ISO-8859-1'),
- 'English_CA' => array('English_CA', 'en_CA.UTF-8', 'en_CA.ISO-8859-1'),
- 'English_GB' => array('English_GB', 'en_GB.UTF-8', 'en_GB.ISO-8859-1'),
- 'French' => array('Français', 'fr_FR.UTF-8', 'fr_FR'),
- 'French_CA' => array('Français_CA', 'fr_CA.UTF-8', 'fr_CA'),
- 'Japanese' => array('Japanese', 'ja_JP.UTF-8', 'ja_JP'),
- 'Spanish' => array('Español', 'es_ES.UTF-8', 'es'),
- 'Spanish' => array('Español_ES', 'es_ES.UTF-8', 'es_ES'),
- 'Swedish' => array('Svenska', 'sv_SE.UTF-8', 'sv_SE'),
- 'Danish' => array('Dansk', 'da_DK.UTF-8', 'da_DK'),
- 'German' => array('Deutsch', 'de_DE', 'de_DE'),
- 'Slovenian' => array('Slovensko', 'si_SI', 'sl_SI'),
- 'Finnish' => array('Suomi', 'fi_FI.UTF-8', 'fi_FI'),
- 'Czech' => array('Czech', 'cs_CZ.UTF-8', 'cs_CZ.UTF-8'),
- 'Polish' => array('Polski', 'pl_PL.UTF-8', 'pl_PL'),
- 'Hungarian' => array('Magyar', 'hu_HU.UTF-8', 'hu_HU'),
- 'Norwegian_NB' => array('Norsk Bokmål', 'nb_NO.UTF-8', array('nb_NO', 'nn_NO', 'se_NO', 'no_NO')),
- );
- private $currentLanguage = '';
- private $translations = array();
- public function __construct($language = null) {
- if (!is_null($language))
- $_SESSION['language'] = $language;
- // Need to detect the language?
- if (empty($_SESSION['language']))
- $_SESSION['language'] = self::get_browser_lang();
- if (empty($_SESSION['language']))
- $_SESSION['language'] = 'English';
- // Set the locale
- setlocale(LC_ALL, self::$Languages[$_SESSION['language']][1]);
- $this->load_translation();
- }
- public function __wakeup() {
- $this->__construct();
- // Note, we don't need to call the parent wakeup here, as we're already a cache hit and we don't
- // need to recache ourself. It's pointless, so just resetup the browser info and call it good.
- }
- /**
- * Returns $str, translated appropriately
- /**/
- public function string($str /* [, arg1, arg2, argN] */ ) {
- // No string?
- if (!$str)
- return '';
- // No translation for this string?
- if (!isset($this->translations[$str]) && !(int)($str) && $str != '0')
- return "!!NoTrans: $str!!";
- // Parse out anything passed in as an array (usually from tn())
- $args = array();
- if (func_num_args() > 1) {
- $a = func_get_args();
- array_shift($a); // shift off $str, we don't need it here
- foreach ($a as $arg) {
- if (is_array($arg)) {
- foreach ($arg as $arg2)
- $args[] = $arg2;
- }
- else
- $args[] = $arg;
- }
- }
- // Pull in the translated string (or default to $str if the translated string is '')
- if ($this->translations[$str])
- $str = $this->translations[$str];
- // Nothing extra to print
- if (count($args) < 1)
- return $str;
- // Otherwise, parse in replacement strings as needed
- foreach ($args as $i => $arg) {
- $str = preg_replace('/\\$'.($i+1).'\b/',
- str_replace('$', '~~$~~', $arg), // Replace $ with ~~$~~ so sub-args don't get reinterpreted
- $str
- );
- }
- $str = str_replace('~~$~~', '$', $str); // re-convert any ~~$~~ sequences
- return $str;
- }
- /**
- * return different translated strings based on the numerical value of int.
- * an optional array of string arguments can be included and will be passed
- * to t() for interpretation. If no array is passed in, a default will be
- * created using the value of t(int).
- /**/
- public function number(/* string1, string2, stringN, int [, array-of-args] */) {
- // Flatten arrays
- $ab = func_get_args();
- $a[] = $ab[0];
- foreach ($ab[1] as $b)
- $a[] = $b;
- // Array of arguments?
- if (is_array($a[count($a)-1]))
- $args = array_pop($a);
- // Pull off the int
- $int = intVal(array_pop($a));
- // Default parameters to $int
- if (!isset($args))
- $args = array(t($int));
- // Return the appropriate translated string
- if ($a[$int-1])
- return $this->string($a[$int-1], $args);
- return $this->string($a[count($a)-1], $args);
- }
- /**
- * Load a translation file into the global translation array
- *
- * @param string $path The path to the translation file
- /**/
- public function load_translation($language = null) {
- if (is_null($language))
- $language = $_SESSION['language'];
- // Already loaded?
- if ($language == $this->currentLanguage)
- return;
- $this->translations = array();
- // Wipe any existing cache
- Cache::clear();
- // Load the primary language file, or English if the other doesn't exist.
- if (file_exists(modules_path.'/_shared/lang/'.$language.'.lang'))
- $path = modules_path.'/_shared/lang/'.$language.'.lang';
- else
- $path = modules_path.'/_shared/lang/English.lang';
- // Load a module override translation if one exists.
- if (file_exists(modules_path.'/'.module.'/lang/'.$language.'.lang'))
- $path = modules_path.'/'.module.'/lang/'.$language.'.lang';
- elseif (file_exists(modules_path.'/'.module.'/lang/English.lang'))
- $path = modules_path.'/'.module.'/lang/English.lang';
- $file = @file_get_contents($path);
- // Error?
- if ($file === false) {
- trigger_error("Failed to open translation file: $path", ERROR);
- return false;
- }
- // Parse the file
- foreach (preg_split('/\n(?=\S)/', $file) as $group) {
- preg_match('/^([^\n]+?)\s*(?:$|\s*\n(\s+)(.+)$)/s', $group, $match);
- $indent = '';
- $trans = '';
- if (count($match) == 2)
- list($match, $key) = $match;
- else
- list($match, $key, $indent, $trans) = $match;
- // Cleanup
- $trans = trim(str_replace("\n$indent", "\n", $trans));
- $key = trim($key);
- if (preg_match('/^["\']/', $key))
- $key = preg_replace('/^(["\'])(.+)\\1$/', '$2', $key);
- // Store
- if ($trans || empty($this->translations[$key]))
- $this->translations[$key] = $trans;
- }
- // No language array defined?
- if (!is_array($this->translations) || !count($this->translations))
- trigger_error('No language strings defined.', FATAL);
- // Generate the date formats
- $session = array();
- $session['date_statusbar'] = $this->string('generic_date').', '.$this->string('generic_time');
- $session['date_scheduled'] = $this->string('generic_date').' ('.$this->string('generic_time').')';
- $session['date_scheduled_popup'] = $this->string('generic_date');
- $session['date_recorded'] = $this->string('generic_date').' ('.$this->string('generic_time').')';
- $session['date_search'] = $this->string('generic_date').', '.$this->string('generic_time');
- $session['date_listing_key'] = $this->string('generic_date').', '.$this->string('generic_time');
- $session['date_listing_jump'] = $this->string('generic_date');
- $session['date_channel_jump'] = $this->string('generic_date');
- $session['date_job_status'] = $this->string('generic_date').', '.$this->string('generic_time');
- $session['time_format'] = $this->string('generic_time');
- foreach ($session as $key => $value)
- if (!isset($_SESSION[$key]) || $_SESSION[$key] == '')
- $_SESSION[$key] = $value;
- unset($session);
- $this->currentLanguage = $language;
- }
- /**
- * The get_browser_lang function is modified from Wouter Verhelst's source.
- * Relevant documentation from his code is included below.
- *
- * accept-to-gettext.inc -- convert information in 'Accept-*' headers to
- * gettext language identifiers.
- * Copyright (c) 2003, Wouter Verhelst <wouter@debian.org>
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Assumptions made:
- * * Charset encodings are written the same way as the Accept-Charset
- * HTTP header specifies them (RFC2616), except that they're parsed
- * case-insensitive.
- * * Country codes and language codes are the same in both gettext and
- * the Accept-Language syntax (except for the case differences, which
- * are dealt with easily). If not, some input may be ignored.
- * * The language is more important than the charset; i.e., if the
- * following is given:
- *
- * Accept-Language: nl-be, nl;q=0.8, en-us;q=0.5, en;q=0.3
- * Accept-Charset: ISO-8859-15, utf-8;q=0.5
- *
- * And the supplied parameter contains (amongst others) nl_BE.UTF-8
- * and nl.ISO-8859-15, then nl_BE.UTF-8 will be picked.
- *
- /**/
- private static function get_browser_lang() {
- // default to "everything is acceptable", as RFC2616 specifies
- $alparts = explode(',', $_SERVER["HTTP_ACCEPT_LANGUAGE"] ? $_SERVER["HTTP_ACCEPT_LANGUAGE"] : '*');
- $acparts = explode(',', $_SERVER["HTTP_ACCEPT_CHARSET"] ? $_SERVER["HTTP_ACCEPT_CHARSET"] : '*');
- // Parse the contents of the Accept-Language header.
- $alscores = array();
- foreach($alparts as $part) {
- $part = trim($part);
- if (strstr($part, ';')) {
- $lang = explode(';', $part);
- $score = explode('=', $lang[1]);
- $alscores[$lang[0]] = $score[1];
- }
- else
- $alscores[$part] = 1;
- }
- // Do the same for the Accept-Charset header. */
- /* RFC2616: ``If no '*' is present in an Accept-Charset field, then
- * all character sets not explicitly mentioned get a quality value of
- * 0, except for ISO-8859-1, which gets a quality value of 1 if not
- * explicitly mentioned.''
- *
- * Making it 2 for the time being, so that we can distinguish between
- * "not specified" and "specified as 1" later on.
- */
- $acscores = array('ISO-8859-1' => 2);
- foreach($acparts as $part) {
- $part = trim($part);
- if (strstr($part, ';')) {
- $cs = explode(';', $part);
- $score = explode('=', $cs[1]);
- $acscores[strtoupper($cs[0])]=$score[1];
- }
- else
- $acscores[strtoupper($part)]=1;
- }
- if ($acscores['ISO-8859-1'] == 2)
- $acscores['ISO-8859-1'] = $acscores['*'] ? $acscores['*'] : 1;
- /*
- * Loop through the available languages/encodings, and pick the one
- * with the highest score, excluding the ones with a charset the user
- * did not include.
- */
- $curlscore = 0;
- $curcscore = 0;
- $curlang = null;
- foreach(self::$Languages as $lang => $details) {
- $encodings = is_array($details[2]) ? $details[2] : array($details[2]);
- if (empty($encodings))
- continue;
- foreach ($encodings as $encoding) {
- if (empty($encoding))
- continue;
- $tmp = @explode('.', str_replace('_', '-', $encoding));
- $allang = strtolower($tmp[0]);
- $cs = strtoupper($tmp[1]);
- $noct = explode('-', $allang);
- $testvals = array(
- array($alscores[$allang], $acscores[$cs]),
- array($alscores[$noct[0]], $acscores[$cs]),
- array($alscores[$allang], $acscores['*']),
- array($alscores[$noct[0]], $acscores['*']),
- array($alscores['*'], $acscores[$cs]),
- array($alscores['*'], $acscores['*'])
- );
- // Scan through the possible test-match values until we find a valid set
- foreach($testvals as $tval) {
- if(!isset($tval[0]) || !isset($tval[1]))
- continue;
- if($curlscore < $tval[0]) {
- $curlscore = $tval[0];
- $curcscore = $tval[1];
- $curlang = $lang;
- }
- elseif ($curlscore == $tval[0] && $curcscore < $tval[1]) {
- $curcscore = $tval[1];
- $curlang = $lang;
- }
- break;
- }
- }
- }
- // Return the language we found
- return $curlang;
- }
- }