PageRenderTime 24ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/gulliver/thirdparty/phing/filters/TranslateGettext.php

https://bitbucket.org/ferOnti/processmaker
PHP | 285 lines | 108 code | 37 blank | 140 comment | 9 complexity | 23b4dbd97cb0f1f1c4906d34e29b007e MD5 | raw file
  1. <?php
  2. /*
  3. * $Id: TranslateGettext.php 3076 2006-12-18 08:52:12Z fabien $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information please see
  19. * <http://phing.info>.
  20. */
  21. require_once 'phing/filters/BaseParamFilterReader.php';
  22. include_once 'phing/filters/ChainableReader.php';
  23. /**
  24. * Replaces gettext("message id") and _("message id") with the translated string.
  25. *
  26. * Gettext is great for creating multi-lingual sites, but in some cases (e.g. for
  27. * performance reasons) you may wish to replace the gettext calls with the translations
  28. * of the strings; that's what this task is for. Note that this is similar to
  29. * ReplaceTokens, but both the find and the replace aspect is more complicated -- hence
  30. * this is a separate, stand-alone filter.
  31. *
  32. * <p>
  33. * Example:<br>
  34. * <pre>
  35. * <translategettext locale="en_US" domain="messages" dir="${webroot}/local"/>
  36. * </pre>
  37. *
  38. * @author Hans Lellelid <hans@xmpl.org>
  39. * @version $Revision: 1.11 $ $Date: 2005/12/08 15:59:56 $
  40. * @access public
  41. * @see BaseFilterReader
  42. * @package phing.filters
  43. */
  44. class TranslateGettext extends BaseParamFilterReader implements ChainableReader {
  45. // constants for specifying keys to expect
  46. // when this is called using <filterreader ... />
  47. const DOMAIN_KEY = "domain";
  48. const DIR_KEY = "dir";
  49. const LOCALE_KEY = "locale";
  50. /** The domain to use */
  51. private $domain = 'messages';
  52. /** The dir containing LC_MESSAGES */
  53. private $dir;
  54. /** The locale to use */
  55. private $locale;
  56. /** The system locale before it was changed for this filter. */
  57. private $storedLocale;
  58. /**
  59. * Set the text domain to use.
  60. * The text domain must correspond to the name of the compiled .mo files.
  61. * E.g. "messages" ==> $dir/LC_MESSAGES/messages.mo
  62. * "mydomain" ==> $dir/LC_MESSAGES/mydomain.mo
  63. * @param string $domain
  64. */
  65. function setDomain($domain) {
  66. $this->domain = $domain;
  67. }
  68. /**
  69. * Get the current domain.
  70. * @return string
  71. */
  72. function getDomain() {
  73. return $this->domain;
  74. }
  75. /**
  76. * Sets the root locale directory.
  77. * @param PhingFile $dir
  78. */
  79. function setDir(PhingFile $dir) {
  80. $this->dir = $dir;
  81. }
  82. /**
  83. * Gets the root locale directory.
  84. * @return PhingFile
  85. */
  86. function getDir() {
  87. return $this->dir;
  88. }
  89. /**
  90. * Sets the locale to use for translation.
  91. * Note that for gettext() to work, you have to make sure this locale
  92. * is specific enough for your system (e.g. some systems may allow an 'en' locale,
  93. * but others will require 'en_US', etc.).
  94. * @param string $locale
  95. */
  96. function setLocale($locale) {
  97. $this->locale = $locale;
  98. }
  99. /**
  100. * Gets the locale to use for translation.
  101. * @return string
  102. */
  103. function getLocale() {
  104. return $this->locale;
  105. }
  106. /**
  107. * Make sure that required attributes are set.
  108. * @throws BuldException - if any required attribs aren't set.
  109. */
  110. protected function checkAttributes() {
  111. if (!$this->domain || !$this->locale || !$this->dir) {
  112. throw new BuildException("You must specify values for domain, locale, and dir attributes.");
  113. }
  114. }
  115. /**
  116. * Initialize the gettext/locale environment.
  117. * This method will change some env vars and locale settings; the
  118. * restoreEnvironment should put them all back :)
  119. *
  120. * @return void
  121. * @throws BuildException - if locale cannot be set.
  122. * @see restoreEnvironment()
  123. */
  124. protected function initEnvironment() {
  125. $this->storedLocale = getenv("LANG");
  126. $this->log("Setting locale to " . $this->locale, PROJECT_MSG_DEBUG);
  127. putenv("LANG=".$this->locale);
  128. $ret = setlocale(LC_ALL, $this->locale);
  129. if ($ret === false) {
  130. $msg = "Could not set locale to " . $this->locale
  131. . ". You may need to use fully qualified name"
  132. . " (e.g. en_US instead of en).";
  133. throw new BuildException($msg);
  134. }
  135. $this->log("Binding domain '".$this->domain."' to " . $this->dir, PROJECT_MSG_DEBUG);
  136. bindtextdomain($this->domain, $this->dir->getAbsolutePath());
  137. textdomain($this->domain);
  138. }
  139. /**
  140. * Restores environment settings and locale.
  141. * This does _not_ restore any gettext-specific settings
  142. * (e.g. textdomain()).
  143. *
  144. * @return void
  145. */
  146. protected function restoreEnvironment() {
  147. putenv("LANG=".$this->storedLocale);
  148. setlocale(LC_ALL, $this->storedLocale);
  149. }
  150. /**
  151. * Performs gettext translation of msgid and returns translated text.
  152. *
  153. * This function simply wraps gettext() call, but provides ability to log
  154. * string replacements. (alternative would be using preg_replace with /e which
  155. * would probably be faster, but no ability to debug/log.)
  156. *
  157. * @param array $matches Array of matches; we're interested in $matches[2].
  158. * @return string Translated text
  159. */
  160. private function xlateStringCallback($matches) {
  161. $charbefore = $matches[1];
  162. $msgid = $matches[2];
  163. $translated = gettext($msgid);
  164. $this->log("Translating \"$msgid\" => \"$translated\"", PROJECT_MSG_DEBUG);
  165. return $charbefore . '"' . $translated . '"';
  166. }
  167. /**
  168. * Returns the filtered stream.
  169. * The original stream is first read in fully, and then translation is performed.
  170. *
  171. * @return mixed the filtered stream, or -1 if the end of the resulting stream has been reached.
  172. *
  173. * @throws IOException - if the underlying stream throws an IOException during reading
  174. * @throws BuildException - if the correct params are not supplied
  175. */
  176. function read($len = null) {
  177. if ( !$this->getInitialized() ) {
  178. $this->_initialize();
  179. $this->setInitialized(true);
  180. }
  181. // Make sure correct params/attribs have been set
  182. $this->checkAttributes();
  183. $buffer = $this->in->read($len);
  184. if($buffer === -1) {
  185. return -1;
  186. }
  187. // Setup the locale/gettext environment
  188. $this->initEnvironment();
  189. // replace any occurrences of _("") or gettext("") with
  190. // the translated value.
  191. //
  192. // ([^\w]|^)_\("((\\"|[^"])*)"\)
  193. // --$1--- -----$2----
  194. // ---$3-- [match escaped quotes or any char that's not a quote]
  195. //
  196. // also match gettext() -- same as above
  197. $buffer = preg_replace_callback('/([^\w]|^)_\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer);
  198. $buffer = preg_replace_callback('/([^\w]|^)gettext\("((\\\"|[^"])*)"\)/', array($this, 'xlateStringCallback'), $buffer);
  199. // Check to see if there are any _('') calls and flag an error
  200. // Check to see if there are any unmatched gettext() calls -- and flag an error
  201. $matches = array();
  202. if (preg_match('/([^\w]|^)(gettext\([^\)]+\))/', $buffer, $matches)) {
  203. $this->log("Unable to perform translation on: " . $matches[2], PROJECT_MSG_WARN);
  204. }
  205. $this->restoreEnvironment();
  206. return $buffer;
  207. }
  208. /**
  209. * Creates a new TranslateGettext filter using the passed in
  210. * Reader for instantiation.
  211. *
  212. * @param Reader $reader A Reader object providing the underlying stream.
  213. * Must not be <code>null</code>.
  214. *
  215. * @return TranslateGettext A new filter based on this configuration, but filtering
  216. * the specified reader
  217. */
  218. function chain(Reader $reader) {
  219. $newFilter = new TranslateGettext($reader);
  220. $newFilter->setProject($this->getProject());
  221. $newFilter->setDomain($this->getDomain());
  222. $newFilter->setLocale($this->getLocale());
  223. $newFilter->setDir($this->getDir());
  224. return $newFilter;
  225. }
  226. /**
  227. * Parses the parameters if this filter is being used in "generic" mode.
  228. */
  229. private function _initialize() {
  230. $params = $this->getParameters();
  231. if ( $params !== null ) {
  232. foreach($params as $param) {
  233. switch($param->getType()) {
  234. case self::DOMAIN_KEY:
  235. $this->setDomain($param->getValue());
  236. break;
  237. case self::DIR_KEY:
  238. $this->setDir($this->project->resolveFile($param->getValue()));
  239. break;
  240. case self::LOCALE_KEY:
  241. $this->setLocale($param->getValue());
  242. break;
  243. } // switch
  244. }
  245. } // if params !== null
  246. }
  247. }
  248. ?>