devdaily home | apple | java | perl | unix | directory | blog

What this is

This file is included in the DevDaily.com "PHP Source Code Warehouse" project. The intent of this project is to help you "Learn PHP by Example" TM.

Other links

The source code

<?php
/*
   Copyright (c) 2003 Danilo Segan <danilo@kvota.net>.

   This file is part of PHP-gettext.

   PHP-gettext is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   PHP-gettext is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with PHP-gettext; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
 


  // For start, we only want to read the MO files

class gettext_reader {
//public:
  var $error = 0; // public variable that holds error code (0 if no error)
//private:
  var $BYTEORDER = 0;
  var $STREAM = NULL;
  var $short_circuit = false;

  function readint() {
    // Reads 4 byte value from $FD and puts it in int
    // $BYTEORDER specifies the byte order: 0 low endian, 1 big endian
    for ($i=0; $i<4; $i++) {
      $byte[$i]=ord($this->STREAM->read(1));
    }
    //print sprintf("pos: %d\n",$this->STREAM->currentpos());
    if ($this->BYTEORDER == 0) 
      return (int)(($byte[0]) | ($byte[1]<<8) | ($byte[2]<<16) | ($byte[3]<<24));
    else 
      return (int)(($byte[3]) | ($byte[2]<<8) | ($byte[1]<<16) | ($byte[0]<<24));
  }

  // constructor that requires StreamReader object
  function gettext_reader($Reader) {
    // If there isn't a StreamReader, turn on short circuit mode.
    if (! $Reader) {
        $this->short_circuit = true;
        return;
    }

    $MAGIC1 = (int)0x950412de;
    $MAGIC2 = (int)0xde120495;


    $this->STREAM = $Reader;
    $magic = $this->readint();
    if ($magic == $MAGIC1) {
      $this->BYTEORDER = 0;
    } elseif ($magic == $MAGIC2) {
      $this->BYTEORDER = 1;
    } else {
      $this->error = 1; // not MO file
      return false;
    }

    // FIXME: Do we care about revision? We should.
    $revision = $this->readint();

    $total = $this->readint();
    $originals = $this->readint();
    $translations = $this->readint();

    $this->total = $total;
    $this->originals = $originals;
    $this->translations = $translations;

  }

  function load_tables($translations=false) {
    // if tables are loaded do not load them again
    if (!is_array($this->ORIGINALS)) {
      $this->ORIGINALS = array();
      $this->STREAM->seekto($this->originals);
      for ($i=0; $i<$this->total; $i++) {
	$len = $this->readint();
	$ofs = $this->readint();
	$this->ORIGINALS[] = array($len,$ofs);
      }
    }

    // similar for translations
    if ($translations and !is_array($this->TRANSLATIONS)) {
      $this->TRANSLATIONS = array();
      $this->STREAM->seekto($this->translations);
      for ($i=0; $i<$this->total; $i++) {
	$len = $this->readint();
	$ofs = $this->readint();
	$this->TRANSLATIONS[] = array($len,$ofs);
      }
    }

  }

  function get_string_number($num) {
    // get a string with particular number
    // TODO: Add simple hashing [check array, add if not already there]
    $this->load_tables();
    $meta = $this->ORIGINALS[$num];
    $length = $meta[0];
    $offset = $meta[1];
    $this->STREAM->seekto($offset);
    $data = $this->STREAM->read($length);
    return (string)$data;
  }

  function get_translation_number($num) {
    // get a string with particular number
    // TODO: Add simple hashing [check array, add if not already there]
    $this->load_tables(true);
    $meta = $this->TRANSLATIONS[$num];
    $length = $meta[0];
    $offset = $meta[1];
    $this->STREAM->seekto($offset);
    $data = $this->STREAM->read($length);
    return (string)$data;
  }
  
  // binary search for string
  function find_string($string, $start,$end) {
    //print "start: $start, end: $end\n";
    if (abs($start-$end)<=1) {
      // we're done, if it's not it, bye bye
      $txt = $this->get_string_number($start);
      if ($string == $txt)
	return $start;
      else
	return -1;
    } elseif ($start>$end) {
      return $this->find_string($string,$end,$start);
    }  else {
      $half = (int)(($start+$end)/2);
      $tst = $this->get_string_number($half);
      $cmp = strcmp($string,$tst);
      if ($cmp == 0) 
	return $half;
      elseif ($cmp<0) 
	return $this->find_string($string,$start,$half);
      else
	return $this->find_string($string,$half,$end);
    }
  }

  function translate($string) {
    if ($this->short_circuit) {
        return $string;
    }

    $num = $this->find_string($string, 0, $this->total);
    if ($num == -1)
      return $string;
    else 
      return $this->get_translation_number($num);
  }

  function get_plural_forms() {
    // lets assume message number 0 is header
    // this is true, right?

    // cache header field for plural forms
    if (is_string($this->pluralheader)) 
      return $this->pluralheader;
    else {
      $header = $this->get_translation_number(0);

      if (eregi("plural-forms: (.*)\n",$header,$regs)) {
	$expr = $regs[1];
      } else {
	$expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
      }
      $this->pluralheader = $expr;
      return $expr;
    }
  }

  function select_string($n) {
    $string = $this->get_plural_forms();
    $string = str_replace('nplurals',"\$total",$string);
    $string = str_replace("n",$n,$string);
    $string = str_replace('plural',"\$plural",$string);

    $total = 0;
    $plural = 0;

    eval("$string");
    if ($plural>=$total) $plural = 0;
    return $plural;
  }

  function ngettext($single, $plural, $number) {
    if ($this->short_circuit) {
      if ($number != 1) return $plural;
      else return $single;
    }

    // find out the appropriate form
    $select = $this->select_string($number); 
    

    // this should contains all strings separated by NULLs
    $result = $this->find_string($single.chr(0).$plural,0,$this->total);
    if ($result == -1) {
      if ($number != 1) return $plural;
      else return $single;
    } else {
      $result = $this->get_translation_number($result);
    
      // lets try to parse all the NUL staff
      //$result = "proba0".chr(0)."proba1".chr(0)."proba2";
      $list = explode (chr(0), $result);
      return $list[$select];
    }
  }

}


?>




Copyright 1998-2008 Alvin Alexander
All Rights Reserved.
 
devdaily.com is based in louisville, kentucky, and this web site is hosted by godaddy.com