Azure Microsoft Translation in Windows Phone (The Easy Way)

There are few things more awesome than the Microsoft Translate service available on the Azure Marketplace. Unfortunately, the vast majority of examples of how to use it are not written for Windows Phone, which is missing some key components available in other .NET solutions.

(Note: Yes, I know you this should be easier with the Azure toolkit for Windows Phone, etc, etc, etc. I didn’t find the classes I needed in there and this blog post is for people like me who got stuck like me.)

So lets get started and we can see exactly how easily we can integrate this awesome translation service into a application.

Get this project on github

Step 1: Sign Up For Microsoft Translation in the Azure Marketplace

Let’s say we’ve never been to the Azure Marketplace. Let’s go there now and click on Sign-In.

image

We’ll go through a pretty typical registration process (name, rank and serial number) and then we’re signed in for an account. Now we need to sign up specifically for the Microsoft Translation service. Click on “Data”.

image

Do a search for “translation’” and Microsoft Translator will pop right up.

image

If we click on it, we’ll see some details as well as all of our pricing options.

image

Helpfully, at the bottom of that, there is an option for 2 million characters for the low, low price of free. This should be enough for us to get a fun little project started.

image

Now that we’re signed up for our free service, we need to register our specific app and get a Client ID and Client Secret to identify our app to this service.  To do so, we will not click on “My Applications” or “My Data” or “Account Keys”, but down on the “Developers” link at the bottom.

image

Once you’re in there, Azure Marketplace will generate a client secret for you. All you need to do is give your application a client ID, some identifying name and a redirect URI. Don’t worry about the redirect URI, we won’t use it with this service.
image

Write down the ClientID and the Client Secret, we’ll need those for our app.

After we hit “Create”, we’ll see our application in the Registered Applications section.

image

And if we like we can click on “My Data” option in the “My Account” menu and try out our brand new service using a web interface.

image

