TranslationWebService.php

Go to the documentation of this file.
00001 <?php
00020 abstract class TranslationWebService {
00021     /* Public api */
00022 
00031     public static function factory( $name, $config ) {
00032         $handlers = array(
00033             'microsoft' => 'MicrosoftWebService',
00034             'apertium' => 'ApertiumWebService',
00035             'yandex' => 'YandexWebService',
00036             'ttmserver' => 'RemoteTTMServerWebService',
00037         );
00038 
00039         if ( !isset( $config['timeout'] ) ) {
00040             $config['timeout'] = 3;
00041         }
00042 
00043         if ( isset( $handlers[$config['type']] ) ) {
00044             $class = $handlers[$config['type']];
00045 
00046             return new $class( $name, $config );
00047         }
00048 
00049         return null;
00050     }
00051 
00066     public function getSuggestions( $translations, $sourceLanguage, $targetLanguage ) {
00067         if ( $this->checkTranslationServiceFailure() ) {
00068             return array();
00069         }
00070 
00071         $from = $this->mapCode( $sourceLanguage );
00072         $to = $this->mapCode( $targetLanguage );
00073 
00074         try {
00075             $results = array();
00076 
00077             // Try to use the source language when possible.
00078             $supported = $this->getSupportedLanguagePairs();
00079             if ( isset( $supported[$from][$to] ) && isset( $translations[$from] ) ) {
00080                 // Delete all the other languages.
00081                 // Use the unmapped code to avoid double mapping
00082                 $translations = array( $sourceLanguage => $translations[$from] );
00083             }
00084 
00085             // Loop of the the translations we have to see which can be used as source
00086             // @todo: Support setting priority of languages like Yandex used to have
00087             foreach ( $translations as $language => $text ) {
00088                 $from = $this->mapCode( $language );
00089 
00090                 if ( isset( $supported[$from][$to] ) ) {
00091                     $sug = $this->doRequest( $text, $from, $to );
00092                     if ( strval( $sug === '' ) ) {
00093                         continue;
00094                     }
00095 
00096                     $results[] = array(
00097                         'target' => $sug,
00098                         'service' => $this->service,
00099                         'source_language' => $language,
00100                         'source' => $text,
00101                     );
00102                 }
00103 
00104                 if ( count( $results ) >= 3 ) {
00105                     break;
00106                 }
00107             }
00108 
00109             return $results;
00110         } catch ( Exception $e ) {
00111             $this->reportTranslationServiceFailure( $e );
00112 
00113             return array();
00114         }
00115     }
00116 
00117     /* Service api */
00118 
00126     abstract protected function mapCode( $code );
00127 
00135     abstract protected function doPairs();
00136 
00145     abstract protected function doRequest( $text, $from, $to );
00146 
00147     /* Default implementation */
00148 
00152     protected $service;
00153 
00157     protected $config;
00158 
00159     protected function __construct( $service, $config ) {
00160         $this->service = $service;
00161         $this->config = $config;
00162     }
00163 
00167     protected function getSupportedLanguagePairs() {
00168         $key = wfMemckey( 'translate-tmsug-pairs-' . $this->service );
00169         $pairs = wfGetCache( CACHE_ANYTHING )->get( $key );
00170         if ( !is_array( $pairs ) ) {
00171             $pairs = $this->doPairs();
00172             // Cache the result for a day
00173             wfGetCache( CACHE_ANYTHING )->set( $key, $pairs, 60 * 60 * 24 );
00174         }
00175 
00176         return $pairs;
00177     }
00178 
00184     protected function wrapUntranslatable( $text ) {
00185         $pattern = '~%[^% ]+%|\$\d|{VAR:[^}]+}|{?{(PLURAL|GRAMMAR|GENDER):[^|]+\||%(\d\$)?[sd]~';
00186         $text = str_replace( "\n", "!N!", $text );
00187         $wrap = '<span class="notranslate" translate="no">\0</span>';
00188         $text = preg_replace( $pattern, $wrap, $text );
00189 
00190         return $text;
00191     }
00192 
00196     protected function unwrapUntranslatable( $text ) {
00197         $pattern = '~<span class="notranslate" translate="no">(.*?)</span>~';
00198         $text = str_replace( '!N!', "\n", $text );
00199         $text = preg_replace( $pattern, '\1', $text );
00200 
00201         return $text;
00202     }
00203 
00204     /* Failure handling and suspending */
00205 
00210     protected $serviceFailureCount = 5;
00215     protected $serviceFailurePeriod = 900;
00216 
00221     public function checkTranslationServiceFailure() {
00222         $service = $this->service;
00223         $key = wfMemckey( "translate-service-$service" );
00224         $value = wfGetCache( CACHE_ANYTHING )->get( $key );
00225         if ( !is_string( $value ) ) {
00226             return false;
00227         }
00228         list( $count, $failed ) = explode( '|', $value, 2 );
00229 
00230         if ( $failed + ( 2 * $this->serviceFailurePeriod ) < wfTimestamp() ) {
00231             if ( $count >= $this->serviceFailureCount ) {
00232                 wfDebugLog( 'translationservices', "Translation service $service (was) restored" );
00233             }
00234             wfGetCache( CACHE_ANYTHING )->delete( $key );
00235 
00236             return false;
00237         } elseif ( $failed + $this->serviceFailurePeriod < wfTimestamp() ) {
00238             /* We are in suspicious mode and one failure is enough to update
00239              * failed timestamp. If the service works however, let's use it.
00240              * Previous failures are forgotten after another failure period
00241              * has passed */
00242             return false;
00243         }
00244 
00245         // Check the failure count against the limit
00246         return $count >= $this->serviceFailureCount;
00247     }
00248 
00252     protected function reportTranslationServiceFailure( Exception $e ) {
00253         $service = $this->service;
00254         wfDebugLog(
00255             'translationservices',
00256             "Translation service $service problem: " . $e->getMessage()
00257         );
00258 
00259         $key = wfMemckey( "translate-service-$service" );
00260         $value = wfGetCache( CACHE_ANYTHING )->get( $key );
00261         if ( !is_string( $value ) ) {
00262             $count = 0;
00263         } else {
00264             list( $count, ) = explode( '|', $value, 2 );
00265         }
00266 
00267         $count += 1;
00268         $failed = wfTimestamp();
00269         wfGetCache( CACHE_ANYTHING )->set(
00270             $key,
00271             "$count|$failed",
00272             $this->serviceFailurePeriod * 5
00273         );
00274 
00275         if ( $count == $this->serviceFailureCount ) {
00276             wfDebugLog( 'translationservices', "Translation service $service suspended" );
00277         } elseif ( $count > $this->serviceFailureCount ) {
00278             wfDebugLog( 'translationservices', "Translation service $service still suspended" );
00279         }
00280     }
00281 }
Generated on Tue Oct 29 00:00:26 2013 for MediaWiki Translate Extension by  doxygen 1.6.3