Using the Microsoft Translator V2 API from delphi
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.


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).
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.
P.S. I’m sorry, I was referring to a GUI application, CUI is harder to understand for some reason.
Gran artículo Rodrigo.
Interesante y muy ilustrador (como siempre).
Gracias.
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.