b2evolution PHP Cross Reference Blogging Systems

Source: /inc/locales/_pofile.class.php - 359 lines - 9199 bytes - Summary - Text - Print

Description: This file implements the {@link POFile} and {@link POTFile} classes, used to handle gettext style .PO and .POT files. b2evolution - {@link http://b2evolution.net/} Released under GNU GPL License - {@link http://b2evolution.net/about/license.html}

   1  <?php
   2  /**
   3   * This file implements the {@link POFile} and {@link POTFile} classes, used to handle gettext style
   4   * .PO and .POT files.
   5   *
   6   * b2evolution - {@link http://b2evolution.net/}
   7   * Released under GNU GPL License - {@link http://b2evolution.net/about/license.html}
   8   * @copyright (c)2003-2007 by Francois PLANQUE - {@link http://fplanque.net/}
   9   * Parts of this file are copyright (c)2004 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal Open Source relicensing agreement:
  12   * Daniel HAHLER grants Francois PLANQUE the right to license
  13   * Daniel HAHLER's contributions to this file and the b2evolution project
  14   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  15   * }}
  16   *
  17   * @package internal
  18   * @author blueyed: Daniel HAHLER
  19   */
  20  
  21  
  22  /**
  23   * A quick and dirty class for PO/POT files
  24   *
  25   * @package internal
  26   */
  27  class POFile
  28  {
  29      var $msgids = array();
  30  
  31  	function POFile($filename=null)
  32      {
  33          $this->filename = str_replace( '\\', '/', $filename );
  34      }
  35  
  36      /**
  37       * Add a MSGID for a specific source file
  38       */
  39  	function addmsgid( $msgid, $sourcefile = '', $trans = '' )
  40      {
  41          if( in_array($msgid, array('trans_locale', 'trans_charset', 'trans_available')) )
  42          { // don't put those into POT file
  43              return;
  44          }
  45  
  46          // replace links
  47          $msgid = preg_replace('/<a\s+([^>]*)>/', '<a %s>', $msgid);
  48          // replace newlines, tabs, carriage returns and double quotes:
  49          $msgid = str_replace( array( "\n", "\t", "\r", '"' ),
  50                                array( '\n', '\t', '\r', '\"' ), $msgid );
  51  
  52          if( !isset($this->msgids[ $msgid ]) )
  53          {
  54              $this->msgids[ $msgid ] = '';
  55          }
  56          if( !empty($sourcefile) )
  57          {
  58              $this->msgids[ $msgid ]['source'][] = $sourcefile;
  59          }
  60          $this->msgids[ $msgid ]['trans'][] = $trans;
  61      }
  62  
  63      /**
  64       * Translate msgid
  65       * @param string MSGID
  66       */
  67  	function translate( $msgid )
  68      {
  69          $omsgid = $msgid;  // remember
  70  
  71          if( preg_match_all('/<a\s+([^>]*)>/', $msgid, $matches) )
  72          { // we have to replace links
  73              // remember a-tag params
  74              $aparams = $matches[1];
  75  
  76              // generate clean msgid like in .po files
  77              $msgid = preg_replace('/<a\s+([^>]*)>/', '<a %s>', $msgid);
  78          }
  79          // replace newlines, tabs, carriage returns and double quotes:
  80          $msgid = str_replace( array( "\n", "\t", "\r", '"' ),
  81                                array( '\n', '\t', '\r', '\"' ), $msgid );
  82  
  83          if( isset($this->msgids[ $msgid ]) )
  84          {
  85              $trans = $this->msgids[ $msgid ]['trans'];
  86  
  87              if( isset($aparams) )
  88              {
  89                  $trans = vsprintf($trans, $aparams);
  90              }
  91  
  92              return str_replace( array( '\n', '\t', '\r', '\"' ),
  93                                  array( "\n", "\t", "\r", '"' ), $trans );
  94          }
  95          else
  96          {
  97              #pre_dump( $msgid, 'not translated!' );
  98              return TRANSTAG_OPEN.$omsgid.TRANSTAG_CLOSE;
  99          }
 100      }
 101  
 102  
 103      /**
 104       * Read a .po file
 105       *
 106       * @param boolean Log source info to {@link $Messages}?
 107       * @return array with msgids => array( 'trans' => msgstr )
 108       */
 109  	function read( $log_source_info = true )
 110      {
 111          $lines = file( $this->filename );
 112          $lines[] = '';    // Adds a blank line at the end in order to ensure complete handling of the file
 113          $all = 0;
 114          $fuzzy = 0;
 115          $untranslated = 0;
 116          $translated = 0;
 117          $status = '-';
 118          $matches = array();
 119          $sources = array();
 120          $loc_vars = array();
 121          $this->msgids = array();
 122          $is_fuzzy = false;
 123          foreach ($lines as $line)
 124          {
 125              // echo 'LINE:', $line, '<br />';
 126              if(trim($line) == '' )
 127              {    // Blank line, go back to base status:
 128                  if( $status == 't' )
 129                  {    // ** End of a translation **:
 130                      if( $msgstr == '' )
 131                      {
 132                          $untranslated++;
 133                          // echo 'untranslated: ', $msgid, '<br />';
 134                      }
 135                      elseif( $is_fuzzy )
 136                      {
 137                          $fuzzy++;
 138                          // echo 'fuzzy: ', $msgid, "<br />\n";
 139                      }
 140                      else
 141                      {
 142                          $translated++;
 143  
 144                          // Inspect where the string is used
 145                          $sources = array_unique( $sources );
 146                          // echo '<p>sources: ', implode( ', ', $sources ), '</p>';
 147                          foreach( $sources as $source )
 148                          {
 149                              if( !isset( $loc_vars[$source]  ) ) $loc_vars[$source] = 1;
 150                              else $loc_vars[$source] ++;
 151                          }
 152  
 153                          // Save the string
 154                          $this->msgids[$msgid]['trans'] = $msgstr;
 155                      }
 156                  }
 157                  $status = '-';
 158                  $msgid = '';
 159                  $msgstr = '';
 160                  $sources = array();
 161                  $is_fuzzy = false;
 162              }
 163              elseif( ($status=='-') && preg_match( '#^msgid "(.*)"#', $line, $matches))
 164              {    // Encountered an original text
 165                  $status = 'o';
 166                  $msgid = $matches[1];
 167                  // echo 'original: "', $msgid, '"<br />';
 168                  $all++;
 169              }
 170              elseif( ($status=='o') && preg_match( '#^msgstr "(.*)"#', $line, $matches))
 171              {    // Encountered a translated text
 172                  $status = 't';
 173                  $msgstr = $matches[1];
 174                  // echo 'translated: "', $msgstr, '"<br />';
 175              }
 176              elseif( preg_match( '#^"(.*)"#', $line, $matches))
 177              {    // Encountered a followup line
 178                  if ($status=='o')
 179                      $msgid .= $matches[1];
 180                  elseif ($status=='t')
 181                      $msgstr .= $matches[1];
 182              }
 183              elseif( ($status=='-') && preg_match( '@^#:(.*)@', $line, $matches))
 184              {    // Encountered a source code location comment
 185                  // echo $matches[0],'<br />';
 186                  $sourcefiles = preg_replace( '@\\\\@', '/', $matches[1] );
 187                  // $c = preg_match_all( '@ ../../../([^:]*):@', $sourcefiles, $matches);
 188                  $c = preg_match_all( '@ ../../../([^/:]*/?)@', $sourcefiles, $matches);
 189                  for( $i = 0; $i < $c; $i++ )
 190                  {
 191                      $sources[] = $matches[1][$i];
 192                  }
 193                  // echo '<br />';
 194              }
 195              elseif(strpos($line,'#, fuzzy') === 0)
 196              {
 197                  $is_fuzzy = true;
 198              }
 199          }
 200  
 201          if( $loc_vars && $log_source_info )
 202          {
 203              global $Messages;
 204              ksort( $loc_vars );
 205  
 206              $list_counts = '';
 207              foreach( $loc_vars as $source => $c )
 208              {
 209                  $list_counts .= "\n<li>$source = $c</li>";
 210              }
 211              $Messages->add( 'Sources and number of strings: <ul>'.$list_counts.'</ul>', 'note' );
 212          }
 213  
 214          return $this->msgids;
 215      }
 216  
 217  
 218      /**
 219       * Write POFile::$msgids into $file_path.
 220       *
 221       * @return true|string True on success, string with error on failure
 222       */
 223  	function write_evo_trans($file_path, $locale)
 224      {
 225          $fp = fopen( $file_path, 'w+' );
 226  
 227          if( ! $fp )
 228          {
 229              return "Could not open $file_path for writing!";
 230          }
 231  
 232          fwrite( $fp, "<?php\n" );
 233          fwrite( $fp, "/*\n" );
 234          fwrite( $fp, " * Global lang file\n" );
 235          fwrite( $fp, " * This file was generated automatically from messages.po\n" );
 236          fwrite( $fp, " */\n" );
 237          fwrite( $fp, "if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );" );
 238          fwrite( $fp, "\n\n" );
 239  
 240  
 241          fwrite( $fp, '$trans[\''.$locale."'] = array(\n" );
 242  
 243          // Write meta/format info:
 244          $charset = 'utf-8'; // default
 245          if( isset($this->msgids['']) )
 246          {
 247              if( preg_match( '~\\\nContent-Type: text/plain; charset=(.*?);?\\\n~', $this->msgids['']['trans'], $match ) )
 248              {
 249                  $charset = strtolower($match[1]);
 250              }
 251          }
 252          fwrite( $fp, "'__meta__' => array('format_version'=>1, 'charset'=>'$charset'),\n" );
 253  
 254          foreach( $this->msgids as $msgid => $msginfo )
 255          {
 256              $msgstr = $msginfo['trans'];
 257  
 258              fwrite( $fp, POFile::quote($msgid).' => '.POFile::quote($msgstr).",\n" );
 259          }
 260          fwrite( $fp, "\n);\n?>" );
 261          fclose( $fp );
 262  
 263          return true;
 264      }
 265  
 266  
 267      /**
 268       * Quote a msgid/msgstr, preferrable with single quotes.
 269       *
 270       * Single quotes are preferred, as PHP just handles them as strings and
 271       * does no extra parsing.
 272       * Double quotes are used, if there's \n, \r or \t in the string.
 273       *
 274       * @param string
 275       * @return string Quoted string (either using double or single quotes (preferred))
 276       */
 277  	function quote($s)
 278      {
 279          if( preg_match('~\\\\[nrt]~', $s) ) // \r, \n or \t in there
 280          {
 281              // NOTE: no need to escape '"', as its escaped in .po files already
 282              return '"'.str_replace( '$', '\$', $s ).'"';
 283          }
 284          else
 285          {
 286              return "'".str_replace( array("'", '\"'), array("\'", '"'), $s )."'";
 287          }
 288      }
 289  
 290  }
 291  
 292  
 293  /**
 294   * A class build upon class POFile to provide specific POT actions (write)
 295   *
 296   * @package internal
 297   */
 298  class POTFile extends POFile
 299  {
 300      /**
 301       * @return boolean
 302       */
 303  	function write()
 304      {
 305          global $targets, $locales;
 306  
 307          log_('Writing POTFile '.$this->filename.'..');
 308          $fh = @fopen( $this->filename, 'w' );
 309          if( ! $fh )
 310          {
 311              log_( sprintf('<p class="error">Could not open %s for writing.</p>', $this->filename) );
 312              return false;
 313          }
 314          fwrite($fh, '# SOME DESCRIPTIVE TITLE.'."\n"
 315              .'# Copyright (C) YEAR Francois PLANQUE'."\n"
 316              .'# This file is distributed under the same license as the PACKAGE package.'."\n"
 317              .'# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.'."\n"
 318              .'#'."\n"
 319              .'#, fuzzy'."\n"
 320              .'msgid ""'."\n"
 321              .'msgstr ""'."\n"
 322              .'"Project-Id-Version: PACKAGE VERSION\n"'."\n"
 323              .'"Report-Msgid-Bugs-To: http://fplanque.net/\n"'."\n"
 324              .'"POT-Creation-Date: 2004-04-26 03:00+0200\n"'."\n"
 325              .'"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"'."\n"
 326              .'"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"'."\n"
 327              .'"Language-Team: LANGUAGE <LL@li.org>\n"'."\n"
 328              .'"MIME-Version: 1.0\n"'."\n"
 329              .'"Content-Type: text/plain; charset=CHARSET\n"'."\n"
 330              .'"Content-Transfer-Encoding: 8bit\n"'."\n"
 331          );
 332  
 333          $count = 0;
 334  
 335          foreach( $this->msgids as $msgid => $arr )
 336          {
 337              if( isset($arr['source']) )
 338              { // write sources of string
 339                  foreach( $arr['source'] as $source )
 340                  {
 341                      fwrite( $fh, '#: ../../'.$source."\n" );
 342                  }
 343              }
 344              fwrite( $fh, 'msgid "'.$msgid.'"'."\nmsgstr ".'""'."\n\n" );
 345  
 346              $count++;
 347  
 348          }
 349  
 350          fclose( $fh );
 351  
 352          log_($count.' msgids written.');
 353          return true;
 354      }
 355  
 356  
 357  }
 358  
 359  ?>

title

Description

title

Description

title

Description

title

title

Body