l10n_server-2.x-dev/l10n_community/src/L10nTranslator.php
l10n_community/src/L10nTranslator.php
<?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 * Filters, with following elements: * - '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. */ public function getStrings(string $langcode, array $filters, ?int $pager = NULL): array { $query = $this->connection ->select('l10n_server_string', 's'); $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 AND t.suggestion = 0 AND ts.has_translation = 1'); // The translation table has a 1:N relationship to sources. We need to limit // results to unique sids. A group by on sid appears to be the fastest // solution (as opposed to eg. DISTINCT). // @note the D7 version also applied a groupBy('NULL'), but it just makes // the query crash. $query->groupBy('sid'); $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); } } // 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', '%' . $this->connection->escapeLike($search) . '%', 'LIKE') ->condition('t.translation', '%' . $this->connection->escapeLike($search) . '%', 'LIKE') ); } // Restriction based on string status by translation / suggestions. if (isset($filters['status'])) { if ($filters['status'] & L10N_COMMUNITY_STATUS_UNTRANSLATED) { $query->condition((new Condition('OR')) ->condition('ts.has_translation', 0) ->isNull('ts.has_translation') ); } elseif ($filters['status'] & L10N_COMMUNITY_STATUS_TRANSLATED) { $query->condition('ts.has_translation', '1'); } elseif ($filters['status'] & L10N_COMMUNITY_STATUS_IS_TRANSLATION) { $query->condition('t.is_suggestion', '0'); } if ($filters['status'] & L10N_COMMUNITY_STATUS_HAS_SUGGESTION) { $query->condition('ts.has_suggestion', '1'); } elseif ($filters['status'] & L10N_COMMUNITY_STATUS_NO_SUGGESTION) { $query->condition((new Condition('OR')) ->condition('ts.has_suggestion', 0) ->isNull('ts.has_suggestion') ); } elseif ($filters['status'] & L10N_COMMUNITY_STATUS_IS_SUGGESTION) { $query->condition('t.is_suggestion', '1'); } } // We either need a pager or a full result. if (isset($pager)) { $query = $query ->extend('Drupal\Core\Database\Query\PagerSelectExtender') ->limit($pager); } $strings = $query->execute() ->fetchAllAssoc('sid'); if (!empty($strings)) { // Also load suggestions, and add them. foreach ($this->getSuggestions($langcode, array_keys($strings)) as $suggestion) { $strings[$suggestion->sid]->suggestions[] = $suggestion; } } return $strings; } /** * Get suggestions for a list of source strings. * * @param string $langcode * Language code to use for the lookup. * @param array $sids * Source string IDs. * * @return array * Suggestions. */ public function getSuggestions(string $langcode, array $sids): array { $query = $this->connection ->select('l10n_server_translation', 't'); $query->leftJoin('users_field_data', 'u', 'u.uid = t.uid'); $query->fields('t', [ 'tid', 'sid', 'translation', 'uid', 'created', 'changed', 'status', 'suggestion', ]); $query->addField('u', 'name', 'username'); $query->condition('t.language', $langcode); $query->condition('t.sid', $sids, 'IN'); $query->condition('t.status', 1); $query->condition('t.suggestion', 1); $query->orderBy('t.created'); return $query->execute()->fetchAll(); } /** * List of project releases where a source string is used. */ public function getSourceDetails($sid) { $query = $this->connection ->select('l10n_server_line', 'l'); $query->innerJoin('l10n_server_project', 'p', 'l.pid = p.pid'); $query->innerJoin('l10n_server_release', 'r', 'l.rid = r.rid'); $query->fields('l', ['pid', 'rid']); $query->addField('p', 'title', 'project_title'); $query->addField('r', 'version'); $query->addExpression('COUNT(l.lineno)', 'occurrence_count'); $query->condition('l.sid', $sid) ->condition('p.status', 1) ->groupBy('l.rid') ->orderBy('l.pid') ->orderBy('l.rid'); return $query->execute()->fetchAll(); } }