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: