[ Index ]

PHP Cross Reference of GlotPress

title

Body

[close]

/gp-templates/ -> helper-functions.php (source)

   1  <?php
   2  /**
   3   * Defines helper functions used by GlotPress.
   4   *
   5   * @package GlotPress
   6   * @since 1.0.0
   7   */
   8  
   9  /**
  10   * Prepare an original string to be printed out in a translation row by adding encoding special
  11   * characters, adding glossary entires and other markup.
  12   *
  13   * @param string $text A single style handle to enqueue or an array or style handles to enqueue.
  14   *
  15   * @return string The prepared string for output.
  16   */
  17  function prepare_original( $text ) {
  18      // Glossaries are injected into the translations prior to escaping and prepare_original() being run.
  19      $glossary_entries = array();
  20      $text             = preg_replace_callback(
  21          '!(<span class="glossary-word"[^>]+>)!i',
  22          function( $m ) use ( &$glossary_entries ) {
  23              $item_number                      = count( $glossary_entries );
  24              $glossary_entries[ $item_number ] = $m[0];
  25              return "<span GLOSSARY={$item_number}>";
  26          },
  27          $text
  28      );
  29  
  30      // Wrap full HTML tags with a notranslate class.
  31      $text = preg_replace( '/(&lt;.+?&gt;)/', '<span class="notranslate">\\1</span>', $text );
  32      // Break out & back into notranslate for translatable attributes.
  33      $text = preg_replace( '/(title|aria-label)=([\'"])([^\\2]+?)\\2/', '\\1=\\2</span>\\3<span class="notranslate">\\2', $text );
  34      // Wrap placeholders with notranslate.
  35      $text = preg_replace( '/(%(\d+\$(?:\d+)?)?[bcdefgosuxEFGX])/', '<span class="notranslate">\\1</span>', $text );
  36  
  37      // Put the glossaries back!
  38      $text = preg_replace_callback(
  39          '!(<span GLOSSARY=(\d+)>)!',
  40          function( $m ) use ( $glossary_entries ) {
  41              return $glossary_entries[ $m[2] ];
  42          },
  43          $text
  44      );
  45  
  46      $text = str_replace( array( "\r", "\n" ), "<span class='invisibles' title='" . esc_attr__( 'New line', 'glotpress' ) . "'>&crarr;</span>\n", $text );
  47      $text = str_replace( "\t", "<span class='invisibles' title='" . esc_attr__( 'Tab character', 'glotpress' ) . "'>&rarr;</span>\t", $text );
  48  
  49      return $text;
  50  }
  51  
  52  /**
  53   * Prepares a translation string to be printed out in a translation row by adding an 'extra' return/newline if
  54   * it starts with one.
  55   *
  56   * @since 3.0.0
  57   *
  58   * @param string $text A single style handle to enqueue or an array or style handles to enqueue.
  59   * @return string The prepared string for output.
  60   */
  61  function gp_prepare_translation_textarea( $text ) {
  62      if ( gp_startswith( $text, "\r\n" ) ) {
  63          $text = "\r\n" . $text;
  64      } elseif ( gp_startswith( $text, "\n" ) ) {
  65          $text = "\n" . $text;
  66      }
  67  
  68      return $text;
  69  }
  70  
  71  /**
  72   * Adds suffixes for use in map_glossary_entries_to_translation_originals().
  73   *
  74   * @param array $glossary_entries An array of glossary entries to sort.
  75   *
  76   * @return array The suffixed entries.
  77   */
  78  function gp_glossary_add_suffixes( $glossary_entries ) {
  79      if ( empty( $glossary_entries ) ) {
  80          return;
  81      }
  82  
  83      $glossary_entries_suffixes = array();
  84  
  85      // Create array of glossary terms, longest first.
  86      foreach ( $glossary_entries as $key => $value ) {
  87          $term = strtolower( $value->term );
  88  
  89          // Check if is multiple word term.
  90          if ( preg_match( '/\s/', $term ) ) {
  91  
  92              // Don't add suffix to terms with multiple words.
  93              $glossary_entries_suffixes[ $term ] = array();
  94              continue;
  95          }
  96  
  97          $suffixes = array();
  98          if ( 'y' === substr( $term, -1 ) ) {
  99              $term = substr( $term, 0, -1 );
 100              $suffixes[] = 'y';
 101              $suffixes[] = 'ies';
 102              $suffixes[] = 'ys';
 103          } elseif ( 'f' === substr( $term, -1 ) ) {
 104              $term = substr( $term, 0, -1 );
 105              $suffixes[] = 'f';
 106              $suffixes[] = 'ves';
 107              $terms[] = substr( $term, 0, -1 ) . 'ves';
 108          } elseif ( 'fe' === substr( $term, -2 ) ) {
 109              $term = substr( $term, 0, -2 );
 110              $suffixes[] = 'fe';
 111              $suffixes[] = 'ves';
 112          } elseif ( 'an' === substr( $term, -2 ) ) {
 113              $term = substr( $term, 0, -2 );
 114              $suffixes[] = 'an';
 115              $suffixes[] = 'en';
 116          } else {
 117              $suffixes[] = 's';
 118              $suffixes[] = 'es';
 119              $suffixes[] = 'ed';
 120              $suffixes[] = 'ing';
 121          }
 122  
 123          $glossary_entries_suffixes[ $term ] = $suffixes;
 124      }
 125  
 126      // Sort by length in descending order.
 127      uksort(
 128          $glossary_entries_suffixes,
 129          function( $a, $b ) {
 130              return mb_strlen( $b ) <=> mb_strlen( $a );
 131          }
 132      );
 133  
 134      return $glossary_entries_suffixes;
 135  }
 136  
 137  /**
 138   * Add markup to a translation original to identify the glossary terms.
 139   *
 140   * @param GP_Translation $translation            A GP Translation object.
 141   * @param GP_Glossary    $glossary               A GP Glossary object.
 142   *
 143   * @return obj The marked up translation entry.
 144   */
 145  function map_glossary_entries_to_translation_originals( $translation, $glossary ) {
 146      static $terms_search, $glossary_entries_reference, $glossary_entries, $cached_glossary;
 147      if ( isset( $terms_search ) && isset( $cached_glossary ) && $cached_glossary === $glossary->id ) {
 148          if ( ! $terms_search ) {
 149              return $translation;
 150          }
 151      } else {
 152          // Build our glossary search.
 153          $glossary_entries = $glossary->get_entries();
 154          $cached_glossary = $glossary->id;
 155          if ( empty( $glossary_entries ) ) {
 156              $terms_search = false;
 157              return $translation;
 158          }
 159  
 160          $glossary_entries_suffixes = gp_glossary_add_suffixes( $glossary_entries );
 161  
 162          $glossary_entries_reference = array();
 163          foreach ( $glossary_entries as $id => $value ) {
 164              $term = strtolower( $value->term );
 165              if ( ! isset( $glossary_entries_reference[ $term ] ) ) {
 166                  $glossary_entries_reference[ $term ] = array( $id );
 167                  continue;
 168              }
 169              $glossary_entries_reference[ $term ][] = $id;
 170          }
 171  
 172          $terms_search = '\b(';
 173          foreach ( $glossary_entries_suffixes as $term => $suffixes ) {
 174              $terms_search .= preg_quote( $term, '/' );
 175  
 176              if ( ! empty( $suffixes ) ) {
 177                  $terms_search .= '(?:' . implode( '|', $suffixes ) . ')?';
 178              }
 179  
 180              $terms_search .= '|';
 181  
 182              $referenced_term = $term;
 183              if ( ! isset( $glossary_entries_reference[ $referenced_term ] ) ) {
 184                  foreach ( $suffixes as $suffix ) {
 185                      if ( isset( $glossary_entries_reference[ $term . $suffix ] ) ) {
 186                          $referenced_term = $term . $suffix;
 187                      }
 188                  }
 189                  if ( ! isset( $glossary_entries_reference[ $referenced_term ] ) ) {
 190                      // This should not happen but we don't want to access a non existing item below.
 191                      continue;
 192                  }
 193              }
 194  
 195              $referenced_term = $glossary_entries_reference[ $referenced_term ];
 196              // Add the suffixed terms to the lookup table.
 197              foreach ( $suffixes as $suffix ) {
 198                  if ( isset( $glossary_entries_reference[ $term . $suffix ] ) ) {
 199                      $glossary_entries_reference[ $term . $suffix ] = array_values( array_unique( array_merge( $glossary_entries_reference[ $term . $suffix ], $referenced_term ) ) );
 200                  } else {
 201                      $glossary_entries_reference[ $term . $suffix ] = $referenced_term;
 202                  }
 203              }
 204          }
 205  
 206          // Remove the trailing |.
 207          $terms_search = substr( $terms_search, 0, -1 );
 208          $terms_search .= ')\b';
 209      }
 210  
 211      // Split the singular string on glossary terms boundaries.
 212      $singular_split = preg_split( '/' . $terms_search . '/i', $translation->singular, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
 213  
 214      // Loop through each chunk of the split to find glossary terms.
 215      if ( is_array( $singular_split ) ) {
 216          $singular_combined = '';
 217  
 218          foreach ( $singular_split as $chunk ) {
 219              // Create an escaped version for use later on.
 220              $escaped_chunk = esc_translation( $chunk );
 221  
 222              // Create a lower case version to compare with the glossary terms.
 223              $lower_chunk = strtolower( $chunk );
 224  
 225              // Search the glossary terms for a matching entry.
 226              if ( isset( $glossary_entries_reference[ $lower_chunk ] ) ) {
 227                  $glossary_data = array();
 228  
 229                  // Add glossary data for each matching entry.
 230                  foreach ( $glossary_entries_reference[ $lower_chunk ] as $glossary_entry_id ) {
 231                      // Get the glossary entry based on the back reference we created earlier.
 232                      $glossary_entry = $glossary_entries[ $glossary_entry_id ];
 233  
 234                      // If this is a locale glossary, make a note for the user.
 235                      $locale_entry = '';
 236                      if ( $glossary_entry->glossary_id !== $glossary->id ) {
 237                          /* translators: Denotes an entry from the locale glossary in the tooltip */
 238                          $locale_entry = _x( 'Locale Glossary', 'Bubble', 'glotpress' );
 239                      }
 240  
 241                      // Create the data to be added to the span.
 242                      $glossary_data[] = array(
 243                          'translation'  => $glossary_entry->translation,
 244                          'pos'          => $glossary_entry->part_of_speech,
 245                          'comment'      => $glossary_entry->comment,
 246                          'locale_entry' => $locale_entry,
 247                      );
 248                  }
 249  
 250                  // Add the span and chunk to our output.
 251                  $singular_combined .= '<span class="glossary-word" data-translations="' . htmlspecialchars( wp_json_encode( $glossary_data ), ENT_QUOTES, 'UTF-8' ) . '">' . $escaped_chunk . '</span>';
 252              } else {
 253                  // No term was found so just add the escaped chunk to the output.
 254                  $singular_combined .= $escaped_chunk;
 255              }
 256          }
 257  
 258          // Assign the output to the translation.
 259          $translation->singular_glossary_markup = $singular_combined;
 260      } else {
 261          $translation->singular_glossary_markup = esc_translation( $translation->singular );
 262      }
 263  
 264      // Add glossary terms to the plural if we have one.
 265      if ( $translation->plural ) {
 266          // Split the plural string on glossary terms boundaries.
 267          $plural_split = @preg_split( '/' . $terms_search . '/i', $translation->plural, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
 268  
 269          // Loop through each chunk of the split to find glossary terms.
 270          if ( is_array( $plural_split ) ) {
 271              $plural_combined = '';
 272  
 273              foreach ( $plural_split as $chunk ) {
 274                  // Create an escaped version for use later on.
 275                  $escaped_chunk = esc_translation( $chunk );
 276  
 277                  // Create a lower case version to compare with the glossary terms.
 278                  $lower_chunk = strtolower( $chunk );
 279  
 280                  // Search the glossary terms for a matching entry.
 281                  if ( isset( $glossary_entries_reference[ $lower_chunk ] ) ) {
 282                      $glossary_data = array();
 283  
 284                      // Add glossary data for each matching entry.
 285                      foreach ( $glossary_entries_reference[ $lower_chunk ] as $glossary_entry_id ) {
 286                          // Get the glossary entry based on the back reference we created earlier.
 287                          $glossary_entry = $glossary_entries[ $glossary_entry_id ];
 288  
 289                          // If this is a locale glossary, make a note for the user.
 290                          $locale_entry = '';
 291                          if ( $glossary_entry->glossary_id !== $glossary->id ) {
 292                              /* translators: Denotes an entry from the locale glossary in the tooltip */
 293                              $locale_entry = _x( 'Locale Glossary', 'Bubble', 'glotpress' );
 294                          }
 295  
 296                          // Create the data to be added to the span.
 297                          $glossary_data[] = array(
 298                              'translation'  => $glossary_entry->translation,
 299                              'pos'          => $glossary_entry->part_of_speech,
 300                              'comment'      => $glossary_entry->comment,
 301                              'locale_entry' => $locale_entry,
 302                          );
 303                      }
 304  
 305                      // Add the span and chunk to our output.
 306                      $plural_combined .= '<span class="glossary-word" data-translations="' . htmlspecialchars( wp_json_encode( $glossary_data ), ENT_QUOTES, 'UTF-8' ) . '">' . $escaped_chunk . '</span>';
 307                  } else {
 308                      // No term was found so just add the escaped chunk to the output.
 309                      $plural_combined .= $escaped_chunk;
 310                  }
 311              }
 312  
 313              // Assign the output to the translation.
 314              $translation->plural_glossary_markup = $plural_combined;
 315          } else {
 316              $translation->plural_glossary_markup = esc_translation( $translation->plural );
 317          }
 318      }
 319  
 320      return $translation;
 321  }
 322  
 323  function textareas( $entry, $permissions, $index = 0 ) {
 324      list( $can_edit, $can_approve ) = $permissions;
 325      ?>
 326      <div class="textareas">
 327          <?php
 328          if ( isset( $entry->warnings[ $index ] ) ) :
 329              $referenceable = $entry->warnings[ $index ];
 330  
 331              foreach ( $referenceable as $key => $value ) :
 332              ?>
 333                  <div class="warning">
 334                      <strong><?php _e( 'Warning:', 'glotpress' ); ?></strong> <?php echo esc_html( $value ); ?>
 335  
 336                      <?php if ( $can_approve ) : ?>
 337                          <a href="#" class="discard-warning" data-nonce="<?php echo esc_attr( wp_create_nonce( 'discard-warning_' . $index . $key ) ); ?>" data-key="<?php echo esc_attr( $key ); ?>" data-index="<?php echo esc_attr( $index ); ?>"><?php _e( 'Discard', 'glotpress' ); ?></a>
 338                      <?php endif; ?>
 339                  </div>
 340          <?php
 341              endforeach;
 342  
 343          endif;
 344          ?>
 345          <blockquote class="translation"><?php echo prepare_original( esc_translation( gp_array_get( $entry->translations, $index ) ) ); ?></blockquote>
 346          <textarea class="foreign-text" name="translation[<?php echo esc_attr( $entry->original_id ); ?>][]" id="translation_<?php echo esc_attr( $entry->original_id ); ?>_<?php echo esc_attr( $index ); ?>" <?php echo disabled( ! $can_edit ); ?>><?php echo gp_prepare_translation_textarea( esc_translation( gp_array_get( $entry->translations, $index ) ) ); ?></textarea>
 347  
 348          <div>
 349              <?php
 350              if ( $can_edit ) {
 351                  gp_entry_actions();
 352              } elseif ( is_user_logged_in() ) {
 353                  _e( 'You are not allowed to edit this translation.', 'glotpress' );
 354              } else {
 355                  printf(
 356                      /* translators: %s: URL. */
 357                      __( 'You <a href="%s">have to log in</a> to edit this translation.', 'glotpress' ),
 358                      esc_url( wp_login_url( gp_url_current() ) )
 359                  );
 360              }
 361              ?>
 362          </div>
 363      </div>
 364      <?php
 365  }
 366  
 367  function display_status( $status ) {
 368      $status_labels = array(
 369          'current'  => _x( 'current', 'Single Status', 'glotpress' ),
 370          'waiting'  => _x( 'waiting', 'Single Status', 'glotpress' ),
 371          'fuzzy'    => _x( 'fuzzy', 'Single Status', 'glotpress' ),
 372          'old'      => _x( 'old', 'Single Status', 'glotpress' ),
 373          'rejected' => _x( 'rejected', 'Single Status', 'glotpress' ),
 374      );
 375      if ( isset( $status_labels[ $status ] ) ) {
 376          $status = $status_labels[ $status ];
 377      }
 378      $status = preg_replace( '/^[+-]/', '', $status );
 379      return $status ? $status : _x( 'untranslated', 'Single Status', 'glotpress' );
 380  }
 381  
 382  function references( $project, $entry ) {
 383  
 384      /**
 385       * Filter whether to show references of a translation string on a translation row.
 386       *
 387       * @since 1.0.0
 388       *
 389       * @param boolean           $references Whether to show references.
 390       * @param GP_Project        $project    The current project.
 391       * @param Translation_Entry $entry      Translation entry object.
 392       */
 393      $show_references = apply_filters( 'gp_show_references', (bool) $entry->references, $project, $entry );
 394  
 395      if ( ! $show_references ) {
 396          return;
 397      }
 398      ?>
 399      <dl><dt>
 400      <?php _e( 'References:', 'glotpress' ); ?>
 401      <ul class="refs">
 402          <?php
 403          foreach ( $entry->references as $reference ) :
 404              list( $file, $line ) = array_pad( explode( ':', $reference ), 2, 0 );
 405              $source_url          = $project->source_url( $file, $line );
 406              if ( $source_url ) :
 407                  ?>
 408                  <li>
 409                      <a target="_blank" tabindex="-1" href="<?php echo esc_url( $source_url ); ?>">
 410                          <?php echo esc_html( $file . ':' . $line ); ?>
 411                      </a>
 412                  </li>
 413                  <?php
 414              else :
 415                  echo '<li>' . esc_html( "$file:$line" ) . '</li>';
 416              endif;
 417          endforeach;
 418          ?>
 419      </ul></dt></dl>
 420  <?php
 421  }
 422  
 423  /**
 424   * Output the bulk actions toolbar in the translations page.
 425   *
 426   * @param string $bulk_action     The URL to submit the form to.
 427   * @param string $can_write       Can the current user write translations to the database.
 428   * @param string $translation_set The current translation set.
 429   * @param string $location        The location of this toolbar, used to make id's unique for each instance on a page.
 430   */
 431  function gp_translations_bulk_actions_toolbar( $bulk_action, $can_write, $translation_set, $location = 'top' ) {
 432  ?>
 433  <form id="bulk-actions-toolbar-<?php echo esc_attr( $location ); ?>" class="bulk-actions" action="<?php echo esc_attr( $bulk_action ); ?>" method="post">
 434      <div>
 435      <select name="bulk[action]" id="bulk-action-<?php echo esc_attr( $location ); ?>" class="bulk-action">
 436          <option value="" selected="selected"><?php _e( 'Bulk Actions', 'glotpress' ); ?></option>
 437          <option value="approve"><?php _ex( 'Approve', 'Action', 'glotpress' ); ?></option>
 438          <option value="reject"><?php _ex( 'Reject', 'Action', 'glotpress' ); ?></option>
 439          <option value="fuzzy"><?php _ex( 'Fuzzy', 'Action', 'glotpress' ); ?></option>
 440      <?php if ( $can_write ) : ?>
 441          <option value="set-priority" class="hide-if-no-js"><?php _e( 'Set Priority', 'glotpress' ); ?></option>
 442      <?php endif; ?>
 443          <?php
 444  
 445          /**
 446           * Fires inside the bulk action menu for translation sets.
 447           *
 448           * Printing out option elements here will add those to the translation
 449           * bulk options drop down menu.
 450           *
 451           * @since 1.0.0
 452           *
 453           * @param GP_Translation_Set $set The translation set.
 454           */
 455          do_action( 'gp_translation_set_bulk_action', $translation_set );
 456          ?>
 457      </select>
 458      <?php if ( $can_write ) : ?>
 459      <select name="bulk[priority]" id="bulk-priority-<?php echo esc_attr( $location ); ?>" class="bulk-priority hidden">
 460      <?php
 461      $labels = array(
 462          'hidden' => _x( 'hidden', 'Priority', 'glotpress' ),
 463          'low'    => _x( 'low', 'Priority', 'glotpress' ),
 464          'normal' => _x( 'normal', 'Priority', 'glotpress' ),
 465          'high'   => _x( 'high', 'Priority', 'glotpress' ),
 466      );
 467  
 468      foreach ( GP::$original->get_static( 'priorities' ) as $value => $label ) {
 469          if ( isset( $labels[ $label ] ) ) {
 470              $label = $labels[ $label ];
 471          }
 472  
 473          echo "\t<option value='" . esc_attr( $value ) . "' " . selected( 'normal', $value, false ) . '>' . esc_html( $label ) . "</option>\n";
 474      }
 475      ?>
 476      </select>
 477      <?php endif; ?>
 478      <input type="hidden" name="bulk[redirect_to]" value="<?php echo esc_attr( gp_url_current() ); ?>" id="bulk-redirect_to-<?php echo esc_attr( $location ); ?>" />
 479      <input type="hidden" name="bulk[row-ids]" value="" id="bulk-row-ids-<?php echo esc_attr( $location ); ?>" />
 480      <input type="submit" class="button" value="<?php esc_attr_e( 'Apply', 'glotpress' ); ?>" />
 481      </div>
 482      <?php
 483          $nonce = gp_route_nonce_field( 'bulk-actions', false );
 484          $nonce = str_replace( 'id="_gp_route_nonce"', 'id="_gp_route_nonce_' . esc_attr( $location ) . '"', $nonce );
 485          // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 486          echo $nonce;
 487      ?>
 488  </form>
 489  <?php
 490  }


Generated: Thu Mar 28 01:01:06 2024 Cross-referenced by PHPXref 0.7.1