Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
DatabaseTtmServer.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TtmServer;
5
8use MediaWiki\MediaWikiServices;
9use MediaWiki\Title\Title;
10use MediaWiki\WikiMap\WikiMap;
11use TTMServer;
12use Wikimedia\Rdbms\DBQueryError;
13use Wikimedia\Rdbms\IDatabase;
14use Wikimedia\Rdbms\IResultWrapper;
15
24 private array $sids;
25
26 private function getDB( int $mode = DB_REPLICA ): IDatabase {
27 return MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection(
28 $mode, 'ttmserver', $this->config['database']
29 );
30 }
31
32 public function update( MessageHandle $handle, ?string $targetText ): bool {
33 if ( !$handle->isValid() || $handle->getCode() === '' ) {
34 return false;
35 }
36
37 $mKey = $handle->getKey();
38 $group = $handle->getGroup();
39 $targetLanguage = $handle->getCode();
40 $sourceLanguage = $group->getSourceLanguage();
41
42 // Skip definitions to not slow down mass imports etc.
43 // These will be added when the first translation is made
44 if ( $targetLanguage === $sourceLanguage ) {
45 return false;
46 }
47
48 $definition = $group->getMessage( $mKey, $sourceLanguage );
49 if ( !is_string( $definition ) || !strlen( trim( $definition ) ) ) {
50 return false;
51 }
52
53 $context = Title::makeTitle( $handle->getTitle()->getNamespace(), $mKey );
54 $dbw = $this->getDB( DB_PRIMARY );
55 /* Check that the definition exists and fetch the sid. If not, add
56 * the definition and retrieve the sid. If the definition changes,
57 * we will create a new entry - otherwise we could at some point
58 * get suggestions which do not match the original definition any
59 * longer. The old translations are still kept until purged by
60 * rerunning the bootstrap script. */
61 $sid = $dbw->newSelectQueryBuilder()
62 ->select( 'tms_sid' )
63 ->from( 'translate_tms' )
64 ->where( [
65 'tms_context' => $context->getPrefixedText(),
66 'tms_text' => $definition,
67 ] )
68 ->caller( __METHOD__ )
69 ->fetchField();
70 if ( $sid === false ) {
71 $sid = $this->insertSource( $context, $sourceLanguage, $definition );
72 }
73
74 // Delete old translations for this message if any. Could also use replace
75 $deleteConditions = [
76 'tmt_sid' => $sid,
77 'tmt_lang' => $targetLanguage,
78 ];
79 $dbw->delete( 'translate_tmt', $deleteConditions, __METHOD__ );
80
81 // Insert the new translation
82 if ( $targetText !== null ) {
83 $row = $deleteConditions + [
84 'tmt_text' => $targetText,
85 ];
86
87 $dbw->insert( 'translate_tmt', $row, __METHOD__ );
88 }
89
90 return true;
91 }
92
93 private function insertSource( Title $context, string $sourceLanguage, string $text ): int {
94 $row = [
95 'tms_lang' => $sourceLanguage,
96 'tms_len' => mb_strlen( $text ),
97 'tms_text' => $text,
98 'tms_context' => $context->getPrefixedText(),
99 ];
100
101 $dbw = $this->getDB( DB_PRIMARY );
102 $dbw->insert( 'translate_tms', $row, __METHOD__ );
103 $sid = $dbw->insertId();
104
105 $fulltext = $this->filterForFulltext( $sourceLanguage, $text );
106 if ( count( $fulltext ) ) {
107 $row = [
108 'tmf_sid' => $sid,
109 'tmf_text' => implode( ' ', $fulltext ),
110 ];
111 $dbw->insert( 'translate_tmf', $row, __METHOD__ );
112 }
113
114 return $sid;
115 }
116
118 protected function filterForFulltext( string $languageCode, string $input ): array {
119 $lang = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( $languageCode );
120
121 $text = preg_replace( '/[^[:alnum:]]/u', ' ', $input );
122 $text = $lang->segmentByWord( $text );
123 $text = $lang->lc( $text );
124 $segments = preg_split( '/\s+/', $text, -1, PREG_SPLIT_NO_EMPTY );
125 if ( count( $segments ) < 4 ) {
126 return [];
127 }
128
129 foreach ( $segments as $i => $segment ) {
130 // Yes strlen
131 $len = strlen( $segment );
132 if ( $len < 4 || $len > 15 ) {
133 unset( $segments[$i] );
134 }
135 }
136
137 return array_slice( array_unique( $segments ), 0, 10 );
138 }
139
140 public function beginBootstrap(): void {
141 $dbw = $this->getDB( DB_PRIMARY );
142 $dbw->delete( 'translate_tms', '*', __METHOD__ );
143 $dbw->delete( 'translate_tmt', '*', __METHOD__ );
144 $dbw->delete( 'translate_tmf', '*', __METHOD__ );
145 $table = $dbw->tableName( 'translate_tmf' );
146 try {
147 $dbw->query( "DROP INDEX tmf_text ON $table", __METHOD__ );
148 } catch ( DBQueryError $e ) {
149 // Perhaps the script was aborted before it got
150 // chance to add the index back.
151 }
152 }
153
154 public function beginBatch(): void {
155 $this->sids = [];
156 }
157
158 public function batchInsertDefinitions( array $batch ): void {
159 $mwInstance = MediaWikiServices::getInstance();
160 $titleFactory = $mwInstance->getTitleFactory();
161 foreach ( $batch as $key => $item ) {
162 [ $handle, $language, $text ] = $item;
163 $context = $titleFactory->makeTitle( $handle->getTitle()->getNamespace(), $handle->getKey() );
164 $this->sids[$key] = $this->insertSource( $context, $language, $text );
165 }
166
167 $mwInstance->getDBLoadBalancerFactory()->waitForReplication( [ 'ifWritesSince' => 10 ] );
168 }
169
170 public function batchInsertTranslations( array $batch ): void {
171 $rows = [];
172 foreach ( $batch as $key => $data ) {
173 [ , $language, $text ] = $data;
174 $rows[] = [
175 'tmt_sid' => $this->sids[$key],
176 'tmt_lang' => $language,
177 'tmt_text' => $text,
178 ];
179 }
180
181 $dbw = $this->getDB( DB_PRIMARY );
182 $dbw->insert( 'translate_tmt', $rows, __METHOD__ );
183
184 MediaWikiServices::getInstance()
185 ->getDBLoadBalancerFactory()
186 ->waitForReplication( [ 'ifWritesSince' => 10 ] );
187 }
188
189 public function endBatch(): void {
190 }
191
192 public function endBootstrap(): void {
193 $dbw = $this->getDB( DB_PRIMARY );
194 $table = $dbw->tableName( 'translate_tmf' );
195 $dbw->query( "CREATE FULLTEXT INDEX tmf_text ON $table (tmf_text)", __METHOD__ );
196 }
197
198 /* Reading interface */
199
200 public function isLocalSuggestion( array $suggestion ): bool {
201 return true;
202 }
203
204 public function expandLocation( array $suggestion ): string {
205 return Title::newFromText( $suggestion['location'] )->getCanonicalURL();
206 }
207
208 public function query( string $sourceLanguage, string $targetLanguage, string $text ): array {
209 // Calculate the bounds of the string length which are able
210 // to satisfy the cutoff percentage in edit distance.
211 $len = mb_strlen( $text );
212 $min = ceil( max( $len * $this->config['cutoff'], 2 ) );
213 $max = floor( $len / $this->config['cutoff'] );
214
215 // We could use fulltext index to narrow the results further
216 $dbr = $this->getDB();
217 $tables = [ 'translate_tmt', 'translate_tms' ];
218 $fields = [ 'tms_context', 'tms_text', 'tmt_lang', 'tmt_text' ];
219
220 $conditions = [
221 'tms_lang' => $sourceLanguage,
222 'tmt_lang' => $targetLanguage,
223 "tms_len BETWEEN $min AND $max",
224 'tms_sid = tmt_sid',
225 ];
226
227 $fulltext = $this->filterForFulltext( $sourceLanguage, $text );
228 if ( $fulltext ) {
229 $tables[] = 'translate_tmf';
230 $list = implode( ' ', $fulltext );
231 $conditions[] = 'tmf_sid = tmt_sid';
232 $conditions[] = "MATCH(tmf_text) AGAINST( '$list' )";
233 }
234
235 $res = $dbr->newSelectQueryBuilder()
236 ->tables( $tables )
237 ->select( $fields )
238 ->where( $conditions )
239 ->caller( __METHOD__ )
240 ->fetchResultSet();
241
242 return $this->processQueryResults( $res, $text, $targetLanguage );
243 }
244
245 private function processQueryResults( IResultWrapper $res, string $text, string $targetLanguage ): array {
246 $timeLimit = microtime( true ) + 5;
247
248 $lenA = mb_strlen( $text );
249 $results = [];
250 $stringComparator = new EditDistanceStringComparator();
251 foreach ( $res as $row ) {
252 if ( microtime( true ) > $timeLimit ) {
253 // Having no suggestions is better than preventing translation
254 // altogether by timing out the request :(
255 break;
256 }
257
258 $a = $text;
259 $b = $row->tms_text;
260 $lenB = mb_strlen( $b );
261 $len = min( $lenA, $lenB );
262 if ( $len > 600 ) {
263 // two strings of length 1500 ~ 10s
264 // two strings of length 2250 ~ 30s
265 $dist = $len;
266 } else {
267 $dist = $stringComparator->levenshtein( $a, $b, $lenA, $lenB );
268 }
269 $quality = 1 - ( $dist * 0.9 / $len );
270
271 if ( $quality >= $this->config['cutoff'] ) {
272 $results[] = [
273 'source' => $row->tms_text,
274 'target' => $row->tmt_text,
275 'context' => $row->tms_context,
276 'location' => $row->tms_context . '/' . $targetLanguage,
277 'quality' => $quality,
278 'wiki' => $row->tms_wiki ?? WikiMap::getCurrentWikiId(),
279 ];
280 }
281 }
282
283 return TTMServer::sortSuggestions( $results );
284 }
285
286 public function setDoReIndex(): void {
287 }
288}
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager( $services->getTitleFactory());}, 'Translate:AggregateGroupMessageGroupFactory'=> static function(MediaWikiServices $services):AggregateGroupMessageGroupFactory { return new AggregateGroupMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateComparator'=> static function(MediaWikiServices $services):ExternalMessageSourceStateComparator { return new ExternalMessageSourceStateComparator(new SimpleStringComparator(), $services->getRevisionLookup(), $services->getPageStore());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), new ServiceOptions(FileBasedMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookDefinedMessageGroupFactory'=> static function(MediaWikiServices $services):HookDefinedMessageGroupFactory { return new HookDefinedMessageGroupFactory( $services->get( 'Translate:HookRunner'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleMessageGroupFactory'=> static function(MediaWikiServices $services):MessageBundleMessageGroupFactory { return new MessageBundleMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'), new ServiceOptions(MessageBundleMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:MessageBundleTranslationLoader'=> static function(MediaWikiServices $services):MessageBundleTranslationLoader { return new MessageBundleTranslationLoader( $services->getLanguageFallback());}, 'Translate:MessageGroupMetadata'=> static function(MediaWikiServices $services):MessageGroupMetadata { return new MessageGroupMetadata( $services->getDBLoadBalancer());}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getDBLoadBalancer(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->get( 'Translate:MessageGroupMetadata'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageGroupSubscription'=> static function(MediaWikiServices $services):MessageGroupSubscription { return new MessageGroupSubscription($services->get( 'Translate:MessageGroupSubscriptionStore'), $services->getJobQueueGroup(), $services->getUserIdentityLookup(), LoggerFactory::getInstance( 'Translate.MessageGroupSubscription'), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):MessageGroupSubscriptionHookHandler { return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getDBLoadBalancerFactory());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=(array) $services->getMainConfig() ->get( 'TranslateMessageIndex');$class=array_shift( $params);$implementationMap=['HashMessageIndex'=> HashMessageIndex::class, 'CDBMessageIndex'=> CDBMessageIndex::class, 'DatabaseMessageIndex'=> DatabaseMessageIndex::class, 'hash'=> HashMessageIndex::class, 'cdb'=> CDBMessageIndex::class, 'database'=> DatabaseMessageIndex::class,];$messageIndexStoreClass=$implementationMap[$class] ?? $implementationMap['database'];return new MessageIndex(new $messageIndexStoreClass, $services->getMainWANObjectCache(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), LoggerFactory::getInstance( 'Translate'), $services->getMainObjectStash(), $services->getDBLoadBalancerFactory(), $services->get( 'Translate:MessageGroupSubscription'), new ServiceOptions(MessageIndex::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getDBLoadBalancer(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore( $services->getDBLoadBalancer());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleDeleter'=> static function(MediaWikiServices $services):TranslatableBundleDeleter { return new TranslatableBundleDeleter($services->getMainObjectStash(), $services->getJobQueueGroup(), $services->get( 'Translate:SubpageListBuilder'), $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup(), $services->getNamespaceInfo(), $services->getTitleFactory());}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getDBLoadBalancerFactory(), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getDBLoadBalancer() ->getConnection(DB_PRIMARY), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getDBLoadBalancer(), $services->getJobQueueGroup(), $services->getLinkRenderer(), MessageGroups::singleton(), $services->get( 'Translate:MessageIndex'), $services->getTitleFormatter(), $services->getTitleParser(), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:TranslatablePageStateStore'), $services->get( 'Translate:TranslationUnitStoreFactory'), $services->get( 'Translate:MessageGroupMetadata'), $services->getWikiPageFactory(), $services->get( 'Translate:TranslatablePageView'));}, 'Translate:TranslatablePageMessageGroupFactory'=> static function(MediaWikiServices $services):TranslatablePageMessageGroupFactory { return new TranslatablePageMessageGroupFactory(new ServiceOptions(TranslatablePageMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStateStore'=> static function(MediaWikiServices $services):TranslatablePageStateStore { return new TranslatablePageStateStore($services->get( 'Translate:PersistentCache'), $services->getPageStore());}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), $services->get( 'Translate:RevTagStore'), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getDBLoadBalancerFactory(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getDBLoadBalancer(), $services->getPermissionManager(), $services->getAuthManager(), $services->getUserGroupManager(), $services->getActorStore(), $services->getUserOptionsManager(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), new ServiceOptions(TranslateSandbox::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnection(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array
Class for pointing to messages, like Title class is for titles.
MySQL/MariaDB-based based backend for translation memory.
setDoReIndex()
Instruct the service to fully wipe the index and start from scratch.
expandLocation(array $suggestion)
Given suggestion returned by this TTMServer, constructs fully qualified URL to the location of the tr...
batchInsertDefinitions(array $batch)
Called multiple times per batch if necessary.
update(MessageHandle $handle, ?string $targetText)
Shovels the new translation into translation memory.
endBatch()
Called after every batch (MessageGroup).
query(string $sourceLanguage, string $targetLanguage, string $text)
Fetches all relevant suggestions for given text.
filterForFulltext(string $languageCode, string $input)
Tokenizes the text for fulltext search.
beginBatch()
Called before every batch (MessageGroup).
isLocalSuggestion(array $suggestion)
Determines if the suggestion returned by this TTMServer comes from this wiki or any other wiki.
beginBootstrap()
Called when starting to fill the translation memory.
batchInsertTranslations(array $batch)
Called multiple times per batch if necessary.
Smart string comparator that uses simple string comparison, and then the levenshtein algorithm to com...
Some general static methods for instantiating TTMServer and helpers.
Definition TTMServer.php:20
static sortSuggestions(array $suggestions)
Definition TTMServer.php:33
Interface for TTMServer that can be queried (=all of them).
Interface for TTMServer that can be updated.