3 * Class for a set of entries for translation and their associated headers
5 * @version $Id: translations.php 291 2009-10-21 05:46:08Z nbachiyski $
7 * @subpackage translations
10 require_once dirname(__FILE__) . '/entry.php';
12 if ( !class_exists( 'Translations' ) ):
14 var $entries = array();
15 var $headers = array();
18 * Add entry to the PO structure
20 * @param object &$entry
21 * @return bool true on success, false if the entry doesn't have a key
23 function add_entry($entry) {
24 if (is_array($entry)) {
25 $entry = new Translation_Entry($entry);
28 if (false === $key) return false;
29 $this->entries[$key] = &$entry;
34 * Sets $header PO header to $value
36 * If the header already exists, it will be overwritten
38 * TODO: this should be out of this class, it is gettext specific
40 * @param string $header header name, without trailing :
41 * @param string $value header value, without trailing \n
43 function set_header($header, $value) {
44 $this->headers[$header] = $value;
47 function set_headers(&$headers) {
48 foreach($headers as $header => $value) {
49 $this->set_header($header, $value);
53 function get_header($header) {
54 return isset($this->headers[$header])? $this->headers[$header] : false;
57 function translate_entry(&$entry) {
59 return isset($this->entries[$key])? $this->entries[$key] : false;
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;
69 * Given the number of items, returns the 0-based index of the plural form to use
71 * Here, in the base Translations class, the commong logic for English is implmented:
72 * 0 if there is one element, 1 otherwise
74 * This function should be overrided by the sub-classes. For example MO/PO can derive the logic
77 * @param integer $count number of items
79 function select_plural_form($count) {
80 return 1 == $count? 0 : 1;
83 function get_plural_forms_count() {
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];
97 return 1 == $count? $singular : $plural;
101 * Merge $other in the current object.
103 * @param Object &$other Another Translation object, whose translations will be merged in this one
106 function merge_with(&$other) {
107 $this->entries = array_merge($this->entries, $other->entries);
111 class Gettext_Translations extends Translations {
113 * The gettext implmentation of select_plural_form.
115 * It lives in this class, because there are more than one descendand, which will use it and
116 * they can't share it effectively.
119 function gettext_select_plural_form($count) {
120 if (!isset($this->_gettext_select_plural_form) || is_null($this->_gettext_select_plural_form)) {
121 list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
122 $this->_nplurals = $nplurals;
123 $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
125 return call_user_func($this->_gettext_select_plural_form, $count);
128 function nplurals_and_expression_from_header($header) {
129 if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches)) {
130 $nplurals = (int)$matches[1];
131 $expression = trim($this->parenthesize_plural_exression($matches[2]));
132 return array($nplurals, $expression);
134 return array(2, 'n != 1');
139 * Makes a function, which will return the right translation index, according to the
140 * plural forms header
142 function make_plural_form_function($nplurals, $expression) {
143 $expression = str_replace('n', '$n', $expression);
145 \$index = (int)($expression);
146 return (\$index < $nplurals)? \$index : $nplurals - 1;";
147 return create_function('$n', $func_body);
151 * Adds parantheses to the inner parts of ternary operators in
152 * plural expressions, because PHP evaluates ternary oerators from left to right
154 * @param string $expression the expression without parentheses
155 * @return string the expression with parentheses added
157 function parenthesize_plural_exression($expression) {
161 for ($i = 0; $i < strlen($expression); ++$i) {
162 $char = $expression[$i];
172 $res .= str_repeat(')', $depth) . ';';
179 return rtrim($res, ';');
182 function make_headers($translation) {
184 // sometimes \ns are used instead of real new lines
185 $translation = str_replace('\n', "\n", $translation);
186 $lines = explode("\n", $translation);
187 foreach($lines as $line) {
188 $parts = explode(':', $line, 2);
189 if (!isset($parts[1])) continue;
190 $headers[trim($parts[0])] = trim($parts[1]);
195 function set_header($header, $value) {
196 parent::set_header($header, $value);
197 if ('Plural-Forms' == $header) {
198 list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
199 $this->_nplurals = $nplurals;
200 $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
206 if ( !class_exists( 'NOOP_Translations' ) ):
208 * Provides the same interface as Translations, but doesn't do anything
210 class NOOP_Translations {
211 var $entries = array();
212 var $headers = array();
214 function add_entry($entry) {
218 function set_header($header, $value) {
221 function set_headers(&$headers) {
224 function get_header($header) {
228 function translate_entry(&$entry) {
232 function translate($singular, $context=null) {
236 function select_plural_form($count) {
237 return 1 == $count? 0 : 1;
240 function get_plural_forms_count() {
244 function translate_plural($singular, $plural, $count, $context = null) {
245 return 1 == $count? $singular : $plural;
248 function merge_with(&$other) {