[geeklog-cvs] geeklog-2/lib/Translations getstrings,NONE,1.1 mergeversions,NONE,1.1 StringExtractor.class.php,NONE,1.1 Translator.class.php,NONE,1.1

geeklog-cvs-admin at lists.geeklog.net geeklog-cvs-admin at lists.geeklog.net
Fri Apr 18 16:13:11 EDT 2003


Update of /usr/cvs/geeklog/geeklog-2/lib/Translations
In directory internal.geeklog.net:/tmp/cvs-serv14419

Added Files:
	getstrings mergeversions StringExtractor.class.php 
	Translator.class.php 
Log Message:
Initial import of translation library


--- NEW FILE: getstrings ---
#!/usr/local/bin/php -q
<?php

/**
* Translations Library - getstrings
*
* This source file is subject to version 2.02 of the PHP license,
* that is bundled with this package in the file LICENSE, and is
* available at through the world-wide-web at
* http://www.php.net/license/2_02.txt.
* If you did not receive a copy of the PHP license and are unable to
* obtain it through the world-wide-web, please send a note to
* license at php.net so we can mail you a copy immediately.
*
* Here's an example of how to use this command:
* ./getstrings --lang=en --source=./tmp/ --target=./output/
*    --appname=Geeklog --appversion=2.0 --author="Tony Bibbs"
*    --email=tony at geeklog.net
*
* @author Tony Bibbs <tony AT geeklog DOT net>
* @copyright Tony Bibbs 2003
* @package net.geeklog.translations
* @version $Id: getstrings,v 1.1 2003/04/18 20:13:09 tony Exp $
*
*/

require_once 'StringExtractor.class.php';

$argArray = array();

foreach ($argv as $curArg) {
    $tmpArray = array();
    if (strstr($curArg,'=')) {
        $tmpArray = explode('=', $curArg);
        $argArray[strtolower($tmpArray[0])] = $tmpArray[1];
    }
}

if (empty($argArray['--lang'])) {
    $argArray['--lang'] == 'en';
}

$extractor = new StringExtractor($argArray['--lang']);
$extractor->getStrings($argArray);

?>
--- NEW FILE: mergeversions ---
#!/usr/local/bin/php -q
<?php

/**
* Translations Library - VersionMerge.class.php
*
* This source file is subject to version 2.02 of the PHP license,
* that is bundled with this package in the file LICENSE, and is
* available at through the world-wide-web at
* http://www.php.net/license/2_02.txt.
* If you did not receive a copy of the PHP license and are unable to
* obtain it through the world-wide-web, please send a note to
* license at php.net so we can mail you a copy immediately.
*
* @author Tony Bibbs <tony AT geeklog DOT net>
* @copyright Tony Bibbs 2003
* @package net.geeklog.translations
* @version $Id: mergeversions,v 1.1 2003/04/18 20:13:09 tony Exp $
*
*/

/**
* Take an outdated XML translation file and merges strings into
* the new, untranslated XML file.
*
* This class makes updating a language file for an application from
* one version to the next easy by taking the old values and merging them
* into the new, untranslated XML file.  From there the translator only
* needs to edit the resulting XML file and add the new strings.
*
* @author Tony Bibbs <tony AT geeklog DOT net>
* @package net.geeklog.translations
*
*/
class VersionMerge {
    /**
    * @access private
    * @var string
    */
    var $_oldFile = null;
    
    /**
    * @access private
    * @var string
    */
    var $_newFile = null;
    
    /**
    * @access private
    * @var string
    */
    var $_outputFile = null;
    
    /**
    * @access private
    * @var object
    */
    var $_outputFp = null;
    
    /**
    * @access private
    * @var array
    */
    var $_oldStrings = null;
    
    /**
    * @access private
    * @var array
    */
    var $_newStrings = null;
    
    /**
    * @access private
    * @var boolean
    */
    var $_isOld = null;
    
    /**
    * @access private
    * @var string
    */
    var $_curElement = null;
    
    /**
    * @access private
    * @var string
    */
    var $_curId = null;

