1<?php
2
3require_once dirname(__FILE__).'/accesscheck.php';
4/*
5
6Languages, countries, and the charsets typically used for them
7http://www.w3.org/International/O-charset-lang.html
8
9*/
10
11//# pick up languages from the lan directory
12$landir = dirname(__FILE__).'/locale/';
13$d = opendir($landir);
14while ($lancode = readdir($d)) {
15    //  print "<br/>".$lancode;
16    if (!in_array($landir,
17            array_keys($LANGUAGES)) && is_dir($landir.'/'.$lancode) && is_file($landir.'/'.$lancode.'/language_info')
18    ) {
19        $lan = @parse_ini_file($landir.'/'.$lancode.'/language_info');
20        if (!isset($lan['gettext'])) {
21            $lan['gettext'] = $lancode;
22        }
23        if (!isset($lan['dir'])) {
24            $lan['dir'] = 'ltr';
25        }
26        if (!empty($lan['name']) && !empty($lan['charset'])) {
27            $LANGUAGES[$lancode] = array($lan['name'], $lan['charset'], $lan['charset'], $lan['gettext'], $lan['dir']);
28        }
29
30//    print '<br/>'.$landir.'/'.$lancode;
31    }
32}
33
34//# pick up other languages from DB
35if (Sql_table_exists('i18n')) {
36    $req = Sql_Query(sprintf('select lan,translation from %s where
37    original = "language-name" and lan not in ("%s")', $GLOBALS['tables']['i18n'],
38        implode('","', array_keys($LANGUAGES))));
39    while ($row = Sql_Fetch_Assoc($req)) {
40        $LANGUAGES[$row['lan']] = array($row['translation'], 'UTF-8', 'UTF-8', $row['lan']);
41    }
42}
43
44function lanSort($a, $b)
45{
46    return strcmp(strtolower($a[3]), strtolower($b[3]));
47}
48
49uasort($LANGUAGES, 'lanSort');
50//print '<pre>';
51//var_dump($LANGUAGES);exit;
52
53if (!empty($GLOBALS['SessionTableName'])) {
54    require_once dirname(__FILE__).'/sessionlib.php';
55}
56@session_start();
57
58if (isset($_POST['setlanguage']) && !empty($_POST['setlanguage']) && is_array($LANGUAGES[$_POST['setlanguage']])) {
59    //# just in case
60    $setlanguage = preg_replace('/[^\w_-]+/', '', $_POST['setlanguage']);
61    $_SESSION['adminlanguage'] = array(
62        'info'    => $setlanguage,
63        'iso'     => $setlanguage,
64        'charset' => $LANGUAGES[$setlanguage][1],
65        'dir'     => $LANGUAGES[$setlanguage][4],
66    );
67    SetCookie ( 'preferredLanguage', $setlanguage,time()+31536000);
68} elseif (empty($_SESSION['adminlanguage']) && isset($_COOKIE['preferredLanguage'])) {
69    $setlanguage = preg_replace('/[^\w_-]+/', '', $_COOKIE['preferredLanguage']);
70    $_SESSION['adminlanguage'] = array(
71        'info'    => $setlanguage,
72        'iso'     => $setlanguage,
73        'charset' => $LANGUAGES[$setlanguage][1],
74        'dir'     => $LANGUAGES[$setlanguage][4],
75    );
76}
77//  var_dump($_SESSION['adminlanguage'] );
78
79/*
80if (!empty($_SESSION['show_translation_colours'])) {
81  $GLOBALS['pageheader']['translationtools'] = '
82    <script type="text/javascript" src="js/jquery.contextMenu.js"></script>
83    <link rel="stylesheet" href="js/jquery.contextMenu.css" />
84    <ul id="translationMenu" class="contextMenu">
85    <li class="translate">
86        <a href="#translate">Translate</a>
87    </li>
88    <li class="quit separator">
89        <a href="#quit">Quit</a>
90    </li>
91</ul>
92  <script type="text/javascript">
93  $(document).ready(function(){
94    $(".translate").contextMenu({
95        menu: "translationMenu"
96    },
97    function(action, el, pos) {
98      alert(
99          "Action: " + action + "\n\n" +
100          "Element ID: " + $(el).attr("id") + "\n\n" +
101          "X: " + pos.x + "  Y: " + pos.y + " (relative to element)\n\n" +
102          "X: " + pos.docX + "  Y: " + pos.docY+ " (relative to document)"
103      );
104    });
105  });
106  </script>
107
108  ';
109}
110*/
111
112if (!isset($_SESSION['adminlanguage']) || !is_array($_SESSION['adminlanguage'])) {
113    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
114        $accept_lan = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
115    } else {
116        $accept_lan = array($GLOBALS['default_system_language']);
117    }
118    $detectlan = '';
119
120    /* @@@TODO
121     * we need a mapping from Accept-Language to gettext, see below
122     *
123     * eg nl-be becomes nl_BE
124     *
125     * currently "nl-be" will become "nl" and not "nl_BE";
126     */
127
128    foreach ($accept_lan as $lan) {
129        if (!$detectlan) {
130            if (preg_match('/^([\w-]+)/', $lan, $regs)) {
131                $code = $regs[1];
132                if (isset($LANGUAGES[$code])) {
133                    $detectlan = $code;
134                } elseif (strpos($code, '-') !== false) {
135                    list($language, $country) = explode('-', $code);
136                    if (isset($LANGUAGES[$language])) {
137                        $detectlan = $language;
138                    }
139                }
140            }
141        }
142    }
143    if (!$detectlan) {
144        $detectlan = $GLOBALS['default_system_language'];
145    }
146
147    $_SESSION['adminlanguage'] = array(
148        'info'    => $detectlan,
149        'iso'     => $detectlan,
150        'charset' => $LANGUAGES[$detectlan][1],
151        'dir'     => $LANGUAGES[$detectlan][4],
152    );
153}
154
155//# this interferes with the frontend if an admin is logged in.
156//# better split the frontend and backend charSets at some point
157//if (!isset($GLOBALS['strCharSet'])) {
158$GLOBALS['strCharSet'] = $_SESSION['adminlanguage']['charset'];
159
160//var_dump($_SESSION['adminlanguage']);
161//print '<h1>'. $GLOBALS['strCharSet'].'</h1>';
162
163// internationalisation (I18N)
164
165class phplist_I18N
166{
167    public $defaultlanguage = 'en';
168    public $language = 'en';
169    public $basedir = '';
170    public $dir = 'ltr';
171    private $hasGettext = false;
172    private $hasDB = false;
173    private $lan = array();
174
175    public function __construct()
176    {
177        $this->basedir = dirname(__FILE__).'/locale/';
178        $this->defaultlanguage = $GLOBALS['default_system_language'];
179        $this->language = $GLOBALS['default_system_language'];
180
181        if (isset($_SESSION['adminlanguage']) && isset($GLOBALS['LANGUAGES'][$_SESSION['adminlanguage']['iso']])) {
182            $this->language = $_SESSION['adminlanguage']['iso'];
183            $this->dir = $_SESSION['adminlanguage']['dir'];
184        } else {
185            unset($_SESSION['adminlanguage']);
186            $this->language = $GLOBALS['default_system_language'];
187        }
188        if (function_exists('gettext')) {
189            $this->hasGettext = true;
190        }
191        if (isset($_SESSION['hasI18Ntable'])) {
192            $this->hasDB = $_SESSION['hasI18Ntable'];
193        } elseif (Sql_Check_For_Table('i18n')) {
194            $_SESSION['hasI18Ntable'] = true;
195            $this->hasDB = true;
196        } else {
197            $_SESSION['hasI18Ntable'] = false;
198        }
199
200        if (isset($_GET['origpage']) && !empty($_GET['ajaxed'])) { //# used in ajaxed requests
201            $page = basename($_GET['origpage']);
202        } elseif (isset($_GET['page'])) {
203            $page = basename($_GET['page']);
204        } else {
205            $page = 'home';
206        }
207        //# as we're including things, let's make sure it's clean
208        $page = preg_replace('/\W/', '', $page);
209
210        if (!empty($_GET['pi'])) {
211            $plugin_languagedir = $this->getPluginBasedir();
212            if (is_dir($plugin_languagedir)) {
213                $this->basedir = $plugin_languagedir;
214                if (isset($GLOBALS['plugins'][$_GET['pi']])) {
215                    $plugin = $GLOBALS['plugins'][$_GET['pi']];
216                    if ($plugin->enabled && $plugin->needI18N && $plugin->i18nLanguageDir()) {
217                        $this->basedir = $plugin->i18nLanguageDir();
218                    }
219                }
220            }
221        }
222
223        $lan = array();
224
225        if (is_file($this->basedir.$this->language.'/'.$page.'.php')) {
226            @include $this->basedir.$this->language.'/'.$page.'.php';
227        } elseif (!isset($GLOBALS['developer_email'])) {
228            @include $this->basedir.$this->defaultlanguage.'/'.$page.'.php';
229        }
230        $this->lan = $lan;
231        $lan = array();
232
233        if (is_file($this->basedir.$this->language.'/common.php')) {
234            @include $this->basedir.$this->language.'/common.php';
235        } elseif (!isset($GLOBALS['developer_email'])) {
236            @include $this->basedir.$this->defaultlanguage.'/common.php';
237        }
238        $this->lan += $lan;
239        $lan = array();
240
241        if (is_file($this->basedir.$this->language.'/frontend.php')) {
242            @include $this->basedir.$this->language.'/frontend.php';
243        } elseif (!isset($GLOBALS['developer_email'])) {
244            @include $this->basedir.$this->defaultlanguage.'/frontend.php';
245        }
246        $this->lan += $lan;
247    }
248
249    public function gettext($text)
250    {
251        bindtextdomain('phplist', './locale');
252        textdomain('phplist');
253
254        /* gettext is a bit messy, at least on my Ubuntu 10.10 machine
255         *
256         * if eg language is "nl" it won't find it. It'll need to be "nl_NL";
257         * also the Ubuntu system needs to have the language installed, even if phpList has it
258         * it won't find it, if it's not on the system
259         *
260         * So, to e.g. get "nl" gettext support in phpList (on ubuntu, but presumably other linuxes), you'd have to do
261         * cd /usr/share/locales
262         * ./install-language-pack nl_NL
263         * dpkg-reconfigure locales
264         *
265         * but when you use "nl_NL", the language .mo can still be in "nl".
266         * However, it needs "nl/LC_MESSAGES/phplist.mo s, put a symlink LC_MESSAGES to itself
267         *
268         * the "utf-8" strangely enough needs to be added but can be spelled all kinds
269         * of ways, eg "UTF8", "utf-8"
270         *
271         *
272         * AND then of course the lovely Accept-Language vs gettext
273         * https://bugs.php.net/bug.php?id=25051
274         *
275         * Accept-Language is lowercase and with - and gettext is country uppercase and with underscore
276         *
277         * More ppl have come across that: http://grep.be/articles/php-accept
278         *
279        */
280
281        //# so, to get the mapping from "nl" to "nl_NL", use a gettext map in the related directory
282        if (is_file(dirname(__FILE__).'/locale/'.$this->language.'/gettext_code')) {
283            $lan_map = file_get_contents(dirname(__FILE__).'/locale/'.$this->language.'/gettext_code');
284            $lan_map = trim($lan_map);
285        } else {
286            //# try to do "fr_FR", or "de_DE", might work in most cases
287            //# hmm, not for eg fa_IR or zh_CN so they'll need the above file
288            // http://www.gnu.org/software/gettext/manual/gettext.html#Language-Codes
289            $lan_map = $this->language.'_'.strtoupper($this->language);
290        }
291
292        putenv('LANGUAGE='.$lan_map.'.utf-8');
293        setlocale(LC_ALL, $lan_map.'.utf-8');
294        bind_textdomain_codeset('phplist', 'UTF-8');
295        $gt = gettext($text);
296        if ($gt && $gt != $text) {
297            return $gt;
298        }
299    }
300
301    public function getCachedTranslation($text)
302    {
303        if (!isset($_SESSION['translations']) || !is_array($_SESSION['translations'])) {
304            return false;
305        }
306        if (isset($_SESSION['translations'][$text])) {
307            $age = time() - $_SESSION['translations'][$text]['ts'];
308            if ($age < 3600) { //# timeout after a while
309                return $_SESSION['translations'][$text]['trans'];
310            } else {
311                unset($_SESSION['translations'][$text]);
312            }
313        }
314    }
315
316    public function setCachedTranslation($text, $translation)
317    {
318        if (!isset($_SESSION['translations']) || !is_array($_SESSION['translations'])) {
319            $_SESSION['translations'] = array();
320        }
321        // mark it as translated even if not, to avoid fetching it every time
322        if (empty($translation)) {
323            $translation = $text;
324        }
325        $_SESSION['translations'][$text] = array(
326            'trans' => $translation,
327            'ts'    => time(),
328        );
329    }
330
331    public function resetCache()
332    {
333        unset($_SESSION['translations']);
334    }
335
336    public function databaseTranslation($text)
337    {
338        if (!$this->hasDB) {
339            return '';
340        }
341        if (empty($GLOBALS['database_connection'])) {
342            return '';
343        }
344        if ($cache = $this->getCachedTranslation($text)) {
345            return $cache;
346        }
347
348        $tr = Sql_Fetch_Row_Query(sprintf('select translation from '.$GLOBALS['tables']['i18n'].' where original = "%s" and lan = "%s"',
349            sql_escape(trim($text)), $this->language), 1);
350        if (empty($tr[0])) {
351            $tr = Sql_Fetch_Row_Query(sprintf('select translation from '.$GLOBALS['tables']['i18n'].' where original = "%s" and lan = "%s"',
352                sql_escape($text), $this->language), 1);
353        }
354        if (empty($tr[0])) {
355            $tr = Sql_Fetch_Row_Query(sprintf('select translation from '.$GLOBALS['tables']['i18n'].' where original = "%s" and lan = "%s"',
356                sql_escape(str_replace('"', '\"', $text)), $this->language), 1);
357        }
358        $translated = !empty($tr[0]) ? stripslashes($tr[0]) : '';
359        $this->setCachedTranslation($text, $translated);
360
361        return $translated;
362    }
363
364    public function pageTitle($page)
365    {
366        //# try gettext and otherwise continue
367        if ($this->hasGettext) {
368            $gettext = $this->gettext($page);
369            if (!empty($gettext)) {
370                return $gettext;
371            }
372        }
373        $page_title = '';
374        $dbTitle = $this->databaseTranslation('pagetitle:'.$page);
375        if ($dbTitle) {
376            //# quite a few translators keep the pagetitle: in the translation
377            $dbTitle = str_ireplace('pagetitle:', '', $dbTitle);
378            $page_title = $dbTitle;
379        } elseif (is_file(dirname(__FILE__).'/locale/'.$this->language.'/pagetitles.php')) {
380            include dirname(__FILE__).'/locale/'.$this->language.'/pagetitles.php';
381        } elseif (is_file(dirname(__FILE__).'/lan/'.$this->language.'/pagetitles.php')) {
382            include dirname(__FILE__).'/lan/'.$this->language.'/pagetitles.php';
383        }
384        if (preg_match('/pi=([\w]+)/', $page, $regs)) {
385            //# @@TODO call plugin to ask for title
386            if (isset($GLOBALS['plugins'][$regs[1]])) {
387                $title = $GLOBALS['plugins'][$regs[1]]->pageTitle($page);
388            } else {
389                $title = $regs[1].' - '.$page;
390            }
391        } elseif (!empty($page_title)) {
392            $title = $page_title;
393        } else {
394            $title = ucfirst($page);
395        }
396
397        return $title;
398    }
399
400    public function pageTitleHover($page)
401    {
402        $hoverText = '';
403        $dbTitle = $this->databaseTranslation('pagetitlehover:'.$page);
404        if ($dbTitle) {
405            $dbTitle = str_ireplace('pagetitlehover:', '', $dbTitle);
406            $hoverText = $dbTitle;
407        } else {
408            $hoverText = $this->pageTitle($page);
409            //# is this returns itself, wipe it, so the linktext is used instead
410            if ($hoverText == $page) {
411                $hoverText = '';
412            }
413        }
414        if (!empty($hoverText)) {
415            return $hoverText;
416        }
417
418        return '';
419    }
420
421    public function formatText($text)
422    {
423        // we've decided to spell phplist with uc L
424        $text = str_ireplace('phplist', 'phpList', $text);
425
426        if (isset($GLOBALS['developer_email'])) {
427            if (!empty($_SESSION['show_translation_colours'])) {
428                return '<span style="color:#A704FF">'.str_replace("\n", '', $text).'</span>';
429            }
430//       return 'TE'.$text.'XT';
431        }
432//    return '<span class="translateabletext">'.str_replace("\n","",$text).'</span>';
433        return str_replace("\n", '', $text);
434    }
435
436    /**
437     * obsolete.
438     */
439    public function missingText($text)
440    {
441        if (isset($GLOBALS['developer_email'])) {
442            if (isset($_GET['page'])) {
443                $page = $_GET['page'];
444            } else {
445                $page = 'home';
446            }
447            $pl = $prefix = '';
448            if (!empty($_GET['pi'])) {
449                $pl = $_GET['pi'];
450                $pl = preg_replace('/\W/', '', $pl);
451                $prefix = $pl.'_';
452            }
453
454            $msg = '
455
456      Undefined text reference in page ' .$page.'
457
458      ' .$text;
459
460            $page = preg_replace('/\W/', '', $page);
461
462            //sendMail($GLOBALS["developer_email"],"phplist dev, missing text",$msg);
463            $line = "'".str_replace("'", "\'", $text)."' => '".str_replace("'", "\'", $text)."',";
464//      if (is_file($this->basedir.'/en/'.$page.'.php') && $_SESSION['adminlanguage']['iso'] == 'en') {
465            if (empty($prefix) && $_SESSION['adminlanguage']['iso'] == 'en') {
466                $this->appendText($this->basedir.'/en/'.$page.'.php', $line);
467            } else {
468                $this->appendText('/tmp/'.$prefix.$page.'.php', $line);
469            }
470
471            if (!empty($_SESSION['show_translation_colours'])) {
472                return '<span style="color: #FF1717">'.$text.'</span>'; //MISSING TEXT
473            }
474        }
475
476        return $text;
477    }
478
479    public function appendText($file, $text)
480    {
481        return;
482        $filecontents = '';
483        if (is_file($file)) {
484            $filecontents = file_get_contents($file);
485        } else {
486            $filecontents = '<?php
487
488$lan = array(
489
490);
491
492      ?>';
493        }
494
495//    print "<br/>Writing $text to $file";
496        $filecontents = preg_replace("/\n/", '@@NL@@', $filecontents);
497        $filecontents = str_replace(');', '  '.$text."\n);", $filecontents);
498        $filecontents = str_replace('@@NL@@', "\n", $filecontents);
499
500        $dir = dirname($file);
501        if (!is_writable($dir) || (is_file($file) && !is_writable($file))) {
502            $newfile = basename($file);
503            $file = '/tmp/'.$newfile;
504        }
505
506        file_put_contents($file, $filecontents);
507    }
508
509    public function getPluginBasedir()
510    {
511        $pl = $_GET['pi'];
512        $pl = preg_replace('/\W/', '', $pl);
513        $pluginroot = '';
514        if (isset($GLOBALS['plugins'][$pl]) && is_object($GLOBALS['plugins'][$pl])) {
515            $pluginroot = $GLOBALS['plugins'][$pl]->coderoot;
516        }
517        if (is_dir($pluginroot.'/lan/')) {
518            return $pluginroot.'/lan/';
519        } else {
520            return $pluginroot.'/';
521        }
522    }
523
524    public function initFSTranslations($language = '')
525    {
526        if (empty($language)) {
527            $language = $this->language;
528        }
529        $translations = parsePO(file_get_contents(dirname(__FILE__).'/locale/'.$language.'/phplist.po'));
530        $time = filemtime(dirname(__FILE__).'/locale/'.$language.'/phplist.po');
531        $this->updateDBtranslations($translations, $time, $language);
532    }
533
534    public function updateDBtranslations($translations, $time, $language = '')
535    {
536        if (empty($language)) {
537            $language = $this->language;
538        }
539        if (count($translations)) {
540            foreach ($translations as $orig => $trans) {
541                Sql_Query('replace into '.$GLOBALS['tables']['i18n'].' (lan,original,translation) values("'.$language.'","'.sql_escape($orig).'","'.sql_escape($trans).'")');
542            }
543        }
544        $this->resetCache();
545        saveConfig('lastlanguageupdate-'.$language, $time, 0);
546    }
547
548    public function getTranslation($text, $page, $basedir)
549    {
550
551        //# try DB, as it will be the latest
552        if ($this->hasDB) {
553            $db_trans = $this->databaseTranslation($text);
554            if (!empty($db_trans)) {
555                return $this->formatText($db_trans);
556            } elseif (is_file(dirname(__FILE__).'/locale/'.$this->language.'/phplist.po')) {
557                if (function_exists('getConfig')) {
558                    $lastUpdate = getConfig('lastlanguageupdate-'.$this->language);
559                    $thisUpdate = filemtime(dirname(__FILE__).'/locale/'.$this->language.'/phplist.po');
560                    if (LANGUAGE_AUTO_UPDATE && $thisUpdate > $lastUpdate && !empty($_SESSION['adminloggedin'])) {
561                        //# we can't translate this, as it'll be recursive
562                        $GLOBALS['pagefooter']['transupdate'] = '<script type="text/javascript">initialiseTranslation("Initialising phpList in your language, please wait.");</script>';
563                    }
564                }
565                //$this->updateDBtranslations($translations,$time);
566            }
567        }
568
569        //# next try gettext, although before that works, it requires loads of setting up
570        //# but who knows
571        if ($this->hasGettext) {
572            $gettext = $this->gettext($text);
573            if (!empty($gettext)) {
574                return $this->formatText($gettext);
575            }
576        }
577
578        $lan = $this->lan;
579
580        if (trim($text) == '') {
581            return '';
582        }
583        if (strip_tags($text) == '') {
584            return $text;
585        }
586        if (isset($lan[$text])) {
587            return $this->formatText($lan[$text]);
588        }
589        if (isset($lan[strtolower($text)])) {
590            return $this->formatText($lan[strtolower($text)]);
591        }
592        if (isset($lan[strtoupper($text)])) {
593            return $this->formatText($lan[strtoupper($text)]);
594        }
595
596        return '';
597    }
598
599    public function get($text)
600    {
601        if (trim($text) == '') {
602            return '';
603        }
604        if (strip_tags($text) == '') {
605            return $text;
606        }
607        $translation = '';
608
609        $this->basedir = dirname(__FILE__).'/lan/';
610        if (isset($_GET['origpage']) && !empty($_GET['ajaxed'])) { //# used in ajaxed requests
611            $page = basename($_GET['origpage']);
612        } elseif (isset($_GET['page'])) {
613            $page = basename($_GET['page']);
614        } else {
615            $page = 'home';
616        }
617        $page = preg_replace('/\W/', '', $page);
618
619        if (!empty($_GET['pi'])) {
620            $plugin_languagedir = $this->getPluginBasedir();
621            if (is_dir($plugin_languagedir)) {
622                $translation = $this->getTranslation($text, $page, $plugin_languagedir);
623            }
624        }
625
626        //# if a plugin did not return the translation, find it in core
627        if (empty($translation)) {
628            $translation = $this->getTranslation($text, $page, $this->basedir);
629        }
630
631        //   print $this->language.' '.$text.' '.$translation. '<br/>';
632
633        // spelling mistake, retry with old spelling
634        if ($text == 'over threshold, user marked unconfirmed' && empty($translation)) {
635            return $this->get('over treshold, user marked unconfirmed');
636        }
637
638        if (!empty($translation)) {
639            return $translation;
640        } else {
641            return $this->missingText($text);
642        }
643    }
644}
645
646function getTranslationUpdates()
647{
648    //# @@@TODO add some more error handling
649    $LU = false;
650    $lan_update = fetchUrl(TRANSLATIONS_XML);
651    if (!empty($lan_update)) {
652        $LU = @simplexml_load_string($lan_update);
653    }
654
655    return $LU;
656}
657
658$I18N = new phplist_I18N();
659if (!empty($setlanguage)) {
660    $I18N->resetCache();
661}
662/* add a shortcut that seems common in other apps
663 * function s($text)
664 * @param $text string the text to find
665 * @params 2-n variable - parameters to pass on to the sprintf of the text
666 * @return translated text with parameters filled in
667 *
668 *
669 * eg s("This is a %s with a %d and a %0.2f","text",6,1.98765);
670 *
671 * will look for the translation of the string and substitute the parameters
672 *
673 **/
674
675function s($text)
676{
677    //# allow overloading with sprintf paramaters
678    $translation = $GLOBALS['I18N']->get($text);
679
680    if (func_num_args() > 1) {
681        $args = func_get_args();
682        array_shift($args);
683        $translation = vsprintf($translation, $args);
684    }
685
686    return $translation;
687}
688
689/**
690 * function snbr
691 * similar to function s, but without overloading params
692 * will return the translated text with spaces turned to &nbsp; so that they won't wrap
693 * mostly useful for buttons.
694 */
695function snbr($text)
696{
697    $trans = s($text);
698    $trans = str_replace(' ', '&nbsp;', $trans);
699
700    return $trans;
701}
702
703/**
704 * function sJS
705 * get the translation from the S function, but escape single quotes for use in Javascript.
706 */
707function sjs($text)
708{
709    $trans = s($text);
710    $trans = str_replace("'", "\'", $trans);
711
712    return $trans;
713}
714
715/**
716 * function sHtmlEntities
717 * get the translation from the s function, but escape it by using htmlentities.
718 */
719function sHtmlEntities ($text) {
720    return htmlentities(s($text));
721}
722
723function parsePo($translationUpdate)
724{
725    $translation_lines = explode("\n", $translationUpdate);
726    $original = '';
727    $flagOrig = $flagTrans = false;
728    $translation = '';
729    $translations = array();
730    foreach ($translation_lines as $line) {
731        if (preg_match('/^msgid "(.*)"/', $line, $regs)) {
732            $original = $regs[1];
733            $flagOrig = true;
734        } elseif (preg_match('/^msgstr "(.*)"/', $line, $regs)) {
735            $flagOrig = false;
736            $flagTrans = true;
737            $translation = $regs[1];
738        } elseif (preg_match('/^"(.*)"/', $line, $regs) && !(preg_match('/^#/', $line) || preg_match('/^\s+$/',
739                    $line) || $line == '')
740        ) {
741            //# wrapped to multiple lines, can be both original and translation
742            if ($flagTrans) {
743                $translation .= $regs[1];
744            } else {
745                $original .= $regs[1];
746            }
747        } elseif (preg_match('/^#/', $line) || preg_match('/^\s+$/', $line) || $line == '') {
748            $original = $translation = '';
749            $flagOrig = $flagTrans = false;
750        }
751        if (!empty($original) && !empty($translation)) {
752            $translations[trim($original)] = trim($translation);
753        }
754    }
755
756    return $translations;
757}
758