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