    /**
    * @access private
    * @var array
    */
    var $_appName = null;
    
    /**
    * @access private
    * @var array
    */
    var $_appVersion = null;
    
    /**
    * @access private
    * @var array
    */
    var $_authorName = null;
    
    /**
    * @access private
    * @var array
    */
    var $_authorEmail = null;
    
    /**
    * @access private
    * @var array
    */
    var $_nativeLang = null;
    
    /**
    * Constructor
    *
    * Initializes some class properties
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    function VersionMerge()
    {
        $this->_oldStrings = array();
        $this->_newStrings = array();
    }
    
    /**
    * Creates the header entries for XML file
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    *
    */
    function _addXMLHeaders()
    {
        fwrite($this->_outputFp,
            sprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<APPLICATION name=\"%s\" version=\"%s\">\n    <TRANSLATOR name=\"%s\" email=\"%s\" />\n    <TRANSLATION language=\"%s\">\n",
            $this->_appName,
            $this->_appVersion,
            $this->_authorName,
            $this->_authorEmail,
            $this->_nativeLang));
    }

    /**
    * Adds XML footer entries to XML file
    *
    * @author Tony Bibbs <tony at geeklog.net>
    *
    */
    function _addXMLFooters()
    {
        fwrite($this->_outputFp,"    </TRANSLATION>\n</APPLICATION>");
        fclose($this->_outputFp);
    }
    
    /**
    * Writes a set of strings to an XML file
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param string $strId ID of string to add
    * @param string $translation Translated string
    *
    */
    function _addString($strId, $translation)
    {
        $translation = htmlspecialchars($translation, ENT_NOQUOTES);
        fwrite($this->_outputFp,
            sprintf("    <ENTRY>\n        <STRING_ID>%s</STRING_ID>\n        <STRING>%s</STRING>\n    </ENTRY>\n",
            $strId, $translation));
    }
    
    /**
    * Prints help text for commandline interface
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    *
    */
    function _getHelp()
    {
        echo "\n--old        Name of your existing translated XML file\n\n";
        echo "--new        Name of the new, untranslated XML file\n\n";
        echo "--target     Fully qualified name for resulting XML file\n\n";
    }
    
    /**
    * Handler that is called when a beginning XML tag is
    * encountered
    *
    * Caches the element so when we get to the data handler we
    * know if we should ignore the data or not.
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param object $parser Expat parser object
    * @param string $name Name of XML element encountered
    * @param array $attributes XML attributes for current element
    *
    */
    function _handleStartElement($parser, $name, $attributes)
    {
        $this->_curElement = $name;
        
        if ($name == 'APPLICATION') {
            if (!$this->_isOld) {
                $this->_appName = $attributes['NAME'];
                $this->_appVersion = $attributes['VERSION'];
            }
        }
        
        if ($name == 'TRANSLATOR' AND $this->_isOld) {
            $this->_authorName = $attributes['NAME'];
            $this->_authorEmail = $attributes['EMAIL'];
        }
        
        if ($name == 'TRANSLATION' and $this->_isOld) {
            $this->_nativeLang = $attributes['LANGUAGE'];
        }
    }
    
    /**
    * Handler that is called when an end XML tag is
    * encountered
    *
    * This will clear out the current element being worked
    * on as well as the current string ID
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    *
    */
    function _handleEndElement()
    {
        $this->_curElement = '';
    }
    
    /**
    * Handles data found while parsing XML
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param object $parser Expat parser
    * @param string $data Data that was encountered
    *
    */
    function _handleData($parser, $data)
    {
        if (empty($this->_curElement)) {
            return;
        }
        
        switch ($this->_curElement) {
            case 'STRING_ID':
                if (!$this->_isOld) {
                    $this->_newStrings[$data] = '';
                } else {
                    $this->_curId = $data;
                }
                break;
            case 'STRING':
                if ($this->_isOld) {
                    print "Found string: $data\n";
                    $this->_oldStrings[$this->_curId] = $data;
                }
                break;
            default:
                continue;
        }
    }
    
    /**
    * Replaces special characters: '&', '<', '&gt' because
    * Expat parser interprets them (unfortunately).
    *
    * Replaces special characters with a token so the Expat parser can
    * process normally.  Don't worry, we replace the tokens for you
    * when we go to write the new file
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param string $inputString String to operate on
    * @return string Modified string
    *
    */
    function _replaceSpecialChars($inputString)
    {
        $inputString = str_replace('&','**_amp_**', $inputString);
        $inputString = str_replace('<','**_lt_**', $inputString);
        $inputString = str_replace('>','**_gt_**', $inputString);
        
        return $inputString;
    }
    
    /**
    * This undoes anything that _replaceSpecialChars may have done
    *
    * Replaces special tokens with their HTML special characters so that
    * we can write the data to the output file
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param string $inputString String to operate on
    * @return string Modified string
    *
    */
    function _reinsertSpecialChars($inputString)
    {
        $inputString = str_replace('**_amp_**', '&', $inputString);
        $inputString = str_replace('**_lt_**', '<', $inputString);
        $inputString = str_replace('**_gt_**', '>', $inputString);
        
        return $inputString;
    }
    
    /**
    * Loads the strings for a given language code into memory
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access private
    * @param string $filename Name of file to get strings from
    * @param boolean $isOld Indicator that lets us know which array to put strings in
    *
    */
    function _loadStrings($filename, $isOld)
    {
        $this->_isOld = $isOld;
     
        if ($this->_isOld) {
            print "\nProcesing old file: $filename\n\n";
        } else {
            print "\nProcessing new file: $filename\n\n";
        }
        $this->_strings = array();
        
        // Make sure we have a valid translation file
        if (!file_exists($filename)) {
            trigger_error("Bad translation file, $filename, in Translator::_loadStrings");
            exit;
        }
        
        // Load the XML
        $xmlData = file_get_contents($filename);
        
        // The Expat XML parser does not handle special characters the way they
        // should so we need to replace them with a token until we are done processing
        // and them jam them back in.
        $xmlData = $this->_replaceSpecialChars($xmlData);
        
        // Create Expat parser object
        $this->_parser = xml_parser_create();
        
        // Lets expat know this class will handle the parsing
        xml_set_object($this->_parser, &$this);
        xml_set_element_handler($this->_parser, '_handleStartElement', '_handleEndElement');
        xml_set_character_data_handler($this->_parser, '_handleData');
        
        // Actually parse the XML now.
        if (!xml_parse($this->_parser, $xmlData)) {
            trigger_error(sprintf("XML error: %s at line %d",
                xml_error_string(xml_get_error_code($this->_parser)),
                xml_get_current_line_number($this->_parser)));
            exit;
        }
        xml_parser_free($this->_parser);
        
        return true;
    }
    
    /**
    * When called from the command line this function will
    * take any arguments passed to the getstrings shell script
    * and set up this object for processing
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param array $args Argument passed to getstring commandline
    *
    */
    function _handleCommandLineArgs($args)
    {
        $validOptions = array('--old'=>1,'--new'=>1,'--target'=>1);
        $optionNames = array_keys($validOptions);
        
        // If they asked for help, give it now and ignore
        // all other arguments
        if (in_array('--help',array_keys($args))) {
            $this->_getHelp();
            exit;
        }
        
        // Make sure we have a set of valid arguments
        foreach ($args as $curArg) {
            if (!in_array(key($args),$optionNames)) {
                echo "Invalid argument: $curArg\n";
                $this->_getHelp();
                exit;
            }
        }
        
        // Make sure all required arguments were provided.
        foreach ($optionNames as $curName) {
            if ($validOptions[$curName] == 1 AND empty($args[$curName])) {
                echo "Missing required argument: $curName\n";
                $this->_getHelp();
                exit;
            }
        }
        reset($args);
        foreach ($args as $curArg) {
            switch (key($args)) {
                case '--old':
                    $this->_loadStrings($curArg, true);
                    break;
                case '--new':
                    $this->_loadStrings($curArg, false);
                    break;
                case '--target':
                    $this->_outputFile = $curArg;
                    $this->_outputFp = fopen($this->_outputFile,'w');
                    break;
                default:
                    continue;
            }
            next($args);
        }
        return;
    }
    
    /**
    * Merges entries from old translation into a new
    * translation file
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    function mergeVersions($args)
    {
        // Validate arguments and load set-up data
        $this->_handleCommandLineArgs($args);
        $this->_addXMLHeaders();
        $idArray = array_keys($this->_newStrings);
        
        foreach ($idArray as $curId) {
            print "Processing ID: $curId\n";
            // NOTE: the call to _reinsertSpecialChars is to replace tokens we added in order to
            // allow Expat parser to handle HTML special chars properly
            if (!empty($this->_oldStrings[$curId])) {
                $this->_addString($this->_reinsertSpecialChars($curId), $this->_oldStrings[$curId]);
            } else {
                $tmpString = $this->_reinsertSpecialChars($curId);
                print "Found new string: $tmpString\n";
                $this->_addString($this->_reinsertSpecialChars($curId), '');
            }
            next($this->_newStrings);
        }
        $this->_addXMLFooters();
    }
    
}

// To avoid having to send translators multiple files we include the main command line code
// here instead of doing a require_once in a separate file.
$argArray = array();

// Parse arguments
foreach ($argv as $curArg) {
    $tmpArray = array();
    if (strstr($curArg,'=')) {
        $tmpArray = explode('=', $curArg);
        $argArray[strtolower($tmpArray[0])] = $tmpArray[1];
    }
}

// Perform the merge
$versionMerge = new VersionMerge();
$versionMerge->mergeVersions($argArray);


?>
--- NEW FILE: StringExtractor.class.php ---
<?php

/**
* Translations Library - StringExtractor.class.php
*
* This source file is subject to version 2.02 of the PHP license,
* that is bundled with this package in the file LICENSE, and is
* available at through the world-wide-web at
* http://www.php.net/license/2_02.txt.
* If you did not receive a copy of the PHP license and are unable to
* obtain it through the world-wide-web, please send a note to
* license at php.net so we can mail you a copy immediately.
*
* @author Tony Bibbs <tony AT geeklog DOT net>
* @copyright Tony Bibbs 2003
* @package net.geeklog.translations
* @version $Id: StringExtractor.class.php,v 1.1 2003/04/18 20:13:09 tony Exp $
*
*/

