The Road to Delphi

Delphi – Free Pascal – Oxygene


7 Comments

Important Note about using the Google Maps API from Desktop Apps

If you are experimenting some strange issues (maps goes blank, some elements of the maps are not displayed) using the Google Maps API from a desktop application.

GoogleMapsError

This is because the JavaScript API v3 no longer supports the IE7 and IE8 rendering mode, as is stated on this document https://developers.google.com/maps/documentation/javascript/browsersupport.

 

The workaround is very simple just add the V=3 parameter to the script tag, So if you script looks like this

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>

Change to

<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3&sensor=true"></script>

 

Note  :   If you do not explicitly specify a version in your JavaScript API bootstrap request, you will receive the experimental version by default (3.22) which not longer supports the IE8.

 

GoogleMapsOK

Another option is tweak the Windows registry how is shown here.

By the way I just updated all the Google Maps related code of this blog (Check the updated code on Github).

Some additional references

 

Regards
Rodrigo.


5 Comments

Using the Google Safe Browsing API from Delphi

The Google Safe Browsing API is a service that enables applications to check URLs against the Google’s lists of suspected phishing and malware pages. Exist two types of APIs for using the Safe Browsing service, Safe Browsing API v2 and Safe Browsing Lookup API in this article I will show how use the Safe Browsing Lookup API from a Delphi application.

The Safe Browsing Lookup API is designed to provide a simple interface through HTTP GET or POST request and get the state of the URL(s) directly from the server.

Like most of the services provided by Google you need to request an API key. In order to obtain an API key you must log in with your existing Google account and sign up for the API at http://www.google.com/safebrowsing/key_signup.html

Using the GET Method

The Get method  allow to the client only lookup one URL per request. To use the GET method you must make a request to this URL

https://sb-ssl.google.com/safebrowsing/api/lookup?client=CLIENT&apikey=APIKEY&appver=APPVER&pver=PVER&url=URL

Parameters

  • The client parameter indicates the type of client, it could be any name of the client’s choice.
  • The apikey parameter indicates the API key.
  • The appver parameter indicates the version of the client.
  • The pver parameter indicates the protocol version that the client supports. Currently this should be “3.0”. The format is “major.minor”. If we update the protocol, we will make sure that minor revisions are always compatible; however major revision will be incompatible and the server MAY NOT be able to cope with an older protocol.
  • The url parameter indicates the url the client wants to lookup. It must be a valid URL (non ASCII characters must be in UTF-8) and needs to be encoded properly to avoid confusion. For example, if the url contains ‘&’, it could be interpreted as the separator of the CGI parameters. We require the API users to use the percent encoding for the set of “reserved characters”, which is defined in RFC 3986 . A summary of the percent encoding can be found here.

Check this Sample Url

https://sb-ssl.google.com/safebrowsing/api/lookup?client=mydemoapp&<strong>apikey</strong>=1234567890&appver=1.0.1&pver=3.0&url=http%3A%2F%2Fwww.google.com%2F

In this case the values passed are

client = mydemoapp
apikey = 1234567890
appver = 1.0.1
pver   = 3.0
url    = http://www.google.com

Response

The service returns the following HTTP response codes for the GET method

  • 200: The queried URL is either phishing, malware or both, see the response body for the specific type.
  • 204: The requested URL is legitimate, no response body returned.
  • 400: Bad Request — The HTTP request was not correctly formed.
  • 401: Not Authorized — The apikey is not authorized
  • 503: Service Unavailable .

Additionally  the server will include the actual type of URL in the response body when the queried URL matches either the phishing or malware lists, so the body will contain one of these values
“phishing” | “malware” | “phishing,malware"

Delphi Code for the GET Request

The next source uses the Wininet functions to make the GET request, feel free to use another components like Indy or synapse to accomplish this task.

{$APPTYPE CONSOLE}
uses
  Classes,
  Windows,
  WinInet,
  SysUtils;
const
  sUserAgent = 'Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101';
  //¡¡¡¡¡¡¡¡¡¡Please be nice and use your own API key, get a key from here http://code.google.com/apis/safebrowsing/key_signup.html ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡
  sApiKey    = 'ABQIAAAAzY4CKjsBFYV4Rxx0ZQaKlxQL2a1oqOk9I7UVXAZVtWa6uSA2XA';
  sServer    = 'sb-ssl.google.com';
  sGetSafeBrowsing   = '/safebrowsing/api/lookup?client=delphi&apikey=%s&appver=1.5.2&pver=3.0&url=%s';

//this function translate a WinInet Error Code to a description of the error.
function GetWinInetError(ErrorCode:Cardinal): string;
const
   winetdll = 'wininet.dll';
var
  Len: Integer;
  Buffer: PChar;