We’re going to build our requests based on these queries, so go ahead and grab the “Service Root URL” at the top (should look like http://api.datamarket.azure.com/Data.ashx/Bing/MicrosoftTranslator/v1/ ). Play around with the query building and then we’ll work on our app.

Step 2: Get And Save Our Token

Let’s write our app already! Open Visual Studio and start a new “Windows Phone App” project.

image

First, so we don’t run into problems later, lets add some references. Right click on the references folder, “Add Reference…” and add System.Servicemodel.Web (which we’ll need to read JSON) and System.Xml.Linq (which we’ll need to read XML).

image

Add a “Services” folder and add two files, TranslationService.cs and TokenService.cs. Add a “Models” folder and add the file “AdmAccessToken.cs”.

image

The way this service works is to go out and get an access token for the client. This token will come back as our AdmAccessToken, which is valid for 10 minutes. We’ll use  that token to authenticate our app with the service. We’re going to assume that we need a new token every time we open the app, but try to use the same token during each app session. (If you want to know more about the access tokens, head over here.)

The first 4 properties in AdmAccessToken.cs are required for proper serialization. The rest of the class is there for determining when the token has expired.

 1: public class AdmAccessToken

 2: {

 3:     // required for deserialization

 4:     public string access_token { get; set; }

 5:     public string token_type { get; set; }

 6:     public string expires_in { get; set; }

 7:     public string scope { get; set; }

 8:

 9:     // properties and methods for determining expired tokens

 10:     private DateTime tokenEndTime { get; set; }

 11:

 12:     public bool IsExpired()

 13:     {

 14:         DateTime now = DateTime.Now;

 15:         double secondsLeft = tokenEndTime.Subtract(now).TotalSeconds;

 16:         if(secondsLeft < 30)

 17:             return true;

 18:         else

 19:             return false;

 20:     }

 21:

 22:     public void Initalize()

 23:     {

 24:         tokenEndTime = DateTime.Now.Add(new TimeSpan(0, 0, 600));

 25:     }

 26: }

Now we’ll write our token service in TokenService.cs. We’ll send an event once we get our token, so we need to define some event args for sending the token in the event. I usually add these to the bottom of my service class file.

 1: public class TokenServiceCompleteEventArgs : EventArgs

 2: {

 3:     public TokenServiceCompleteEventArgs(bool isSuccess, AdmAccessToken token)

 4:     {

 5:         IsSuccess = isSuccess;

 6:         TranslationToken = token;

 7:     }

 8:

 9:     public bool IsSuccess { get; private set; }

 10:     public AdmAccessToken TranslationToken {get; private set;}

 11: }

And add the event to the service class so we can raise it when we’re done.

 1: public event EventHandler<TokenServiceCompleteEventArgs> AccessTokenComplete;

 2: private void RaiseAccessTokenComplete(bool isSuccess, AdmAccessToken token)

 3: {

 4:     if(AccessTokenComplete!=null)

 5:         AccessTokenComplete(this, new TokenServiceCompleteEventArgs(isSuccess, token));

 6: }

We’re going to save our Client ID, Client Secret, and OAuth URL as string in the top of the class:

 1: private static readonly string CLIENT_ID = "My_Translation_App";

 2: private static readonly string CLIENT_SECRET = "[client secret from the registration process]";

 3: private static readonly string OAUTH_URI = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13";

And then add our call for the token. Our process here is 1) check to see if we have a valid token. 2) If we have a valid token, return it. 3) If we don’t have a valid token, start our Http POST request to get a token.

 1: public void GetToken()

 2: {

 3:     if (IsolatedStorageSettings.ApplicationSettings.Contains("admAccessToken"))

 4:     {

 5:         AdmAccessToken savedToken = (AdmAccessToken)IsolatedStorageSettings.ApplicationSettings["admAccessToken"];

 6:         if (!savedToken.IsExpired())

 7:         {

 8:             RaiseAccessTokenComplete(true, savedToken);

 9:             return;

 10:         }

 11:     }

 12:

 13:     // Create our HTTP request

 14:     WebRequest request = WebRequest.Create(OAUTH_URI);

 15:     request.Method = "POST";

 16:     request.ContentType = "application/x-www-form-urlencoded";

 17:

 18:     //IAsyncResult postCallback = (IAsyncResult)request.BeginGetRequestStream(new AsyncCallback(RequestStreamReady), request);

 19:     request.BeginGetRequestStream(new AsyncCallback(RequestStreamReady), request);

 20: }

Then we write our POST data…

 1: private void RequestStreamReady(IAsyncResult asyncResult)

 2: {

 3:     try

 4:     {

 5:         // Create the data we're going to write into the POST body

 6:         string clientID = CLIENT_ID;

 7:         string clientSecret = CLIENT_SECRET;

 8:         string scope = "scope=" + HttpUtility.UrlEncode("http://api.microsofttranslator.com");

 9:         string grant_type = "grant_type=" + HttpUtility.UrlEncode("client_credentials");

 10:         String postBody = string.Format("{0}&client_id={1}&client_secret={2}&{3}", grant_type, HttpUtility.UrlEncode(clientID), HttpUtility.UrlEncode(clientSecret), scope);

 11:

 12:         // Write the data to the POST body

 13:         HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;

 14:         byte[] bytes = System.Text.Encoding.UTF8.GetBytes(postBody);

 15:         Stream postStream = request.EndGetRequestStream(asyncResult);

 16:         postStream.Write(bytes, 0, bytes.Length);

 17:         postStream.Close();

 18:

 19:         // Get the response (including the token)

 20:         request.BeginGetResponse(new AsyncCallback(GetTokenResponseCallback), request);

 21:     }

 22:     catch (WebException webExc)

 23:     {

 24:         RaiseAccessTokenComplete(false, null);

 25:     }

 26: }