/**
* Extracts native language strings from code files
*
* The string extractor is very similar to xgettext in that it will
* recursively parse the code files in a given directory, get the native
* string from all calls to the translate() method and output an XML file
* that can be distributed to translators.  Simply put all strings needing
* to be translated are extracted from the PHP code itself and inserted
* into an XML file.  The XML file is what project administrators would
* send to their translators.  To help incremental updates, the commandline
* program mergeversions will assist translators by taking all previously
* translated text in their old XML file and adding it to a copy of the new
* file leaving only the new entries empty.
*
* @author Tony Bibbs <tony AT geeklog DOT net>
* @package net.geeklog.translations
*
*/
class StringExtractor {
    /**
    * @access private
    * @var string
    */
    var $_nativeLang = null;
    
    /**
    * @access private
    * @var string
    */
    var $_codeDir = null;
    
    /**
    * @access private
    * @var array
    */
    var $_validExtensions = null;
    
    /**
    * @access private
    * @var array
    */
    var $_outputDir = null;
    
    /**
    * @access private
    * @var array
    */
    var $_codeDirs = null;
    
    /**
    * @access private
    * @var array
    */
    var $_outputFp = null;
    
    /**
    * @access private
    * @var array
    */
    var $_appName = null;
    
    /**
    * @access private
    * @var array
    */
    var $_appVersion = null;
    
