1<?php 2/** 3 * PHPTAL templating engine 4 * 5 * PHP Version 5 6 * 7 * @category HTML 8 * @package PHPTAL 9 * @author Laurent Bedubourg <lbedubourg@motion-twin.com> 10 * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk> 11 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License 12 * @version SVN: $Id$ 13 * @link http://phptal.org/ 14 */ 15 16 17/** 18 * PHPTAL_TranslationService gettext implementation. 19 * 20 * Because gettext is the most common translation library in use, this 21 * implementation is shipped with the PHPTAL library. 22 * 23 * Please refer to the PHPTAL documentation for usage examples. 24 * 25 * @package PHPTAL 26 * @author Laurent Bedubourg <lbedubourg@motion-twin.com> 27 */ 28class PHPTAL_GetTextTranslator implements PHPTAL_TranslationService 29{ 30 private $_vars = array(); 31 private $_currentDomain; 32 private $_encoding = 'UTF-8'; 33 private $_canonicalize = false; 34 35 public function __construct() 36 { 37 if (!function_exists('gettext')) throw new PHPTAL_ConfigurationException("Gettext not installed"); 38 $this->useDomain("messages"); // PHP bug #21965 39 } 40 41 /** 42 * set encoding that is used by template and is expected from gettext 43 * the default is UTF-8 44 * 45 * @param string $enc encoding name 46 */ 47 public function setEncoding($enc) 48 { 49 $this->_encoding = $enc; 50 } 51 52 /** 53 * if true, all non-ASCII characters in keys will be converted to C<xxx> form. This impacts performance. 54 * by default keys will be passed to gettext unmodified. 55 * 56 * This function is only for backwards compatibility 57 * 58 * @param bool $bool enable old behavior 59 */ 60 public function setCanonicalize($bool) 61 { 62 $this->_canonicalize = $bool; 63 } 64 65 /** 66 * It expects locale names as arguments. 67 * Choses first one that works. 68 * 69 * setLanguage("en_US.utf8","en_US","en_GB","en") 70 * 71 * @return string - chosen language 72 */ 73 public function setLanguage(/*...*/) 74 { 75 $langs = func_get_args(); 76 77 $langCode = $this->trySettingLanguages(LC_ALL, $langs); 78 if ($langCode) return $langCode; 79 80 if (defined("LC_MESSAGES")) { 81 $langCode = $this->trySettingLanguages(LC_MESSAGES, $langs); 82 if ($langCode) return $langCode; 83 } 84 85 throw new PHPTAL_ConfigurationException('Language(s) code(s) "'.implode(', ', $langs).'" not supported by your system'); 86 } 87 88 private function trySettingLanguages($category, array $langs) 89 { 90 foreach ($langs as $langCode) { 91 putenv("LANG=$langCode"); 92 putenv("LC_ALL=$langCode"); 93 putenv("LANGUAGE=$langCode"); 94 if (setlocale($category, $langCode)) { 95 return $langCode; 96 } 97 } 98 return null; 99 } 100 101 /** 102 * Adds translation domain (usually it's the same as name of .po file [without extension]) 103 * 104 * Encoding must be set before calling addDomain! 105 */ 106 public function addDomain($domain, $path='./locale/') 107 { 108 bindtextdomain($domain, $path); 109 if ($this->_encoding) { 110 bind_textdomain_codeset($domain, $this->_encoding); 111 } 112 $this->useDomain($domain); 113 } 114 115 /** 116 * Switches to one of the domains previously set via addDomain() 117 * 118 * @param string $domain name of translation domain to be used. 119 * 120 * @return string - old domain 121 */ 122 public function useDomain($domain) 123 { 124 $old = $this->_currentDomain; 125 $this->_currentDomain = $domain; 126 textdomain($domain); 127 return $old; 128 } 129 130 /** 131 * used by generated PHP code. Don't use directly. 132 */ 133 public function setVar($key, $value) 134 { 135 $this->_vars[$key] = $value; 136 } 137 138 /** 139 * translate given key. 140 * 141 * @param bool $htmlencode if true, output will be HTML-escaped. 142 */ 143 public function translate($key, $htmlencode=true) 144 { 145 if ($this->_canonicalize) $key = self::_canonicalizeKey($key); 146 147 $value = gettext($key); 148 149 if ($htmlencode) { 150 $value = htmlspecialchars($value, ENT_QUOTES, $this->_encoding); 151 } 152 while (preg_match('/\${(.*?)\}/sm', $value, $m)) { 153 list($src, $var) = $m; 154 if (!array_key_exists($var, $this->_vars)) { 155 throw new PHPTAL_VariableNotFoundException('Interpolation error. Translation uses ${'.$var.'}, which is not defined in the template (via i18n:name)'); 156 } 157 $value = str_replace($src, $this->_vars[$var], $value); 158 } 159 return $value; 160 } 161 162 /** 163 * For backwards compatibility only. 164 */ 165 private static function _canonicalizeKey($key_) 166 { 167 $result = ""; 168 $key_ = trim($key_); 169 $key_ = str_replace("\n", "", $key_); 170 $key_ = str_replace("\r", "", $key_); 171 for ($i = 0; $i<strlen($key_); $i++) { 172 $c = $key_[$i]; 173 $o = ord($c); 174 if ($o < 5 || $o > 127) { 175 $result .= 'C<'.$o.'>'; 176 } else { 177 $result .= $c; 178 } 179 } 180 return $result; 181 } 182} 183 184