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: | */ |
7: | class 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: |