    /**
    * @access private
    * @var array
    */
    var $_authorName = null;
    
    /**
    * @access private
    * @var array
    */
    var $_authorEmail = null;
    
    /**
    * @access private
    * @var array
    */
    var $_commandLineMode = null;
    
    /**
    * Constructor
    *
    * Initializes a few private variables
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @param string $nativeLang Native language used in code files
    *
    */
    function StringExtractor($nativeLang)
    {
        $this->_validExtensions = Array();
        $this->_codeDirs = Array();
        $this->_nativeLang = $nativeLang;
        $this->_commandLineMode = false;
        
    }
    
    /**
    * Gets all the directories that need to be traversed
    *
    * Recursively traverses _codeDir looking for all directories
    * that should be searched
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access private
    * @param string $curDir Current working directory
    *
    */
    function _getDirs($curDir = '')
    {
        $dirsFound = array();
        
        if (empty($curDir)) {
            $curDir = $this->_codeDir;
        }
        $this->_codeDirs[] = $curDir;
        
        $fd = opendir($curDir);
        while (($dir = @readdir( $fd )) == TRUE) {
            if (is_dir($curDir . $dir) AND $dir <> '.' AND $dir <> '..' AND $dir <> 'CVS' AND substr($dir, 0 , 1) <> '.' ) {
                clearstatcache();
                $dirsFound[] = $curDir . $dir . '/';
            }
        }
        foreach ($dirsFound as $nextDir) {
            $this->_getDirs($nextDir);
        }
        closedir($fd);
    }
    