And get our response. We’ll save our token to IsolatedStorageSettings and then fire our event with the token attached.

 1: private void GetTokenResponseCallback(IAsyncResult asyncResult)

 2: {

 3:     try

 4:     {

 5:         HttpWebRequest endRequest = (HttpWebRequest)asyncResult.AsyncState;

 6:         HttpWebResponse response = (HttpWebResponse)endRequest.EndGetResponse(asyncResult);

 7:         DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AdmAccessToken));

 8:         AdmAccessToken token = (AdmAccessToken)serializer.ReadObject(response.GetResponseStream());

 9:

 10:         // Set the token so that it will begin the expiration process

 11:         token.Initalize();

 12:         // Let's set this token to be accessible on the app level

 13:         App.SetTranslationToken(token);

 14:         // And save the token to our app settings

 15:         if (IsolatedStorageSettings.ApplicationSettings.Contains("admAccessToken"))

 16:             IsolatedStorageSettings.ApplicationSettings["admAccessToken"] = token;

 17:         else

 18:             IsolatedStorageSettings.ApplicationSettings.Add("admAccessToken", token);

 19:         IsolatedStorageSettings.ApplicationSettings.Save();

 20:         // Finally, we're ready to send our token out into the wild

 21:         RaiseAccessTokenComplete(true, token);

 22:     }

 23:     catch (WebException webExc)

 24:     {

 25:         RaiseAccessTokenComplete(false, null);

 26:     }

 27: }

We are now sufficiently token-ed and we’re ready to translate some things.

Step 3: Translate All The Things

Our real goal with the translation service is to enter some text, pick a source language, pick a target language and away we go.

To that end, we’ll set up some properties in TranslationService.cs to help us do this.

 1: public class TranslationService

 2: {

 3:     private TokenService _tokenService;

 4:     private string _originalText;

 5:     private string _sourceLanguage;

 6:     private string _targetLanguage;

 7:

 8:     public TranslationService()

 9:     {

 10:         _tokenService = new TokenService();

 11:         _tokenService.AccessTokenComplete += _tokenService_AccessTokenComplete;

 12:     }

 13: }

Then when we move to start the translation, we’ll run the token service, which will check to see if we have a valid token and, if we do, return it. (If we’ don’t, it will go and get one for us. When it is done with either task, it will raise the AccessTokenComplete event, which we’ll use to kick off the translation service.

 1: public void GetTranslation(string originalText, string sourceLanguage, string targetLanguage)

 2: {

 3:     _originalText = originalText;

 4:     _sourceLanguage = sourceLanguage;

 5:     _targetLanguage = targetLanguage;

 6:

 7:     _tokenService.GetToken();

 8: }

 9:

 10: void _tokenService_AccessTokenComplete(object sender, TokenServiceCompleteEventArgs e)

 11: {

 12:     if (e.IsSuccess)

 13:         StartTranslationWithToken(e.TranslationToken);

 14:     else

 15:         RaiseTranslationFailed("There was a problem securing an access token");

 16: }

The http service work is done in the StartTranslationWithToken method. We’ll set up the service uri we need to do the translation, create a WebRequest and set the Authorization header of our request to use “Bearer “ plus our access token. When the response comes back, we should have our XML with the data in it. We can read this using the XDocument utility (make sure to add System.XML.Linq to your project resources to get access to XDocument). Finally, we’ll raise an event that says everything went according to plan and returns our translation, as well as the original input data.

 1: private void StartTranslationWithToken(AdmAccessToken token)

 2: {

 3:     string translateUri = string.Format("http://api.microsofttranslator.com/v2/Http.svc/Translate?text={0}&from={1}&to={2}",

 4:         HttpUtility.UrlEncode(_originalText),

 5:         HttpUtility.UrlEncode(_sourceLanguage),

 6:         HttpUtility.UrlEncode(_targetLanguage));

 7:

 8:     WebRequest translationRequest = HttpWebRequest.Create(translateUri);

 9:

 10:     // We need to put our access token into the Authorization header 

 11:     // with "Bearer " preceeding it.

 12:     string bearerHeader = "Bearer " + token.access_token;

 13:     translationRequest.Headers["Authorization"] = bearerHeader;

 14:

 15:     // Finally call our translation service

 16:

 17:     translationRequest.BeginGetResponse(asyncResult =>

 18:     {

 19:         try

 20:         {

 21:             HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;

 22:

 23:             HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);

 24:

 25:             // Read the contents of the response into a string

 26:             Stream streamResponse = response.GetResponseStream();

 27:             StreamReader streamRead = new StreamReader(streamResponse);

 28:             string translationData = streamRead.ReadToEnd();

 29:

 30:             // Read the XML return from the translator

 31:             // you can get a preview of this XML if you go to your Azure

 32:             // account, click on My Data on the left and then on "Use" on

 33:             // Microsoft Translator. run a trial query and then click the 

 34:             // XML button at the top of the query tool.

 35:

 36:             // You'll need to add "System.XML.Linq" to your project to use XDocument

 37:             XDocument translationXML = XDocument.Parse(translationData);

 38:             string translationText = translationXML.Root.FirstNode.ToString();

 39:

 40:             RaiseTranslationComplete(_originalText, _sourceLanguage, _targetLanguage, translationText);

 41:         }

 42:         catch (WebException webExc)

 43:         {

 44:             RaiseTranslationFailed(webExc.Status.ToString());

 45:         }

 46:     }, translationRequest);

 47: }

