Unverified Commit 9655e6ff authored by Stefan Auditor's avatar Stefan Auditor
Browse files

Issue #3282696 by sanduhrs: Port of l10n_community: Add translator service

parent 6c63a28c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -8,3 +8,6 @@ services:
  l10n_community.access:
    class: Drupal\l10n_community\L10nAccess
    arguments: ['@current_route_match', '@module_handler']
  l10n_community.translator:
    class: Drupal\l10n_community\L10nTranslator
    arguments: ['@database']
+199 −0
Original line number Diff line number Diff line
<?php
declare(strict_types=1);

namespace Drupal\l10n_community;

use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\Condition;

/**
 * Service description.
 */
class L10nTranslator {

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected Connection $connection;

  /**
   * Constructs a L10nTranslator object.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection.
   */
  public function __construct(
      Connection $connection
  ) {
    $this->connection = $connection;
  }

  /**
   * Get strings under some conditions.
   *
   * @param string $langcode
   *   Language code to use for the lookup.
   * @param array $filters
   *   - 'project'
   *     Project object to look up strings for.
   *   - 'status'
   *     Filter strings by status. See L10N_STATUS_ALL,
   *     L10N_STATUS_UNTRANSLATED, L10N_STATUS_HAS_SUGGESTION and
   *     L10N_STATUS_TRANSLATED.
   *   - 'release'
   *     Release id of the particular project release to filter with.
   *     Use NULL to not filter on releases.
   *   - 'search'
   *     Substring to search for in all source and translation strings.
   *   - 'context'
   *     From Drupal 7, separate contexts are supported. POTX_CONTEXT_NONE is
   *     the default, if the code does not specify a context otherwise.
   * @param int|null $pager
   *   Number of strings to be returned in a pager. Should be NULL if
   *   no pager should be used.
   *
   * @return array
   *   An array of string records from database.
   */
  function getStrings(string $langcode, array $filters, int $pager = NULL): array {
    $query = $this->connection
      ->select('l10n_server_string', 's')
      ->distinct();
    $query
      ->leftJoin('l10n_server_status_flag', 'ts', 's.sid = ts.sid AND ts.language = :language', [
        ':language' => $langcode,
      ]);
    $query
      ->leftJoin('l10n_server_translation', 't', 'ts.sid = t.sid AND ts.language = t.language AND t.status = 1');
    $query
      ->leftJoin('users_field_data', 'u', 'u.uid = t.uid');
    $query
      ->fields('s', ['sid', 'value', 'context'])
      ->fields('t', [
        'tid', 'language', 'translation', 'uid', 'created', 'changed', 'suggestion', 'status',
      ])
      ->fields('ts', ['has_suggestion', 'has_translation']);
    $query
      ->addField('u', 'name', 'username');

    // Add sid filtering.
    if (!empty($filters['sid'])) {
      $query->condition('s.sid', $filters['sid']);
    }

    // Add submitted by condition
    if (!empty($filters['author'])) {
      $query->condition('t.uid', $filters['author']->id());
    }

    // Release restriction.
    $release = empty($filters['release']) || $filters['release'] === 'all' ? NULL : $filters['release'];
    $project = $filters['project'];
    if ($release || $project) {
      $query->innerJoin('l10n_server_line', 'l', 's.sid = l.sid');
      // If we have a release we ignore the project.
      if ($release) {
        // Release restriction.
        $query->condition('l.rid', $release);
      }
      elseif ($project) {
        $query->condition('l.pid', $project->pid);
      }
    }

    // Context based filtering.
    if (isset($filters['context']) && $filters['context'] != 'all') {
      // We use 'none' for no context, so '' can be the defaut (for all contexts).
      $context = $filters['context'] == 'none' ? '' : $filters['context'];
      $query->condition('s.context', $context);
    }

    if (!empty($filters['search'])) {
      // Search in the source or target strings.
      $search = $filters['search'];
      $query->condition((new Condition('OR'))
        ->condition('s.value', '%' . db_like($search) . '%', 'LIKE')
        ->condition('t.translation', '%' . db_like($search) . '%', 'LIKE')
      );
    }

    // Restriction based on string status by translation / suggestions.
    if (isset($filters['status'])) {
      if ($filters['status'] & L10N_STATUS_UNTRANSLATED) {
        $query->condition((new Condition('OR'))
          ->condition('ts.has_translation', 0)
          ->isNull('ts.has_translation')
        );
      }
      elseif ($filters['status'] & L10N_STATUS_TRANSLATED) {
        $query->condition('ts.has_translation', '1');
      }
      elseif ($filters['status'] & L10N_STATUS_IS_TRANSLATION) {
        $query->condition('t.is_suggestion', '0');
      }

      if ($filters['status'] & L10N_STATUS_HAS_SUGGESTION) {
        $query->condition('ts.has_suggestion', '1');
      }
      elseif ($filters['status'] & L10N_STATUS_NO_SUGGESTION) {
        $query->condition((new Condition('OR'))
          ->condition('ts.has_suggestion', 0)
          ->isNull('ts.has_suggestion')
        );
      }
      elseif ($filters['status'] & L10N_STATUS_IS_SUGGESTION) {
        $query->condition('t.is_suggestion', '1');
      }
    }

    // We either need a pager or a full result.
    if (isset($pager)) {
      $strings = $query
        ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
        ->limit($pager)
        ->execute();
    }
    else {
      $strings = $query->execute();
    }
    $result = [];
    foreach ($strings as $string) {
      if ($string->suggestion) {
        // This string is not a translation, but we need that as a "parent" to display.
        if (!$string->has_translation) {
          // No parent translation. Pretend this does not exist.
          // The display code will call for the suggestions.
          $string->translation = '';
        }
        else {
          $q = $this->connection
            ->select('l10n_server_translation', 't');
          $q->leftJoin('users', 'u', 'u.uid = t.uid');
          $q->fields('t', [
            'tid', 'translation', 'uid', 'created', 'changed', 'suggestion', 'status',
          ]);
          $q->addField('u', 'name', 'username');
          $q->condition('t.language',  $string->language)
            ->condition('t.sid', $string->sid)
            ->condition('t.status', 1)
            ->condition('t.suggestion', 0);
          $translation = $q
            ->execute()
            ->fetchAssoc();

          if (!empty($translation)) {
            // It does have a translation however, so let's load it, and override.
            foreach ($translation as $key => $value) {
              $string->$key = $value;
            }
          }
        }
      }
      $result[] = $string;
    }
    return $result;
  }

}