    /**
    * Extracts all the strings from the PHP files in the given
    * directory.
    *
    * Takes all strings found in PHP files in the given directory
    * and adds them to the default language XML file
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param string $dirName name of directory to parse (must end with '/')
    * @todo Need to have this file check _validExtensions so that files other than
    * .php can be used.
    *
    */
    function _processFiles($dirName)
    {
        $results = array();
        $fd = opendir($dirName);
        while (($curFile = @readdir($fd)) == true) {
            if (is_file($dirName . $curFile) AND stristr($curFile,'.php')) {
                $fileContents = file_get_contents($dirName . $curFile);
                preg_match_all("/.->translate\(['\"](.*)['\"]\);/im", $fileContents, $results);
                $matches = $results[1];
                if ($this->_commandLineMode) {
                    echo 'processing file: ' . $dirName . $curFile . "\n";
                    echo 'found ' . count($matches) . " strings\n";
                }
                
                $this->_addStrings($matches);
            }
        }
        closedir($fd);
    }
    
    /**
    * Writes a set of strings to the native XML file
    *
    * This function is called as we find strings needing translation in the
    * code files.  This method will actually add the appropriate XML for the
    * string found.
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param array $matches Array of strings
    *
    */
    function _addStrings($matches)
    {
        foreach ($matches as $curString) {
            // NOTE: following line will convert only &,< and >
            // quotes are going to be OK within XML tags
            $curString = htmlspecialchars($curString, ENT_NOQUOTES);
            fwrite($this->_outputFp,
                sprintf("    <ENTRY>\n        <STRING_ID>%s</STRING_ID>\n        <STRING></STRING>\n    </ENTRY>\n",
                $curString));
        }
    }
    
