Due which the Google Translate API has been officially deprecated is time to look for alternatives and a good one is the Microsoft Translator V2, in this article I will show how you can access this API from a Delphi desktop application.
In order to gain access to this API you must obtain a Bing AppID, so go to this page register and get your Bing AppID to play with this.
The Microsoft Translator V2 can be accessed via HTTP, Ajax or SOAP in this post we will use the HTTP interface to make the requests. Now to start using the HTTP API for the Microsoft Translator service you must send a request to the appropriate http://api.microsofttranslator.com/V2/HTTP.svc url and then parse the returned response.
This is the list of the functions supported by the HTTP API
Name | Description |
---|---|
Microsoft.Translator.AddTranslation Method | Adds a translation to the translation memory. |
Microsoft.Translator.AddTranslationArray Method | Adds an array of translations to the translation memory. |
Microsoft.Translator.BreakSentences Method | Returns an array of sentence lengths for each sentence of the given text. |
Microsoft.Translator.Detect Method | Detects the language of a selection of text. |
Microsoft.Translator.DetectArray Method | Detects the language of an array of strings. |
Microsoft.Translator.GetAppIdToken Method | Returns a tokenized AppID which can be used as AppID parameter in any method. |
Microsoft.Translator.GetLanguageNames Method | Obtains a list of the languages supported by the Translator Service. |
Microsoft.Translator.GetLanguagesForSpeak Method | Obtains a list of the language codes supported by the Translator Service for speech synthesis. |
Microsoft.Translator.GetLanguagesForTranslate Method | Obtains a list of the language codes supported by the Translator Service. |
Microsoft.Translator.GetTranslations Method | Returns an array of alternative translations of the given text. |
Microsoft.Translator.GetTranslationsArray Method | Returns an array of alternative translations of the passed array of text. |
Microsoft.Translator.Speak Method | Returns a stream of a wave-file speaking the passed-in text in the desired language. |
Microsoft.Translator.Translate Method | Converts a text string from one language to another. |
Microsoft.Translator.TranslateArray Method | Translates an array of texts into another language. |
The next samples uses two helper functions to make a http request (off course which you can use you own method or component too)
uses Windows, WinInet; 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 try 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; finally 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;
Translating a Text
To translate a given text you must use the Translate method making a request to this URL http://api.microsofttranslator.com/V2/Http.svc/Translate using these parameters
Parameter | Description |
---|---|
appId | A string containing the Bing AppID. |
text | A string representing the text to translate. |
from | A string representing the language code of the translation text. |
to | A string representing the language code to translate the text into. |
contentType | The format of the text being translated. The supported formats are “text/plain” and “text/html”. Any HTML needs to be well-formed. |
category | The category of the text to translate. The only supported category is “general”. |
In delphi you can construct this URI in this way
const MicrosoftTranslatorTranslateUri = 'http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=%s&text=%s&from=%s&to=%s';
Check this sample code which make a http request and a translate a text
function TranslateText(const AText,SourceLng,DestLng:string):string; var XmlDoc : OleVariant; Node : OleVariant; begin //Make the http request Result:=WinInet_HttpGet(Format(MicrosoftTranslatorTranslateUri,[BingAppId,AText,SourceLng,DestLng])); //Create a XML object o parse the result XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; //load the XML retuned string XmlDoc.LoadXML(Result); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; if not VarIsClear(Node) then Result:=XmlDoc.Text; finally XmlDoc:=Unassigned; end; end;
Detecting the language
To detect the language of a text you must use the Detect Method making a http request to this URI http://api.microsofttranslator.com/V2/Http.svc/Detect with these parameters.
Parameter | Description |
---|---|
appId | A string containing the Bing AppID. |
text | A string containing some text whose language is to be identified. |
In delphi you can construct this URI in this way
const MicrosoftTranslatorDetectUri = 'http://api.microsofttranslator.com/v2/Http.svc/Detect?appId=%s&text=%s';
function DetectLanguage(const AText:string ):string; var XmlDoc : OleVariant; Node : OleVariant; begin //make the http request Result:=WinInet_HttpGet(Format(MicrosoftTranslatorDetectUri,[BingAppId,AText])); XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; //load the returned xml string XmlDoc.LoadXML(Result); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; //get the detected language from the node if not VarIsClear(Node) then Result:=XmlDoc.Text; finally XmlDoc:=Unassigned; end; end; end;
Getting the list of supported languages
The GetLanguagesForTranslate method return a list of the supported languages for translation
The URL of this method is http://api.microsofttranslator.com/V2/Http.svc/GetLanguagesForTranslate and the parameters are
Parameter | Description |
---|---|
appId | A string containing the Bing AppID. |
const MicrosoftTranslatorGetLngUri = 'http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=%s';
And here you a have a sample code to make the request and parse the response
function GetLanguagesForTranslate: TList<string>; var XmlDoc : OleVariant; Node : OleVariant; Nodes : OleVariant; lNodes : Integer; i : Integer; sValue : string; begin Result:=TList<string>.Create; //make the http request sValue:=WinInet_HttpGet(Format(MicrosoftTranslatorGetLngUri,[BingAppId])); XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; XmlDoc.LoadXML(sValue); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; if not VarIsClear(Node) then begin //get the nodes Nodes := Node.childNodes; if not VarIsClear(Nodes) then begin lNodes:= Nodes.Length; for i:=0 to lNodes-1 do Result.Add(Nodes.Item(i).Text); end; end; finally XmlDoc:=Unassigned; end; end;
Getting the list of supported languages for speak
the GetLanguagesForSpeak Method returns a list of the suported languages for speak, the URL for this method is http://api.microsofttranslator.com/V2/Http.svc/GetLanguagesForSpeak
Parameter | Description |
---|---|
appId | A string containing the Bing AppID. |
the delphi equivalent declaration of the URI
const MicrosoftTranslatorGetSpkUri = 'http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForSpeak?appId=%s';
Now the function to get the supported languages
function GetLanguagesForSpeak: TList<string>; var XmlDoc : OleVariant; Node : OleVariant; Nodes : OleVariant; lNodes : Integer; i : Integer; sValue : string; begin Result:=TList<string>.Create; sValue:=WinInet_HttpGet(Format(MicrosoftTranslatorGetSpkUri,[BingAppId])); XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; XmlDoc.LoadXML(sValue); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; if not VarIsClear(Node) then begin Nodes := Node.childNodes; if not VarIsClear(Nodes) then begin lNodes:= Nodes.Length; for i:=0 to lNodes-1 do Result.Add(Nodes.Item(i).Text); end; end; finally XmlDoc:=Unassigned; end; end;
Making the API speak
The speak method returns a stream of a audio file speaking the passed-in text in the desired language. the URI of this function is http://api.microsofttranslator.com/V2/Http.svc/Speak and the parameters are
Parameter | Description |
---|---|
appId | A string containing the Bing AppID. |
text | A string containing a sentence or sentences of the specified language to be spoken for the wave stream. |
language | A string representing the supported language code to speak the text in. The code must be present in the list of codes returned from the method GetLanguagesForSpeak. |
format | Optional. A string specifying the format of the wafe-file to be returned. The default value is “audio/wav” which is the only currently allowed value. |
The delphi equivalent URI
const MicrosoftTranslatorSpeakUri = 'http://api.microsofttranslator.com/v2/Http.svc/Speak?appId=%s&text=%s&language=%s';
And the function to get the audio stream
procedure Speak(const FileName,AText,Lng:string); var Stream : TFileStream; begin Stream:=TFileStream.Create(FileName,fmCreate); try WinInet_HttpGet(Format(MicrosoftTranslatorSpeakUri,[BingAppId,AText,Lng]),Stream); finally Stream.Free; end; end;
Finally this is the full code of a sample console application with all the funcions covered in this post
program MicrosoftTranslatorApi; {$APPTYPE CONSOLE} uses ShellApi, ActiveX, Classes, ComObj, Variants, Windows, WinInet, Generics.Collections, SysUtils; const MicrosoftTranslatorTranslateUri = 'http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=%s&text=%s&from=%s&to=%s'; MicrosoftTranslatorDetectUri = 'http://api.microsofttranslator.com/v2/Http.svc/Detect?appId=%s&text=%s'; MicrosoftTranslatorGetLngUri = 'http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=%s'; MicrosoftTranslatorGetSpkUri = 'http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForSpeak?appId=%s'; MicrosoftTranslatorSpeakUri = 'http://api.microsofttranslator.com/v2/Http.svc/Speak?appId=%s&text=%s&language=%s'; //this AppId if for demo only please be nice and use your own , it's easy get one from here http://msdn.microsoft.com/en-us/library/ff512386.aspx BingAppId = '73C8F474CA4D1202AD60747126813B731199ECEA'; Msxml2_DOMDocument = 'Msxml2.DOMDocument.6.0'; 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 try 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; finally 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; function TranslateText(const AText,SourceLng,DestLng:string):string; var XmlDoc : OleVariant; Node : OleVariant; begin Result:=WinInet_HttpGet(Format(MicrosoftTranslatorTranslateUri,[BingAppId,AText,SourceLng,DestLng])); XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; XmlDoc.LoadXML(Result); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; if not VarIsClear(Node) then Result:=XmlDoc.Text; finally XmlDoc:=Unassigned; end; end; function DetectLanguage(const AText:string ):string; var XmlDoc : OleVariant; Node : OleVariant; begin Result:=WinInet_HttpGet(Format(MicrosoftTranslatorDetectUri,[BingAppId,AText])); XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; XmlDoc.LoadXML(Result); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; if not VarIsClear(Node) then Result:=XmlDoc.Text; finally XmlDoc:=Unassigned; end; end; function GetLanguagesForTranslate: TList<string>; var XmlDoc : OleVariant; Node : OleVariant; Nodes : OleVariant; lNodes : Integer; i : Integer; sValue : string; begin Result:=TList<string>.Create; sValue:=WinInet_HttpGet(Format(MicrosoftTranslatorGetLngUri,[BingAppId])); XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; XmlDoc.LoadXML(sValue); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; if not VarIsClear(Node) then begin Nodes := Node.childNodes; if not VarIsClear(Nodes) then begin lNodes:= Nodes.Length; for i:=0 to lNodes-1 do Result.Add(Nodes.Item(i).Text); end; end; finally XmlDoc:=Unassigned; end; end; function GetLanguagesForSpeak: TList<string>; var XmlDoc : OleVariant; Node : OleVariant; Nodes : OleVariant; lNodes : Integer; i : Integer; sValue : string; begin Result:=TList<string>.Create; sValue:=WinInet_HttpGet(Format(MicrosoftTranslatorGetSpkUri,[BingAppId])); XmlDoc:= CreateOleObject(Msxml2_DOMDocument); try XmlDoc.Async := False; XmlDoc.LoadXML(sValue); if (XmlDoc.parseError.errorCode <> 0) then raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]); Node:= XmlDoc.documentElement; if not VarIsClear(Node) then begin Nodes := Node.childNodes; if not VarIsClear(Nodes) then begin lNodes:= Nodes.Length; for i:=0 to lNodes-1 do Result.Add(Nodes.Item(i).Text); end; end; finally XmlDoc:=Unassigned; end; end; procedure Speak(const FileName,AText,Lng:string); var Stream : TFileStream; begin Stream:=TFileStream.Create(FileName,fmCreate); try WinInet_HttpGet(Format(MicrosoftTranslatorSpeakUri,[BingAppId,AText,Lng]),Stream); finally Stream.Free; end; end; var lng : TList<string>; s : string; FileName : string; begin try ReportMemoryLeaksOnShutdown:=True; CoInitialize(nil); try Writeln(TranslateText('Hello World','en','es')); Writeln(DetectLanguage('Hello World')); Writeln('Languages for translate supported'); lng:=GetLanguagesForTranslate; try for s in lng do Writeln(s); finally lng.free; end; Writeln('Languages for speak supported'); lng:=GetLanguagesForSpeak; try for s in lng do Writeln(s); finally lng.free; end; FileName:=ExtractFilePath(ParamStr(0))+'Demo.wav'; Speak(FileName,'This is a demo using the Microsoft Translator Api from delphi, enjoy','en'); ShellExecute(0, 'open', PChar(FileName),nil,nil, SW_SHOWNORMAL) ; finally CoUninitialize; end; except on E:Exception do Writeln(E.Classname, ':', E.Message); end; Writeln('Press Enter to exit'); Readln; end.
May 30, 2011 at 2:30 am
Very nice article! Thanks for the info and detailed sample code.
You could use WinHttp.dll instead of WinInet for much faster access to the Internet. It’s almost the same interface, but with much less overhead (and less features, but I’m quite sure dialup dialog boxes are less useful today).
May 30, 2011 at 5:16 am
Very good article indeed, just one thing: can you also post a download link to a ready made test application(bin and/or src)? it would do wonders for Delphi beginners.
May 30, 2011 at 5:45 am
P.S. I’m sorry, I was referring to a GUI application, CUI is harder to understand for some reason.
May 30, 2011 at 6:20 am
Gran artículo Rodrigo.
Interesante y muy ilustrador (como siempre).
Gracias.
Pingback: Detecting the language from a text using the Google translate API v1 « The Road to Delphi – a Blog about programming
Pingback: Using the Google Translate API V2 (Labs) from Delphi « The Road to Delphi – a Blog about programming
May 31, 2011 at 4:05 pm
Very nice Rodrigo.
Will certainly interest a lot of developers having to work in multi-language situation. Just hoping it will stay available for some (long) time…
Wasn’t too happy when BabelFish stopped working.
August 3, 2012 at 1:58 am
hello,
can this code work on Delphi XE2 Ios application?
August 3, 2012 at 2:15 am
No directly, because this code uses WinInet and Msxml2 which are part of Windows , but you can access Microsoft Translator V2 API from a Firemonkey IOS application using another method to make the GET requests and then process the XML response.
October 17, 2012 at 3:34 pm
Hi Rodrigo,
This is very helpful and I have tried testing it out but have run into problems since MS has now deactivated the use of Bing IDs for new accounts. Instead you now seem to need to use Windows Azure Market place accounts and “access tokens”. The good news is that you still get 2 million characters translated per month for free.
I managed to sign up OK for Windows Azure but could not find any good Delphi examples of how to get and work with access tokens. I did find some guidance here: http://msdn.microsoft.com/en-us/library/hh454950.aspx but I’m afraid its beyond my level of expertise to implement this in Delphi. Have you (or anyone else) already done so or can you offer any tips?
Thanks in advance!
October 17, 2012 at 3:54 pm
Are you read this article Using the Bing search API (Windows Azure Marketplace version) from Delphi?
October 17, 2012 at 4:11 pm
Thanks again! I had not seen that. That’s very helpful.
I will try to adapt this to work with the Translate API. If I can get it working do you want me to send it back to you?
October 17, 2012 at 4:14 pm
One more quick question if you don’t mind What would the URI be for the Translate API? (Sorry this is probably a dumb question – my programming expertise is pretty minimal!)
October 17, 2012 at 4:36 pm
This is the URL https://api.datamarket.azure.com/Bing/MicrosoftTranslator/v1/ , you can get more info in the Microsoft Translator site https://datamarket.azure.com/dataset/1899a118-d202-492c-aa16-ba21c33c06cb#schema
October 18, 2012 at 12:36 am
Hi again Rodrigo,
Sorry to bother you.
Could I ask your advice one more time? I tried to adapt your bing search code and get it working with V2 of the Microsoft Translate API, but I am getting HTTP400 errors. I don’t think I really understand what an AppID is. I have been using the Account key I got from Azure market Place/My Account/Account Keys.
Anyhow, below is what I was trying. I would appreciate it if you could have a quick look at this and let me know what I am doing wrong.
Thanks!
function TranslateText(const AText, SourceLng, DestLng: string): string;
const
MicrosoftTranslatorTranslateUri = ‘http://api.microsofttranslator.com/V2/Http.svc/Translate? appId=%s&text=%s&from=%s&to=%s’;
Msxml2_DOMDocument = ‘Msxml2.DOMDocument.6.0’;
AppID = ‘XXXXXXXXXXXXX;
//For AppID iI used my account key from Azure market Place/My Account/Account Keys
// Also tried “Bearer Account Key”
var
LIdHTTP: TIdHTTP;
LIOHandler: TIdSSLIOHandlerSocketOpenSSL;
LIndex: integer;
URI, Response: string;
XmlDoc, Node: OLEvariant;
begin
LIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
LIOHandler.SSLOptions.Method := sslvTLSv1;
LIOHandler.SSLOptions.Mode := sslmUnassigned;
LIOHandler.SSLOptions.VerifyMode := [];
LIOHandler.SSLOptions.VerifyDepth := 0;
LIOHandler.Host := ”;
LIdHTTP := TIdHTTP.Create(nil);
try
LIdHTTP.Request.ContentEncoding := ‘utf-8’;
LIdHTTP.Request.BasicAuthentication := true;
LIdHTTP.Request.UserName := AppID;
LIdHTTP.Request.Password := AppID;
LIdHTTP.IOHandler := LIOHandler;
URI := Format(MicrosoftTranslatorTranslateUri, [QuotedStr(”), TIdURI.PathEncode(QuotedStr(AText)), SourceLng, DestLng]);
Response := LIdHTTP.Get(URI);
XmlDoc := CreateOleObject(Msxml2_DOMDocument);
try
XmlDoc.Async := false;
XmlDoc.LoadXML(Response);
if (XmlDoc.parseError.ErrorCode 0) then
raise Exception.CreateFmt(‘Error in Xml Data %s’, [XmlDoc.parseError]);
Node := XmlDoc.documentElement;
if not VarIsClear(Node) then
Result := XmlDoc.Text;
finally
XmlDoc := Unassigned;
end;
finally
LIdHTTP.Free;
end;
finally
LIOHandler.Free;
end;
end;
October 18, 2012 at 1:29 pm
Ok this is a sample of how to use this api
And use like so
October 18, 2012 at 9:05 pm
This is great – it worked. many thanks!
Charlie
October 18, 2013 at 2:47 pm
Charlie and Rodrigo,
Thanks ( the best post)
October 27, 2012 at 11:34 am
It is working! But not Translator string Unicode :(
Thanks you!
February 11, 2013 at 1:16 pm
Rodrigo,
what is the BingAPPid, when i need the new registration form on Windows azure marketplace.
especially,,how can I create them?
February 11, 2013 at 3:36 pm
Hi, try this article https://theroadtodelphi.wordpress.com/2012/08/08/using-the-bing-search-api-windows-azure-marketplace-version-from-delphi/ which is about the Bing search API, but the sames applies to the translation API.
January 4, 2017 at 4:08 am
Thanks Delphi!, thank you Rodrigo!