/node_modules/grunt-wp-i18n/vendor/wp-i18n-tools/pomo/translations.php

https://gitlab.com/CueFox/cf-utility-pro · PHP · 284 lines · 191 code · 31 blank · 62 comment · 27 complexity · 7247c526786572dd45c9b8d288f01865 MD5 · raw file

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