    /**
    * Creates the header entries for XML file
    *
    * The XML schema adds the APPLICATION, TRANSLATOR and
    * TRANSLATION XML tags along with their attributes
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    *
    */
    function _addXMLHeaders()
    {
        fwrite($this->_outputFp,
            sprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<APPLICATION name=\"%s\" version=\"%s\">\n    <TRANSLATOR name=\"%s\" email=\"%s\" />\n    <TRANSLATION language=\"%s\">\n",
            $this->_appName,
            $this->_appVersion,
            $this->_authorName,
            $this->_authorEmail,
            $this->_nativeLang));
    }

    /**
    * Adds XML footer entries to XML file
    *
    * This method simply closes the TRANSLATION and APPLICATION
    * tags.
    *
    * @author Tony Bibbs <tony at geeklog.net>
    *
    */
    function _addXMLFooters()
    {
        fwrite($this->_outputFp,"    </TRANSLATION>\n</APPLICATION>");
        fclose($this->_outputFp);
    }
    
    /**
    * When called from the command line this function will
    * take any arguments passed to the getstrings shell script
    * and set up this object for processing
    *
    * This validates arguments given to the command line script.  When
    * an error is encountered the help page is displayed
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param array $args Argument passed to getstring commandline
    *
    */
    function _handleCommandLineArgs($args)
    {
        // This array hold valid arguments and whether or not they are required
        // where 1 = required.
        $validOptions = array('--lang'=>1,'--source'=>1,'--target'=>1,'--appname'=>1,
            '--appversion'=>0,'--author'=>0,'--email'=>0,'--help'=>0);
        $optionNames = array_keys($validOptions);
        
        // If they asked for help, give it now and ignore
        // all other arguments
        if (in_array('--help',array_keys($args))) {
            $this->_getHelp();
            return;
        }
        
        // Make sure we have a set of valid arguments
        foreach ($args as $curArg) {
            if (!in_array(key($args),$optionNames)) {
                echo "Invalid argument: $curArg\n";
                $this->_getHelp();
                return;
            }
        }
        
        // Make sure all required arguments were provided.
        foreach ($optionNames as $curName) {
            if ($validOptions[$curName] == 1 AND empty($args[$curName])) {
                echo "Missing required argument: $curName\n";
                $this->_getHelp();
                return;
            }
        }
        
        // Validation is done so now we can go ahead and start setting up the
        // object.
        reset($args);
        $appName = '';
        $appVersion = '';
        $author = '';
        $email = '';
        foreach ($args as $curArg) {
            switch (key($args)) {
                case '--source':
                    $this->setCodeDir($curArg);
                    break;
                case '--target':
                    $this->setOutputDir($curArg);
                    break;
                case '--appname':
                    if (!empty($appVersion)) {
                        $this->setAppProperties($curArg, $appVersion);
                    } else {
                        $appName = $curArg;
                    }
                    break;
                case '--appversion':
                    if (!empty($appName)) {
                        $this->setAppProperties($appName, $curArg);
                    } else {
                        $appVersion = $curArg;
                    }
                    break;
                case '--author':
                    if (!empty($email)) {
                        $this->setAuthorProperties($curArg, $email);
                    } else {
                        $name = $curArg;
                    }
                    break;
                case '--email':
                    if (!empty($name)) {
                        $this->setAuthorProperties($name, $curArg);
                    } else {
                        $email = $curArg;
                    }
                    break;
                default:
                    continue;
            }
            next($args);
        }
        return;
    }
    
    /**
    * Prints help text for commandline interface
    *
    * When the commandline is used and --help is passed to it
    * or an error occurs this will be called to show a list of
    * all options along with a description of the option.
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    *
    */
    function _getHelp()
    {
        echo "\n--lang       Native language used in code.  This is used to\n";
        echo "             set the name of the outputted XML file.  So if\n";
        echo "             you pass 'en' the XML file is named en.xml\n\n";
        echo "--source     Top level directory where PHP source code can\n";
        echo "             be found\n\n";
        echo "--target     Directory to store the XML file\n\n";
        echo "--appname    Name of your PHP application\n\n";
        echo "--appversion Version number for your PHP application\n\n";
        echo "--author     Name of the author(s) of your PHP application\n\n";
        echo "--email      Email of the author(s) of your PHP application\n\n";
    }
    
    /**
    * Sets the application properties which are put into the
    * translation XML file you will need to send to translators
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $appName Name of the application
    * @param string $appVersion Application version
    *
    */
    function setAppProperties($appName, $appVersion)
    {
        $this->_appName = $appName;
        $this->_appVersion = $appVersion;
    }
    
    /**
    * Sets the author properties which are put into the translation
    * XML file you will need to send to translators
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $authorName Name of author
    * @param string $authorEmail
    *
    */
    function setAuthorProperties($authorName, $authorEmail)
    {
        $this->_authorName = $authorName;
        $this->_authorEmail = $authorEmail;
    }
    
    /**
    * Sets the main directory where code resides
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access public
    * @param string $path Path of base code directory
    *
    */
    function setCodeDir($path)
    {
        $this->_codeDir = $path;
    }
    
    /**
    * Sets the output directory where the resulting XML file should
    * be placed
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access public
    * @param string $path Path where XML file should be placed
    *
    */
    function setOutputDir($path = './') {
        if (!is_dir($path) OR !is_writable($path)) {
            trigger_error('Specified Output Directory ' . $path . ' is not writeable');
            exit;
        }
        $this->_outputDir = $path;
        $this->_outputFile = $path . $this->_nativeLang . '.xml';
        $this->_outputFp = fopen($this->_outputFile,'w');
    }
    
    /**
    * Sets valid file extensions for code
    *
    * This allows developers to set what extensions to parse for
    * strings.  The default is php only
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access public
    * @param array $extensionArray Array of valid file extensions
    * @todo While this function exists, the code needed to actually
    * use the valid extension array needs to be built
    *
    */
    function setValidExtensions($extensionArray)
    {
        if (count($extensionArray) == 0) {
            $extensionArray = Array('php');
        }
        $this->_validExtensions = $extensionArray;
    }
    
    /*
    * Starts the extraction process
    *
    * This starts the process of traversing the code files and
    * pulling out the strings into an XML file that could be
    * translated
    *
    * @author Tony Bibbs <tony At geeklog DOT net>
    * @access public
    *
    */
    function getStrings($args = '')
    {
        if (is_array($args)) {
            $this->_commandLineMode = true;
            $this->_handleCommandLineArgs($args);
        }
        if (empty($this->_outputDir)) {
            $this->setOutputDir();
        }
        
        if (empty($this->_codeDir)) {
            trigger_error('No code directory specified');
            exit;
        }
        
        if (empty($this->_validExtensions)) {
            $this->setValidExtensions(Array());
        }
        
        $this->_getDirs();
        $this->_addXMLHeaders();
        foreach ($this->_codeDirs as $nextDir) {
            $this->_processFiles($nextDir);
        }
        $this->_addXMLFooters();
        
        if ($this->_commandLineMode) {
            echo "Done processing strings. XML file generated can be found in " . $this->_outputDir . $this->_nativeLang . ".xml\n";
        }
    }
}