In the interest of completeness, here is the class we made for the EventArgs that we use to return the translation (I added them to the bottom of my service cs file).

 1: public class TranslationCompleteEventArgs : EventArgs

 2: {

 3:     public TranslationCompleteEventArgs(string originalText, string fromLang, string toLang, string translation)

 4:     {

 5:         OriginalText = originalText;

 6:         FromLanguage = fromLang;

 7:         ToLanguage = toLang;

 8:         Translation = translation;

 9:     }

 10:

 11:     public string OriginalText { get; private set; }

 12:     public string FromLanguage { get; private set; }

 13:     public string ToLanguage { get; private set; }

 14:     public string Translation { get; private set; }

 15: }

 16:

 17: public class TranslationFailedEventArgs :EventArgs

 18: {

 19:     public TranslationFailedEventArgs(string error)

 20:     {

 21:         ErrorDescription = error;

 22:     }

 23:

 24:     public string ErrorDescription { get; private set; }

 25: }

And here are the events in my TranslationService class that we’ll fire after the translation is complete (or has failed).

 1: public event EventHandler<TranslationCompleteEventArgs> TranslationComplete;

 2: private void RaiseTranslationComplete(string originalText, string fromLang,

 3:                                         string toLang, string translation)

 4: {

 5:     if (TranslationComplete != null)

 6:         TranslationComplete(this, new TranslationCompleteEventArgs(originalText, fromLang, toLang, translation));

 7: }

 8:

 9: public event EventHandler<TranslationFailedEventArgs> TranslationFailed;

 10: private void RaiseTranslationFailed(string error)

 11: {

 12:     if(TranslationFailed != null)

 13:     TranslationFailed(this, new TranslationFailedEventArgs(error));

 14: }

Now our translation is ready to rock and roll, so the only thing we have left to do is put together a simple user interface for interacting with our translator.

Step 3: Using Our Translator

Now lets build the UI that we’ll use to execute a translation and then (finally) translate something. Because this post is already running absurdly long We’re going to create UI for translating from English to Spanish rather than build up all the infrastructure for selecting multiple languages.

Open your project using Blend (you should be using Blend to create your Windows Phone UI) and go to the MainPage.xaml. Change the top text to the name of your app and the text beneath it to “translate”.

Beneath that, get rid of anything inside the ContentPanel and the hover your mouse along the left edge of the ContentPanel. you should see an orange line show up.

image

Click it and you’ll see that you’ve created a separation in your Grid layout. You’ll see numbers that indicate the status of the Grid.Row that you’ve made. Hover over those numbers and you’ll see an editing system pop up (note, this is for Blend 5 and up). Click in that pop-up to edit the size of the row.

image

I’m editing mine to be: 1*, 1*, which will look in the XAML like

 1: <Grid.RowDefinitions>

 2:     <RowDefinition Height="1*"/>

 3:     <RowDefinition Height="1*"/>

 4: </Grid.RowDefinitions>

The top area will be for the source text (what we want to translate), the middle area will be for selection of the translation languages (which we won’t implement in this tutorial) and the bottom will be where we’ll place the translated text.

To this end, let’s add a TextBlock indicating the function of the top and bottom areas and a TextBox for adding or copying text. I’m going to add some text into our source TextBox just to give us something to work with.

