1<?php 2 3/** 4 * Represents a language and defines localizable string formatting and 5 * other functions, as well as the localized messages for HTML Purifier. 6 */ 7class HTMLPurifier_Language 8{ 9 10 /** 11 * ISO 639 language code of language. Prefers shortest possible version. 12 * @type string 13 */ 14 public $code = 'en'; 15 16 /** 17 * Fallback language code. 18 * @type bool|string 19 */ 20 public $fallback = false; 21 22 /** 23 * Array of localizable messages. 24 * @type array 25 */ 26 public $messages = array(); 27 28 /** 29 * Array of localizable error codes. 30 * @type array 31 */ 32 public $errorNames = array(); 33 34 /** 35 * True if no message file was found for this language, so English 36 * is being used instead. Check this if you'd like to notify the 37 * user that they've used a non-supported language. 38 * @type bool 39 */ 40 public $error = false; 41 42 /** 43 * Has the language object been loaded yet? 44 * @type bool 45 * @todo Make it private, fix usage in HTMLPurifier_LanguageTest 46 */ 47 public $_loaded = false; 48 49 /** 50 * @type HTMLPurifier_Config 51 */ 52 protected $config; 53 54 /** 55 * @type HTMLPurifier_Context 56 */ 57 protected $context; 58 59 /** 60 * @param HTMLPurifier_Config $config 61 * @param HTMLPurifier_Context $context 62 */ 63 public function __construct($config, $context) 64 { 65 $this->config = $config; 66 $this->context = $context; 67 } 68 69 /** 70 * Loads language object with necessary info from factory cache 71 * @note This is a lazy loader 72 */ 73 public function load() 74 { 75 if ($this->_loaded) { 76 return; 77 } 78 $factory = HTMLPurifier_LanguageFactory::instance(); 79 $factory->loadLanguage($this->code); 80 foreach ($factory->keys as $key) { 81 $this->$key = $factory->cache[$this->code][$key]; 82 } 83 $this->_loaded = true; 84 } 85 86 /** 87 * Retrieves a localised message. 88 * @param string $key string identifier of message 89 * @return string localised message 90 */ 91 public function getMessage($key) 92 { 93 if (!$this->_loaded) { 94 $this->load(); 95 } 96 if (!isset($this->messages[$key])) { 97 return "[$key]"; 98 } 99 return $this->messages[$key]; 100 } 101 102 /** 103 * Retrieves a localised error name. 104 * @param int $int error number, corresponding to PHP's error reporting 105 * @return string localised message 106 */ 107 public function getErrorName($int) 108 { 109 if (!$this->_loaded) { 110 $this->load(); 111 } 112 if (!isset($this->errorNames[$int])) { 113 return "[Error: $int]"; 114 } 115 return $this->errorNames[$int]; 116 } 117 118 /** 119 * Converts an array list into a string readable representation 120 * @param array $array 121 * @return string 122 */ 123 public function listify($array) 124 { 125 $sep = $this->getMessage('Item separator'); 126 $sep_last = $this->getMessage('Item separator last'); 127 $ret = ''; 128 for ($i = 0, $c = count($array); $i < $c; $i++) { 129 if ($i == 0) { 130 } elseif ($i + 1 < $c) { 131 $ret .= $sep; 132 } else { 133 $ret .= $sep_last; 134 } 135 $ret .= $array[$i]; 136 } 137 return $ret; 138 } 139 140 /** 141 * Formats a localised message with passed parameters 142 * @param string $key string identifier of message 143 * @param array $args Parameters to substitute in 144 * @return string localised message 145 * @todo Implement conditionals? Right now, some messages make 146 * reference to line numbers, but those aren't always available 147 */ 148 public function formatMessage($key, $args = array()) 149 { 150 if (!$this->_loaded) { 151 $this->load(); 152 } 153 if (!isset($this->messages[$key])) { 154 return "[$key]"; 155 } 156 $raw = $this->messages[$key]; 157 $subst = array(); 158 $generator = false; 159 foreach ($args as $i => $value) { 160 if (is_object($value)) { 161 if ($value instanceof HTMLPurifier_Token) { 162 // factor this out some time 163 if (!$generator) { 164 $generator = $this->context->get('Generator'); 165 } 166 if (isset($value->name)) { 167 $subst['$'.$i.'.Name'] = $value->name; 168 } 169 if (isset($value->data)) { 170 $subst['$'.$i.'.Data'] = $value->data; 171 } 172 $subst['$'.$i.'.Compact'] = 173 $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value); 174 // a more complex algorithm for compact representation 175 // could be introduced for all types of tokens. This 176 // may need to be factored out into a dedicated class 177 if (!empty($value->attr)) { 178 $stripped_token = clone $value; 179 $stripped_token->attr = array(); 180 $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token); 181 } 182 $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown'; 183 } 184 continue; 185 } elseif (is_array($value)) { 186 $keys = array_keys($value); 187 if (array_keys($keys) === $keys) { 188 // list 189 $subst['$'.$i] = $this->listify($value); 190 } else { 191 // associative array 192 // no $i implementation yet, sorry 193 $subst['$'.$i.'.Keys'] = $this->listify($keys); 194 $subst['$'.$i.'.Values'] = $this->listify(array_values($value)); 195 } 196 continue; 197 } 198 $subst['$' . $i] = $value; 199 } 200 return strtr($raw, $subst); 201 } 202} 203 204// vim: et sw=4 sts=4 205