?>
--- NEW FILE: Translator.class.php ---
<?php

/**
* Translations Library - Translator.class.php
*
* This source file is subject to version 2.02 of the PHP license,
* that is bundled with this package in the file LICENSE, and is
* available at through the world-wide-web at
* http://www.php.net/license/2_02.txt.
* If you did not receive a copy of the PHP license and are unable to
* obtain it through the world-wide-web, please send a note to
* license at php.net so we can mail you a copy immediately.
*
* @author Tony Bibbs <tony AT geeklog DOT net>
* @copyright Tony Bibbs 2003
* @package net.geeklog.translations
* @version $Id: Translator.class.php,v 1.1 2003/04/18 20:13:09 tony Exp $
*
*/

/**
* This class translates English text to the user's desired language
*
* @author Tony Bibs <tony AT geeklog DOT net>
* @package net.geeklog.translations
*
*/
class Translator {
    /**
    * @access private
    * @var string
    */
    var $_strings = null;
    
    /**
    * @access private
    * @var string
    */
    var $_nativeLang = null;
    
    /**
    * @access private
    * @var string
    */
    var $_userLang = null;
    
    /**
    * @access private
    * @var string
    */
    var $_translationDir = null;
    
    /**
    * @access private
    * @var string
    */
    var $_parser = null;
    
    /**
    * @access private
    * @var string
    */
    var $_curElement = null;
    
    /**
    * @access private
    * @var string
    */
    var $_curID = null;
    
