]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/pomo/translations.php
WordPress 4.2
[autoinstalls/wordpress.git] / wp-includes / pomo / translations.php
1 <?php
2 /**
3  * Class for a set of entries for translation and their associated headers
4  *
5  * @version $Id: translations.php 718 2012-10-31 00:32:02Z nbachiyski $
6  * @package pomo
7  * @subpackage translations
8  */
9
10 require_once dirname(__FILE__) . '/entry.php';
11
12 if ( !class_exists( 'Translations' ) ):
13 class Translations {
14         var $entries = array();
15         var $headers = array();
16
17         /**
18          * Add entry to the PO structure
19          *
20          * @param array|Translation_Entry &$entry
21          * @return bool true on success, false if the entry doesn't have a key
22          */
23         function add_entry($entry) {
24                 if (is_array($entry)) {
25                         $entry = new Translation_Entry($entry);
26                 }
27                 $key = $entry->key();
28                 if (false === $key) return false;
29                 $this->entries[$key] = &$entry;
30                 return true;
31         }
32
33         /**
34          * @param array|Translation_Entry $entry
35          * @return bool
36          */
37         function add_entry_or_merge($entry) {
38                 if (is_array($entry)) {
39                         $entry = new Translation_Entry($entry);
40                 }
41                 $key = $entry->key();
42                 if (false === $key) return false;
43                 if (isset($this->entries[$key]))
44                         $this->entries[$key]->merge_with($entry);
45                 else
46                         $this->entries[$key] = &$entry;
47                 return true;
48         }
49
50         /**
51          * Sets $header PO header to $value
52          *
53          * If the header already exists, it will be overwritten
54          *
55          * TODO: this should be out of this class, it is gettext specific
56          *
57          * @param string $header header name, without trailing :
58          * @param string $value header value, without trailing \n
59          */
60         function set_header($header, $value) {
61                 $this->headers[$header] = $value;
62         }
63
64         /**
65          * @param array $headers
66          */
67         function set_headers($headers) {
68                 foreach($headers as $header => $value) {
69                         $this->set_header($header, $value);
70                 }
71         }
72
73         /**
74          * @param string $header
75          */
76         function get_header($header) {
77                 return isset($this->headers[$header])? $this->headers[$header] : false;
78         }
79
80         /**
81          * @param Translation_Entry $entry
82          */
83         function translate_entry(&$entry) {
84                 $key = $entry->key();
85                 return isset($this->entries[$key])? $this->entries[$key] : false;
86         }
87
88         /**
89          * @param string $singular
90          * @param string $context
91          * @return string
92          */
93         function translate($singular, $context=null) {
94                 $entry = new Translation_Entry(array('singular' => $singular, 'context' => $context));
95                 $translated = $this->translate_entry($entry);
96                 return ($translated && !empty($translated->translations))? $translated->translations[0] : $singular;
97         }
98
99         /**
100          * Given the number of items, returns the 0-based index of the plural form to use
101          *
102          * Here, in the base Translations class, the common logic for English is implemented:
103          *      0 if there is one element, 1 otherwise
104          *
105          * This function should be overrided by the sub-classes. For example MO/PO can derive the logic
106          * from their headers.
107          *
108          * @param integer $count number of items
109          */
110         function select_plural_form($count) {
111                 return 1 == $count? 0 : 1;
112         }
113
114         function get_plural_forms_count() {
115                 return 2;
116         }
117
118         /**
119          * @param string $singular
120          * @param string $plural
121          * @param int    $count
122          * @param string $context
123          */
124         function translate_plural($singular, $plural, $count, $context = null) {
125                 $entry = new Translation_Entry(array('singular' => $singular, 'plural' => $plural, 'context' => $context));
126                 $translated = $this->translate_entry($entry);
127                 $index = $this->select_plural_form($count);
128                 $total_plural_forms = $this->get_plural_forms_count();
129                 if ($translated && 0 <= $index && $index < $total_plural_forms &&
130                                 is_array($translated->translations) &&
131                                 isset($translated->translations[$index]))
132                         return $translated->translations[$index];
133                 else
134                         return 1 == $count? $singular : $plural;
135         }
136
137         /**
138          * Merge $other in the current object.
139          *
140          * @param Object &$other Another Translation object, whose translations will be merged in this one
141          * @return void
142          **/
143         function merge_with(&$other) {
144                 foreach( $other->entries as $entry ) {
145                         $this->entries[$entry->key()] = $entry;
146                 }
147         }
148
149         function merge_originals_with(&$other) {
150                 foreach( $other->entries as $entry ) {
151                         if ( !isset( $this->entries[$entry->key()] ) )
152                                 $this->entries[$entry->key()] = $entry;
153                         else
154                                 $this->entries[$entry->key()]->merge_with($entry);
155                 }
156         }
157 }
158
159 class Gettext_Translations extends Translations {
160         /**
161          * The gettext implementation of select_plural_form.
162          *
163          * It lives in this class, because there are more than one descendand, which will use it and
164          * they can't share it effectively.
165          *
166          * @param int $count
167          */
168         function gettext_select_plural_form($count) {
169                 if (!isset($this->_gettext_select_plural_form) || is_null($this->_gettext_select_plural_form)) {
170                         list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
171                         $this->_nplurals = $nplurals;
172                         $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
173                 }
174                 return call_user_func($this->_gettext_select_plural_form, $count);
175         }
176
177         /**
178          * @param string $header
179          * @return array
180          */
181         function nplurals_and_expression_from_header($header) {
182                 if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches)) {
183                         $nplurals = (int)$matches[1];
184                         $expression = trim($this->parenthesize_plural_exression($matches[2]));
185                         return array($nplurals, $expression);
186                 } else {
187                         return array(2, 'n != 1');
188                 }
189         }
190
191         /**
192          * Makes a function, which will return the right translation index, according to the
193          * plural forms header
194          * @param int    $nplurals
195          * @param string $expression
196          */
197         function make_plural_form_function($nplurals, $expression) {
198                 $expression = str_replace('n', '$n', $expression);
199                 $func_body = "
200                         \$index = (int)($expression);
201                         return (\$index < $nplurals)? \$index : $nplurals - 1;";
202                 return create_function('$n', $func_body);
203         }
204
205         /**
206          * Adds parentheses to the inner parts of ternary operators in
207          * plural expressions, because PHP evaluates ternary oerators from left to right
208          *
209          * @param string $expression the expression without parentheses
210          * @return string the expression with parentheses added
211          */
212         function parenthesize_plural_exression($expression) {
213                 $expression .= ';';
214                 $res = '';
215                 $depth = 0;
216                 for ($i = 0; $i < strlen($expression); ++$i) {
217                         $char = $expression[$i];
218                         switch ($char) {
219                                 case '?':
220                                         $res .= ' ? (';
221                                         $depth++;
222                                         break;
223                                 case ':':
224                                         $res .= ') : (';
225                                         break;
226                                 case ';':
227                                         $res .= str_repeat(')', $depth) . ';';
228                                         $depth= 0;
229                                         break;
230                                 default:
231                                         $res .= $char;
232                         }
233                 }
234                 return rtrim($res, ';');
235         }
236
237         /**
238          * @param string $translation
239          * @return array
240          */
241         function make_headers($translation) {
242                 $headers = array();
243                 // sometimes \ns are used instead of real new lines
244                 $translation = str_replace('\n', "\n", $translation);
245                 $lines = explode("\n", $translation);
246                 foreach($lines as $line) {
247                         $parts = explode(':', $line, 2);
248                         if (!isset($parts[1])) continue;
249                         $headers[trim($parts[0])] = trim($parts[1]);
250                 }
251                 return $headers;
252         }
253
254         /**
255          * @param string $header
256          * @param string $value
257          */
258         function set_header($header, $value) {
259                 parent::set_header($header, $value);
260                 if ('Plural-Forms' == $header) {
261                         list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
262                         $this->_nplurals = $nplurals;
263                         $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
264                 }
265         }
266 }
267 endif;
268
269 if ( !class_exists( 'NOOP_Translations' ) ):
270 /**
271  * Provides the same interface as Translations, but doesn't do anything
272  */
273 class NOOP_Translations {
274         var $entries = array();
275         var $headers = array();
276
277         function add_entry($entry) {
278                 return true;
279         }
280
281         function set_header($header, $value) {
282         }
283
284         function set_headers($headers) {
285         }
286
287         function get_header($header) {
288                 return false;
289         }
290
291         function translate_entry(&$entry) {
292                 return false;
293         }
294
295         /**
296          * @param string $singular
297          * @param string $context
298          */
299         function translate($singular, $context=null) {
300                 return $singular;
301         }
302
303         function select_plural_form($count) {
304                 return 1 == $count? 0 : 1;
305         }
306
307         function get_plural_forms_count() {
308                 return 2;
309         }
310
311         /**
312          * @param string $singular
313          * @param string $plural
314          * @param int    $count
315          * @param string $context
316          */
317         function translate_plural($singular, $plural, $count, $context = null) {
318                         return 1 == $count? $singular : $plural;
319         }
320
321         function merge_with(&$other) {
322         }
323 }
324 endif;