image

Let’s also give our TextBoxes names so we can add and extract text from them in the code-behind. By clicking on the element in the Objects and Timeline tree, we can see the properties on the right hand side.

image

Just change the name at the top to make it accessible by that name in the code-behind.

image

We’ll start the translation using an app bar button. Instead of explaining all of the application bar creation, I’ll point you to this post that goes through it on Blend.

When the user clicks on the ApplicationBar button, we’ll fire an event that starts up the translation service. To define this event, select your ApplicationBarIconButton in the Objects and Timeline panel.

image
Then click the lightning icon in Properties panel. This will bring up a list of events that are available to that object. In the “Click” box, type the name of the event handler you’d like to use and Blend will insert the appropriate code into the code behind.

image

Now, to look at our code behind (MainPage.xaml.cs). Before we call the translation service, we need to initiate it. So at the top of our class, we’ll add our translation service, instantiate it and add some event handlers in our constructor.

 1: TranslationService _translator;

 2:

 3: public MainPage()

 4: {

 5:     InitializeComponent();

 6:     _translator = new TranslationService();

 7:     _translator.TranslationComplete += _translator_TranslationComplete;

 8:     _translator.TranslationFailed += _translator_TranslationFailed;

 9: }

Let’s start our translation service in our ApplicationBarIconButton event handler. We’ll hard code the language information for now from English to Spanish, but if you need a list of supported language codes, check out this… um… list of supported language codes.

 1: private void On_CheckClicked(object sender, System.EventArgs e)

 2: {

 3:     _translator.GetTranslation(sourceTextBox.Text, "en", "es");

 4: }

And all we need to do is handle the resulting events and we’re done. The TranslationService will return the information off the UI thread, so in order to pass the information along without throwing a thread exception, Deployment.Current.Dispatcher.BeginInvoke to the rescue!

 1: void _translator_TranslationComplete(object sender, TranslationCompleteEventArgs e)

 2: {

 3:     Deployment.Current.Dispatcher.BeginInvoke(() =>

 4:     {

 5:         targetTextBox.Text = e.Translation;

 6:     });

 7: }

 8:

 9: void _translator_TranslationFailed(object sender, TranslationFailedEventArgs e)

 10: {

 11:     Deployment.Current.Dispatcher.BeginInvoke(() =>

 12:     {

 13:         MessageBox.Show("Bummer, the translation failed. \n " + e.ErrorDescription);

 14:     });

 15: }

And we’re done! Don’t forget, you can grab the entire project on github. So check it out.

Thanks to Laurence Moroney of netNavi.tv for his post on this topic that helped me fill in some key missing pieces.

