WordPress.org

Make WordPress Core

Changeset 11626


Ignore:
Timestamp:
06/23/2009 04:32:52 PM (12 years ago)
Author:
ryan
Message:

Merge latest pomo. Works around mbstring.func_overload. Props nbachiyski. fixes #10236 for trunk

Location:
trunk/wp-includes/pomo
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-includes/pomo/entry.php

    r10584 r11626  
    33 * Contains Translation_Entry class
    44 *
    5  * @version $Id: entry.php 13 2008-04-21 12:03:37Z nbachiyski $
     5 * @version $Id: entry.php 115 2009-05-11 18:56:15Z nbachiyski $
    66 * @package pomo
    77 * @subpackage entry
     
    4949        $object_varnames = array_keys(get_object_vars($this));
    5050        foreach ($args as $varname => $value) {
    51             if (in_array($varname, $object_varnames)) {
    52                 $this->$varname = $value;
    53             }
     51            $this->$varname = $value;
    5452        }
    5553        if (isset($args['plural'])) $this->is_plural = true;
  • trunk/wp-includes/pomo/mo.php

    r10810 r11626  
    33 * Class for working with MO files
    44 *
    5  * @version $Id: mo.php 33 2009-02-16 09:33:39Z nbachiyski $
     5 * @version $Id: mo.php 106 2009-04-23 19:48:22Z nbachiyski $
    66 * @package pomo
    77 * @subpackage mo
     
    1111require_once dirname(__FILE__) . '/streams.php';
    1212
    13 class MO extends Translations {
     13class MO extends Gettext_Translations {
    1414
    1515    var $_nplurals = 2;
    16 
    17     function set_header($header, $value) {
    18         parent::set_header($header, $value);
    19         if ('Plural-Forms' == $header)
    20             $this->_gettext_select_plural_form = $this->_make_gettext_select_plural_form($value);
    21     }
    2216
    2317    /**
     
    3327        return $this->import_from_reader($reader);
    3428    }
     29   
     30    function export_to_file($filename) {
     31        $fh = fopen($filename, 'wb');
     32        if ( !$fh ) return false;
     33        $entries = array_filter($this->entries, create_function('$e', 'return !empty($e->translations);'));
     34        ksort($entries);
     35        $magic = 0x950412de;
     36        $revision = 0;
     37        $total = count($entries) + 1; // all the headers are one entry
     38        $originals_lenghts_addr = 28;
     39        $translations_lenghts_addr = $originals_lenghts_addr + 8 * $total;
     40        $size_of_hash = 0;
     41        $hash_addr = $translations_lenghts_addr + 8 * $total;
     42        $current_addr = $hash_addr;
     43        fwrite($fh, pack('V*', $magic, $revision, $total, $originals_lenghts_addr,
     44            $translations_lenghts_addr, $size_of_hash, $hash_addr));
     45        fseek($fh, $originals_lenghts_addr);
     46       
     47        // headers' msgid is an empty string
     48        fwrite($fh, pack('VV', 0, $current_addr));
     49        $current_addr++;
     50        $originals_table = chr(0);
     51
     52        foreach($entries as $entry) {
     53            $originals_table .= $this->export_original($entry) . chr(0);
     54            $length = strlen($this->export_original($entry));
     55            fwrite($fh, pack('VV', $length, $current_addr));
     56            $current_addr += $length + 1; // account for the NULL byte after
     57        }
     58       
     59        $exported_headers = $this->export_headers();
     60        fwrite($fh, pack('VV', strlen($exported_headers), $current_addr));
     61        $current_addr += strlen($exported_headers) + 1;
     62        $translations_table = $exported_headers . chr(0);
     63       
     64        foreach($entries as $entry) {
     65            $translations_table .= $this->export_translations($entry) . chr(0);
     66            $length = strlen($this->export_translations($entry));
     67            fwrite($fh, pack('VV', $length, $current_addr));
     68            $current_addr += $length + 1;
     69        }
     70       
     71        fwrite($fh, $originals_table);
     72        fwrite($fh, $translations_table);
     73        fclose($fh);
     74    }
     75   
     76    function export_original($entry) {
     77        //TODO: warnings for control characters
     78        $exported = $entry->singular;
     79        if ($entry->is_plural) $exported .= chr(0).$entry->plural;
     80        if (!is_null($entry->context)) $exported = $entry->context . chr(4) . $exported;
     81        return $exported;
     82    }
     83   
     84    function export_translations($entry) {
     85        //TODO: warnings for control characters
     86        return implode(chr(0), $entry->translations);
     87    }
     88   
     89    function export_headers() {
     90        $exported = '';
     91        foreach($this->headers as $header => $value) {
     92            $exported.= "$header: $value\n";
     93        }
     94        return $exported;
     95    }
    3596
    3697    function get_byteorder($magic) {
     
    43104        // 0xde120495
    44105        $magic_big = ((int) - 569244523) && 0xFFFFFFFF;
    45 
     106       
    46107        if ($magic_little == $magic || $magic_little_64 == $magic) {
    47108            return 'little';
     
    64125        $total = $reader->readint32();
    65126        // get addresses of array of lenghts and offsets for original string and translations
    66         $originals_lo_addr = $reader->readint32();
    67         $translations_lo_addr = $reader->readint32();
     127        $originals_lenghts_addr = $reader->readint32();
     128        $translations_lenghts_addr = $reader->readint32();
    68129
    69         $reader->seekto($originals_lo_addr);
    70         $originals_lo = $reader->readint32array($total * 2); // each of
    71         $reader->seekto($translations_lo_addr);
    72         $translations_lo = $reader->readint32array($total * 2);
     130        $reader->seekto($originals_lenghts_addr);
     131        $originals_lenghts = $reader->readint32array($total * 2); // each of
     132        $reader->seekto($translations_lenghts_addr);
     133        $translations_lenghts = $reader->readint32array($total * 2);
    73134
    74135        $length = create_function('$i', 'return $i * 2 + 1;');
     
    76137
    77138        for ($i = 0; $i < $total; ++$i) {
    78             $reader->seekto($originals_lo[$offset($i)]);
    79             $original = $reader->read($originals_lo[$length($i)]);
    80             $reader->seekto($translations_lo[$offset($i)]);
    81             $translation = $reader->read($translations_lo[$length($i)]);
     139            $reader->seekto($originals_lenghts[$offset($i)]);
     140            $original = $reader->read($originals_lenghts[$length($i)]);
     141            $reader->seekto($translations_lenghts[$offset($i)]);
     142            $translation = $reader->read($translations_lenghts[$length($i)]);
    82143            if ('' == $original) {
    83144                $this->set_headers($this->make_headers($translation));
     
    87148        }
    88149        return true;
    89     }
    90 
    91     function make_headers($translation) {
    92         $headers = array();
    93         $lines = explode("\n", $translation);
    94         foreach($lines as $line) {
    95             $parts = explode(':', $line, 2);
    96             if (!isset($parts[1])) continue;
    97             $headers[trim($parts[0])] = trim($parts[1]);
    98         }
    99         return $headers;
    100150    }
    101151
  • trunk/wp-includes/pomo/po.php

    r10810 r11626  
    33 * Class for working with PO files
    44 *
    5  * @version $Id: po.php 33 2009-02-16 09:33:39Z nbachiyski $
     5 * @version $Id: po.php 123 2009-05-13 19:35:43Z nbachiyski $
    66 * @package pomo
    77 * @subpackage po
     
    1717 * Routines for working with PO files
    1818 */
    19 class PO extends Translations {
    20 
     19class PO extends Gettext_Translations {
     20   
    2121
    2222    /**
     
    7676    }
    7777
    78 
    7978    /**
    8079     * Formats a string in PO-style
     
    8887        $slash = '\\';
    8988        $newline = "\n";
    90         $tab = "\t";
    9189
    9290        $replaces = array(
    9391            "$slash"    => "$slash$slash",
    94             "$tab"      => '\t',
    9592            "$quote"    => "$slash$quote",
     93            "\t"        => '\t',
    9694        );
     95
    9796        $string = str_replace(array_keys($replaces), array_values($replaces), $string);
    9897
    99         $po = array();
    100         foreach (explode($newline, $string) as $line) {
    101             $po[] = wordwrap($line, PO_MAX_LINE_LEN - 2, " $quote$newline$quote");
    102         }
    103         $po = $quote.implode("${slash}n$quote$newline$quote", $po).$quote;
     98        $po = $quote.implode("${slash}n$quote$newline$quote", explode($newline, $string)).$quote;
    10499        // add empty string on first line for readbility
    105         if (false !== strpos($po, $newline)) {
     100        if (false !== strpos($string, $newline) &&
     101                (substr_count($string, $newline) > 1 || !($newline === substr($string, -strlen($newline))))) {
    106102            $po = "$quote$quote$newline$po";
    107103        }
     
    110106        return $po;
    111107    }
    112 
    113     /**
    114      * Inserts $with in the beginning of every new line of $string and
     108   
     109    /**
     110     * Gives back the original string from a PO-formatted string
     111     *
     112     * @static
     113     * @param string $string PO-formatted string
     114     * @return string enascaped string
     115     */
     116    function unpoify($string) {
     117        $escapes = array('t' => "\t", 'n' => "\n", '\\' => '\\');
     118        $lines = array_map('trim', explode("\n", $string));
     119        $lines = array_map(array('PO', 'trim_quotes'), $lines);
     120        $unpoified = '';
     121        $previous_is_backslash = false;
     122        foreach($lines as $line) {
     123            preg_match_all('/./u', $line, $chars);
     124            $chars = $chars[0];
     125            foreach($chars as $char) {
     126                if (!$previous_is_backslash) {
     127                    if ('\\' == $char)
     128                        $previous_is_backslash = true;
     129                    else
     130                        $unpoified .= $char;
     131                } else {
     132                    $previous_is_backslash = false;
     133                    $unpoified .= isset($escapes[$char])? $escapes[$char] : $char;
     134                }
     135            }
     136        }
     137        return $unpoified;
     138    }
     139
     140    /**
     141     * Inserts $with in the beginning of every new line of $string and
    115142     * returns the modified string
    116143     *
     
    158185        if (!empty($entry->extracted_comments)) $po[] = PO::comment_block($entry->extracted_comments, '.');
    159186        if (!empty($entry->references)) $po[] = PO::comment_block(implode(' ', $entry->references), ':');
    160         if (!empty($entry->flags)) $po[] = PO::comment_block(implode("\n", $entry->flags), ',');
     187        if (!empty($entry->flags)) $po[] = PO::comment_block(implode(", ", $entry->flags), ',');
    161188        if (!is_null($entry->context)) $po[] = 'msgctxt '.PO::poify($entry->context);
    162189        $po[] = 'msgid '.PO::poify($entry->singular);
     
    174201    }
    175202
     203    function import_from_file($filename) {
     204        $f = fopen($filename, 'r');
     205        if (!$f) return false;
     206        $lineno = 0;
     207        while (true) {
     208            $res = $this->read_entry($f, $lineno);
     209            if (!$res) break;
     210            if ($res['entry']->singular == '') {
     211                $this->set_headers($this->make_headers($res['entry']->translations[0]));
     212            } else {
     213                $this->add_entry($res['entry']);
     214            }
     215        }
     216        PO::read_line($f, 'clear');
     217        return $res !== false;
     218    }
     219   
     220    function read_entry($f, $lineno = 0) {
     221        $entry = new Translation_Entry();
     222        // where were we in the last step
     223        // can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural
     224        $context = '';
     225        $msgstr_index = 0;
     226        $is_final = create_function('$context', 'return $context == "msgstr" || $context == "msgstr_plural";');
     227        while (true) {
     228            $lineno++;
     229            $line = PO::read_line($f);
     230            if (!$line)  {
     231                if (feof($f)) {
     232                    if ($is_final($context))
     233                        break;
     234                    elseif (!$context) // we haven't read a line and eof came
     235                        return null;
     236                    else
     237                        return false;
     238                } else {
     239                    return false;
     240                }
     241            }
     242            if ($line == "\n") continue;
     243            $line = trim($line);
     244            if (preg_match('/^#/', $line, $m)) {
     245                // the comment is the start of a new entry
     246                if ($is_final($context)) {
     247                    PO::read_line($f, 'put-back');
     248                    $lineno--;
     249                    break;
     250                }
     251                // comments have to be at the beginning
     252                if ($context && $context != 'comment') {
     253                    return false;
     254                }
     255                // add comment
     256                $this->add_comment_to_entry($entry, $line);;
     257            } elseif (preg_match('/^msgctxt\s+(".*")/', $line, $m)) {
     258                if ($is_final($context)) {
     259                    PO::read_line($f, 'put-back');
     260                    $lineno--;
     261                    break;
     262                }
     263                if ($context && $context != 'comment') {
     264                    return false;
     265                }
     266                $context = 'msgctxt';
     267                $entry->context .= PO::unpoify($m[1]);
     268            } elseif (preg_match('/^msgid\s+(".*")/', $line, $m)) {
     269                if ($is_final($context)) {
     270                    PO::read_line($f, 'put-back');
     271                    $lineno--;
     272                    break;
     273                }
     274                if ($context && $context != 'msgctxt' && $context != 'comment') {
     275                    return false;
     276                }
     277                $context = 'msgid';
     278                $entry->singular .= PO::unpoify($m[1]);
     279            } elseif (preg_match('/^msgid_plural\s+(".*")/', $line, $m)) {
     280                if ($context != 'msgid') {
     281                    return false;
     282                }
     283                $context = 'msgid_plural';
     284                $entry->is_plural = true;
     285                $entry->plural .= PO::unpoify($m[1]);
     286            } elseif (preg_match('/^msgstr\s+(".*")/', $line, $m)) {
     287                if ($context != 'msgid') {
     288                    return false;
     289                }
     290                $context = 'msgstr';
     291                $entry->translations = array(PO::unpoify($m[1]));
     292            } elseif (preg_match('/^msgstr\[(\d+)\]\s+(".*")/', $line, $m)) {
     293                if ($context != 'msgid_plural' && $context != 'msgstr_plural') {
     294                    return false;
     295                }
     296                $context = 'msgstr_plural';
     297                $msgstr_index = $m[1];
     298                $entry->translations[$m[1]] = PO::unpoify($m[2]);
     299            } elseif (preg_match('/^".*"$/', $line)) {
     300                $unpoified = PO::unpoify($line);
     301                switch ($context) {
     302                    case 'msgid':
     303                        $entry->singular .= $unpoified; break;
     304                    case 'msgctxt':
     305                        $entry->context .= $unpoified; break;
     306                    case 'msgid_plural':
     307                        $entry->plural .= $unpoified; break;
     308                    case 'msgstr':
     309                        $entry->translations[0] .= $unpoified; break;
     310                    case 'msgstr_plural':
     311                        $entry->translations[$msgstr_index] .= $unpoified; break;
     312                    default:
     313                        return false;
     314                }
     315            } else {
     316                return false;
     317            }
     318        }
     319        if (array() == array_filter($entry->translations)) $entry->translations = array();
     320        return array('entry' => $entry, 'lineno' => $lineno);
     321    }
     322   
     323    function read_line($f, $action = 'read') {
     324        static $last_line = '';
     325        static $use_last_line = false;
     326        if ('clear' == $action) {
     327            $last_line = '';
     328            return true;
     329        }
     330        if ('put-back' == $action) {
     331            $use_last_line = true;
     332            return true;
     333        }
     334        $line = $use_last_line? $last_line : fgets($f);
     335        $last_line = $line;
     336        $use_last_line = false;
     337        return $line;
     338    }
     339   
     340    function add_comment_to_entry(&$entry, $po_comment_line) {
     341        $first_two = substr($po_comment_line, 0, 2);
     342        $comment = trim(substr($po_comment_line, 2));
     343        if ('#:' == $first_two) {
     344            $entry->references = array_merge($entry->references, preg_split('/\s+/', $comment));
     345        } elseif ('#.' == $first_two) {
     346            $entry->extracted_comments = trim($entry->extracted_comments . "\n" . $comment);
     347        } elseif ('#,' == $first_two) {
     348            $entry->flags = array_merge($entry->flags, preg_split('/,\s*/', $comment));
     349        } else {
     350            $entry->translator_comments = trim($entry->translator_comments . "\n" . $comment);
     351        }
     352    }
     353   
     354    function trim_quotes($s) {
     355        if ( substr($s, 0, 1) == '"') $s = substr($s, 1);
     356        if ( substr($s, -1, 1) == '"') $s = substr($s, 0, -1);
     357        return $s;
     358    }
    176359}
    177360?>
  • trunk/wp-includes/pomo/streams.php

    r10584 r11626  
    44 * Based on the classes from Danilo Segan <danilo@kvota.net>
    55 *
    6  * @version $Id: streams.php 33 2009-02-16 09:33:39Z nbachiyski $
     6 * @version $Id: streams.php 138 2009-06-23 13:22:09Z nbachiyski $
    77 * @package pomo
    88 * @subpackage streams
     
    1818  var $_str;
    1919
    20   function POMO_StringReader($str = '') {
    21     $this->_str = $str;
    22     $this->_pos = 0;
    23   }
     20    function POMO_StringReader($str = '') {
     21        $this->_str = $str;
     22        $this->_pos = 0;
     23        $this->is_overloaded = ((ini_get("mbstring.func_overload") & 2) != 0) && function_exists('mb_substr');
     24    }
    2425
    25   function read($bytes) {
    26     $data = substr($this->_str, $this->_pos, $bytes);
    27     $this->_pos += $bytes;
    28     if (strlen($this->_str)<$this->_pos)
    29       $this->_pos = strlen($this->_str);
     26    function _substr($string, $start, $length) {
     27        if ($this->is_overloaded) {
     28            return mb_substr($string,$start,$length,'ascii');
     29        } else {
     30            return substr($string,$start,$length);
     31        }
     32    }
     33   
     34    function _strlen($string) {
     35        if ($this->is_overloaded) {
     36            return mb_strlen($string,'ascii');
     37        } else {
     38            return strlen($string);
     39        }
     40    }
    3041
    31     return $data;
    32   }
     42    function read($bytes) {
     43        $data = $this->_substr($this->_str, $this->_pos, $bytes);
     44        $this->_pos += $bytes;
     45        if ($this->_strlen($this->_str) < $this->_pos) $this->_pos = $this->_strlen($this->_str);
     46        return $data;
     47    }
    3348
    34   function seekto($pos) {
    35     $this->_pos = $pos;
    36     if (strlen($this->_str)<$this->_pos)
    37       $this->_pos = strlen($this->_str);
    38     return $this->_pos;
    39   }
     49    function seekto($pos) {
     50        $this->_pos = $pos;
     51        if ($this->_strlen($this->_str) < $this->_pos) $this->_pos = $this->_strlen($this->_str);
     52        return $this->_pos;
     53    }
    4054
    41   function pos() {
    42     return $this->_pos;
    43   }
     55    function pos() {
     56        return $this->_pos;
     57    }
    4458
    45   function length() {
    46     return strlen($this->_str);
    47   }
     59    function length() {
     60        return $this->_strlen($this->_str);
     61    }
    4862
    4963}
     
    5468class POMO_CachedFileReader extends POMO_StringReader {
    5569    function POMO_CachedFileReader($filename) {
     70        parent::POMO_StringReader();
    5671        $this->_str = file_get_contents($filename);
    5772        if (false === $this->_str)
    5873            return false;
    59         $this->pos = 0;
     74        $this->_pos = 0;
    6075    }
    6176}
     
    97112    function readint32() {
    98113        $bytes = $this->read(4);
    99         if (4 != strlen($bytes))
     114        if (4 != $this->_strlen($bytes))
    100115            return false;
    101116        $endian_letter = ('big' == $this->endian)? 'N' : 'V';
     
    113128    function readint32array($count) {
    114129        $bytes = $this->read(4 * $count);
    115         if (4*$count != strlen($bytes))
     130        if (4*$count != $this->_strlen($bytes))
    116131            return false;
    117132        $endian_letter = ('big' == $this->endian)? 'N' : 'V';
  • trunk/wp-includes/pomo/translations.php

    r10810 r11626  
    33 * Class for a set of entries for translation and their associated headers
    44 *
    5  * @version $Id: translations.php 35 2009-02-16 12:54:57Z nbachiyski $
     5 * @version $Id: translations.php 114 2009-05-11 17:30:38Z nbachiyski $
    66 * @package pomo
    77 * @subpackage translations
     
    2020     * @return bool true on success, false if the entry doesn't have a key
    2121     */
    22     function add_entry(&$entry) {
     22    function add_entry($entry) {
     23        if (is_array($entry)) {
     24            $entry = new Translation_Entry($entry);
     25        }
    2326        $key = $entry->key();
    2427        if (false === $key) return false;
    25         $this->entries[$key] = &$entry;
     28        $this->entries[$key] = $entry;
    2629        return true;
    2730    }
     
    8891        if ($translated && 0 <= $index && $index < $total_plural_forms &&
    8992                is_array($translated->translations) &&
    90                 count($translated->translations) == $total_plural_forms)
     93                isset($translated->translations[$index]))
    9194            return $translated->translations[$index];
    9295        else
     
    9497    }
    9598
     99    /**
     100     * Merge $other in the current object.
     101     *
     102     * @param Object &$other Another Translation object, whose translations will be merged in this one
     103     * @return void
     104     **/
     105    function merge_with(&$other) {
     106        $this->entries = array_merge($this->entries, $other->entries);
     107    }
     108}
     109
     110class Gettext_Translations extends Translations {
    96111    /**
    97112     * The gettext implmentation of select_plural_form.
     
    131146     * Adds parantheses to the inner parts of ternary operators in
    132147     * plural expressions, because PHP evaluates ternary oerators from left to right
    133      *
     148     * 
    134149     * @param string $expression the expression without parentheses
    135150     * @return string the expression with parentheses added
     
    159174        return rtrim($res, ';');
    160175    }
     176   
     177    function make_headers($translation) {
     178        $headers = array();
     179        // sometimes \ns are used instead of real new lines
     180        $translation = str_replace('\n', "\n", $translation);
     181        $lines = explode("\n", $translation);
     182        foreach($lines as $line) {
     183            $parts = explode(':', $line, 2);
     184            if (!isset($parts[1])) continue;
     185            $headers[trim($parts[0])] = trim($parts[1]);
     186        }
     187        return $headers;
     188    }
    161189
    162     /**
    163      * Merge $other in the current object.
    164      *
    165      * @param Object &$other Another Translation object, whose translations will be merged in this one
    166      * @return void
    167      **/
    168     function merge_with(&$other) {
    169         $this->entries = array_merge($this->entries, $other->entries);
     190    function set_header($header, $value) {
     191        parent::set_header($header, $value);
     192        if ('Plural-Forms' == $header)
     193            $this->_gettext_select_plural_form = $this->_make_gettext_select_plural_form($value);
    170194    }
     195
     196   
    171197}
    172198
Note: See TracChangeset for help on using the changeset viewer.