Changeset 2394
- Timestamp:
- 02/28/2005 04:31:01 PM (21 years ago)
- Location:
- trunk/wp-includes
- Files:
-
- 3 edited
-
gettext.php (modified) (7 diffs)
-
streams.php (modified) (3 diffs)
-
wp-l10n.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-includes/gettext.php
r1817 r2394 2 2 /* 3 3 Copyright (c) 2003 Danilo Segan <danilo@kvota.net>. 4 4 Copyright (c) 2005 Nico Kaiser <nico@siriux.net> 5 5 6 This file is part of PHP-gettext. 6 7 … … 21 22 */ 22 23 23 24 25 // For start, we only want to read the MO files 26 24 /** 25 * Provides a simple gettext replacement that works independently from 26 * the system's gettext abilities. 27 * It can read MO files and use them for translating strings. 28 * The files are passed to gettext_reader as a Stream (see streams.php) 29 * 30 * This version has the ability to cache all strings and translations to 31 * speed up the string lookup. 32 * While the cache is enabled by default, it can be switched off with the 33 * second parameter in the constructor (e.g. whenusing very large MO files 34 * that you don't want to keep in memory) 35 */ 27 36 class gettext_reader { 28 //public: 29 var $error = 0; // public variable that holds error code (0 if no error) 30 //private: 31 var $BYTEORDER = 0; 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 32 42 var $STREAM = NULL; 33 43 var $short_circuit = false; 34 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 52 53 54 /* Methods */ 55 56 57 /** 58 * Reads a 32bit Integer from the Stream 59 * 60 * @access private 61 * @return Integer from the Stream 62 */ 35 63 function readint() { 36 // Reads 4 byte value from $FD and puts it in int 37 // $BYTEORDER specifies the byte order: 0 low endian, 1 big endian 38 for ($i=0; $i<4; $i++) { 39 $byte[$i]=ord($this->STREAM->read(1)); 40 } 41 //print sprintf("pos: %d\n",$this->STREAM->currentpos()); 42 if ($this->BYTEORDER == 0) 43 return (int)(($byte[0]) | ($byte[1]<<8) | ($byte[2]<<16) | ($byte[3]<<24)); 44 else 45 return (int)(($byte[3]) | ($byte[2]<<8) | ($byte[1]<<16) | ($byte[0]<<24)); 46 } 47 48 // constructor that requires StreamReader object 49 function gettext_reader($Reader) { 64 if ($this->BYTEORDER == 0) { 65 // low endian 66 return array_shift(unpack('V', $this->STREAM->read(4))); 67 } else { 68 // big endian 69 return array_shift(unpack('N', $this->STREAM->read(4))); 70 } 71 } 72 73 /** 74 * Reads an array of Integers from the Stream 75 * 76 * @param int count How many elements should be read 77 * @return Array of Integers 78 */ 79 function readintarray($count) { 80 if ($this->BYTEORDER == 0) { 81 // low endian 82 return unpack('V'.$count, $this->STREAM->read(4 * $count)); 83 } else { 84 // big endian 85 return unpack('N'.$count, $this->STREAM->read(4 * $count)); 86 } 87 } 88 89 /** 90 * Constructor 91 * 92 * @param object Reader the StreamReader object 93 * @param boolean enable_cache Enable or disable caching of strings (default on) 94 */ 95 function gettext_reader($Reader, $enable_cache = true) { 50 96 // If there isn't a StreamReader, turn on short circuit mode. 51 97 if (! $Reader) { 52 $this->short_circuit = true; 53 return; 54 } 55 56 // $MAGIC1 = (int)0x950412de; //bug in PHP 5 57 $MAGIC1 = (int) - 1794895138; 58 // $MAGIC2 = (int)0xde120495; //bug 59 $MAGIC2 = (int) - 569244523; 60 98 $this->short_circuit = true; 99 return; 100 } 101 102 // Caching can be turned off 103 $this->enable_cache = $enable_cache; 104 105 // $MAGIC1 = (int)0x950412de; //bug in PHP 5 106 $MAGIC1 = (int) - 1794895138; 107 // $MAGIC2 = (int)0xde120495; //bug 108 $MAGIC2 = (int) - 569244523; 61 109 62 110 $this->STREAM = $Reader; … … 70 118 return false; 71 119 } 72 120 73 121 // FIXME: Do we care about revision? We should. 74 122 $revision = $this->readint(); 75 76 $total = $this->readint(); 77 $originals = $this->readint(); 78 $translations = $this->readint(); 79 80 $this->total = $total; 81 $this->originals = $originals; 82 $this->translations = $translations; 83 84 } 85 86 function load_tables($translations=false) { 87 // if tables are loaded do not load them again 88 if (!is_array($this->ORIGINALS)) { 89 $this->ORIGINALS = array(); 90 $this->STREAM->seekto($this->originals); 91 for ($i=0; $i<$this->total; $i++) { 92 $len = $this->readint(); 93 $ofs = $this->readint(); 94 $this->ORIGINALS[] = array($len,$ofs); 95 } 96 } 97 98 // similar for translations 99 if ($translations and !is_array($this->TRANSLATIONS)) { 100 $this->TRANSLATIONS = array(); 101 $this->STREAM->seekto($this->translations); 102 for ($i=0; $i<$this->total; $i++) { 103 $len = $this->readint(); 104 $ofs = $this->readint(); 105 $this->TRANSLATIONS[] = array($len,$ofs); 106 } 107 } 108 109 } 110 111 function get_string_number($num) { 112 // get a string with particular number 113 // TODO: Add simple hashing [check array, add if not already there] 114 $this->load_tables(); 115 $meta = $this->ORIGINALS[$num]; 116 $length = $meta[0]; 117 $offset = $meta[1]; 118 if (! $length) { 119 return ''; 120 } 123 124 $this->total = $this->readint(); 125 $this->originals = $this->readint(); 126 $this->translations = $this->readint(); 127 } 128 129 /** 130 * Loads the translation tables from the MO file into the cache 131 * If caching is enabled, also loads all strings into a cache 132 * to speed up translation lookups 133 * 134 * @access private 135 */ 136 function load_tables() { 137 if (is_array($this->cache_translations) && 138 is_array($this->table_originals) && 139 is_array($this->table_translations)) 140 return; 141 142 /* get original and translations tables */ 143 $this->STREAM->seekto($this->originals); 144 $this->table_originals = $this->readintarray($this->total * 2); 145 $this->STREAM->seekto($this->translations); 146 $this->table_translations = $this->readintarray($this->total * 2); 147 148 if ($this->enable_cache) { 149 $this->cache_translations = array (); 150 /* read all strings in the cache */ 151 for ($i = 0; $i < $this->total; $i++) { 152 $this->STREAM->seekto($this->table_originals[$i * 2 + 2]); 153 $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]); 154 $this->STREAM->seekto($this->table_translations[$i * 2 + 2]); 155 $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]); 156 $this->cache_translations[$original] = $translation; 157 } 158 } 159 } 160 161 /** 162 * Returns a string from the "originals" table 163 * 164 * @access private 165 * @param int num Offset number of original string 166 * @return string Requested string if found, otherwise '' 167 */ 168 function get_original_string($num) { 169 $length = $this->table_originals[$num * 2 + 1]; 170 $offset = $this->table_originals[$num * 2 + 2]; 171 if (! $length) 172 return ''; 121 173 $this->STREAM->seekto($offset); 122 174 $data = $this->STREAM->read($length); 123 175 return (string)$data; 124 176 } 125 126 function get_translation_number($num) { 127 // get a string with particular number 128 // TODO: Add simple hashing [check array, add if not already there] 129 $this->load_tables(true); 130 $meta = $this->TRANSLATIONS[$num]; 131 $length = $meta[0]; 132 $offset = $meta[1]; 177 178 /** 179 * Returns a string from the "translations" table 180 * 181 * @access private 182 * @param int num Offset number of original string 183 * @return string Requested string if found, otherwise '' 184 */ 185 function get_translation_string($num) { 186 $length = $this->table_translations[$num * 2 + 1]; 187 $offset = $this->table_translations[$num * 2 + 2]; 188 if (! $length) 189 return ''; 133 190 $this->STREAM->seekto($offset); 134 191 $data = $this->STREAM->read($length); … … 136 193 } 137 194 138 // binary search for string 139 function find_string($string, $start,$end) { 140 //print "start: $start, end: $end\n"; 141 if (abs($start-$end)<=1) { 142 // we're done, if it's not it, bye bye 143 $txt = $this->get_string_number($start); 195 /** 196 * Binary search for string 197 * 198 * @access private 199 * @param string string 200 * @param int start (internally used in recursive function) 201 * @param int end (internally used in recursive function) 202 * @return int string number (offset in originals table) 203 */ 204 function find_string($string, $start = -1, $end = -1) { 205 if (($start == -1) or ($end == -1)) { 206 // find_string is called with only one parameter, set start end end 207 $start = 0; 208 $end = $this->total; 209 } 210 if (abs($start - $end) <= 1) { 211 // We're done, now we either found the string, or it doesn't exist 212 $txt = $this->get_original_string($start); 144 213 if ($string == $txt) 145 return $start; 146 else 147 return -1; 148 } elseif ($start>$end) { 149 return $this->find_string($string,$end,$start); 150 } else { 151 $half = (int)(($start+$end)/2); 152 $tst = $this->get_string_number($half); 153 $cmp = strcmp($string,$tst); 154 if ($cmp == 0) 155 return $half; 156 elseif ($cmp<0) 157 return $this->find_string($string,$start,$half); 158 else 159 return $this->find_string($string,$half,$end); 160 } 161 } 162 214 return $start; 215 else 216 return -1; 217 } else if ($start > $end) { 218 // start > end -> turn around and start over 219 return $this->find_string($string, $end, $start); 220 } else { 221 // Divide table in two parts 222 $half = (int)(($start + $end) / 2); 223 $cmp = strcmp($string, $this->get_original_string($half)); 224 if ($cmp == 0) 225 // string is exactly in the middle => return it 226 return $half; 227 else if ($cmp < 0) 228 // The string is in the upper half 229 return $this->find_string($string, $start, $half); 230 else 231 // The string is in the lower half 232 return $this->find_string($string, $half, $end); 233 } 234 } 235 236 /** 237 * Translates a string 238 * 239 * @access public 240 * @param string string to be translated 241 * @return string translated string (or original, if not found) 242 */ 163 243 function translate($string) { 164 if ($this->short_circuit) { 244 if ($this->short_circuit) 245 return $string; 246 $this->load_tables(); 247 248 if ($this->enable_cache) { 249 // Caching enabled, get translated string from cache 250 if (array_key_exists($string, $this->cache_translations)) 251 return $this->cache_translations[$string]; 252 else 165 253 return $string; 166 } 167 168 $num = $this->find_string($string, 0, $this->total); 169 if ($num == -1) 170 return $string; 171 else 172 return $this->get_translation_number($num); 173 } 174 254 } else { 255 // Caching not enabled, try to find string 256 $num = $this->find_string($string); 257 if ($num == -1) 258 return $string; 259 else 260 return $this->get_translation_string($num); 261 } 262 } 263 264 /** 265 * Get possible plural forms from MO header 266 * 267 * @access private 268 * @return string plural form header 269 */ 175 270 function get_plural_forms() { 176 // lets assume message number 0 is header 271 // lets assume message number 0 is header 177 272 // this is true, right? 178 273 $this->load_tables(); 274 179 275 // cache header field for plural forms 180 if ( is_string($this->pluralheader))181 return $this->pluralheader;182 else {183 $header = $this->get_translation_number(0);184 185 if (eregi("plural-forms: (.*)\n",$header,$regs)) {186 $expr = $regs[1]; 187 } else {188 $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; 189 }276 if (! is_string($this->pluralheader)) { 277 if ($this->enable_cache) { 278 $header = $this->cache_translations[""]; 279 } else { 280 $header = $this->get_translation_string(0); 281 } 282 if (eregi("plural-forms: (.*)\n", $header, $regs)) 283 $expr = $regs[1]; 284 else 285 $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; 190 286 $this->pluralheader = $expr; 191 return $expr; 192 } 193 } 194 287 } 288 return $this->pluralheader; 289 } 290 291 /** 292 * Detects which plural form to take 293 * 294 * @access private 295 * @param n count 296 * @return int array index of the right plural form 297 */ 195 298 function select_string($n) { 196 299 $string = $this->get_plural_forms(); … … 198 301 $string = str_replace("n",$n,$string); 199 302 $string = str_replace('plural',"\$plural",$string); 200 303 201 304 $total = 0; 202 305 $plural = 0; 203 306 204 307 eval("$string"); 205 if ($plural >=$total) $plural = 0;308 if ($plural >= $total) $plural = 0; 206 309 return $plural; 207 310 } 208 311 312 /** 313 * Plural version of gettext 314 * 315 * @access public 316 * @param string single 317 * @param string plural 318 * @param string number 319 * @return translated plural form 320 */ 209 321 function ngettext($single, $plural, $number) { 210 322 if ($this->short_circuit) { 211 if ($number != 1) return $plural; 212 else return $single; 323 if ($number != 1) 324 return $plural; 325 else 326 return $single; 213 327 } 214 328 … … 216 330 $select = $this->select_string($number); 217 331 218 219 332 // this should contains all strings separated by NULLs 220 $result = $this->find_string($single.chr(0).$plural,0,$this->total); 221 if ($result == -1) { 222 if ($number != 1) return $plural; 223 else return $single; 333 $key = $single.chr(0).$plural; 334 335 336 if ($this->enable_cache) { 337 if (! array_key_exists($key, $this->cache_translations)) { 338 return ($number != 1) ? $plural : $single; 339 } else { 340 $result = $this->cache_translations[$key]; 341 $list = explode(chr(0), $result); 342 return $list[$select]; 343 } 224 344 } else { 225 $result = $this->get_translation_number($result); 226 227 // lets try to parse all the NUL staff 228 //$result = "proba0".chr(0)."proba1".chr(0)."proba2"; 229 $list = explode (chr(0), $result); 230 return $list[$select]; 345 $num = $this->find_string($key); 346 if ($num == -1) { 347 return ($number != 1) ? $plural : $single; 348 } else { 349 $result = $this->get_translation_string($num); 350 $list = explode(chr(0), $result); 351 return $list[$select]; 352 } 231 353 } 232 354 } … … 234 356 } 235 357 236 237 358 ?> -
trunk/wp-includes/streams.php
r1080 r2394 1 1 <?php 2 2 /* 3 Copyright (c) 2003 Danilo Segan <danilo@kvota.net>.3 Copyright (c) 2003, 2005 Danilo Segan <danilo@kvota.net>. 4 4 5 5 This file is part of PHP-gettext. … … 104 104 105 105 function read($bytes) { 106 fseek($this->_fd, $this->_pos); 107 $data = fread($this->_fd, $bytes); 108 $this->_pos = ftell($this->_fd); 109 110 return $data; 106 if ($bytes) { 107 fseek($this->_fd, $this->_pos); 108 $data = fread($this->_fd, $bytes); 109 $this->_pos = ftell($this->_fd); 110 111 return $data; 112 } else return ''; 111 113 } 112 114 … … 131 133 } 132 134 135 // Preloads entire file in memory first, then creates a StringReader 136 // over it (it assumes knowledge of StringReader internals) 137 class CachedFileReader extends StringReader { 138 function CachedFileReader($filename) { 139 if (file_exists($filename)) { 140 141 $length=filesize($filename); 142 $fd = fopen($filename,'rb'); 143 144 if (!$fd) { 145 $this->error = 3; // Cannot read file, probably permissions 146 return false; 147 } 148 $this->_str = fread($fd, $length); 149 fclose($fd); 150 151 } else { 152 $this->error = 2; // File doesn't exist 153 return false; 154 } 155 } 156 } 157 158 133 159 ?> -
trunk/wp-includes/wp-l10n.php
r2238 r2394 67 67 68 68 if ( is_readable($mofile)) { 69 $input = new FileReader($mofile);69 $input = new CachedFileReader($mofile); 70 70 } else { 71 71 return;
Note: See TracChangeset
for help on using the changeset viewer.