WordPress.org

Make WordPress Core

Ticket #4005: gettext-plurals.diff

File gettext-plurals.diff, 22.2 KB (added by nbachiyski, 11 years ago)

moeffju's fixes + select_string enhancements + indent + none whitespace at the end of lines

  • wp-includes/gettext.php

     
    11<?php
    22/*
    3    Copyright (c) 2003 Danilo Segan <danilo@kvota.net>.
    4    Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
    5    
    6    This file is part of PHP-gettext.
     3         Copyright (c) 2003 Danilo Segan <danilo@kvota.net>.
     4         Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
    75
    8    PHP-gettext is free software; you can redistribute it and/or modify
    9    it under the terms of the GNU General Public License as published by
    10    the Free Software Foundation; either version 2 of the License, or
    11    (at your option) any later version.
     6         This file is part of PHP-gettext.
    127
    13    PHP-gettext is distributed in the hope that it will be useful,
    14    but WITHOUT ANY WARRANTY; without even the implied warranty of
    15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16    GNU General Public License for more details.
     8         PHP-gettext is free software; you can redistribute it and/or modify
     9         it under the terms of the GNU General Public License as published by
     10         the Free Software Foundation; either version 2 of the License, or
     11         (at your option) any later version.
    1712
    18    You should have received a copy of the GNU General Public License
    19    along with PHP-gettext; if not, write to the Free Software
    20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     13         PHP-gettext is distributed in the hope that it will be useful,
     14         but WITHOUT ANY WARRANTY; without even the implied warranty of
     15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16         GNU General Public License for more details.
    2117
     18         You should have received a copy of the GNU General Public License
     19         along with PHP-gettext; if not, write to the Free Software
     20         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     21
    2222*/
    23  
     23
    2424/**
    2525 * Provides a simple gettext replacement that works independently from
    2626 * the system's gettext abilities.
    2727 * It can read MO files and use them for translating strings.
    2828 * The files are passed to gettext_reader as a Stream (see streams.php)
    29  * 
     29 *
    3030 * This version has the ability to cache all strings and translations to
    3131 * speed up the string lookup.
    3232 * While the cache is enabled by default, it can be switched off with the
     
    3434 * that you don't want to keep in memory)
    3535 */
    3636class gettext_reader {
    37   //public:
    38    var $error = 0; // public variable that holds error code (0 if no error)
    39    
    40    //private:
    41   var $BYTEORDER = 0;        // 0: low endian, 1: big endian
    42   var $STREAM = NULL;
    43   var $short_circuit = false;
    44   var $enable_cache = false;
    45   var $originals = NULL;      // offset of original table
    46   var $translations = NULL;    // offset of translation table
    47   var $pluralheader = NULL;    // cache header field for plural forms
    48   var $total = 0;          // total string count
    49   var $table_originals = NULL;  // table for original strings (offsets)
    50   var $table_translations = NULL;  // table for translated strings (offsets)
    51   var $cache_translations = NULL;  // original -> translation mapping
     37        //public:
     38         var $error = 0; // public variable that holds error code (0 if no error)
    5239
     40         //private:
     41        var $BYTEORDER = 0;        // 0: low endian, 1: big endian
     42        var $STREAM = NULL;
     43        var $short_circuit = false;
     44        var $enable_cache = false;
     45        var $originals = NULL;      // offset of original table
     46        var $translations = NULL;    // offset of translation table
     47        var $pluralheader = NULL;    // cache header field for plural forms
     48        var $select_string_function = NULL; // cache function, which chooses plural forms
     49        var $total = 0;          // total string count
     50        var $table_originals = NULL;  // table for original strings (offsets)
     51        var $table_translations = NULL;  // table for translated strings (offsets)
     52        var $cache_translations = NULL;  // original -> translation mapping
    5353
    54   /* Methods */
    55  
    56    
    57   /**
    58    * Reads a 32bit Integer from the Stream
    59    *
    60    * @access private
    61    * @return Integer from the Stream
    62    */
    63   function readint() {
    64       if ($this->BYTEORDER == 0) {
    65         // low endian
    66         $low_end = unpack('V', $this->STREAM->read(4));
    67         return array_shift($low_end);
    68       } else {
    69         // big endian
    70         $big_end = unpack('N', $this->STREAM->read(4));
    71         return array_shift($big_end);
    72       }
    73     }
    7454
    75   /**
    76    * Reads an array of Integers from the Stream
    77    *
    78    * @param int count How many elements should be read
    79    * @return Array of Integers
    80    */
    81   function readintarray($count) {
    82     if ($this->BYTEORDER == 0) {
    83         // low endian
    84         return unpack('V'.$count, $this->STREAM->read(4 * $count));
    85       } else {
    86         // big endian
    87         return unpack('N'.$count, $this->STREAM->read(4 * $count));
    88       }
    89   }
    90  
    91   /**
    92    * Constructor
    93    *
    94    * @param object Reader the StreamReader object
    95    * @param boolean enable_cache Enable or disable caching of strings (default on)
    96    */
    97   function gettext_reader($Reader, $enable_cache = true) {
    98     // If there isn't a StreamReader, turn on short circuit mode.
    99     if (! $Reader || isset($Reader->error) ) {
    100       $this->short_circuit = true;
    101       return;
    102     }
    103    
    104     // Caching can be turned off
    105     $this->enable_cache = $enable_cache;
     55        /* Methods */
    10656
    107     // $MAGIC1 = (int)0x950412de; //bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565
    108     $MAGIC1 = (int) - 1794895138;
    109     // $MAGIC2 = (int)0xde120495; //bug
    110     $MAGIC2 = (int) - 569244523;
    111     // 64-bit fix
    112     $MAGIC3 = (int) 2500072158;
    11357
    114     $this->STREAM = $Reader;
    115     $magic = $this->readint();
    116     if ($magic == ($MAGIC1 & 0xFFFFFFFF) || $magic == ($MAGIC3 & 0xFFFFFFFF)) { // to make sure it works for 64-bit platforms
    117       $this->BYTEORDER = 0;
    118     } elseif ($magic == ($MAGIC2 & 0xFFFFFFFF)) {
    119       $this->BYTEORDER = 1;
    120     } else {
    121       $this->error = 1; // not MO file
    122       return false;
    123     }
    124    
    125     // FIXME: Do we care about revision? We should.
    126     $revision = $this->readint();
    127    
    128     $this->total = $this->readint();
    129     $this->originals = $this->readint();
    130     $this->translations = $this->readint();
    131   }
    132  
    133   /**
    134    * Loads the translation tables from the MO file into the cache
    135    * If caching is enabled, also loads all strings into a cache
    136    * to speed up translation lookups
    137    *
    138    * @access private
    139    */
    140   function load_tables() {
    141     if (is_array($this->cache_translations) &&
    142       is_array($this->table_originals) &&
    143       is_array($this->table_translations))
    144       return;
    145    
    146     /* get original and translations tables */
    147     $this->STREAM->seekto($this->originals);
    148     $this->table_originals = $this->readintarray($this->total * 2);
    149     $this->STREAM->seekto($this->translations);
    150     $this->table_translations = $this->readintarray($this->total * 2);
    151    
    152     if ($this->enable_cache) {
    153       $this->cache_translations = array ();
    154       /* read all strings in the cache */
    155       for ($i = 0; $i < $this->total; $i++) {
    156         $this->STREAM->seekto($this->table_originals[$i * 2 + 2]);
    157         $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]);
    158         $this->STREAM->seekto($this->table_translations[$i * 2 + 2]);
    159         $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]);
    160         $this->cache_translations[$original] = $translation;
    161       }
    162     }
    163   }
    164  
    165   /**
    166    * Returns a string from the "originals" table
    167    *
    168    * @access private
    169    * @param int num Offset number of original string
    170    * @return string Requested string if found, otherwise ''
    171    */
    172   function get_original_string($num) {
    173     $length = $this->table_originals[$num * 2 + 1];
    174     $offset = $this->table_originals[$num * 2 + 2];
    175     if (! $length)
    176       return '';
    177     $this->STREAM->seekto($offset);
    178     $data = $this->STREAM->read($length);
    179     return (string)$data;
    180   }
    181  
    182   /**
    183    * Returns a string from the "translations" table
    184    *
    185    * @access private
    186    * @param int num Offset number of original string
    187    * @return string Requested string if found, otherwise ''
    188    */
    189   function get_translation_string($num) {
    190     $length = $this->table_translations[$num * 2 + 1];
    191     $offset = $this->table_translations[$num * 2 + 2];
    192     if (! $length)
    193       return '';
    194     $this->STREAM->seekto($offset);
    195     $data = $this->STREAM->read($length);
    196     return (string)$data;
    197   }
    198  
    199   /**
    200    * Binary search for string
    201    *
    202    * @access private
    203    * @param string string
    204    * @param int start (internally used in recursive function)
    205    * @param int end (internally used in recursive function)
    206    * @return int string number (offset in originals table)
    207    */
    208   function find_string($string, $start = -1, $end = -1) {
    209     if (($start == -1) or ($end == -1)) {
    210       // find_string is called with only one parameter, set start end end
    211       $start = 0;
    212       $end = $this->total;
    213     }
    214     if (abs($start - $end) <= 1) {
    215       // We're done, now we either found the string, or it doesn't exist
    216       $txt = $this->get_original_string($start);
    217       if ($string == $txt)
    218         return $start;
    219       else
    220         return -1;
    221     } else if ($start > $end) {
    222       // start > end -> turn around and start over
    223       return $this->find_string($string, $end, $start);
    224     } else {
    225       // Divide table in two parts
    226       $half = (int)(($start + $end) / 2);
    227       $cmp = strcmp($string, $this->get_original_string($half));
    228       if ($cmp == 0)
    229         // string is exactly in the middle => return it
    230         return $half;
    231       else if ($cmp < 0)
    232         // The string is in the upper half
    233         return $this->find_string($string, $start, $half);
    234       else
    235         // The string is in the lower half
    236         return $this->find_string($string, $half, $end);
    237     }
    238   }
    239  
    240   /**
    241    * Translates a string
    242    *
    243    * @access public
    244    * @param string string to be translated
    245    * @return string translated string (or original, if not found)
    246    */
    247   function translate($string) {
    248     if ($this->short_circuit)
    249       return $string;
    250     $this->load_tables();     
    251    
    252     if ($this->enable_cache) {
    253       // Caching enabled, get translated string from cache
    254       if (array_key_exists($string, $this->cache_translations))
    255         return $this->cache_translations[$string];
    256       else
    257         return $string;
    258     } else {
    259       // Caching not enabled, try to find string
    260       $num = $this->find_string($string);
    261       if ($num == -1)
    262         return $string;
    263       else
    264         return $this->get_translation_string($num);
    265     }
    266   }
     58        /**
     59         * Reads a 32bit Integer from the Stream
     60         *
     61         * @access private
     62         * @return Integer from the Stream
     63         */
     64        function readint() {
     65                if ($this->BYTEORDER == 0) {
     66                        // low endian
     67                        $low_end = unpack('V', $this->STREAM->read(4));
     68                        return array_shift($low_end);
     69                } else {
     70                        // big endian
     71                        $big_end = unpack('N', $this->STREAM->read(4));
     72                        return array_shift($big_end);
     73                }
     74        }
    26775
    268   /**
    269    * Get possible plural forms from MO header
    270    *
    271    * @access private
    272    * @return string plural form header
    273    */
    274   function get_plural_forms() {
    275     // lets assume message number 0 is header 
    276     // this is true, right?
    277     $this->load_tables();
    278    
    279     // cache header field for plural forms
    280     if (! is_string($this->pluralheader)) {
    281       if ($this->enable_cache) {
    282         $header = $this->cache_translations[""];
    283       } else {
    284         $header = $this->get_translation_string(0);
    285       }
    286       if (eregi("plural-forms: ([^\n]*)\n", $header, $regs))
    287         $expr = $regs[1];
    288       else
    289         $expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
    290       $this->pluralheader = $expr;
    291     }
    292     return $this->pluralheader;
    293   }
     76        /**
     77         * Reads an array of Integers from the Stream
     78         *
     79         * @param int count How many elements should be read
     80         * @return Array of Integers
     81         */
     82        function readintarray($count) {
     83        if ($this->BYTEORDER == 0) {
     84                        // low endian
     85                        return unpack('V'.$count, $this->STREAM->read(4 * $count));
     86                } else {
     87                        // big endian
     88                        return unpack('N'.$count, $this->STREAM->read(4 * $count));
     89                }
     90        }
    29491
    295   /**
    296    * Detects which plural form to take
    297    *
    298    * @access private
    299    * @param n count
    300    * @return int array index of the right plural form
    301    */
    302   function select_string($n) {
    303     $string = $this->get_plural_forms();
    304     $string = str_replace('nplurals',"\$total",$string);
    305     $string = str_replace("n",$n,$string);
    306     $string = str_replace('plural',"\$plural",$string);
     92        /**
     93         * Constructor
     94         *
     95         * @param object Reader the StreamReader object
     96         * @param boolean enable_cache Enable or disable caching of strings (default on)
     97         */
     98        function gettext_reader($Reader, $enable_cache = true) {
     99                // If there isn't a StreamReader, turn on short circuit mode.
     100                if (! $Reader || isset($Reader->error) ) {
     101                        $this->short_circuit = true;
     102                        return;
     103                }
    307104
    308     # poEdit doesn't put any semicolons, which
    309     # results in parse error in eval
    310     $string .= ';';
     105                // Caching can be turned off
     106                $this->enable_cache = $enable_cache;
    311107
    312     $total = 0;
    313     $plural = 0;
     108                // $MAGIC1 = (int)0x950412de; //bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565
     109                $MAGIC1 = (int) - 1794895138;
     110                // $MAGIC2 = (int)0xde120495; //bug
     111                $MAGIC2 = (int) - 569244523;
     112                // 64-bit fix
     113                $MAGIC3 = (int) 2500072158;
    314114
    315     eval("$string");
    316     if ($plural >= $total) $plural = $total - 1;
    317     return $plural;
    318   }
     115                $this->STREAM = $Reader;
     116                $magic = $this->readint();
     117                if ($magic == ($MAGIC1 & 0xFFFFFFFF) || $magic == ($MAGIC3 & 0xFFFFFFFF)) { // to make sure it works for 64-bit platforms
     118                        $this->BYTEORDER = 0;
     119                } elseif ($magic == ($MAGIC2 & 0xFFFFFFFF)) {
     120                        $this->BYTEORDER = 1;
     121                } else {
     122                        $this->error = 1; // not MO file
     123                        return false;
     124                }
    319125
    320   /**
    321    * Plural version of gettext
    322    *
    323    * @access public
    324    * @param string single
    325    * @param string plural
    326    * @param string number
    327    * @return translated plural form
    328    */
    329   function ngettext($single, $plural, $number) {
    330     if ($this->short_circuit) {
    331       if ($number != 1)
    332         return $plural;
    333       else
    334         return $single;
    335     }
     126                // FIXME: Do we care about revision? We should.
     127                $revision = $this->readint();
    336128
    337     // find out the appropriate form
    338     $select = $this->select_string($number);
    339    
    340     // this should contains all strings separated by NULLs
    341     $key = $single.chr(0).$plural;
    342    
    343    
    344     if ($this->enable_cache) {
    345       if (! array_key_exists($key, $this->cache_translations)) {
    346         return ($number != 1) ? $plural : $single;
    347       } else {
    348         $result = $this->cache_translations[$key];
    349         $list = explode(chr(0), $result);
    350         return $list[$select];
    351       }
    352     } else {
    353       $num = $this->find_string($key);
    354       if ($num == -1) {
    355         return ($number != 1) ? $plural : $single;
    356       } else {
    357         $result = $this->get_translation_string($num);
    358         $list = explode(chr(0), $result);
    359         return $list[$select];
    360       }
    361     }
    362   }
     129                $this->total = $this->readint();
     130                $this->originals = $this->readint();
     131                $this->translations = $this->readint();
     132        }
    363133
     134        /**
     135         * Loads the translation tables from the MO file into the cache
     136         * If caching is enabled, also loads all strings into a cache
     137         * to speed up translation lookups
     138         *
     139         * @access private
     140         */
     141        function load_tables() {
     142                if (is_array($this->cache_translations) &&
     143                        is_array($this->table_originals) &&
     144                        is_array($this->table_translations))
     145                        return;
     146
     147                /* get original and translations tables */
     148                $this->STREAM->seekto($this->originals);
     149                $this->table_originals = $this->readintarray($this->total * 2);
     150                $this->STREAM->seekto($this->translations);
     151                $this->table_translations = $this->readintarray($this->total * 2);
     152
     153                if ($this->enable_cache) {
     154                        $this->cache_translations = array ();
     155                        /* read all strings in the cache */
     156                        for ($i = 0; $i < $this->total; $i++) {
     157                                $this->STREAM->seekto($this->table_originals[$i * 2 + 2]);
     158                                $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]);
     159                                $this->STREAM->seekto($this->table_translations[$i * 2 + 2]);
     160                                $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]);
     161                                $this->cache_translations[$original] = $translation;
     162                        }
     163                }
     164        }
     165
     166        /**
     167         * Returns a string from the "originals" table
     168         *
     169         * @access private
     170         * @param int num Offset number of original string
     171         * @return string Requested string if found, otherwise ''
     172         */
     173        function get_original_string($num) {
     174                $length = $this->table_originals[$num * 2 + 1];
     175                $offset = $this->table_originals[$num * 2 + 2];
     176                if (! $length)
     177                        return '';
     178                $this->STREAM->seekto($offset);
     179                $data = $this->STREAM->read($length);
     180                return (string)$data;
     181        }
     182
     183        /**
     184         * Returns a string from the "translations" table
     185         *
     186         * @access private
     187         * @param int num Offset number of original string
     188         * @return string Requested string if found, otherwise ''
     189         */
     190        function get_translation_string($num) {
     191                $length = $this->table_translations[$num * 2 + 1];
     192                $offset = $this->table_translations[$num * 2 + 2];
     193                if (! $length)
     194                        return '';
     195                $this->STREAM->seekto($offset);
     196                $data = $this->STREAM->read($length);
     197                return (string)$data;
     198        }
     199
     200        /**
     201         * Binary search for string
     202         *
     203         * @access private
     204         * @param string string
     205         * @param int start (internally used in recursive function)
     206         * @param int end (internally used in recursive function)
     207         * @return int string number (offset in originals table)
     208         */
     209        function find_string($string, $start = -1, $end = -1) {
     210                if (($start == -1) or ($end == -1)) {
     211                        // find_string is called with only one parameter, set start end end
     212                        $start = 0;
     213                        $end = $this->total;
     214                }
     215                if (abs($start - $end) <= 1) {
     216                        // We're done, now we either found the string, or it doesn't exist
     217                        $txt = $this->get_original_string($start);
     218                        if ($string == $txt)
     219                                return $start;
     220                        else
     221                                return -1;
     222                } else if ($start > $end) {
     223                        // start > end -> turn around and start over
     224                        return $this->find_string($string, $end, $start);
     225                } else {
     226                        // Divide table in two parts
     227                        $half = (int)(($start + $end) / 2);
     228                        $cmp = strcmp($string, $this->get_original_string($half));
     229                        if ($cmp == 0)
     230                                // string is exactly in the middle => return it
     231                                return $half;
     232                        else if ($cmp < 0)
     233                                // The string is in the upper half
     234                                return $this->find_string($string, $start, $half);
     235                        else
     236                                // The string is in the lower half
     237                                return $this->find_string($string, $half, $end);
     238                }
     239        }
     240
     241        /**
     242         * Translates a string
     243         *
     244         * @access public
     245         * @param string string to be translated
     246         * @return string translated string (or original, if not found)
     247         */
     248        function translate($string) {
     249                if ($this->short_circuit)
     250                        return $string;
     251                $this->load_tables();
     252
     253                if ($this->enable_cache) {
     254                        // Caching enabled, get translated string from cache
     255                        if (array_key_exists($string, $this->cache_translations))
     256                                return $this->cache_translations[$string];
     257                        else
     258                                return $string;
     259                } else {
     260                        // Caching not enabled, try to find string
     261                        $num = $this->find_string($string);
     262                        if ($num == -1)
     263                                return $string;
     264                        else
     265                                return $this->get_translation_string($num);
     266                }
     267        }
     268
     269        /**
     270         * Get possible plural forms from MO header
     271         *
     272         * @access private
     273         * @return string plural form header
     274         */
     275        function get_plural_forms() {
     276                // lets assume message number 0 is header
     277                // this is true, right?
     278                $this->load_tables();
     279
     280                // cache header field for plural forms
     281                if (! is_string($this->pluralheader)) {
     282                        if ($this->enable_cache) {
     283                                $header = $this->cache_translations[""];
     284                        } else {
     285                                $header = $this->get_translation_string(0);
     286                        }
     287                        $header .= "\n"; //make sure our regex matches
     288                        if (eregi("plural-forms: ([^\n]*)\n", $header, $regs))
     289                                $expr = $regs[1];
     290                        else
     291                                $expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
     292
     293                        // add parentheses
     294                        // important since PHP's ternary evaluates from left to right
     295                        $expr.= ';';
     296                        $res= '';
     297                        $p= 0;
     298                        for ($i= 0; $i < strlen($expr); $i++) {
     299                                $ch= $expr[$i];
     300                                switch ($ch) {
     301                                        case '?':
     302                                                $res.= ' ? (';
     303                                                $p++;
     304                                                break;
     305                                        case ':':
     306                                                $res.= ') : (';
     307                                                break;
     308                                        case ';':
     309                                                $res.= str_repeat( ')', $p) . ';';
     310                                                $p= 0;
     311                                                break;
     312                                        default:
     313                                                $res.= $ch;
     314                                }
     315                        }
     316                        $this->pluralheader = $res;
     317                }
     318
     319                return $this->pluralheader;
     320        }
     321
     322        /**
     323         * Detects which plural form to take
     324         *
     325         * @access private
     326         * @param n count
     327         * @return int array index of the right plural form
     328         */
     329        function select_string($n) {
     330                if (is_null($this->select_string_function)) {
     331                        $string = $this->get_plural_forms();
     332                        if (preg_match("/nplurals\s*=\s*(\d+)\s*\;\s*plural\s*=\s*(.*?)\;+/", $string, $matches)) {
     333                                $nplurals = $matches[1];
     334                                $expression = $matches[2];
     335                                $expression = str_replace("n", '$n', $expression);
     336                        } else {
     337                                $nplurals = 2;
     338                                $expression = ' $n == 1 ? 0 : 1 ';
     339                        }
     340                        $func_body = "
     341                                \$plural = ($expression);
     342                                return (\$plural <= $nplurals)? \$plural : \$plural - 1;";
     343                        $this->select_string_function = create_function('$n', $func_body);
     344                }
     345                return call_user_func($this->select_string_function, $n);
     346        }
     347
     348        /**
     349         * Plural version of gettext
     350         *
     351         * @access public
     352         * @param string single
     353         * @param string plural
     354         * @param string number
     355         * @return translated plural form
     356         */
     357        function ngettext($single, $plural, $number) {
     358                if ($this->short_circuit) {
     359                        if ($number != 1)
     360                                return $plural;
     361                        else
     362                                return $single;
     363                }
     364
     365                // find out the appropriate form
     366                $select = $this->select_string($number);
     367
     368                // this should contains all strings separated by NULLs
     369                $key = $single.chr(0).$plural;
     370
     371
     372                if ($this->enable_cache) {
     373                        if (! array_key_exists($key, $this->cache_translations)) {
     374                                return ($number != 1) ? $plural : $single;
     375                        } else {
     376                                $result = $this->cache_translations[$key];
     377                                $list = explode(chr(0), $result);
     378                                return $list[$select];
     379                        }
     380                } else {
     381                        $num = $this->find_string($key);
     382                        if ($num == -1) {
     383                                return ($number != 1) ? $plural : $single;
     384                        } else {
     385                                $result = $this->get_translation_string($num);
     386                                $list = explode(chr(0), $result);
     387                                return $list[$select];
     388                        }
     389                }
     390        }
     391
    364392}
    365393
    366394?>