]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/pomo/translations.php
Wordpress 3.1
[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 406 2010-02-07 11:10:24Z 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 object &$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          * Sets $header PO header to $value
35          *
36          * If the header already exists, it will be overwritten
37          *
38          * TODO: this should be out of this class, it is gettext specific
39          *
40          * @param string $header header name, without trailing :
41          * @param string $value header value, without trailing \n
42          */
43         function set_header($header, $value) {
44                 $this->headers[$header] = $value;
45         }
46
47         function set_headers(&$headers) {
48                 foreach($headers as $header => $value) {
49                         $this->set_header($header, $value);
50                 }
51         }
52
53         function get_header($header) {
54                 return isset($this->headers[$header])? $this->headers[$header] : false;
55         }
56
57         function translate_entry(&$entry) {
58                 $key = $entry->key();
59                 return isset($this->entries[$key])? $this->entries[$key] : false;
60         }
61
62         function translate($singular, $context=null) {
63                 $entry = new Translation_Entry(array('singular' => $singular, 'context' => $context));
64                 $translated = $this->translate_entry($entry);
65                 return ($translated && !empty($translated->translations))? $translated->translations[0] : $singular;
66         }
67
68         /**
69          * Given the number of items, returns the 0-based index of the plural form to use
70          *
71          * Here, in the base Translations class, the commong logic for English is implmented:
72          *      0 if there is one element, 1 otherwise
73          *
74          * This function should be overrided by the sub-classes. For example MO/PO can derive the logic
75          * from their headers.
76          *
77          * @param integer $count number of items
78          */
79         function select_plural_form($count) {
80                 return 1 == $count? 0 : 1;
81         }
82
83         function get_plural_forms_count() {
84                 return 2;
85         }
86
87         function translate_plural($singular, $plural, $count, $context = null) {
88                 $entry = new Translation_Entry(array('singular' => $singular, 'plural' => $plural, 'context' => $context));
89                 $translated = $this->translate_entry($entry);
90                 $index = $this->select_plural_form($count);
91                 $total_plural_forms = $this->get_plural_forms_count();
92                 if ($translated && 0 <= $index && $index < $total_plural_forms &&
93                                 is_array($translated->translations) &&
94                                 isset($translated->translations[$index]))
95                         return $translated->translations[$index];
96                 else
97                         return 1 == $count? $singular : $plural;
98         }
99
100         /**
101          * Merge $other in the current object.
102          *
103          * @param Object &$other Another Translation object, whose translations will be merged in this one
104          * @return void
105          **/
106         function merge_with(&$other) {
107                 foreach( $other->entries as $entry ) {
108                         $this->entries[$entry->key()] = $entry;
109                 }
110         }
111 }
112
113 class Gettext_Translations extends Translations {
114         /**
115          * The gettext implmentation of select_plural_form.
116          *
117          * It lives in this class, because there are more than one descendand, which will use it and
118          * they can't share it effectively.
119          *
120          */
121         function gettext_select_plural_form($count) {
122                 if (!isset($this->_gettext_select_plural_form) || is_null($this->_gettext_select_plural_form)) {
123                         list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
124                         $this->_nplurals = $nplurals;
125                         $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
126                 }
127                 return call_user_func($this->_gettext_select_plural_form, $count);
128         }
129
130         function nplurals_and_expression_from_header($header) {
131                 if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches)) {
132                         $nplurals = (int)$matches[1];
133                         $expression = trim($this->parenthesize_plural_exression($matches[2]));
134                         return array($nplurals, $expression);
135                 } else {
136                         return array(2, 'n != 1');
137                 }
138         }
139
140         /**
141          * Makes a function, which will return the right translation index, according to the
142          * plural forms header
143          */
144         function make_plural_form_function($nplurals, $expression) {
145                 $expression = str_replace('n', '$n', $expression);
146                 $func_body = "
147                         \$index = (int)($expression);
148                         return (\$index < $nplurals)? \$index : $nplurals - 1;";
149                 return create_function('$n', $func_body);
150         }
151
152         /**
153          * Adds parantheses to the inner parts of ternary operators in
154          * plural expressions, because PHP evaluates ternary oerators from left to right
155          *
156          * @param string $expression the expression without parentheses
157          * @return string the expression with parentheses added
158          */
159         function parenthesize_plural_exression($expression) {
160                 $expression .= ';';
161                 $res = '';
162                 $depth = 0;
163                 for ($i = 0; $i < strlen($expression); ++$i) {
164                         $char = $expression[$i];
165                         switch ($char) {
166                                 case '?':
167                                         $res .= ' ? (';
168                                         $depth++;
169                                         break;
170                                 case ':':
171                                         $res .= ') : (';
172                                         break;
173                                 case ';':
174                                         $res .= str_repeat(')', $depth) . ';';
175                                         $depth= 0;
176                                         break;
177                                 default:
178                                         $res .= $char;
179                         }
180                 }
181                 return rtrim($res, ';');
182         }
183
184         function make_headers($translation) {
185                 $headers = array();
186                 // sometimes \ns are used instead of real new lines
187                 $translation = str_replace('\n', "\n", $translation);
188                 $lines = explode("\n", $translation);
189                 foreach($lines as $line) {
190                         $parts = explode(':', $line, 2);
191                         if (!isset($parts[1])) continue;
192                         $headers[trim($parts[0])] = trim($parts[1]);
193                 }
194                 return $headers;
195         }
196
197         function set_header($header, $value) {
198                 parent::set_header($header, $value);
199                 if ('Plural-Forms' == $header) {
200                         list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
201                         $this->_nplurals = $nplurals;
202                         $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
203                 }
204         }
205 }
206 endif;
207
208 if ( !class_exists( 'NOOP_Translations' ) ):
209 /**
210  * Provides the same interface as Translations, but doesn't do anything
211  */
212 class NOOP_Translations {
213         var $entries = array();
214         var $headers = array();
215
216         function add_entry($entry) {
217                 return true;
218         }
219
220         function set_header($header, $value) {
221         }
222
223         function set_headers(&$headers) {
224         }
225
226         function get_header($header) {
227                 return false;
228         }
229
230         function translate_entry(&$entry) {
231                 return false;
232         }
233
234         function translate($singular, $context=null) {
235                 return $singular;
236         }
237
238         function select_plural_form($count) {
239                 return 1 == $count? 0 : 1;
240         }
241
242         function get_plural_forms_count() {
243                 return 2;
244         }
245
246         function translate_plural($singular, $plural, $count, $context = null) {
247                         return 1 == $count? $singular : $plural;
248         }
249
250         function merge_with(&$other) {
251         }
252 }
253 endif;