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