Source for file phpgettext.catalog.php

Documentation is available at phpgettext.catalog.php

  1. <?php
  2. /**
  3.  * @version        0.9
  4.  * @author      Carlos Souza
  5.  * @copyright   Copyright (c) 2005 Carlos Souza <csouza@web-sense.net>
  6.  * @package     PHPGettext
  7.  * @license        MIT License (http://www.opensource.org/licenses/mit-license.php)
  8.  * @link        http://phpgettext.web-sense.net
  9.  */
  10. defined'_VALID_MOS' or die'Direct Access to this location is not allowed.' );
  11.  
  12. define('_MODE_MO_''mo');
  13. define('_MODE_PO_''po');
  14. define('_MODE_POT_','pot');
  15. define('_MODE_GLO_','go');
  16.  
  17. require_once(dirname(__FILE__).'/phpgettext.message.php');
  18. {
  19.     var $mode = _MODE_MO_;
  20.     var $name = '';
  21.     var $path = '';
  22.     var $lang = 'en';
  23.     var $charset = 'utf-8';
  24.     var $category = 'LC_MESSAGES';
  25.     var $comments = array();
  26.     var $headers = array();
  27.     var $strings = array();
  28.     function PHPGettext_Catalog($name$path)
  29.     {
  30.         $this->name = trim($name);
  31.         $this->path = trim($path);
  32.     }
  33.  
  34.     function load()
  35.     {
  36.         switch ($this->mode)
  37.         {
  38.             case _MODE_MO_:
  39.             return $this->_readMO();
  40.             break;
  41.             case _MODE_PO_:
  42.             return $this->_readPO();
  43.             break;
  44.             case _MODE_POT_:
  45.             return $this->_readPO();            
  46.             break;
  47.             case _MODE_GLO_:
  48.             $this->mode = _MODE_PO_;
  49.             $this->lang = 'glossary';
  50.             return $this->_readPO();            
  51.             break;
  52.             default:
  53.             trigger_error('Unrecognized mode in '.__CLASS__.'->'.__FUNCTION__E_USER_ERROR);
  54.             return false;
  55.             break;
  56.         }
  57.     }
  58.  
  59.     function save()
  60.     {
  61.  
  62.         $langdir $this->path.DIRECTORY_SEPARATOR.$this->lang;
  63.         if (!is_dir($langdir)) {
  64.             $this->createdir($langdir.DIRECTORY_SEPARATOR.$this->category);
  65.         }
  66.  
  67.         switch ($this->mode)
  68.         {
  69.             case _MODE_MO_:
  70.             return $this->_writeMO();
  71.             break;
  72.             case _MODE_PO_:
  73.             $this->setRevisionDate();
  74.             return $this->_writePO();
  75.             case _MODE_POT_:
  76.             return $this->_writePO();
  77.             break;
  78.             case _MODE_GLO_:
  79.             $this->mode = _MODE_PO_;
  80.             $this->lang = 'glossary';
  81.             return $this->_writePO();
  82.             break;
  83.             default:
  84.             trigger_error('Unrecognized mode in '.__CLASS__.'->'.__FUNCTION__E_USER_ERROR);
  85.             return false;
  86.             break;
  87.         }
  88.     }
  89.  
  90.  
  91.     function filename()
  92.     {
  93.         $return $this->path.DIRECTORY_SEPARATOR;
  94.         $return .= !empty($this->lang$this->lang.DIRECTORY_SEPARATOR '';
  95.         $return .= (!empty($this->category&& $this->mode == _MODE_MO_$this->category.DIRECTORY_SEPARATOR '';
  96.         $return .= $this->name.'.'.$this->mode;
  97.         return  $return;
  98.     }
  99.  
  100.     function createdir($path)
  101.     {
  102.         if (!file_exists($path))
  103.         {
  104.             // The directory doesn't exist.  Recurse, passing in the parent
  105.             // directory so that it gets created.
  106.             $this->createdir(dirname($path));
  107.             mkdir($path0777);
  108.         }
  109.     }
  110.  
  111.     function setRevisionDate()
  112.     {
  113.         $this->headers['PO-Revision-Date'date('Y-m-d G:iO');
  114.     }
  115.     
  116.     function setComments($comments)
  117.     {        
  118.         $comments   trim($comments);
  119.         if (is_string($comments)) {
  120.             $comments explode("\n"$comments);
  121.             if (is_array($comments)) {
  122.                 foreach ($comments as $comment)   {
  123.                     if (strpos($comment'#'== 1)  {
  124.                         $this->comments .= $comment."\n";
  125.                     }
  126.                 }
  127.             }
  128.         }
  129.     }
  130.     
  131.     function setHeaders($headers)
  132.     {
  133.         if (!is_array($headers)) {
  134.             return false;
  135.         }
  136.         foreach ($headers as $key => $value{
  137.             $this->headers[$key$value;
  138.         }
  139.     }
  140.  
  141.     function setproperty($property$value null{
  142.         $return $this->$property;
  143.         $this->$property $value;
  144.         return $return;
  145.     }
  146.     function getproperty($property{
  147.         return $this->$property;
  148.     }
  149.  
  150.     function addentry($msgid$msgid_plural=null$msgstr=null$comments=array())
  151.     {
  152.         $entry =  new PHPGettext_Message($msgid$msgid_plural);
  153.         if (!is_null($msgstr)) $entry->setmsgstr($msgstr);
  154.         if (!empty($comments)) $entry->setcomments($comments);
  155.         $this->strings[$msgid$entry;
  156.     }
  157.  
  158.     function translate(&$translations)
  159.     {
  160.         $n count($this->strings);
  161.         for ($i=0;$i<$n;$i++){
  162.             if (!empty($translations[$this->strings[$i]->msgid])){
  163.                $this->strings[$i]->setmsgstr ($translations[$this->strings[$i]->msgid]);
  164.             }
  165.         }
  166.     }
  167.  
  168.     function merge(&$glossary)
  169.     {
  170.         foreach ($this->strings as $msgid => $string){
  171.               if (!$glossary[$string->msgid]){
  172.                   $glossary[$string->msgid$string;
  173.               }else{
  174.                   $glossary[$string->msgid]->comments array_merge($glossary[$string->msgid]->comments,$string->comments);
  175.                   $glossary[$string->msgid]->comments array_unique($glossary[$string->msgid]->comments);
  176.               }
  177.         }
  178.         $n count($glossary);
  179.         unset($this->strings);
  180.         foreach ($glossary as $msgid => $string){
  181.            $this->addentry($msgid$string->msgid_plural$string->msgstr$string->comments);
  182.         }
  183.     }
  184.  
  185.     function _writePO()
  186.     {
  187.         $file $this->filename();
  188.  
  189.         // open PO file
  190.         if (!is_resource($res @fopen($file'w'))) {
  191.             trigger_error("Cannot create '$file'. "E_USER_WARNING);
  192.         }
  193.         // lock PO file exclusively
  194.         if (!@flock($resLOCK_EX)) {
  195.             @fclose($res);
  196.             trigger_error("Cannot lock '$file'. "E_USER_WARNING);
  197.         }
  198.         // write comments
  199.         if (count($this->comments > 0)) {
  200.             foreach ($this->comments as $line{
  201.                 fwrite($restrim($line)."\n");
  202.             }
  203.         }
  204.         // write meta info
  205.         if (count($this->headers > 0)) {
  206.             $header 'msgid ""' "\nmsgstr " '""' "\n";
  207.             foreach ($this->headers as $k => $v{
  208.                 $header .= '"' $k ': ' $v '\n"' "\n";
  209.             }
  210.             fwrite($res$header "\n");
  211.         }
  212.  
  213.         // write strings
  214.         if (count($this->strings > 0)) {
  215.             foreach ($this->strings as $string{
  216.                 fwrite($res$string->toString());
  217.             }
  218.         }
  219.         //done
  220.         @flock($resLOCK_UN);
  221.         @fclose($res);
  222.         return true;
  223.     }
  224.  
  225.     function _readPO()
  226.     {
  227.         $file $this->filename();
  228.         if (!is_readable($file)) {
  229.             trigger_error('Gettext Catalog '.$this->filename().' not found'E_USER_WARNING);
  230.             return false;
  231.         }
  232.         // load file
  233.         if (!$data @file($file)) {
  234.             trigger_error('Gettext Catalog '.$this->filename().' not found'E_USER_WARNING);
  235.             return false;
  236.         }
  237.         $count      0;
  238.         $comments   '';
  239.         $is_fuzzy   false;
  240.         $nplural    false;
  241.  
  242.         // get all strings
  243.         foreach ($data as $line{
  244.             // comments
  245.             if (strncmp($line"#"1== 0{
  246.                 if ($count 1{
  247.                     $this->comments[$line;
  248.                 else {
  249.                     if (strncmp($line"#,"2== && preg_match('/fuzzy/'$line)) {
  250.                         unset($line);
  251.                         $is_fuzzy true;
  252.                     }
  253.                     if (isset($line)) {
  254.                         $comments[$line;
  255.                     }
  256.                 }
  257.             }// msgid
  258.             elseif (preg_match('/^msgid\s*"(.*)"\s*|^msgid_plural\s*"(.*)"\s*/s'$line$matches)) {
  259.                 if (preg_match('/^msgid_plural\s*/s'$line$arr)) {
  260.                     $nplural true;
  261.                     $strings[$count]['msgid_plural'$matches[2];
  262.                 else {
  263.                     $count++;
  264.                     $strings[$count]['comments'= isset($comments$comments '';
  265.                     $strings[$count]['msgid']    '';
  266.                     $strings[$count]['msgid_plural']    '';
  267.                     $strings[$count]['msgstr']   '';
  268.                     $strings[$count]['is_fuzzy'$is_fuzzy;
  269.                     if (!empty($matches[1])) {
  270.                         $strings[$count]['msgid'$matches[1];
  271.                     }
  272.                     unset($msgstr);
  273.                     unset($comments);
  274.                     $nplural  false;
  275.                     $is_fuzzy false;
  276.  
  277.                 }
  278.             // msgstr
  279.             elseif  (preg_match('/^msgstr\s*"(.*)"\s*|^msgstr\[[0-9]\]\s*"(.*)"\s*/s'$line$matches)) {
  280.                 $msgstr true;
  281.                 if ($nplural{
  282.                     $strings[$count]['msgstr'][= isset($matches[2]$matches[2'';
  283.                 else {
  284.                     $strings[$count]['msgstr'$matches[1];
  285.                 }
  286.             // multiline msgid or msgstr
  287.             elseif (preg_match('/^"(.*)"\s*$/s'$line$matches)) {
  288.                 // headers
  289.                 if (isset($msgstr&& $count == 1{
  290.                     list($key$valueexplode(':'$matches[1]2);
  291.                     $this->headers[$key$value;
  292.                 }
  293.                 elseif (isset($msgstr&& $count 1{
  294.                     $strings[$count]['msgstr'.= $matches[1];
  295.                 }
  296.                 else // msgid
  297.                     $strings[$count]['msgid']  .= $matches[1];
  298.                 }
  299.             }
  300.         }
  301.  
  302.         // load the strings
  303.         array_shift($strings);
  304.         for ($a=0$a count($strings)$a++{
  305.             $this->strings[$anew PHPGettext_Message($strings[$a]['msgid']$strings[$a]['msgid_plural']);
  306.             $this->strings[$a]->setmsgstr($strings[$a]['msgstr']);
  307.             $this->strings[$a]->setfuzzy($strings[$a]['is_fuzzy']);
  308.             $this->strings[$a]->setcomments($strings[$a]['comments']);
  309.         }
  310.         return true;
  311.     }
  312.  
  313.     function _writeMO()
  314.     {
  315.         $file $this->filename();
  316.  
  317.         // open MO file
  318.         if (!is_resource($res @fopen($file'w'))) {
  319.             trigger_error("Cannot create '$file'. "E_USER_WARNING);
  320.         }
  321.         // lock MO file exclusively
  322.         if (!@flock($resLOCK_EX)) {
  323.             @fclose($res);
  324.             trigger_error("Cannot lock '$file'. "E_USER_WARNING);
  325.         }
  326.  
  327.         // get the headers
  328.         $headers "";
  329.         foreach ($this->headers as $key => $val{
  330.             $headers .= $key ': ' $val "\n";
  331.         }
  332.         $strings[array('msgid' => ""'msgstr' => $headers);
  333.  
  334.         // don't write fuzzy entries
  335.         foreach ($this->strings as $message{
  336.             if (!$message->is_fuzzy{
  337.                 $strings[array('msgid' => $message->msgid,'msgid_plural' => $message->msgid_plural'msgstr' => $message->msgstr);
  338.             }
  339.  
  340.         }
  341.  
  342.         $count count($strings);
  343.         fwrite($respack('L'(int) 0x950412de));  // magic number
  344.         fwrite($respack('L'0));                 // revision  0
  345.         fwrite($respack('L'$count));            // N - number of strings
  346.         $offset 28;
  347.         fwrite($respack('L'$offset));           // O - offset of table with original strings
  348.         $offset += ($count 8);
  349.         fwrite($respack('L'$offset));           // T - offset of table with translation strings
  350.         fwrite($respack('L'0));                 // S - size of hashing table (set to 0 to omit the table)
  351.         $offset += ($count 8);
  352.         fwrite($respack('L'$offset));           // H - offset of hashing table
  353.  
  354.         // offsets for original strings
  355.         for ($a=0$a<$count$a++{
  356.             if (isset($strings[$a]['msgid_plural'])) // plurals
  357.                 $strings[$a]['msgid'$strings[$a]['msgid'."\0".$strings[$a]['msgid_plural'];
  358.             }
  359.             $len strlen($strings[$a]['msgid']);
  360.             fwrite($respack('L'$len));
  361.             fwrite($respack('L'$offset));
  362.             $offset += $len 1;
  363.         }
  364.  
  365.         // offsets for translated strings
  366.         for ($a=0$a<$count$a++{
  367.             if (is_array($strings[$a]['msgstr'])) // plurals
  368.                 $strings[$a]['msgstr'implode("\0"$strings[$a]['msgstr']);
  369.             }
  370.             $len strlen($strings[$a]['msgstr']);
  371.             fwrite($respack('L'$len));
  372.             fwrite($respack('L'$offset));
  373.             $offset += $len 1;
  374.         }
  375.  
  376.         // write original strings
  377.         foreach ($strings as $str{
  378.             fwrite($res$str['msgid'"\0");
  379.         }
  380.         // write translated strings
  381.         foreach ($strings as $str{
  382.             fwrite($res$str['msgstr'"\0");
  383.         }
  384.         // done
  385.         @flock($resLOCK_UN);
  386.         @fclose($res);
  387.         return true;
  388.     }
  389.  
  390.  
  391.     function _readMO()
  392.     {
  393.         $file $this->filename();
  394.         if (!file_exists($file)) return false;
  395.  
  396.         //  read in data file completely
  397.         $f fopen($file"rb");
  398.         $data fread($f1<<20);
  399.         fclose($f);
  400.  
  401.         //  extract header fields and check file magic
  402.         if ($data{
  403.             $header substr($data020);
  404.             $header unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn"$header);
  405.             extract($header);
  406.             if ((dechex($magic== "950412de"&& ($version == 0)) {
  407.                 //  fetch all strings
  408.                 for ($a=0$a<$count$a++{
  409.                     //  msgid
  410.                     $r unpack("L1len/L1offs"substr($data$o_msg $a 88));
  411.                     $msgid substr($data$r["offs"]$r["len"]);
  412.                     unset($msgid_plural);
  413.                     if (strpos($msgid"\0")) // plurals
  414.                         list($msgid$msgid_pluralexplode("\0"$msgid);
  415.                     }
  416.                     //  msgstr
  417.                     $r unpack("L1len/L1offs"substr($data$o_trn $a 88));
  418.                     $msgstr substr($data$r["offs"]$r["len"]);
  419.                     if (isset($msgid_plural)) // plurals
  420.                         $msgstr explode("\0"$msgstr);
  421.                     }
  422.                     $strings[$a]['msgid'$msgid;
  423.                     $strings[$a]['msgstr'$msgstr;
  424.                     $strings[$a]['msgid_plural'= isset($msgid_plural$msgid_plural '';
  425.                 }
  426.                 if (!empty($strings[0]['msgstr']))// header
  427.                     $str explode("\n"$strings[0]['msgstr']);
  428.                     foreach ($str as $s){
  429.                         if (!empty($s)) {
  430.                             @list($key$valueexplode(':'$s2);
  431.                             $this->headers[$key$value;
  432.                         }
  433.                     }
  434.                 }
  435.                 // load the strings
  436.                 array_shift($strings);
  437.                 for ($a=0$a count($strings)$a++{
  438.                     $this->strings[$anew PHPGettext_Message($strings[$a]['msgid']$strings[$a]['msgid_plural']);
  439.                     $this->strings[$a]->setmsgstr($strings[$a]['msgstr']);
  440.                 }
  441.                 return true;
  442.             }
  443.         }
  444.         return false;
  445.     }
  446.  
  447.  
  448. }
  449. ?>

Documentation generated on Mon, 05 May 2008 16:22:20 +0400 by phpDocumentor 1.4.0