<?php

/**
 * A simple interface to Google Language API
 * Author: fbparis@gmail.com (http://fbparis.com/)
 * Please take a look on http://code.google.com/apis/ajaxlanguage/documentation/reference.html#_intro_fonje before using this class
 */

class GoogleLanguage {
    const 
translateURL 'http://ajax.googleapis.com/ajax/services/language/translate';
    const 
detectURL 'http://ajax.googleapis.com/ajax/services/language/detect';
    
    
/**
     * May be usefull :)
     * Apply for a key here: http://code.google.com/apis/ajaxsearch/signup.html
     */
    
public $google_api_key ''
    public 
$max_length 5000// max length of the text to pass to translate api for translate_text and translate_html methods
    
    
public $lastUrl ''// last accessed url
    
public $lastResponseHeaders = array(); // last HTTP response headers
    
public $lastResponseCode 0// last HTTP response code
    
public $lastResponse ''// last response, the raw version
    
    
public $responseStatus 0// last response status sent by google api
    
public $responseDetails ''// last response details sent by google api
    
    
public $version '1.0'// Google Search API version
    
public $user_ip ''// This propertie supplies the IP address of the end-user on whose behalf the request is being made (optional)
    
public $referer ''// "Applications MUST always include a valid and accurate http referer header in their requests" says Google :)
        
    
protected $host '';
    
    
/**
     * Constructor
     */
    
function __construct() {
        
$this->context stream_context_get_default();
        
$this->http_option('max_redirects',1);
    }
        
    
/**
     * You can use this method to get or set some http context options (see http://php.net/manual/en/context.http.php)
     * For example, to set the user agent to "Mozilla/5" call $object->http_option('user_agent','Mozilla/5')
     * If you must set non http options (ie: socket), you can use directly the stream_context_get_options() function with $object->context as context
     */
    
public function http_option($key,$value=null) {
        if (
$value === null) { // return the current value for this key
            
$options stream_context_get_options($this->context);
            return @
$options['http'][$key] ? $options['http'][$key] : '';
        } else { 
// set the option, return false if failed
            
return stream_context_set_option($this->context,'http',$key,$value);
        }
    }
            
    
/**
     * Return translated $html according to $langPair
     */
    
public function translate_html($html,$langPair,$parameters=array()) {
        
$paramaters['format'] = 'html';
        
$html preg_replace('#<script.*?</script>#si','',$html);
        
$html preg_replace('#<style.*?</style>#si','',$html);
        
$html preg_replace('#<!--.*?-->#si','',$html);
        
$dom = new DOMDocument;
        
$dom->formatOutput true;
        
$dom->preserveWhiteSpace false;
        
$dom->loadHTML($html);
        
$r $this->html_split_and_translate($dom,$dom->getElementsByTagName('body')->item(0)->childNodes,$langPair,$parameters);
        return 
implode('',$r);
    }
    
    
/**
     * Return translated $text according to $langPair
     */
    
public function translate_text($text,$langPair,$parameters=array()) {
        
$paramaters['format'] = 'text';
        
$r $this->text_split_and_translate($text,$langPair,$parameters);
        return 
implode('',$r);
    }
    
    
/**
     * Translate $text according to $langPair
     */
    
public function translate($text,$langPair,$parameters=array()) {
        
$parameters['q'] = trim($text);
        
$parameters['langpair'] = $langPair;
        
$this->host self::translateURL;
        
$r $this->api_call('POST',$parameters);
        return 
$r;
    }
    
    
/**
     * Detect the language of $text
     */
    
public function detect($text,$parameters=array()) {
        
$parameters['q'] = trim($text);
        
$this->host self::detectURL;
        
$r $this->api_call('GET',$parameters);
        return 
$r;
    }
    
    
/**
     * Return translated $text or html according to $langPair (preserving trimed spaces)
     */
    
protected function translation($text,$langPair,$parameters=array()) {
        if (!
trim($text)) return $text;
        
preg_match('#^(\s*)(.*?)(\s*)$#s',$text,$m);
        
$r $this->translate($m[2],$langPair,$parameters);
        return 
is_object($r) && property_exists($r,'translatedText') ? $m[1] . $r->translatedText $m[3] : $text;
    }
    
    
/**
     * Split and translate html string respecting DOM, sentences and words
     */
    
protected function html_split_and_translate(&$dom,&$nodelist,$langPair,$parameters) {
        
$r = array();
        if (!
$this->max_length) return $r;
        
$current '';
        foreach (
$nodelist as $node) {
            
$output $dom->saveXML($node);
            if (
strlen($current $output) <= $this->max_length$current $current $output;
            else {
                if (
$node->nodeType == XML_TEXT_NODE) {
                    while (
strlen($current $output) > $this->max_length) {
                        
$n $this->max_length strlen($current);
                        if (!
$n) {
                            
$r[] = $this->translation($current,$langPair,$parameters);
                            
$current '';
                            
$n $this->max_length;
                        }
                        
$sub substr($output,0,$n);
                        
$part preg_replace('#[^.?;:!]+$#s','',$sub);
                        if (!
$part) {
                            
$part preg_replace('#[^\t ,()0-9$%€"]+$#s','',$sub);
                            if (!
$part$part $sub;
                        }
                        
$r[] = $this->translation($current $part,$langPair,$parameters);
                        
$current '';
                        
$output substr($output,strlen($part));
                    }
                    
$current $output;
                } else {
                    if (!empty(
$current)) $r[] = $this->translation($current,$langPair,$parameters);
                    if (
strlen($output) > $this->max_length) {
                        
preg_match('#^(<.*?>).*(<[^>]*>)$#s',$output,$m);
                        
$r[] = $m[1];
                        
$r array_merge($r,$this->html_split_and_translate($dom,$node->childNodes,$langPair,$parameters),array($m[2]));
                        
$current '';
                    } else {
                        
$current $output;
                    }
                }
            }
        }
        if (!empty(
$current)) $r[] = $this->translation($current,$langPair,$parameters);
        return 
$r;        
    }
        
    
/**
     * Split and translate text string respecting sentences and words
     */
    
protected function text_split_and_translate($text,$langPair,$parameters) {
        
$r = array();
        if (!
$this->max_length) return $r;
        while (
strlen($text) > $this->max_length) {
            
$sub substr($text,0,$this->max_length);
            
$part preg_replace('#[^.?;:!]+$#s','',$sub);
            if (!
$part) {
                
$part preg_replace('#[^\t ,()0-9$%€"]+$#s','',$sub);
                if (!
$part$part $sub;
            }
            
$r[] = $this->translation($part,$langPair,$parameters);
            
$text substr($text,strlen($part));
        }
        if (!empty(
$text)) $r[] = $this->translation($text,$langPair,$parameters);
        return 
$r;
    }
        
    
/**
     * Format query and return results
     */
    
protected function api_call($method,$parameters) {
        
$this->responseStatus '';
        
$this->responseDetails '';
        
$this->http_option('header',$this->referer "Referer: $this->referer'');
        if (
$this->version$parameters['v'] = $this->version;
        if (
$this->google_api_key$parameters['key'] = $this->google_api_key;
        if (
$this->user_ip$parameters['user_ip'] = $this->user_ip;
        switch (
$method) {
        case 
'GET':
            
$r json_decode($this->http(sprintf('%s?%s',$this->host,http_build_query($parameters)),'GET'));
            break;
        case 
'POST':
            
$r json_decode($this->http($this->host,'POST',http_build_query($parameters)));
            break;
        default:
            return 
false;
        }
        
$this->http_option('header','');
        if (
is_object($r)) {
            if (
property_exists($r,'responseStatus')) $this->responseStatus $r->responseStatus;
            if (
property_exists($r,'responseDetails')) $this->responseDetails $r->responseDetails;
            return 
property_exists($r,'responseData') ? $r->responseData null;
        }
        return 
$r;
    }

    
/**
     * Make an HTTP request
     * Infinite loops of redirections are not handled...
     * Last URL opened will be notified in $object->lastUrl
     * Last HTTP Response Code will be notified in $object->lastResponseCode (can be 0 if the request has failed)
     * Last HTTP Response Headers will be notified in $object->lastResponseHeaders (see http://php.net/manual/en/reserved.variables.httpresponseheader.php)
     * You can access the last raw response via $object->lastResponse
     */ 
    
protected function http($url,$method,$postfields='') {
        
$this->lastResponseCode 0;
        
$this->http_option('method',$method);
        
$this->http_option('content',$postfields);
        
$response = @file_get_contents($url,false,$this->context);
        
$this->lastUrl $url;
        
$this->lastResponseHeaders $http_response_header;
        
$this->lastResponse $response;
        if (
is_array($this->lastResponseHeaders)) {
            foreach (
$this->lastResponseHeaders as $i=>$header) {
                if (
$i == 0$this->lastResponseCode preg_match('#^HTTP/[0-9.]+ ([0-9]+)#s',$header,$m) ? intval($m[1]) : 0;
                elseif (
preg_match('#^Location: (.*)$#s',$header,$m)) return $this->http(trim($m[1]),'GET');
            }
        } else 
$this->lastResponseHeaders = array();
        return 
$response;
    }
}

?>