diff options
Diffstat (limited to 'buildscripts/phing/classes/phing/filters/TranslateGettext.php')
-rw-r--r-- | buildscripts/phing/classes/phing/filters/TranslateGettext.php | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/buildscripts/phing/classes/phing/filters/TranslateGettext.php b/buildscripts/phing/classes/phing/filters/TranslateGettext.php new file mode 100644 index 00000000..f71823e3 --- /dev/null +++ b/buildscripts/phing/classes/phing/filters/TranslateGettext.php @@ -0,0 +1,285 @@ +<?php + +/* + * $Id: TranslateGettext.php,v 1.11 2005/12/08 15:59:56 hlellelid Exp $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. +*/ + +require_once 'phing/filters/BaseParamFilterReader.php'; +include_once 'phing/filters/ChainableReader.php'; + +/** + * Replaces gettext("message id") and _("message id") with the translated string. + * + * Gettext is great for creating multi-lingual sites, but in some cases (e.g. for + * performance reasons) you may wish to replace the gettext calls with the translations + * of the strings; that's what this task is for. Note that this is similar to + * ReplaceTokens, but both the find and the replace aspect is more complicated -- hence + * this is a separate, stand-alone filter. + * + * <p> + * Example:<br> + * <pre> + * <translategettext locale="en_US" domain="messages" dir="${webroot}/local"/> + * </pre> + * + * @author Hans Lellelid <hans@xmpl.org> + * @version $Revision: 1.11 $ $Date: 2005/12/08 15:59:56 $ + * @access public + * @see BaseFilterReader + * @package phing.filters + */ +class TranslateGettext extends BaseParamFilterReader implements ChainableReader { + + // constants for specifying keys to expect + // when this is called using <filterreader ... /> + const DOMAIN_KEY = "domain"; + const DIR_KEY = "dir"; + const LOCALE_KEY = "locale"; + + /** The domain to use */ + private $domain = 'messages'; + + /** The dir containing LC_MESSAGES */ + private $dir; + + /** The locale to use */ + private $locale; + + /** The system locale before it was changed for this filter. */ + private $storedLocale; + + /** + * Set the text domain to use. + * The text domain must correspond to the name of the compiled .mo files. + * E.g. "messages" ==> $dir/LC_MESSAGES/messages.mo + * "mydomain" ==> $dir/LC_MESSAGES/mydomain.mo + * @param string $domain + */ + function setDomain($domain) { + $this->domain = $domain; + } + + /** + * Get the current domain. + * @return string + */ + function getDomain() { + return $this->domain; + } + + /** + * Sets the root locale directory. + * @param PhingFile $dir + */ + function setDir(PhingFile $dir) { + $this->dir = $dir; + } + + /** + * Gets the root locale directory. + * @return PhingFile + */ + function getDir() { + return $this->dir; + } + + /** + * Sets the locale to use for translation. + * Note that for gettext() to work, you have to make sure this locale + * is specific enough for your system (e.g. some systems may allow an 'en' locale, + * but others will require 'en_US', etc.). + * @param string $locale + */ + function setLocale($locale) { + $this->locale = $locale; + } + + /** + * Gets the locale to use for translation. + * @return string + */ + function getLocale() { + return $this->locale; + } + + /** + * Make sure that required attributes are set. + * @throws BuldException - if any required attribs aren't set. + */ + protected function checkAttributes() { + if (!$this->domain || !$this->locale || !$this->dir) { + throw new BuildException("You must specify values for domain, locale, and dir attributes."); + } + } + + /** + * Initialize the gettext/locale environment. + * This method will change some env vars and locale settings; the + * restoreEnvironment should put them all back :) + * + * @return void + * @throws BuildException - if locale cannot be set. + * @see restoreEnvironment() + */ + protected function initEnvironment() { + $this->storedLocale = getenv("LANG"); + + $this->log("Setting locale to " . $this->locale, PROJECT_MSG_DEBUG); + putenv("LANG=".$this->locale); + $ret = setlocale(LC_ALL, $this->locale); + if ($ret === false) { + $msg = "Could not set locale to " . $this->locale + . ". You may need to use fully qualified name" + . " (e.g. en_US instead of en)."; + throw new BuildException($msg); + } + + $this->log("Binding domain '".$this->domain."' to " . $this->dir, PROJECT_MSG_DEBUG); + bindtextdomain($this->domain, $this->dir->getAbsolutePath()); + textdomain($this->domain); + } + + /** + * Restores environment settings and locale. + * This does _not_ restore any gettext-specific settings + * (e.g. textdomain()). + * + * @return void + */ + protected function restoreEnvironment() { + putenv("LANG=".$this->storedLocale); + setlocale(LC_ALL, $this->storedLocale); + } + + /** + * Performs gettext translation of msgid and returns translated text. + * + * This function simply wraps gettext() call, but provides ability to log + * string replacements. (alternative would be using preg_replace with /e which + * would probably be faster, but no ability to debug/log.) + * + * @param array $matches Array of matches; we're interested in $matches[2]. + * @return string Translated text + */ + private function xlateStringCallback($matches) { + $charbefore = $matches[1]; + $msgid = $matches[2]; + $translated = gettext($msgid); + $this->log("Translating \"$msgid\" => \"$translated\"", PROJECT_MSG_DEBUG); + return $charbefore . '"' . $translated . '"'; + } + + /** + * Returns the filtered stream. + * The original stream is first read in fully, and then translation is performed. + * + * @return mixed the filtered stream, or -1 if the end of the resulting stream has been reached. + * + * @throws IOException - if the underlying stream throws an IOException during reading + * @throws BuildException - if the correct params are not supplied + */ + function read($len = null) { + + if ( !$this->getInitialized() ) { + $this->_initialize(); + $this->setInitialized(true); + } + + // Make sure correct params/attribs have been set + $this->checkAttributes(); + + $buffer = $this->in->read($len); + if($buffer === -1) { + return -1; + } + + // Setup the locale/gettext environment + $this->initEnvironment(); + + + // replace any occurrences of _("") or gettext("") with + // the translated value. + // + // ([^\w]|^)_\("((\\"|[^"])*)"\) + // --$1--- -----$2---- + // ---$3-- [match escaped quotes or any char that's not a quote] + // + // also match gettext() -- same as above + + $buffer = preg_replace_callback('/([^\w]|^)_\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer); + $buffer = preg_replace_callback('/([^\w]|^)gettext\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer); + + // Check to see if there are any _('') calls and flag an error + + // Check to see if there are any unmatched gettext() calls -- and flag an error + + $matches = array(); + if (preg_match('/([^\w]|^)(gettext\([^\)]+\))/', $buffer, $matches)) { + $this->log("Unable to perform translation on: " . $matches[2], PROJECT_MSG_WARN); + } + + $this->restoreEnvironment(); + + return $buffer; + } + + /** + * Creates a new TranslateGettext filter using the passed in + * Reader for instantiation. + * + * @param Reader $reader A Reader object providing the underlying stream. + * Must not be <code>null</code>. + * + * @return TranslateGettext A new filter based on this configuration, but filtering + * the specified reader + */ + function chain(Reader $reader) { + $newFilter = new TranslateGettext($reader); + $newFilter->setProject($this->getProject()); + $newFilter->setDomain($this->getDomain()); + $newFilter->setLocale($this->getLocale()); + $newFilter->setDir($this->getDir()); + return $newFilter; + } + + /** + * Parses the parameters if this filter is being used in "generic" mode. + */ + private function _initialize() { + $params = $this->getParameters(); + if ( $params !== null ) { + foreach($params as $param) { + switch($param->getType()) { + case self::DOMAIN_KEY: + $this->setDomain($param->getValue()); + break; + case self::DIR_KEY: + $this->setDir($this->project->resolveFile($param->getValue())); + break; + + case self::LOCALE_KEY: + $this->setLocale($param->getValue()); + break; + } // switch + } + } // if params !== null + } +} + +?> |