    /**
    * Constructor
    *
    * Sets up the native language and defaults the user
    * language to the native language if the user lang
    * doesn't already exist
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access public
    *
    */
    function Translator($nativeLangCode = 'en')
    {
        $this->_nativeLang = $nativeLangCode;
        if (empty($this->_userLang)) {
            $this->_userLang = $nativeLangCode;
        }
    }
    
    /**
    * Handler that is called when a beginning XML tag is
    * encountered
    *
    * Caches the element so when we get to the data handler we
    * know if we should ignore the data or not.
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param object $parser Expat parser object
    * @param string $name Name of XML element encountered
    * @param array $attributes XML attributes for current element
    *
    */
    function _handleStartElement($parser, $name, $attributes)
    {
        $this->_curElement = $name;
    }
    
    /**
    * Handler that is called when an end XML tag is
    * encountered
    *
    * This will clear out the current element being worked
    * on as well as the current string ID
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    *
    */
    function _handleEndElement()
    {
        $this->_curElement = '';
    }
    
    /**
    * Handles data found while parsing XML
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param object $parser Expat parser
    * @param string $data Data that was encountered
    *
    */
    function _handleData($parser, $data)
    {
        if (empty($this->_curElement) OR !trim($data)) return;
        
        // Grab the native language string from the XML
        if ($this->_curElement == 'STRING_ID') {
            $this->_curID = $data;
        }
        
        // OK, store the new translation
        if ($this->_curElement == 'STRING') {
            $this->_strings[$this->_curID] = $data;
        }
    }
    
    /**
    * Loads the strings for a given language code into memory
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access private
    *
    */
    function _loadStrings()
    {
        $this->_strings = array();
        
        // Make sure we have a valid translation file
        $filename = $this->_translationDir . $this->_userLang . '.xml';
        if (!file_exists($filename)) {
            trigger_error('Bad translation file in Translator::_loadStrings');
            exit;
        }
        
        // Load the XML
        $xmlData = file_get_contents($filename);
        
        // Create Expat parser object
        $this->_parser = xml_parser_create();
        
        // Lets expat know this class will handle the parsing
        xml_set_object($this->_parser, &$this);
        xml_set_element_handler($this->_parser, '_handleStartElement', '_handleEndElement');
        xml_set_character_data_handler($this->_parser, '_handleData');
        
        // Actually parse the XML now.
        if (!xml_parse($this->_parser, $xmlData)) {
            trigger_error(sprintf("XML error: %s at line %d",
                xml_error_string(xml_get_error_code($this->_parser)),
                xml_get_current_line_number($this->_parser)));
            exit;
        }
        xml_parser_free($this->_parser);
        
        return true;
    }
    
    /**
    * Sets the language the user wants site translated to
    *
    * NOTE: this does not effect he default language.  This is
    * the language the user wants in order to process the current
    * request.
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access public
    * @param string $langCode Language to translate strings to
    *
    */
    function setUserLanguage($langCode = '')
    {
        // Default the user language to the native
        // language if none was provided
        if (empty($langCode)) {
            $langCode = $this->_nativeLang;
        }
        
        $this->_userLang = $langCode;
    }
    
    /**
    * Sets base directory where all
    * translations can be found
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $path Path to base translation directory
    *
    */
    function setTranslationDir($path)
    {
        if (is_dir($path)) {
            $this->_translationDir = $path;
        }
    }
    
    /**
    * Translates an given string
    *
    * NOTE: the native language can be set up to be something
    * besides english.  If you are using this translation package
    * it will be important that you set the native language to the
    * one used most on the site as that will reduce the I/O and
    * memory usage. That same language should be the one used for
    * string passed to this method.
    *
    * @author Tony Bibbs <tony AT geeklog DOT net>
    * @access public
    * @param string $originalText Text in _nativeLang language
    * @return string Translated text
    *
    */
    function translate($originalText)
    {
        // Of course if they are asking for native language then we are
        // already done.
        if ($this->_userLang == $this->_nativeLang) {
            return $originalText;
        }
        if (!$this->_loadStrings()) {
            return $originalText;
        }
        return $this->_strings[$originalText];
    }
}




More information about the geeklog-cvs mailing list