Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
67.19% |
43 / 64 |
|
33.33% |
2 / 6 |
CRAP | |
0.00% |
0 / 1 |
TranslationAidDataProvider | |
67.19% |
43 / 64 |
|
33.33% |
2 / 6 |
25.04 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getDefinition | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
20 | |||
hasDefinition | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getDefinitionContent | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGoodTranslations | |
88.24% |
15 / 17 |
|
0.00% |
0 / 1 |
4.03 | |||
loadTranslationData | |
100.00% |
26 / 26 |
|
100.00% |
1 / 1 |
4 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\TranslatorInterface\Aid; |
5 | |
6 | use MediaWiki\Content\Content; |
7 | use MediaWiki\Content\ContentHandler; |
8 | use MediaWiki\Content\TextContent; |
9 | use MediaWiki\Extension\Translate\MessageGroupProcessing\RevTagStore; |
10 | use MediaWiki\Extension\Translate\MessageLoading\MessageHandle; |
11 | use MediaWiki\Extension\Translate\TranslatorInterface\TranslationHelperException; |
12 | use MediaWiki\MediaWikiServices; |
13 | use MediaWiki\Revision\RevisionRecord; |
14 | use MediaWiki\Revision\SlotRecord; |
15 | use MessageGroup; |
16 | use Wikimedia\Rdbms\IDatabase; |
17 | |
18 | /** |
19 | * @author Niklas Laxström |
20 | * @license GPL-2.0-or-later |
21 | * @since 2018.01 |
22 | */ |
23 | class TranslationAidDataProvider { |
24 | /** @var MessageHandle */ |
25 | private $handle; |
26 | /** @var MessageGroup */ |
27 | private $group; |
28 | /** @var string|null */ |
29 | private $definition; |
30 | /** @var array */ |
31 | private $translations; |
32 | |
33 | public function __construct( MessageHandle $handle ) { |
34 | $this->handle = $handle; |
35 | $this->group = $handle->getGroup(); |
36 | } |
37 | |
38 | /** |
39 | * Get the message definition. Cached for performance. |
40 | * @return string |
41 | */ |
42 | public function getDefinition(): string { |
43 | if ( $this->definition !== null ) { |
44 | return $this->definition; |
45 | } |
46 | |
47 | // Optional performance optimization |
48 | if ( method_exists( $this->group, 'getMessageContent' ) ) { |
49 | // @phan-suppress-next-line PhanUndeclaredMethod |
50 | $this->definition = $this->group->getMessageContent( $this->handle ); |
51 | } else { |
52 | $this->definition = $this->group->getMessage( |
53 | $this->handle->getKey(), |
54 | $this->group->getSourceLanguage() |
55 | ); |
56 | } |
57 | |
58 | if ( $this->definition === null ) { |
59 | throw new TranslationHelperException( |
60 | 'Did not find message definition for ' . $this->handle->getTitle()->getPrefixedText() . |
61 | ' in group ' . $this->group->getId() |
62 | ); |
63 | } |
64 | return $this->definition; |
65 | } |
66 | |
67 | public function hasDefinition(): bool { |
68 | try { |
69 | $this->getDefinition(); |
70 | return true; |
71 | } catch ( TranslationHelperException $e ) { |
72 | return false; |
73 | } |
74 | } |
75 | |
76 | public function getDefinitionContent(): Content { |
77 | return ContentHandler::makeContent( $this->getDefinition(), $this->handle->getTitle() ); |
78 | } |
79 | |
80 | /** |
81 | * Get the translations in all languages. Cached for performance. |
82 | * Fuzzy translation are not included. |
83 | * @return array Language code => Translation |
84 | */ |
85 | public function getGoodTranslations(): array { |
86 | if ( $this->translations !== null ) { |
87 | return $this->translations; |
88 | } |
89 | |
90 | $mwServices = MediaWikiServices::getInstance(); |
91 | $data = self::loadTranslationData( |
92 | $mwServices->getDBLoadBalancer()->getConnection( DB_REPLICA ), |
93 | $this->handle |
94 | ); |
95 | $translations = []; |
96 | $prefixLength = strlen( $this->handle->getTitleForBase()->getDBkey() . '/' ); |
97 | $languageNameUtils = $mwServices->getLanguageNameUtils(); |
98 | |
99 | foreach ( $data as $page => $translation ) { |
100 | // Could use MessageHandle here, but that queries the message index. |
101 | // Instead, we can get away with simple string manipulation. |
102 | $code = substr( $page, $prefixLength ); |
103 | if ( !$languageNameUtils->isKnownLanguageTag( $code ) ) { |
104 | continue; |
105 | } |
106 | |
107 | $translations[ $code ] = $translation; |
108 | } |
109 | |
110 | $this->translations = $translations; |
111 | |
112 | return $translations; |
113 | } |
114 | |
115 | private static function loadTranslationData( IDatabase $db, MessageHandle $handle ): array { |
116 | $revisionStore = MediaWikiServices::getInstance()->getRevisionStore(); |
117 | $conditions = []; |
118 | |
119 | // The list of pages we want to select, and their latest versions |
120 | $conditions['page_namespace'] = $handle->getTitle()->getNamespace(); |
121 | $base = $handle->getKey(); |
122 | $conditions[] = 'page_title ' . $db->buildLike( "$base/", $db->anyString() ); |
123 | $conditions[] = 'rev_id=page_latest'; |
124 | |
125 | // For fuzzy tags we need the join with revtag and also: |
126 | $conditions[ 'rt_type' ] = null; |
127 | |
128 | $rows = $revisionStore->newSelectQueryBuilder( $db ) |
129 | ->joinPage() |
130 | ->joinComment() |
131 | ->leftJoin( 'revtag', null, [ |
132 | 'page_id=rt_page', |
133 | 'page_latest=rt_revision', |
134 | 'rt_type' => RevTagStore::FUZZY_TAG |
135 | ] ) |
136 | ->where( $conditions ) |
137 | ->caller( __METHOD__ ) |
138 | ->fetchResultSet(); |
139 | |
140 | $pages = []; |
141 | $revisions = $revisionStore->newRevisionsFromBatch( $rows, [ 'slots' => [ SlotRecord::MAIN ] ] ) |
142 | ->getValue(); |
143 | foreach ( $rows as $row ) { |
144 | /** @var RevisionRecord|null $rev */ |
145 | $rev = $revisions[$row->rev_id]; |
146 | if ( $rev && $rev->getContent( SlotRecord::MAIN ) instanceof TextContent ) { |
147 | $pages[$row->page_title] = $rev->getContent( SlotRecord::MAIN )->getText(); |
148 | } |
149 | } |
150 | |
151 | return $pages; |
152 | } |
153 | } |