50 thoughts on “Azure Microsoft Translation in Windows Phone (The Easy Way)

  1. We’re a bunch of volunteers and opening a new scheme in our community.
    Your website provided us with helpful information to work on. You have
    performed a formidable job and our whole group will likely be thankful to you.

  2. I used to be recommended this website by means of my cousin.
    I am now not sure whether this publish is written through him
    as nobody else recognize such distinctive approximately my trouble.
    You’re amazing! Thank you!

  3. You do not need a physical store or office, so your financial commitment is a
    lot less than you would have to pay for a traditional bricks-and-mortar business.
    This mechanical energy moves pistons up and down inside piston chambers.
    Take the time to figure out your total costs to produce your product.

  4. Woah! I’m really enjoying the template/theme of this site.
    It’s simple, yett effective. A lot of times it’s
    hard to get that “perfect balance” between usability and visual appearance.
    I must say that you’ve dopne a awesxome job with this.

    Additionally, the blog loads super fast for me onn Firefox.

    Excellent Blog!

  5. Good day! I could have sworn I’ve been to this website before but after reading through some of the
    post I realized it’s new to me. Anyways, I’m definitely glad I found it and I’ll be
    book-marking and checking back often!

  6. Good day! I know this is kind of off topic but I was wondering if
    you knew where I could find a captcha plugin for my comment form?
    I’m using the same blog platform as yours and I’m having difficulty
    finding one? Thanks a lot!

  7. In Vista the process is similar and your goal is “Service Pack 1”.
    I have had no problems at all with it so do not
    worry about virus’. Your choice depends on the level of encryption needed for yourself (or to bypass)
    and your budget.

  8. One or more projectors are used to process programs. It would be a
    dream come true, partying with the cartoon character voted the 2nd most influential in history.
    Hence, the people are well educated and highly concerned about the development of the region and keenly look forward to make their
    lives always easy.

    Here is my web blog … Nos pires voisins streaming

  9. There’s only a single strategy to grow to be a 2000+ rated participant
    in League of Legends – get far better. An intern in a hospital is dispatched to the
    morgue located in the basement of the building.
    This portrayal of friars being the flatulence
    of Satan, shows us something about the summoner’s view
    of a friar in Chaucer’s time.

    Check out my site; kim kardashian hollywood cheats

  10. Thanks for sharing your thoughts. I really appreciate
    your efforts and I am waiting for your next write ups thanks once again.

  11. Hey there! Do you use Twitter? I’d like to follow you if that would be okay.
    I’m undoubtedly enjoying your blog and look forward to new updates.

  12. 118428 539481Thank you for the sensible critique. Me and my neighbor were just preparing to do some research on this. We got a grab a book from our location library but I feel I learned far more clear from this post. I

  13. Excellent weblog right here! Additionally your website loads up fast!
    What host are you the usage of? Can I get your affiliate link to your host?
    I wish my site loaded up as fast as yours lol

  14. 27920 198382 You created some decent points there. I looked on the internet for the problem and discovered most individuals will go along with together with your web site. 353689

  15. 698026 813726You could uncover two to three new levels inside L . a . Weight loss and any 1 someone is extremely critical. Initial stage could be real melting away rrn the body. shed weight 545891

  16. 213925 676099Soon after study several with the weblog articles for your internet internet site now, and i also genuinely such as your strategy for blogging. I bookmarked it to my bookmark internet internet site list and are checking back soon. Pls take a appear at my internet page in addition and tell me what you believe. 750893

  17. 267925 275117This sort of in search of get the enhancements made on this special lifestyle and diet, begin your L . a . Shifting the pounds diet solution is really a huge procedure into accesing which generally hope. weight loss 416235

  18. 935208 781247Any person several opportune pieces, it comes surely, as properly as you bring in crave of various the several other types of hikers close to you with hard part your question. pre owned awnings 552873

  19. I do not even know the way I finished up right here, but I thought
    this post used to be good. I do not recognize who you
    might be however certainly you’re going to a well-known blogger when you aren’t already.
    Cheers!

  20. Microsoft (MSFT) may be a bit behind Google (GOOG) and Apple (AAPL) when it comes to creating a voice-enabled personal assistant for its mobile devices, but it seems the company does have plans to add better speech recognition capabilities to its Bing mobile app in the near future. MSFTKitchen has posted a video demonstration of a new prototype for voice recognition software on Windows Phone devices that’s intended to show how Microsoft has worked to reduce latency and word recognition errors while improving phones’ ability to accurately hear you in crowded, noisy areas.*

    Our own blog
    <'http://www.beautyfashiondigest.com/black-and-white-wedding-dresses/

  21. Hi,
    I really thank you for this article, I searched and searched on the web looking for a valid sample to use Azure Translation service on WP8! I got a big problem by the way… even running your code I encounter a strange Web Exception… always thrown executing this instruction:
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult); in the TranslationService.cs

    here there are the details :

    MESSAGE:
    The remote server returned an error: NotFound.
    STACK TRACE:
    at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
    at System.Net.Browser.ClientHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
    at WalkAndTalk.Services.TranslationService.b__0(IAsyncResult asyncResult)

    I’m not running Fiddler on background.

    Any idea of which could be the reason for this error? thanks a lot in advance.
    Andrea

  22. I Guess You Are Using VS12 for Windows Phone Development. As I Know there is no SDK that is supporting VS12 as new SDK for wp8 is not out and no templates available for VS12. Are you using leaked version of SDK or you grabbed Developer preview of wp8 SDk. 🙂

Comments are closed.