begin
  Len := FormatMessage(
  FORMAT_MESSAGE_FROM_HMODULE or FORMAT_MESSAGE_FROM_SYSTEM or
  FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_IGNORE_INSERTS or  FORMAT_MESSAGE_ARGUMENT_ARRAY,
  Pointer(GetModuleHandle(winetdll)), ErrorCode, 0, @Buffer, SizeOf(Buffer), nil);
  try
    while (Len > 0) and {$IFDEF UNICODE}(CharInSet(Buffer[Len - 1], [#0..#32, '.'])) {$ELSE}(Buffer[Len - 1] in [#0..#32, '.']) {$ENDIF} do Dec(Len);
    SetString(Result, Buffer, Len);
  finally
    LocalFree(HLOCAL(Buffer));
  end;
end;

//make a GET request using the WinInet functions
function Https_Get(const ServerName,Resource : string;Var Response:AnsiString): Integer;
const
  BufferSize=1024*64;
var
  hInet    : HINTERNET;
  hConnect : HINTERNET;
  hRequest : HINTERNET;
  ErrorCode : Integer;
  lpvBuffer : PAnsiChar;
  lpdwBufferLength: DWORD;
  lpdwReserved    : DWORD;
  dwBytesRead     : DWORD;
  lpdwNumberOfBytesAvailable: DWORD;
begin
  Result  :=0;
  Response:='';
  hInet := InternetOpen(PChar(sUserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

  if hInet=nil then
  begin
    ErrorCode:=GetLastError;
    raise Exception.Create(Format('InternetOpen Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
  end;

  try
    hConnect := InternetConnect(hInet, PChar(ServerName), INTERNET_DEFAULT_HTTPS_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
    if hConnect=nil then
    begin
      ErrorCode:=GetLastError;
      raise Exception.Create(Format('InternetConnect Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
    end;

    try
      //make the request
      hRequest := HttpOpenRequest(hConnect, 'GET', PChar(Resource), HTTP_VERSION, '', nil, INTERNET_FLAG_SECURE, 0);
      if hRequest=nil then
      begin
        ErrorCode:=GetLastError;
        raise Exception.Create(Format('HttpOpenRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
      end;

      try
        //send the GET request
        if not HttpSendRequest(hRequest, nil, 0, nil, 0) then
        begin
          ErrorCode:=GetLastError;
          raise Exception.Create(Format('HttpSendRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
        end;

          lpdwBufferLength:=SizeOf(Result);
          lpdwReserved    :=0;
          //get the status code
          if not HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER, @Result, lpdwBufferLength, lpdwReserved) then
          begin
            ErrorCode:=GetLastError;
            raise Exception.Create(Format('HttpQueryInfo Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
          end;

         if Result=200 then //read the body response in case which the status code is 200
          if InternetQueryDataAvailable(hRequest, lpdwNumberOfBytesAvailable, 0, 0) then
          begin
            GetMem(lpvBuffer,lpdwBufferLength);
            try
              SetLength(Response,lpdwNumberOfBytesAvailable);
              InternetReadFile(hRequest, @Response[1], lpdwNumberOfBytesAvailable, dwBytesRead);
            finally
              FreeMem(lpvBuffer);
            end;
          end
          else
          begin
            ErrorCode:=GetLastError;
            raise Exception.Create(Format('InternetQueryDataAvailable Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
          end;

      finally
        InternetCloseHandle(hRequest);
      end;
    finally
      InternetCloseHandle(hConnect);
    end;
  finally
    InternetCloseHandle(hInet);
  end;
end;

//encode a Url
function URLEncode(const Url: string): string;
var
  i: Integer;
begin
  Result := '';
  for i := 1 to Length(Url) do
  begin
    case Url[i] of
      'A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.':
        Result := Result + Url[i];
    else
        Result := Result + '%' + IntToHex(Ord(Url[i]), 2);
    end;
  end;
end;

//Send The GET request and process the returned body
Procedure TestGet(const AUrl : string);
var
 Response     : AnsiString;
 ResponseCode : Integer;
begin
   ResponseCode:=Https_Get(sServer,Format(sGetSafeBrowsing,[sApiKey,URLEncode(AUrl)]), Response);
   case ResponseCode of
     200: Writeln(Format('The queried URL (%s) is %s',[AUrl,Response]));
     204: Writeln(Format('The queried URL (%s) is %s',[AUrl,'legitimate']));
     400: Writeln('Bad Request — The HTTP request was not correctly formed.');
     401: Writeln('Not Authorized — The apikey is not authorized');
     503: Writeln('Service Unavailable — The server cannot handle the request.');
   else
         Writeln('Unknow response');
   end;
end;

begin
  try
     //Now check some urls.
     TestGet('http://malware.testing.google.test/testing/malware/');
     TestGet('orgsite.info');
     TestGet('http://www.google.com');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

This will return

The queried URL (http://malware.testing.google.test/testing/malware/) is malware
The queried URL (orgsite.info) is malware
The queried URL (http://www.google.com) is legitimate

Using the POST Method

The post request is more powerful because the client can also look up a set of URLs (up to 500) through HTTP POST request. To use the POST method you must make a request to this URL

https://sb-ssl.google.com/safebrowsing/api/lookup?client=CLIENT&apikey=APIKEY&appver=APPVER&pver=PVER

Parameters

  • The client parameter indicates the type of client, it could be any name of the client’s choice.
  • The apikey parameter indicates the API key.
  • The appver parameter indicates the version of the client.
  • The pver parameter indicates the protocol version that the client supports.

Check this Sample Url

https://sb-ssl.google.com/safebrowsing/api/lookup?client=mydemoapp&<strong>apikey</strong>=1234567890&appver=1.0.1&pver=3.0

Request Body

The client specifies the queried URLs in the POST request body using the following format:
POST_REQ_BODY = NUM LF URL (LF URL)*
NUM = (DIGIT)+
URL = url string following the RFC 1738

The request’s body contains several lines separated by LF. The first line is a number indicating how many URLs are included in the body. The next several lines are URLs to be looked up. Each line contains one URL and the client must specify at least one URL in the body.

check this sample

2
http://www.google.com/
http://malware.testing.google.test/testing/malware/

Response

The server generates the following HTTP response codes for the POST request:

  • 200: AT LEAST ONE of the queried URLs are matched in either the phishing or malware lists, the actual results are returned through the response body
  • 204: NONE of the queried URLs matched the phishing or malware lists, no response body returned
  • 400: Bad Request — The HTTP request was not correctly formed
  • 401: Not Authorized.
  • 503: Service Unavailable.

Body

In the POST request, the server will return a list of  URLs queried in the response body when at least one of the queried URLs matches in the suspected phishing or malware lists.
POST_RESP_BODY = VERDICT (LF VERDICT)*
VERDICT = “phishing” | “malware” | “phishing,malware” | “ok”

The type has the same meaning as in the GET response body except that some of the URLs may be legitimate (recall that the server returns empty content only when none of the queried URLs matches the phishing or malware lists). In this case, we return “ok” for the non-matching URLs. The results are separated by the LF. There is a one-on-one mapping between the results in the response body and the queried URLs in the request body. For example, assume there are 10 URLs specified in the request body, the server will return exactly 10 results with the original order. That is, the first line corresponds to the result of the first queried URL, the second line corresponds to the result of the second queried URL, and so on.

Delphi Code for the POST Request

{$APPTYPE CONSOLE}
uses
  Classes,
  Windows,
  WinInet,
  SysUtils;

const
  sUserAgent = 'Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101';
  //¡¡¡¡¡¡¡¡¡¡Please be nice and use your own API key, get a key from here http://code.google.com/apis/safebrowsing/key_signup.html ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡
  sApiKey    = 'ABQIAAAAzY4CKjsBFYV4Rxx0ZQaKlxQL2a1oqOk9I7UVXAZVtWa6uSA2XA';
  sServer    = 'sb-ssl.google.com';
  sPostSafeBrowsing  = '/safebrowsing/api/lookup?client=delphi&apikey=%s&appver=1.5.2&pver=3.0';

function GetWinInetError(ErrorCode:Cardinal): string;
const
   winetdll = 'wininet.dll';
var
  Len: Integer;
  Buffer: PChar;
begin
  Len := FormatMessage(
  FORMAT_MESSAGE_FROM_HMODULE or FORMAT_MESSAGE_FROM_SYSTEM or
  FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_IGNORE_INSERTS or  FORMAT_MESSAGE_ARGUMENT_ARRAY,
  Pointer(GetModuleHandle(winetdll)), ErrorCode, 0, @Buffer, SizeOf(Buffer), nil);
  try
    while (Len > 0) and {$IFDEF UNICODE}(CharInSet(Buffer[Len - 1], [#0..#32, '.'])) {$ELSE}(Buffer[Len - 1] in [#0..#32, '.']) {$ENDIF} do Dec(Len);
    SetString(Result, Buffer, Len);
  finally
    LocalFree(HLOCAL(Buffer));
  end;
end;

function Https_Post(const ServerName,Resource: String;const PostData : AnsiString;Var Response:AnsiString): Integer;
const
  BufferSize=1024*64;
var
  hInet    : HINTERNET;
  hConnect : HINTERNET;
  hRequest : HINTERNET;
  ErrorCode : Integer;
  lpdwBufferLength: DWORD;
  lpdwReserved    : DWORD;
  dwBytesRead     : DWORD;
  lpdwNumberOfBytesAvailable: DWORD;
begin
  Result  :=0;
  Response:='';
  hInet := InternetOpen(PChar(sUserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

  if hInet=nil then
  begin
    ErrorCode:=GetLastError;
    raise Exception.Create(Format('InternetOpen Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
  end;

  try
    hConnect := InternetConnect(hInet, PChar(ServerName), INTERNET_DEFAULT_HTTPS_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
    if hConnect=nil then
    begin
      ErrorCode:=GetLastError;
      raise Exception.Create(Format('InternetConnect Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
    end;

    try
      hRequest := HttpOpenRequest(hConnect, 'POST', PChar(Resource), HTTP_VERSION, '', nil, INTERNET_FLAG_SECURE, 0);
      if hRequest=nil then
      begin
        ErrorCode:=GetLastError;
        raise Exception.Create(Format('HttpOpenRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
      end;

      try
        //send the post request
        if not HTTPSendRequest(hRequest, nil, 0, @PostData[1], Length(PostData)) then
        begin
          ErrorCode:=GetLastError;
          raise Exception.Create(Format('HttpSendRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
        end;

          lpdwBufferLength:=SizeOf(Result);
          lpdwReserved    :=0;
          //get the response code
          if not HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER, @Result, lpdwBufferLength, lpdwReserved) then
          begin
            ErrorCode:=GetLastError;
            raise Exception.Create(Format('HttpQueryInfo Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
          end;

         //if the response code =200 then get the body
         if Result=200 then
          if InternetQueryDataAvailable(hRequest, lpdwNumberOfBytesAvailable, 0, 0) then
          begin
            SetLength(Response,lpdwNumberOfBytesAvailable);
            InternetReadFile(hRequest, @Response[1], lpdwNumberOfBytesAvailable, dwBytesRead);
          end
          else
          begin
            ErrorCode:=GetLastError;
            raise Exception.Create(Format('InternetQueryDataAvailable Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
          end;

      finally
        InternetCloseHandle(hRequest);
      end;
    finally
      InternetCloseHandle(hConnect);
    end;
  finally
    InternetCloseHandle(hInet);
  end;
end;

function URLEncode(const Url: string): string;
var
  i: Integer;
begin
  Result := '';
  for i := 1 to Length(Url) do
  begin
    case Url[i] of
      'A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.':
        Result := Result + Url[i];
    else
        Result := Result + '%' + IntToHex(Ord(Url[i]), 2);
    end;
  end;
end;

Procedure TestPost(const UrlList : Array of AnsiString);
var
 Response     : AnsiString;
 ResponseCode : Integer;
 Data         : AnsiString;
 i            : integer;
 LstUrl       : TStringList;
begin
   //create the body request with the url to lookup
   Data:=AnsiString(IntToStr(Length(UrlList)))+#10;
   for i:= low(UrlList) to high(UrlList) do
     Data:=Data+UrlList[i]+#10;

   //make the post request
   ResponseCode:=Https_Post(sServer,Format(sPostSafeBrowsing,[sApiKey]), Data, Response);

   //process the response
   case ResponseCode of
     200:
          begin
             LstUrl:=TStringList.Create;
             try
               LstUrl.Text:=string(Response);
                for i:=0 to  LstUrl.Count-1  do
                 Writeln(Format('The queried URL (%s) is %s',[UrlList[i],LstUrl[i]]));

             finally
               LstUrl.Free;
             end;
          end;
     204: Writeln('NONE of the queried URLs matched the phishing or malware lists, no response body returned');
     400: Writeln('Bad Request — The HTTP request was not correctly formed.');
     401: Writeln('Not Authorized — The apikey is not authorized');
     503: Writeln('Service Unavailable — The server cannot handle the request.');
   else
         Writeln(Format('Unknow response Code (%d)',[ResponseCode]));
   end;
end;

begin
  try
     //check these three urls at once
     TestPost(['orgsite.info','http://www.google.com','http://malware.testing.google.test/testing/malware/']);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

finally the result will be

The queried URL (orgsite.info) is malware
The queried URL (http://www.google.com) is ok
The queried URL (http://malware.testing.google.test/testing/malware/) is malware


22 Comments

Using the Google Maps API V3 from Delphi – Part III Getting the latitude and longitude of a mouse click

In this post I will show, how you can interact with a google map embedded in a TWebbrowser component in order to get the location ( latitude and longitude) of  a point when you click in the map.

JavaScript

To get the location of the mouse when you make a click in the map you must add a  Google maps Event Listener, passing a function to process the event, the values of the current location are retrieved in the event.latLng variable , the next step is store the values returned in a hidden field element to after get these values from Delphi.

Check this sample JavaScript snippet which create an event listener and store the values in the LatValue and LngValue fields.

    google.maps.event.addListener(map, "click",
         function(event)
           {
            document.getElementById("LatValue").value = event.latLng.lat();
            document.getElementById("LngValue").value = event.latLng.lng();
            PutMarker(document.getElementById("LatValue").value, document.getElementById("LngValue").value,"")
           }
   );

This is the PutMarker function which creates a marker in the current location

function PutMarker(Lat, Lang, Msg)
{
 var latlng = new google.maps.LatLng(Lat,Lang);
 var marker = new google.maps.Marker({
     position: latlng,
     map: map,
     title: Msg+" ("+Lat+","+Lang+")"
  });

   //put the created marker in an array
   markersArray.push(marker);

   //compute the index to associate an image to the marker
   index= (markersArray.length % 10);
   if (index==0) { index=10 }
   icon = "http://www.google.com/mapfiles/kml/paddle/"+index+"-lv.png";
   marker.setIcon(icon);
 }

And this is the code to create the 2 input hidden fields in the html page to store the values returned by the Event listener

<body onload="initialize()">
  <div id="map_canvas" style="width:100%; height:100%"></div>
  <div id="latlong">
  <input id="<span class=" type="hidden" />LatValue" >
  <input id="<span class=" type="hidden" />LngValue" >
  </div>
</body>

Delphi

Now from the Delphi side, you must detect the click event in the TWebBrowser component and then read the values stored in the hidden fields. Exists several ways to detect the click in the TWebBrowser, in this case I will use the OnCommandStateChange event.

Check this code which detect the click event and then read the values stored in the hidden fields.

procedure TForm1.WebBrowser1CommandStateChange(ASender: TObject;  Command: Integer; Enable: WordBool);
var
  ADocument : IHTMLDocument2;
  ABody     : IHTMLElement2;
  Lat : string;
  Lng : string;

      //get the value from a field
      function GetIdValue(const Id : string):string;
      var
        Tag      : IHTMLElement;
        TagsList : IHTMLElementCollection;
        Index    : Integer;
      begin
        Result:='';
        TagsList := ABody.getElementsByTagName('input');
        for Index := 0 to TagsList.length-1 do
        begin
          Tag:=TagsList.item(Index, EmptyParam) As IHTMLElement;
          if CompareText(Tag.id,Id)=0 then
            Result := Tag.getAttribute('value', 0);
        end;
      end;

begin
  //is a valid command?
  if TOleEnum(Command) <> CSC_UPDATECOMMANDS then //-1
    Exit;

  //The page is loaded?
  ADocument := WebBrowser1.Document as IHTMLDocument2;
  if not Assigned(ADocument) then
    Exit;

  //the page has body?
  if not Supports(ADocument.body, IHTMLElement2, ABody) then
    exit;

  // get the values of the Latitude and Longitude
  Lat :=GetIdValue('LatValue');
  Lng :=GetIdValue('LngValue');

  //Now process the data
  if  (Lat<>'') and (Lng<>'') and ((Lat<>Latitude.Text) or (Lng<>Longitude.Text)) then
  begin
    Latitude.Text :=Lat;
    Longitude.Text:=Lng;

  end;
end;

Finally this is the full source code for the demo application

unit uMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtrls, SHDocVw, StdCtrls, ExtCtrls, XPMan, ComCtrls,MSHTML;

type
  TFrmMain = class(TForm)
    WebBrowser1: TWebBrowser;
    PanelHeader: TPanel;
    ButtonGotoLocation: TButton;
    XPManifest1: TXPManifest;
    LabelLatitude: TLabel;
    LabelLongitude: TLabel;
    Longitude: TEdit;
    Latitude: TEdit;
    ButtonClearMarkers: TButton;
    ListView1: TListView;
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure ButtonClearMarkersClick(Sender: TObject);
    procedure WebBrowser1CommandStateChange(ASender: TObject; Command: Integer;  Enable: WordBool);
    procedure ButtonGotoLocationClick(Sender: TObject);
  private
    HTMLWindow2: IHTMLWindow2;
    procedure AddLatLngToList(const Lat,Lng:string);
  public
  end;

var
  FrmMain: TFrmMain;

implementation

{$R *.dfm}

uses
   ActiveX;

const
HTMLStr: AnsiString =
'<html> '+
'<head> '+
'<meta name="viewport" content="initial-scale=1.0, user-scalable=yes" /> '+
'<script type="text/javascript">// <![CDATA[
src</span>="http://maps.google.com/maps/api/js?sensor=false&language=en">
// ]]></script> '+
//'<script type="text/javascript">// <![CDATA[
src</span>="http://maps.google.com/maps/api/js?sensor=false">
// ]]></script> '+
'<script type="text/javascript"> '+
''+
''+
'  var geocoder; '+
'  var map;  '+
'  var markersArray = [];'+
''+
''+
'  function initialize() { '+
'    geocoder = new google.maps.Geocoder();'+
'    var latlng = new google.maps.LatLng(40.714776,-74.019213); '+
'    var myOptions = { '+
'      zoom: 13, '+
'      center: latlng, '+
'      mapTypeId: google.maps.MapTypeId.ROADMAP '+
'    }; '+
'    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); '+
'    map.set("streetViewControl", false);'+
'    google.maps.event.addListener(map, "click", '+
'         function(event) '+
'                        {'+
'                         document.getElementById("LatValue").value = event.latLng.lat(); '+
'                         document.getElementById("LngValue").value = event.latLng.lng(); '+
'                         PutMarker(document.getElementById("LatValue").value, document.getElementById("LngValue").value,"") '+
'                        } '+
'   ); '+
''+
'  } '+
''+
''+
'  function GotoLatLng(Lat, Lang) { '+
'   var latlng = new google.maps.LatLng(Lat,Lang);'+
'   map.setCenter(latlng);'+
'  }'+
''+
''+
'function ClearMarkers() {  '+
'  if (markersArray) {        '+
'    for (i in markersArray) {  '+
'      markersArray[i].setMap(null); '+
'    } '+
'  } '+
'}  '+
''+
'  function PutMarker(Lat, Lang, Msg) { '+
'   var latlng = new google.maps.LatLng(Lat,Lang);'+
'   var marker = new google.maps.Marker({'+
'      position: latlng, '+
'      map: map,'+
'      title: Msg+" ("+Lat+","+Lang+")"'+
'  });'+
'  markersArray.push(marker); '+
'  index= (markersArray.length % 10);'+
'  if (index==0) { index=10 } '+
'  icon = "http://www.google.com/mapfiles/kml/paddle/"+index+"-lv.png"; '+
'  marker.setIcon(icon); '+
'  }'+
''+
''+
''+'</script> '+
'</head> '+
''+
'<body onload="initialize()"> '+
'  <div id="map_canvas" style="width:100%; height:100%"></div> '+
'  <div id="latlong"> '+
'  <input type="hidden" id="LatValue" >'+
'  <input type="hidden" id="LngValue" >'+
'  </div>  '+
''+
'</body> '+
'</html> ';

procedure TFrmMain.FormCreate(Sender: TObject);
var
  aStream     : TMemoryStream;
begin
   WebBrowser1.Navigate('about:blank');
    if Assigned(WebBrowser1.Document) then
    begin
      aStream := TMemoryStream.Create;
      try
         aStream.WriteBuffer(Pointer(HTMLStr)^, Length(HTMLStr));
         //aStream.Write(HTMLStr[1], Length(HTMLStr));
         aStream.Seek(0, soFromBeginning);
         (WebBrowser1.Document as IPersistStreamInit).Load(TStreamAdapter.Create(aStream));
      finally
         aStream.Free;
      end;
      HTMLWindow2 := (WebBrowser1.Document as IHTMLDocument2).parentWindow;
    end;
end;

procedure TFrmMain.WebBrowser1CommandStateChange(ASender: TObject;  Command: Integer; Enable: WordBool);
var
  ADocument : IHTMLDocument2;
  ABody     : IHTMLElement2;
  Lat : string;
  Lng : string;

      function GetIdValue(const Id : string):string;
      var
        Tag      : IHTMLElement;
        TagsList : IHTMLElementCollection;
        Index    : Integer;
      begin
        Result:='';
        TagsList := ABody.getElementsByTagName('input');
        for Index := 0 to TagsList.length-1 do
        begin
          Tag:=TagsList.item(Index, EmptyParam) As IHTMLElement;
          if CompareText(Tag.id,Id)=0 then
            Result := Tag.getAttribute('value', 0);
        end;
      end;

begin
  if TOleEnum(Command) <> CSC_UPDATECOMMANDS then
    Exit;

  ADocument := WebBrowser1.Document as IHTMLDocument2;
  if not Assigned(ADocument) then
    Exit;

  if not Supports(ADocument.body, IHTMLElement2, ABody) then
    exit;

  Lat :=GetIdValue('LatValue');
  Lng :=GetIdValue('LngValue');
  if  (Lat<>'') and (Lng<>'') and ((Lat<>Latitude.Text) or (Lng<>Longitude.Text)) then
  begin
    Latitude.Text :=Lat;
    Longitude.Text:=Lng;
    AddLatLngToList(Lat, Lng);
  end;
end;

procedure TFrmMain.AddLatLngToList(const Lat, Lng: string);
var
  Item  : TListItem;
begin
   if (Lat<>'') and (Lng<>'') then
   begin
     Item:=ListView1.Items.Add;
     Item.Caption:=Lng;
     Item.SubItems.Add(Lat);
     Item.MakeVisible(False);
   end;
end;

procedure TFrmMain.ButtonClearMarkersClick(Sender: TObject);
begin
  HTMLWindow2.execScript('ClearMarkers()', 'JavaScript');
  ListView1.Items.Clear;
end;

procedure TFrmMain.ButtonGotoLocationClick(Sender: TObject);
begin
  if Assigned(ListView1.Selected) then
    HTMLWindow2.execScript(Format('GotoLatLng(%s,%s)',[ListView1.Selected.SubItems[0],ListView1.Selected.Caption]), 'JavaScript');
end;

end.

Check the source code on Github.


3 Comments

Detecting the language from a text using the Google translate API v1

UPDATE

The Google Translate API has been officially deprecated  an alternative  is the Microsoft Translator V2, check this article for more details.

 

In this past post we use the Google translate API v2 to translate from an language to another. today will show how use the current version of this API (v1) which include a nice functionality to detect the language from a given text.

This API can be accessed via JavaScript  in a web page or using a HTTP request. in the next sample i will use the second option.

Before to continue you must aware of this warning of Google.

Note: The Google Translate API must be used for user-generated translations. Automated or batched queries of any kind are strictly prohibited.

Here you can find the Terms of Service of this API.

The next code is for educational purposes only.

To detect the language from a text you must make  a request to his URI

https://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q=Hello+World

As you can see the only required parameter is the encoded text to detect.

the  JSON response to this request will be something like this

{ "responseData" : { "confidence" : 0.11489271400000001,
      "isReliable" : false,
      "language" : "en"
    },
  "responseDetails" : null,
  "responseStatus" : 200
}

the responseStatus contain the result of the operation, the 200 indicate which the language from text was successfully detected.

So now we need to define a few types, functions and constants to make the work more easy.

type
  //the supported languages
  TGoogleLanguages=
  (Autodetect,Afrikaans,Albanian,Arabic,Basque,Belarusian,Bulgarian,Catalan,Chinese,Chinese_Traditional,
  Croatian,Czech,Danish,Dutch,English,Estonian,Filipino,Finnish,French,Galician,German,Greek,
  Haitian_Creole,Hebrew,Hindi,Hungarian,Icelandic,Indonesian,Irish,Italian,Japanese,Latvian,
  Lithuanian,Macedonian,Malay,Maltese,Norwegian,Persian,Polish,Portuguese,Romanian,Russian,
  Serbian,Slovak,Slovenian,Spanish,Swahili,Swedish,Thai,Turkish,Ukrainian,Vietnamese,Welsh,Yiddish,Unknow);

  //the string representation for the enumerated types
  const
  GoogleLanguagesStr : array[TGoogleLanguages] of string =
  ('Autodetect','Afrikaans','Albanian','Arabic','Basque','Belarusian','Bulgarian','Catalan','Chinese','Chinese_Traditional',
  'Croatian','Czech','Danish','Dutch','English','Estonian','Filipino','Finnish','French','Galician','German','Greek',
  'Haitian_Creole','Hebrew','Hindi','Hungarian','Icelandic','Indonesian','Irish','Italian','Japanese','Latvian',
  'Lithuanian','Macedonian','Malay','Maltese','Norwegian','Persian','Polish','Portuguese','Romanian','Russian',
  'Serbian','Slovak','Slovenian','Spanish','Swahili','Swedish','Thai','Turkish','Ukrainian','Vietnamese','Welsh','Yiddish','Unknow');

  //The languages code to be used in HTTP request
  GoogleLanguagesArr : array[TGoogleLanguages] of string =
  ( 'Autodetect','af','sq','ar','eu','be','bg','ca','zh-CN','zh-TW','hr','cs','da','nl','en','et','tl','fi','fr','gl',
    'de','el','ht','iw','hi','hu','is','id','ga','it','ja','lv','lt','mk','ms','mt','no','fa','pl','pt',
    'ro','ru','sr','sk','sl','es','sw','sv','th','tr','uk','vi','cy','yi','Unknow');

  //URI to translate a text using the V1 from the API
  GoogleTranslateUrl='https://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=%s&langpair=%s';
  //URI to detect the language from a text
  GoogleLngDetectUrl='https://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q=%s';

//return a stream containing the HTTP response InternetOpen
procedure WinInet_HttpGet(const Url: string;Stream:TStream);overload;
const
BuffSize = 1024*1024;
var
  hInter   : HINTERNET;
  UrlHandle: HINTERNET;
  BytesRead: DWORD;
  Buffer   : Pointer;
begin
  hInter := InternetOpen('', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(hInter) then
  begin
    Stream.Seek(0,0);
    GetMem(Buffer,BuffSize);
    try
        UrlHandle := InternetOpenUrl(hInter, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
        if Assigned(UrlHandle) then
        begin
          repeat
            InternetReadFile(UrlHandle, Buffer, BuffSize, BytesRead);
            if BytesRead>0 then
             Stream.WriteBuffer(Buffer^,BytesRead);
          until BytesRead = 0;
          InternetCloseHandle(UrlHandle);
        end;
    finally
      FreeMem(Buffer);
    end;
    InternetCloseHandle(hInter);
  end
end;

//return a string containing the HTTP response.
function WinInet_HttpGet(const Url: string): string;overload;
Var
  StringStream : TStringStream;
begin
  Result:='';
    StringStream:=TStringStream.Create('',TEncoding.UTF8);
    try
        WinInet_HttpGet(Url,StringStream);
        if StringStream.Size>0 then
        begin
          StringStream.Seek(0,0);
          Result:=StringStream.ReadString(StringStream.Size);
        end;
    finally
      StringStream.Free;
    end;
end;

And now to process the response using the DBXJSON unit

function DetectLanguage_DBXJSON(const Text:string):TGoogleLanguages;
var
  EncodedRequest: string;
  json          : TJSONObject;
  jPair         : TJSONPair;
  jValue        : TJSONValue;
  Response      : string;
  Lng           : TGoogleLanguages;
  LngStr        : string;
begin
  Result:=Unknow;
  EncodedRequest:=Format(GoogleLngDetectUrl,[HTTPEncode(Text)]);
  Response:=WinInet_HttpGet(EncodedRequest);

  if Response<>'' then
  begin
      json    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Response),0) as TJSONObject;
    try
      jPair   := json.Get(2);//get the responseStatus
      if jPair.JsonValue.ToString<>'200' then  //200 is all ok
        Result := Unknow
      else
      begin
        jPair  := json.Get(0);
        jValue := TJSONObject(jPair.JsonValue).Get(0).JsonValue;
        LngStr := jValue.Value;
        for lng:=Low(TGoogleLanguages) to High(TGoogleLanguages)  do
        if GoogleLanguagesArr[Lng]=LngStr then
        begin
         Result:=lng;
         exit;
        end;
      end;
    finally
       json.Free;
    end;
  end;
end;

Another alternative using the JSON superobject library

function DetectLanguage_JSONsuperobject(const Text:string):TGoogleLanguages;
var
  EncodedRequest: string;
  Response      : string;
  Lng           : TGoogleLanguages;
  LngStr        : string;
begin
  Result:=Unknow;
  EncodedRequest:=Format(GoogleLngDetectUrl,[HTTPEncode(Text)]);
  Response:=WinInet_HttpGet(EncodedRequest);
  if Response<>'' then
  begin
    if SO(Response)['responseStatus'].AsInteger<>200 then   //if responseStatus<>200 then exist a error in the response
      Result:=Unknow
    else
    begin
      LngStr:=SO(Response)['responseData.language'].AsString;
      for lng:=Low(TGoogleLanguages) to High(TGoogleLanguages)  do
      if GoogleLanguagesArr[Lng]=LngStr then
      begin
       Result:=lng;
       exit;
      end;
    end;
  end;
end;

and finally a option without JSON

function DetectLanguage_JSONLess(const Text:string):TGoogleLanguages;
const
  TagErr='{"responseData": null,';
  TagIOk='{"responseData": {"language":"';
  TagFOk='","isReliable":';
var
  EncodedRequest: string;
  Response      : string;
  Lng           : TGoogleLanguages;
  LngStr        : string;
begin
  Result:=Unknow;
  EncodedRequest:=Format(GoogleLngDetectUrl,[HTTPEncode(Text)]);
  Response:=WinInet_HttpGet(EncodedRequest);

  if Response<>'' then
  begin
    if StartsStr(TagErr,(Response)) then  //Response  Error
    begin
      Result:=Unknow
    end
    else
    begin  //Response Ok
      LngStr:=StringReplace(Response,TagIOk,'',[rfReplaceAll]);
      LngStr:=Copy(LngStr,1,Pos(TagFOk,LngStr)-1);
        for lng:=Low(TGoogleLanguages) to High(TGoogleLanguages)  do
        if GoogleLanguagesArr[Lng]=LngStr then
        begin
         Result:=lng;
         exit;
        end;
    end;
  end;
end;

To finish here i leave the full source code from a console application which show all the alternatives to decode the JSON response and as extra include the routine to translate a text using the Google translate API v1.

program GoogleAPITranslateV1;

{$APPTYPE CONSOLE}
{$DEFINE USE_SUPER_OBJECT}
{$DEFINE USE_DBXJSON}
{$DEFINE USE_JSONLess}

uses
  Windows
  ,Classes
  ,WinInet
  ,Activex
  ,HTTPApp
  ,SysUtils
  {$IFDEF USE_JSONLess}
  ,StrUtils
  {$ENDIF}
  {$IFDEF USE_SUPER_OBJECT}
  ,superobject
  {$ENDIF}
  {$IFDEF USE_DBXJSON}
  ,DBXJSON
  {$ENDIF}
  ;

type
  TGoogleLanguages=
  (Autodetect,Afrikaans,Albanian,Arabic,Basque,Belarusian,Bulgarian,Catalan,Chinese,Chinese_Traditional,
  Croatian,Czech,Danish,Dutch,English,Estonian,Filipino,Finnish,French,Galician,German,Greek,
  Haitian_Creole,Hebrew,Hindi,Hungarian,Icelandic,Indonesian,Irish,Italian,Japanese,Latvian,
  Lithuanian,Macedonian,Malay,Maltese,Norwegian,Persian,Polish,Portuguese,Romanian,Russian,
  Serbian,Slovak,Slovenian,Spanish,Swahili,Swedish,Thai,Turkish,Ukrainian,Vietnamese,Welsh,Yiddish,Unknow);

  const
  GoogleLanguagesStr : array[TGoogleLanguages] of string =
  ('Autodetect','Afrikaans','Albanian','Arabic','Basque','Belarusian','Bulgarian','Catalan','Chinese','Chinese_Traditional',
  'Croatian','Czech','Danish','Dutch','English','Estonian','Filipino','Finnish','French','Galician','German','Greek',
  'Haitian_Creole','Hebrew','Hindi','Hungarian','Icelandic','Indonesian','Irish','Italian','Japanese','Latvian',
  'Lithuanian','Macedonian','Malay','Maltese','Norwegian','Persian','Polish','Portuguese','Romanian','Russian',
  'Serbian','Slovak','Slovenian','Spanish','Swahili','Swedish','Thai','Turkish','Ukrainian','Vietnamese','Welsh','Yiddish','Unknow');

  GoogleLanguagesArr : array[TGoogleLanguages] of string =
  ( 'Autodetect','af','sq','ar','eu','be','bg','ca','zh-CN','zh-TW','hr','cs','da','nl','en','et','tl','fi','fr','gl',
    'de','el','ht','iw','hi','hu','is','id','ga','it','ja','lv','lt','mk','ms','mt','no','fa','pl','pt',
    'ro','ru','sr','sk','sl','es','sw','sv','th','tr','uk','vi','cy','yi','Unknow');

  GoogleTranslateUrl='https://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=%s&langpair=%s';
  //http://code.google.com/apis/language/translate/v1/using_rest_translate.html
  GoogleLngDetectUrl='https://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q=%s';

procedure WinInet_HttpGet(const Url: string;Stream:TStream);overload;
const
BuffSize = 1024*1024;
var
  hInter   : HINTERNET;
  UrlHandle: HINTERNET;
  BytesRead: DWORD;
  Buffer   : Pointer;
begin
  hInter := InternetOpen('Mozilla/3.0', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(hInter) then
  begin
    Stream.Seek(0,0);
    GetMem(Buffer,BuffSize);
    try
        UrlHandle := InternetOpenUrl(hInter, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
        if Assigned(UrlHandle) then
        begin
          repeat
            InternetReadFile(UrlHandle, Buffer, BuffSize, BytesRead);
            if BytesRead>0 then
             Stream.WriteBuffer(Buffer^,BytesRead);
          until BytesRead = 0;
          InternetCloseHandle(UrlHandle);
        end;
    finally
      FreeMem(Buffer);
    end;
    InternetCloseHandle(hInter);
  end
end;

function WinInet_HttpGet(const Url: string): string;overload;
Var
  StringStream : TStringStream;
begin
  Result:='';
    StringStream:=TStringStream.Create('',TEncoding.UTF8);
    try
        WinInet_HttpGet(Url,StringStream);
        if StringStream.Size>0 then
        begin
          StringStream.Seek(0,0);
          Result:=StringStream.ReadString(StringStream.Size);
        end;
    finally
      StringStream.Free;
    end;
end;

{$IFDEF USE_SUPER_OBJECT}
function DetectLanguage_JSONsuperobject(const Text:string):TGoogleLanguages;
var
  EncodedRequest: string;
  Response      : string;
  Lng           : TGoogleLanguages;
  LngStr        : string;
begin
  Result:=Unknow;
  EncodedRequest:=Format(GoogleLngDetectUrl,[HTTPEncode(Text)]);
  Response:=WinInet_HttpGet(EncodedRequest);
  if Response<>'' then
  begin
    if SO(Response)['responseStatus'].AsInteger<>200 then   //if responseStatus<>200 then exist a error in the response
      Result:=Unknow
    else
    begin
      LngStr:=SO(Response)['responseData.language'].AsString;
      for lng:=Low(TGoogleLanguages) to High(TGoogleLanguages)  do
      if GoogleLanguagesArr[Lng]=LngStr then
      begin
       Result:=lng;
       exit;
      end;
    end;
  end;
end;

function Translate_JSONsuperobject(const Text:string;Source,Dest:TGoogleLanguages):string;
var
  EncodedRequest: string;
  Response      : string;
begin
  Result:='';
  if Source=Autodetect then
   EncodedRequest:=Format(GoogleTranslateUrl,[HTTPEncode(Text),'%7C'+GoogleLanguagesArr[Dest]])
  else
   EncodedRequest:=Format(GoogleTranslateUrl,[HTTPEncode(Text),GoogleLanguagesArr[Source]+'%7C'+GoogleLanguagesArr[Dest]]);

  Response:=WinInet_HttpGet(EncodedRequest);
  if Response<>'' then
  begin
    if SO(Response)['responseData'].AsObject=nil then   //if the first element is null then ocurrs an error
      Result:=Format('Error Code %d %s',[SO(Response)['responseStatus'].AsInteger,SO(Response)['responseDetails'].AsString])
    else
      Result:=SO(Response)['responseData.translatedText'].AsString;
  end;
end;
{$ENDIF}

{$IFDEF USE_DBXJSON}
function DetectLanguage_DBXJSON(const Text:string):TGoogleLanguages;
var
  EncodedRequest: string;
  json          : TJSONObject;
  jPair         : TJSONPair;
  jValue        : TJSONValue;
  Response      : string;
  Lng           : TGoogleLanguages;
  LngStr        : string;
begin
  Result:=Unknow;
  EncodedRequest:=Format(GoogleLngDetectUrl,[HTTPEncode(Text)]);
  Response:=WinInet_HttpGet(EncodedRequest);
  if Response<>'' then
  begin
      json    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Response),0) as TJSONObject;
    try
      jPair   := json.Get(2);//get the responseStatus
      if jPair.JsonValue.ToString<>'200' then  //200 is all ok
        Result := Unknow
      else
      begin
        jPair  := json.Get(0);
        jValue := TJSONObject(jPair.JsonValue).Get(0).JsonValue;
        LngStr := jValue.Value;
        for lng:=Low(TGoogleLanguages) to High(TGoogleLanguages)  do
        if GoogleLanguagesArr[Lng]=LngStr then
        begin
         Result:=lng;
         exit;
        end;
      end;
    finally
       json.Free;
    end;
  end;
end;

function Translate_DBXJSON(const Text:string;Source,Dest:TGoogleLanguages):string;
var
  EncodedRequest: string;
  json          : TJSONObject;
  jPair         : TJSONPair;
  jValue        : TJSONValue;
  Response      : string;
begin
  Result:='';
  if Source=Autodetect then
   EncodedRequest:=Format(GoogleTranslateUrl,[HTTPEncode(Text),'%7C'+GoogleLanguagesArr[Dest]])
  else
   EncodedRequest:=Format(GoogleTranslateUrl,[HTTPEncode(Text),GoogleLanguagesArr[Source]+'%7C'+GoogleLanguagesArr[Dest]]);

  Response:=WinInet_HttpGet(EncodedRequest);
  if Response<>'' then
  begin
      json    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Response),0) as TJSONObject;
    try
      jPair   := json.Get(2);//get the responseStatus
      if jPair.JsonValue.ToString<>'200' then  //200 is all ok
        //{"responseData": null, "responseDetails": "invalid translation language pair", "responseStatus": 400}
        Result := Format('Error Code %s message %s',[json.Get(2).JsonValue.ToString,json.Get(1).JsonValue.ToString])
      else
      begin
        jPair  := json.Get(0);
        jValue := TJSONObject(jPair.JsonValue).Get(0).JsonValue;
        Result := jValue.ToString;
      end;
    finally
       json.Free;
    end;
      Result:=HTMLDecode(Result);
  end;
end;
{$ENDIF}

{$IFDEF USE_JSONLess}
function DetectLanguage_JSONLess(const Text:string):TGoogleLanguages;
const
  TagErr='{"responseData": null,';
  TagIOk='{"responseData": {"language":"';
  TagFOk='","isReliable":';
var
  EncodedRequest: string;
  Response      : string;
  Lng           : TGoogleLanguages;
  LngStr        : string;
begin
  Result:=Unknow;
  EncodedRequest:=Format(GoogleLngDetectUrl,[HTTPEncode(Text)]);
  Response:=WinInet_HttpGet(EncodedRequest); //{"responseData": {"language":"en","isReliable":false,"confidence":0.114892714},"responseDetails": null, "responseStatus": 200}

  if Response<>'' then
  begin
    if StartsStr(TagErr,(Response)) then  //Response  Error
    begin
      Result:=Unknow
    end
    else
    begin  //Response Ok
      LngStr:=StringReplace(Response,TagIOk,'',[rfReplaceAll]);
      LngStr:=Copy(LngStr,1,Pos(TagFOk,LngStr)-1);
        for lng:=Low(TGoogleLanguages) to High(TGoogleLanguages)  do
        if GoogleLanguagesArr[Lng]=LngStr then
        begin
         Result:=lng;
         exit;
        end;
    end;
  end;
end;

function Translate_JSONLess(const Text:string;Source,Dest:TGoogleLanguages):string;
const
  TagErr='{"responseData": null,';

  TagIOk='{"responseData": {"translatedText":"';
  TagAut=',"detectedSourceLanguage":"';

  TagFOk='"}, "responseDetails":';
var
  EncodedRequest: string;
  Response      : string;
begin
  Result:='';
  if Source=Autodetect then
   EncodedRequest:=Format(GoogleTranslateUrl,[HTTPEncode(Text),'%7C'+GoogleLanguagesArr[Dest]])
  else
   EncodedRequest:=Format(GoogleTranslateUrl,[HTTPEncode(Text),GoogleLanguagesArr[Source]+'%7C'+GoogleLanguagesArr[Dest]]);

  Response:=WinInet_HttpGet(EncodedRequest);
  if Response<>'' then
  begin
    if StartsStr(TagErr,(Response)) then  //Response  Error
    begin
      Result:='Error'
    end
    else
    begin  //Response Ok
      if Source=Autodetect then
      begin
        Result:=StringReplace(Response,TagIOk,'',[rfReplaceAll]);
        Result:=Copy(Result,1,Pos(TagAut,Result)-2);
      end
      else
      begin
        Result:=StringReplace(Response,TagIOk,'',[rfReplaceAll]);
        Result:=Copy(Result,1,Pos(TagFOk,Result)-1);
      end;
    end;

    Result:=HTMLDecode(Result);
  end;
end;
{$ENDIF}

Const
 Text   ='Hello World';
 TextEn ='Hello World';
 TextEs ='Hola Mundo';
Var
 TranslatedText : string;
begin
  try
    CoInitialize(nil);
    try
       {$IFDEF USE_JSONLess}
       Writeln('Without JSON (very ugly)');
       Writeln('');
       TranslatedText:=Translate_JSONLess(Text,Autodetect,Spanish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,Chinese_Traditional);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,German);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,Danish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,Portuguese);
       Writeln(TranslatedText);
       Writeln('');

       Writeln('Detecting language Without JSON');
       Writeln('');
       Writeln(Format('language detected for "%s"  : %s',[TextEn,GoogleLanguagesStr[DetectLanguage_JSONLess(TextEn)]]));
       Writeln(Format('language detected for "%s"  : %s',[TextEs,GoogleLanguagesStr[DetectLanguage_JSONLess(TextEs)]]));
       {$ENDIF}

       {$IFDEF USE_SUPER_OBJECT}
       Writeln('Using the superobject library');
       Writeln('');
       TranslatedText:=Translate_JSONsuperobject(Text,Autodetect,Spanish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,Chinese_Traditional);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,German);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,Danish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,Portuguese);
       Writeln(TranslatedText);
       Writeln('');

       Writeln('Detecting language using the superobject library');
       Writeln('');
       Writeln(Format('language detected for "%s"  : %s',[TextEn,GoogleLanguagesStr[DetectLanguage_JSONsuperobject(TextEn)]]));
       Writeln(Format('language detected for "%s"  : %s',[TextEs,GoogleLanguagesStr[DetectLanguage_JSONsuperobject(TextEs)]]));
       {$ENDIF}

       {$IFDEF USE_DBXJSON}
       Writeln('Using the DBXJSON unit');
       Writeln('');
       TranslatedText:=Translate_DBXJSON(Text,Autodetect,Spanish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,Chinese_Traditional);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,German);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,Danish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,Portuguese);
       Writeln(TranslatedText);
       Writeln('');

       Writeln('Detecting language using the DBXJSON unit');
       Writeln('');
       Writeln(Format('language detected for "%s"  : %s',[TextEn,GoogleLanguagesStr[DetectLanguage_DBXJSON(TextEn)]]));
       Writeln(Format('language detected for "%s"  : %s',[TextEs,GoogleLanguagesStr[DetectLanguage_DBXJSON(TextEs)]]));
       {$ENDIF}

    finally
     CoUninitialize;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.


25 Comments

Generating Qr Codes with Delphi

 The QR Codes are a  special kind of images that used to represent two-dimensional barcodes. They are also known as hardlinks or physical world hyperlinks.

The QR Codes can store up to 4,296 alphanumeric characters of arbitrary text. The  QR codes can be read by an optical device with the appropriate software. Such devices range from dedicated QR code readers to mobile phones.

On this post I will show you how using the Google Chart Tools / Image Charts (aka Chart API) you can easily generate QR codes.

Using this API is very  straightforward, all you need to do is to generate a QR Code is make a Get request to this URI

http://chart.apis.google.com/chart?chs=200x200&cht=qr&chld=M&chl=Go+Delphi+Go

And how response you will get a png image (you can change the output format to gif adding the chof parameter to the URI  like so : chof=gif).

On the Google Chart Documentation you can find more info about the parameters to generate a QR Code.

Note : If you want encode more of 2000 chars do you need make a post request (this up to you).

Finally this a sample Delphi source to generate a QR Code.


uses
 PngImage,
 HTTPApp,
 WinInet;

type
TQrImage_ErrCorrLevel=(L,M,Q,H);

const
UrlGoogleQrCode='http://chart.apis.google.com/chart?chs=%dx%d&cht=qr&chld=%s&chl=%s';
QrImgCorrStr   : array [TQrImage_ErrCorrLevel] of string=('L','M','Q','H');

procedure WinInet_HttpGet(const Url: string;Stream:TStream);
const
BuffSize = 1024*1024;
var
  hInter   : HINTERNET;
  UrlHandle: HINTERNET;
  BytesRead: DWORD;
  Buffer   : Pointer;
begin
  hInter := InternetOpen('', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(hInter) then
  begin
    Stream.Seek(0,0);
    GetMem(Buffer,BuffSize);
    try
        UrlHandle := InternetOpenUrl(hInter, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
        if Assigned(UrlHandle) then
        begin
          repeat
            InternetReadFile(UrlHandle, Buffer, BuffSize, BytesRead);
            if BytesRead>0 then
             Stream.WriteBuffer(Buffer^,BytesRead);
          until BytesRead = 0;
          InternetCloseHandle(UrlHandle);
        end;
    finally
      FreeMem(Buffer);
    end;
    InternetCloseHandle(hInter);
  end
end;

//this function return a Stream (PngImage inside) with a Qr code.
procedure GetQrCode(Width,Height:Word;Correction_Level:TQrImage_ErrCorrLevel;const Data:string;StreamImage : TMemoryStream);
Var
 EncodedURL  : string;
begin
  EncodedURL:=Format(UrlGoogleQrCode,[Width,Height,QrImgCorrStr[Correction_Level],HTTPEncode(Data)]);
  WinInet_HttpGet(EncodedURL,StreamImage);
end;

Check the source code of the application on Github


18 Comments

Using the Google Translate API V2 (Labs) from Delphi

UPDATE

The Google Translate API has been officially deprecated  an alternative  is the Microsoft Translator V2, check this article for more details.

 

In this post i will show you how work with the Google Translate API V2 (Labs),  this API lets you automatically translates text from one language to another.

Disclaimer

  • This version of the Google Translate API is in Labs, and its features might change unexpectedly until it graduates.
  • The Google Translate API requires the use of an API key, which you can get from the Google APIs console
  • Before to use this API check the Google Translate API Terms of Use.

To use the Google Translate API you must send a HTTP GET request to its URI.

The URI for a request has the following format:

https://www.googleapis.com/language/translate/v2?parameters

Example to making a request to translate the Hello World text from English (en) to Spanish (es) the URI must be constructed in this way

https://www.googleapis.com/language/translate/v2?key=INSERT-YOUR-KEY&source=en&target=es&q=Hello%20world

The response in JSON format will be

{"data":{"translations":[{"translatedText":"Hola Mundo"}]}}

To activate the auto-detection of the source language you must avoid the use of the source keyword

https://www.googleapis.com/language/translate/v2?key=INSERT-YOUR-KEY&target=es&q=Hello%20world

and the JSON response in this case will be

{"data":{"translations":[{"translatedText":"Hola a todos","detectedSourceLanguage":"en"}]}

if you pass incorrect parameters the response will look like this

{"error":{"errors":[{"domain":"global","reason":"invalid","message":"Invalid Value"}],"code":400,"message":"Invalid Value"}}

some conversions between languages are not allowed by the API, in thi case you will get a response of this type

{"error":{"errors":[{"domain":"global","reason":"badRequest","message":"Bad language pair: en|zh-TW"}],"code":400,"message":"Bad language pair: en|zh-TW"}}

Now I will show 3 ways to process the data

Using the JSON – SuperObject , this library is very well written and is very easy to use, also is compatible with olders versions of Delphi and Freepascal (win32/64 linux32/64).

function Translate_JSONsuperobject(const Text:string;Source,Dest:TGoogleLanguages):string;
var
  XMLHTTPRequest: IXMLHTTPRequest;
  EncodedRequest: string;
  Response      : string;
begin
  Result:='';
  if Source=Autodetect then //build the URI
    EncodedRequest:=Format(GoogleTranslateUrlAuto,[GoogleLanguageApiKey,GoogleLanguagesArr[Dest],HTTPEncode(Text)])
  else
    EncodedRequest:=Format(GoogleTranslateUrl,[GoogleLanguageApiKey,HTTPEncode(Text),GoogleLanguagesArr[Source],GoogleLanguagesArr[Dest]]);

  XMLHTTPRequest := CoXMLHTTP.Create;
  XMLHTTPRequest.open('GET', EncodedRequest, False, EmptyParam, EmptyParam); //Make the request
  XMLHTTPRequest.send('');
  Response:=XMLHTTPRequest.responseText;
  if Response<>'' then
  begin
    if SO(Response)['error']=nil then //all ok
     Result := SO(Response)['data.translations[0].translatedText'].AsString
    else //exist an error response
     Result := Format('Error Code %d message %s',[SO(Response)['error.code'].AsInteger,SO(Response)['error.message'].AsString]);
     Result:=HTMLDecode(Result);
  end;

end;

Using the DBXJSON unit included since Delphi 2010

function Translate_DBXJSON(const Text:string;Source,Dest:TGoogleLanguages):string;
var
  XMLHTTPRequest: IXMLHTTPRequest;
  EncodedRequest: string;
  json          : TJSONObject;
  jPair         : TJSONPair;
  jValue        : TJSONValue;
  Response      : string;
begin
  Result:='';
  if Source=Autodetect then //buil the URI
    EncodedRequest:=Format(GoogleTranslateUrlAuto,[GoogleLanguageApiKey,GoogleLanguagesArr[Dest],HTTPEncode(Text)])
  else
    EncodedRequest:=Format(GoogleTranslateUrl,[GoogleLanguageApiKey,HTTPEncode(Text),GoogleLanguagesArr[Source],GoogleLanguagesArr[Dest]]);

  XMLHTTPRequest := CoXMLHTTP.Create;
  XMLHTTPRequest.open('GET', EncodedRequest, False, EmptyParam, EmptyParam); //make the request
  XMLHTTPRequest.send('');
  Response:=XMLHTTPRequest.responseText;

  if Response<>'' then
  begin
      json    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Response),0) as TJSONObject; //create a TJSONObject instance
    try
      jPair   := json.Get(0);
      if jPair.JsonString.value='error' then //if error in response
        Result := Format('Error Code %s message %s',[TJSONObject(jPair.JsonValue).Get(1).JsonValue.Value,TJSONObject(jPair.JsonValue).Get(2).JsonValue.Value])
      else //all ok, show the response,
      begin
        jValue := TJSONArray(TJSONObject(jPair.JsonValue).Get(0).JsonValue).Get(0);
        Result := TJSONObject(jValue).Get(0).JsonValue.Value;
      end;
    finally
       json.Free;
    end;

      Result:=HTMLDecode(Result);
  end;
end;

and finally without using JSON, a very ugly way, but works.

function Translate_JSONLess(const Text:string;Source,Dest:TGoogleLanguages):string;
const
  TagIOk='{"data":{"translations":[{"translatedText":"';
  TagFOk='"}]}}';
  TagErr='{"error":{"errors":[{';
  TagAut=',"detectedSourceLanguage":"';
var
  XMLHTTPRequest: IXMLHTTPRequest;
  EncodedRequest: string;
  Response      : string;
begin
  Result:='';

  if Source=Autodetect then //build the URI
    EncodedRequest:=Format(GoogleTranslateUrlAuto,[GoogleLanguageApiKey,GoogleLanguagesArr[Dest],HTTPEncode(Text)])
  else
    EncodedRequest:=Format(GoogleTranslateUrl,[GoogleLanguageApiKey,HTTPEncode(Text),GoogleLanguagesArr[Source],GoogleLanguagesArr[Dest]]);

  XMLHTTPRequest := CoXMLHTTP.Create;
  XMLHTTPRequest.open('GET', EncodedRequest, False, EmptyParam, EmptyParam); //make the request
  XMLHTTPRequest.send('');
  Response:=XMLHTTPRequest.responseText;
  if Response<>'' then
  begin
    if StartsStr(TagErr,(Response)) then  //Response  Error
    begin
      Result:='Error'
    end
    else
    begin  //Response Ok
      if Source=Autodetect then
      begin
        Result:=StringReplace(Response,TagIOk,'',[rfReplaceAll]); //remove tags
        Result:=Copy(Result,1,Pos(TagAut,Result)-2);//remove tags
      end
      else
      begin
        Result:=StringReplace(Response,TagIOk,'',[rfReplaceAll]);//remove tags
        Result:=StringReplace(Result,TagFOk,'',[rfReplaceAll]);//remove tags
      end;
    end;

    Result:=HTMLDecode(Result);
  end;
end;

Check the full source showing the 3 ways to access the Google Translate API, listed in this entry.

program GoogleAPITranslate;
//Author  : Rodrigo Ruz V. 2010-12-03  03;30 A.M

{$APPTYPE CONSOLE}
{$DEFINE USE_SUPER_OBJECT}
{$DEFINE USE_DBXJSON}
{$DEFINE USE_JSONLess}

uses
   msxml
  ,Activex
  ,HTTPApp
  ,Variants
  ,SysUtils
  {$IFDEF USE_JSONLess}
  ,StrUtils
  {$ENDIF}
  {$IFDEF USE_SUPER_OBJECT}
  ,superobject
  {$ENDIF}
  {$IFDEF USE_DBXJSON}
  ,DBXJSON
  {$ENDIF}
  ;

  type
  TGoogleLanguages=
  (Autodetect,Afrikaans,Albanian,Arabic,Basque,Belarusian,Bulgarian,Catalan,Chinese,Chinese_Traditional,
  Croatian,Czech,Danish,Dutch,English,Estonian,Filipino,Finnish,French,Galician,German,Greek,
  Haitian_Creole,Hebrew,Hindi,Hungarian,Icelandic,Indonesian,Irish,Italian,Japanese,Latvian,
  Lithuanian,Macedonian,Malay,Maltese,Norwegian,Persian,Polish,Portuguese,Romanian,Russian,
  Serbian,Slovak,Slovenian,Spanish,Swahili,Swedish,Thai,Turkish,Ukrainian,Vietnamese,Welsh,Yiddish);

  const
  GoogleLanguagesArr : array[TGoogleLanguages] of string =
  ( 'Autodetect','af','sq','ar','eu','be','bg','ca','zh-CN','zh-TW','hr','cs','da','nl','en','et','tl','fi','fr','gl',
    'de','el','ht','iw','hi','hu','is','id','ga','it','ja','lv','lt','mk','ms','mt','no','fa','pl','pt',
    'ro','ru','sr','sk','sl','es','sw','sv','th','tr','uk','vi','cy','yi');

  //¡¡¡¡¡¡Please be nice and create your own Google Api Key ¡¡¡¡¡¡¡
  GoogleLanguageApiKey   ='AIzaSyDb18pd1IfkYyupC2XUIANcRoB3f9J2DJg';
  GoogleTranslateUrl     ='https://www.googleapis.com/language/translate/v2?key=%s&q=%s&source=%s&target=%s';
  GoogleTranslateUrlAuto ='https://www.googleapis.com/language/translate/v2?key=%s&target=%s&q=%s';

{$IFDEF USE_DBXJSON}
function Translate_DBXJSON(const Text:string;Source,Dest:TGoogleLanguages):string;
var
  XMLHTTPRequest: IXMLHTTPRequest;
  EncodedRequest: string;
  json          : TJSONObject;
  jPair         : TJSONPair;
  jValue        : TJSONValue;
  Response      : string;
begin
  Result:='';
  if Source=Autodetect then
    EncodedRequest:=Format(GoogleTranslateUrlAuto,[GoogleLanguageApiKey,GoogleLanguagesArr[Dest],HTTPEncode(Text)])
  else
    EncodedRequest:=Format(GoogleTranslateUrl,[GoogleLanguageApiKey,HTTPEncode(Text),GoogleLanguagesArr[Source],GoogleLanguagesArr[Dest]]);

  XMLHTTPRequest := CoXMLHTTP.Create;
  XMLHTTPRequest.open('GET', EncodedRequest, False, EmptyParam, EmptyParam);
  XMLHTTPRequest.send('');
  Response:=XMLHTTPRequest.responseText;

  if Response<>'' then
  begin
      json    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Response),0) as TJSONObject;
    try
      jPair   := json.Get(0);
      if jPair.JsonString.value='error' then
        //{"error":{"errors":[{"domain":"global","reason":"invalid","message":"Invalid Value"}],"code":400,"message":"Invalid Value"}}
        Result := Format('Error Code %s message %s',[TJSONObject(jPair.JsonValue).Get(1).JsonValue.Value,TJSONObject(jPair.JsonValue).Get(2).JsonValue.Value])
      else
      begin
        //{"data":{"translations":[{"translatedText":"Hola a todos","detectedSourceLanguage":"en"}]}}
        jValue := TJSONArray(TJSONObject(jPair.JsonValue).Get(0).JsonValue).Get(0);
        Result := TJSONObject(jValue).Get(0).JsonValue.Value;
      end;
    finally
       json.Free;
    end;

      Result:=HTMLDecode(Result);
  end;
end;
{$ENDIF}

{$IFDEF USE_SUPER_OBJECT}
function Translate_JSONsuperobject(const Text:string;Source,Dest:TGoogleLanguages):string;
var
  XMLHTTPRequest: IXMLHTTPRequest;
  EncodedRequest: string;
  Response      : string;
begin
  Result:='';
  if Source=Autodetect then
    EncodedRequest:=Format(GoogleTranslateUrlAuto,[GoogleLanguageApiKey,GoogleLanguagesArr[Dest],HTTPEncode(Text)])
  else
    EncodedRequest:=Format(GoogleTranslateUrl,[GoogleLanguageApiKey,HTTPEncode(Text),GoogleLanguagesArr[Source],GoogleLanguagesArr[Dest]]);

  XMLHTTPRequest := CoXMLHTTP.Create;
  XMLHTTPRequest.open('GET', EncodedRequest, False, EmptyParam, EmptyParam);
  XMLHTTPRequest.send('');
  Response:=XMLHTTPRequest.responseText;
  if Response<>'' then
  begin
  //{"data":{"translations":[{"translatedText":"Hola a todos","detectedSourceLanguage":"en"}]}}
    if SO(Response)['error']=nil then
     Result := SO(Response)['data.translations[0].translatedText'].AsString
    else
     //{"error":{"errors":[{"domain":"global","reason":"invalid","message":"Invalid Value"}],"code":400,"message":"Invalid Value"}}
     //{"error":{"errors":[{"domain":"global","reason":"badRequest","message":"Bad language pair: en|zh-TW"}],"code":400,"message":"Bad language pair: en|zh-TW"}}
     Result := Format('Error Code %d message %s',[SO(Response)['error.code'].AsInteger,SO(Response)['error.message'].AsString]);
     Result:=HTMLDecode(Result);
  end;

end;
{$ENDIF}

{$IFDEF USE_JSONLess}
function Translate_JSONLess(const Text:string;Source,Dest:TGoogleLanguages):string;
const
  TagIOk='{"data":{"translations":[{"translatedText":"';
  TagFOk='"}]}}';
  TagErr='{"error":{"errors":[{';
  TagAut=',"detectedSourceLanguage":"';
var
  XMLHTTPRequest: IXMLHTTPRequest;
  EncodedRequest: string;
  Response      : string;
begin
  Result:='';

  if Source=Autodetect then
    EncodedRequest:=Format(GoogleTranslateUrlAuto,[GoogleLanguageApiKey,GoogleLanguagesArr[Dest],HTTPEncode(Text)])
  else
    EncodedRequest:=Format(GoogleTranslateUrl,[GoogleLanguageApiKey,HTTPEncode(Text),GoogleLanguagesArr[Source],GoogleLanguagesArr[Dest]]);

  XMLHTTPRequest := CoXMLHTTP.Create;
  XMLHTTPRequest.open('GET', EncodedRequest, False, EmptyParam, EmptyParam);
  XMLHTTPRequest.send('');
  Response:=XMLHTTPRequest.responseText;
  if Response<>'' then
  begin
    if StartsStr(TagErr,(Response)) then  //Response  Error
    begin
      Result:='Error'
    end
    else
    begin  //Response Ok
      if Source=Autodetect then
      begin
        Result:=StringReplace(Response,TagIOk,'',[rfReplaceAll]);
        Result:=Copy(Result,1,Pos(TagAut,Result)-2);
      end
      else
      begin
        Result:=StringReplace(Response,TagIOk,'',[rfReplaceAll]);
        Result:=StringReplace(Result,TagFOk,'',[rfReplaceAll]);
      end;
    end;

    Result:=HTMLDecode(Result);
  end;
end;
{$ENDIF}

Const
 Text ='"Hello  World"';
Var
 TranslatedText : string;
begin
  try
    CoInitialize(nil);
    try
       {$IFDEF USE_JSONLess}
       Writeln('Without JSON (very ugly)');
       Writeln('');
       TranslatedText:=Translate_JSONLess(Text,Autodetect,Spanish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,Chinese_Traditional);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,German);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,Danish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONLess(Text,English,Portuguese);
       Writeln(TranslatedText);
       Writeln('');
       {$ENDIF}

       {$IFDEF USE_SUPER_OBJECT}
       Writeln('Using the superobject library');
       Writeln('');
       TranslatedText:=Translate_JSONsuperobject(Text,Autodetect,Spanish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,Chinese_Traditional);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,German);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,Danish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_JSONsuperobject(Text,English,Portuguese);
       Writeln(TranslatedText);
       Writeln('');
       {$ENDIF}

       {$IFDEF USE_DBXJSON}
       Writeln('Using the DBXJSON unit');
       Writeln('');
       TranslatedText:=Translate_DBXJSON(Text,Autodetect,Spanish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,Chinese_Traditional);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,German);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,Danish);
       Writeln(TranslatedText);
       TranslatedText:=Translate_DBXJSON(Text,English,Portuguese);
       Writeln(TranslatedText);
       Writeln('');
       {$ENDIF}

    finally
     CoUninitialize;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
Check this link  <a href=”http://code.google.com/apis/language/translate/terms.html&#8221; rel=”nofollow”>Google Translate API Terms of Use</a>.


6 Comments

Using Google maps (Static Maps) without TWebBrowser

Commonly we use the TWebBrowser component to access Google maps, but there is another option, using the Google Static Map service.

The Google Static Map service allows you get an static image from a location without requiring JavaScript or any dynamic page loading. The Google Static Map service creates your map based on URL parameters sent through a standard HTTP request.

So the code to interact with this service is fairly easy. all we need is an TIdHTTP and a TImage component.

First we need to set the TIdHTTP property UserAgent (the UserAgent is what a browser uses to identify itself to the HTTP server
) to an valid Agent, if you use the default value Mozilla/3.0 (compatible; Indy Library) you will get a awful message like this HTTP 1.1/ 403 Forbidden. so we can change this value to Mozilla/3.0 or to another valid agent.

then we need build the url to request the image from an location. a valid URL look like this

http://maps.google.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=12&size=400x400&sensor=false

You can see the full syntax for build a valid URL on this page.

Now using the TIdHTTP component, we send the request and get the image

var
  StreamData :TMemoryStream;
  JPEGImage  : TJPEGImage;
begin
  EditURL.Text:=buildUrl;//build the url with the params
  StreamData := TMemoryStream.Create;
  JPEGImage  := TJPEGImage.Create;
  try
    try
     idhttp1.Get(EditURL.Text, StreamData); //Send the request and get the image
     StreamData.Seek(0,soFromBeginning);
     JPEGImage.LoadFromStream(StreamData);//load the image in a Stream
     ImageMap.Picture.Assign(JPEGImage);//Load the image
    Except On E : Exception Do
     MessageDlg('Exception: '+E.Message,mtError, [mbOK], 0);
    End;
  finally
    StreamData.free;
    JPEGImage.Free;
  end;
end;

finally a very important note from google

Use of the Google Static Maps API is subject to a query limit of 1000 unique (different) image requests per viewer per day. Since this restriction is a quota per viewer, most developers should not need to worry about exceeding their quota. However, note that we enforce an additional request rate limit to prevent abuse of the service. Requests of identical images, in general, do not count towards this limit beyond the original request.

If a user exceeds the limit as proscribed above, the following image will be displayed indicating that the quota has been exceeded:

This limit is enforced to prevent abuse and/or repurposing of the Static Maps API, and this limit may be changed in the future without notice. If you exceed the 24-hour limit or otherwise abuse the service, the Static Maps API may stop working for you temporarily. If you continue to exceed this limit, your access to the Static Maps API may be blocked.

Static Map URLs are restricted to 2048 characters in size. In practice, you will probably not have need for URLs longer than this, unless you produce complicated maps with a high number of markers and paths. Note, however, that certain characters may be URL-encoded by browsers and/or services before sending them off to the Static Map service, resulting in increased character usage.

…Note that static maps may only be displayed within browser content; use of static maps outside of the browser is not allowed. (Google Maps API Premier users are waived of this requirement.)

check the source code for educational use only (do you need a API Premier account to  use the Static maps ouside of an browser), you can download the full project from this location.

unit UnitMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
  StdCtrls, ExtCtrls, XPMan, ComCtrls;

type
  TFormMain = class(TForm)
    ImageMap: TImage;
    IdHTTP10: TIdHTTP;
    XPManifest1: TXPManifest;
    ScrollBoxMap: TScrollBox;
    Panel1: TPanel;
    EditURL: TEdit;
    ButtonGet: TButton;
    CheckBoxRealTime: TCheckBox;
    Panel2: TPanel;
    Panel3: TPanel;
    EditWidth: TEdit;
    UpDown3: TUpDown;
    UpDown2: TUpDown;
    UpDown1: TUpDown;
    ComboBoxMapType: TComboBox;
    EditZoom: TEdit;
    ComboBoxFormat: TComboBox;
    EditHeight: TEdit;
    EditLongitude: TEdit;
    EditLatitude: TEdit;
    Label7: TLabel;
    Label6: TLabel;
    Label5: TLabel;
    Label4: TLabel;
    Label3: TLabel;
    Label2: TLabel;
    Label1: TLabel;
    CheckBoxMarker: TCheckBox;
    ProgressBar1: TProgressBar;
    IdHTTP1: TIdHTTP;
    procedure ButtonGetClick(Sender: TObject);
    procedure EditZoomChange(Sender: TObject);
    procedure ComboBoxMapTypeChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ImageMapMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ImageMapMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure IdHTTP10WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
      AWorkCountMax: Integer);
    procedure IdHTTP10Work(ASender: TObject; AWorkMode: TWorkMode;
      AWorkCount: Integer);
    procedure IdHTTP10WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
  private
    { Private declarations }
    SX: Integer;
    SY: Integer;
    LX: Integer;
    LY: Integer;
    function  buildUrl:string;
    procedure GetMapImage;
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

uses
jpeg; //this project only supports jpg, you can add addtional units to support gif and png

{$R *.dfm}

const
UrlPrefix='http://maps.google.com/maps/api/staticmap?';

function TFormMain.buildUrl: string; //build the url based in the user input
begin
  Result:=UrlPrefix+'center='+EditLatitude.Text+','+EditLongitude.Text+'&zoom='+EditZoom.Text+'&size='+EditWidth.Text+'x'+EditHeight.Text+'&maptype='+ComboBoxMapType.Text+'&sensor=false&format='+ComboBoxFormat.Text;
  if CheckBoxMarker.Checked then
  Result:=Result+'&markers=color:blue|'+EditLatitude.Text+','+EditLongitude.Text;
end;

procedure TFormMain.ButtonGetClick(Sender: TObject);
begin
 GetMapImage;
end;

procedure TFormMain.ComboBoxMapTypeChange(Sender: TObject);
begin
 if CheckBoxRealTime.Checked then
 GetMapImage;
end;

procedure TFormMain.EditZoomChange(Sender: TObject);
begin
 if CheckBoxRealTime.Checked then
 GetMapImage;
end;

procedure TFormMain.FormCreate(Sender: TObject);
begin
 ScrollBoxMap.DoubleBuffered := True; //avoid the flicker when the pan effect is activated
end;

procedure TFormMain.GetMapImage; //Get the image from the Google Service
var
  StreamData : TMemoryStream;
  JPEGImage  : TJPEGImage;
begin
  EditURL.Text:=buildUrl;
  StreamData := TMemoryStream.Create;
  JPEGImage  := TJPEGImage.Create;
  try
    try
     idhttp1.Get(EditURL.Text, StreamData);
     StreamData.Seek(0,soFromBeginning);

     ImageMap.Top := 0;
     ImageMap.Left := 0;
     JPEGImage.LoadFromStream(StreamData);
     LX := (ImageMap.Width - ScrollBoxMap.ClientWidth) * -1;
     LY := (ImageMap.Height - ScrollBoxMap.ClientHeight) * -1;

     ImageMap.Picture.Assign(JPEGImage);
    Except On E : Exception Do
     MessageDlg('Exception: '+E.Message,mtError, [mbOK], 0);
    End;
  finally
    StreamData.free;
    JPEGImage.Free;
  end;
end;

procedure TFormMain.IdHTTP10Work(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Integer);
begin
  ProgressBar1.Position := AWorkCount;
end;

procedure TFormMain.IdHTTP10WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Integer);
begin
  ProgressBar1.Position := 0;
  ProgressBar1.Max      := IdHTTP1.Response.ContentLength;
end;

procedure TFormMain.IdHTTP10WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  //ProgressBar1.Position := 0;
end;

procedure TFormMain.ImageMapMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   SX := X;
   SY := Y;
end;

procedure TFormMain.ImageMapMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); //Allow pannig over the image
var NX: Integer;
    NY: Integer;
begin
    if not (ssLeft in Shift) then   Exit;
    NX := ImageMap.Left + X - SX;
    NY := ImageMap.Top + Y - SY;

    if (NX < 0) and (NX > LX) then  ImageMap.Left := NX;
    if (NY < 0) and (NY > LY) then  ImageMap.Top := NY;
end;

end.