Saturday, April 13, 2013

How to use the latest Microsoft Azure translator API with access tokens, in PHP and jQuery

Unless you're working with Microsoft tools, it's hard to find sample Javascript code for the Azure translator API that actually works. There's a lot of obsolete code written for the version that used appId, deprecated since 2012.

Microsoft Translator API has gone through at least 3 major revisions.  This doc is about the latest version as of Dec 2016.  From Microsoft:

Beginning January 1, 2017, subscribers to the Microsoft Translator Text and Speech APIs will have limited access in Azure DataMarket. We recommend that you move to Azure now to avoid service disruption.
The good news is it turns out only a minor change is needed to the small php script that requests tokens.

Wang Pidong's very helpful post shows the bare-bones of how to use the new access token methods, from the unix command line.  

Here's how it works with PHP and jQuery.  Online demo of the code listed below:





To get started, you need to host a server-side script whose only job is to get access tokens from Windows Azure.   Each token is valid for 10 minutes.   It will be stored in the browser and used for direct access between the user's machine and Azure's servers, avoiding the need to proxy all requests through your server.  This method also lets you avoid exposing your subscription key in the client-side javascript.   

Here's a simple PHP script, named token.php, to get the token. You will need to supply your own subscription key at the top of the file.

<?php

// Get a 10-minute access token for Microsoft Translator API.

$url = 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken';
$sub = "your Azure subscription key for text translation";
$pair = "Ocp-Apim-Subscription-Key=$sub";
$data = "{body}";
$headers = array($pair);

$ch = curl_init();

$data_string = json_encode($data);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);  

curl_setopt($ch, CURLOPT_HTTPHEADER, array(                     
    'Content-Type: application/json', 
    'Content-Length: ' . strlen($data_string),
    'Ocp-Apim-Subscription-Key: ' . $sub
    )        
); 

curl_setopt($ch, CURLOPT_URL, $url); 
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);  

$token =  curl_exec($ch); 

// Return the token inside some json, as expected by index.html.
print '{"access_token":"' . $token . '"}';
?>


Here is the url to that script running on my server:  token.php

To check your setup, make sure token.php returns something similar to this:

{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6Imh0dHBzOi8vYXBpLm1pY3Jvc29mdHRyYW5zbGF0b3IuY29tLyIsInN1YnNjcmlwdGlvbi1pZCI6IjZlMmI3ODM5MmJjMDQ1NGFhNzVlNDRhYzY0ZjFlZjc1IiwicHJvZHVjdC1pZCI6IlRleHRUcmFuc2xhdG9yLkYwIiwiY29nbml0aXZlLXNlcnZpY2VzLWVuZHBvaW50IjoiaHR0cHM6Ly9hcGkuY29nbml0aXZlLm1pY3Jvc29mdC5jb20vaW50ZXJuYWwvdjEuMC8iLCJhenVyZS1yZXNvdXJjZS1pZCI6Ii9zdWJzY3JpcHRpb25zL2U5MWM3NjgwLTk2ZWItNDAzYi05ZmZiLTNlZmQ1MjgyOTRiOS9yZXNvdXJjZUdyb3Vwcy9hX3Jlc19ncm91cC9wcm92aWRlcnMvTWljcm9zb2Z0LkNvZ25pdGl2ZVNlcnZpY2VzL2FjY291bnRzL3RyYW5zbGF0ZV90ZXh0IiwiaXNzIjoidXJuOm1zLmNvZ25pdGl2ZXNlcnZpY2VzIiwiYXVkIjoidXJuOm1zLm1pY3Jvc29mdHRyYW5zbGF0b3IiLCJleHAiOjE0ODI4MjM1NDh9.rVfSPUd2yBF0j64L-SrBZC7nIHOsKCIrHOiRs0YpCmE"}


Now that you can get a token, it's time to use it to do a translation.  Here's an html page that displays a two-box interface that translates English to French.  The javascript accesses two server scripts:
  • token.php:  the script listed above.  This needs to be installed on the same server as the html page below.  The html page accesses the token.php script using json.  The token is refreshed every 9 minutes.
  • Translate at api.microsoftranslator.com: Since your script cannot reside on microsoft's server, we have to use jsonp to allow cross-site access. This script is accessed each time a translation is requested.



<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<script language="javascript">
var g_token = '';

function onLoad() {
  // Get an access token now.  Good for 10 minutes.
  getToken();
  // Get a new one every 9 minutes.
  setInterval(getToken, 9 * 60 * 1000);
}

function getToken() {
  var requestStr = "./token.php";

  $.ajax({
    url: requestStr,
    type: "GET",
    cache: true,
    dataType: 'json',
    success: function (data) {
      g_token = data.access_token;
    }
  });
}

function translate(text, from, to) {
  var p = new Object;
  p.text = text;
  p.from = from;
  p.to = to;

  // A major puzzle solved.  Who would have guessed you specify the jsonp callback in oncomplete?
  p.oncomplete = 'ajaxTranslateCallback';
  
 // Another major puzzle.  The authentication is supplied in the deprecated appId param.
  p.appId = "Bearer " + g_token;

  var requestStr = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate";

  $.ajax({
    url: requestStr,
    type: "GET",
    data: p,
    dataType: 'jsonp',
    cache: true
  });
}

function ajaxTranslateCallback(response) {
  // Display translated text in the right textarea.
  $("#target").text(response);
}

function translateSourceTarget() {
  // Translate the text typed by the user into the left textarea.
  var src = $("#source").val();
  translate(src, "en", "fr");
}
</script>
<style>
#source, #target {
  position:relative;
  float:left;
  width:400px;
  height: 50px;
  padding:10px;
  margin: 10px;
  border: 1px solid black;
}

#translateButton {
  float:left;
  margin: 10px;
  height:50px;
}
</style>
</head>

<body onload="onLoad();">

<textarea id="source">Text typed here will be translated.</textarea>
<button id="translateButton" onclick="translateSourceTarget();">Translate English to French</button>
<textarea id="target"></textarea>

</body>
</html>







22 comments:

  1. Hi,
    I am using this cdee. But i receive first response immediately. And after that i receive response after 10 minutes.
    How do i get response whenever the translate button is clicked?

    Thanks in advance..

    ReplyDelete
  2. Sorry, what is the response you receive immediately?

    This approach decouples getting the token from doing translations with it. It should maintain a valid token at all times in the global variable g_token, ready to be used whenever the translate button is clicked.

    ReplyDelete
    Replies
    1. After loading the page, if clicking the translate button, then it displays the translated text. After that i did not get the translated text immediately.

      Delete
    2. Thanks for reporting the problem. What browser are you using? Are you hosting the PHP script on your own server? What version of PHP? What's your url?

      Delete
    3. i am using FireFox 23.0.1.
      Hosting my own server.
      And using php 5.1.6 version.
      example url : http://tst.ibrtek.com/test/translate_ex.php

      Please help which was wrong?

      Can i test this code locally?

      Thanks in advance

      Delete
    4. I used below code in token.php now it is working locally.
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

      But i get the response text for every 10 minutes.
      Please help me how could i get the response text for every click of translate button?

      I stucked here for a long time ....

      Thanks in advance ...

      Delete
  3. I just tested my code in FireFox 23.0.1 and it works fine. Is it working for you?

    You've made some modifications, probably when you were trying to run locally without success. Revert to the original code and it should work. I see that you are trying to get a new token for each translation. Bing may not like it when you do that.

    The python web server is another easy way to test locally: python -m SimpleHTTPServer 8000

    Your html page doesn't need to be generated using php. A static file is fine, as long as it is served from the same server that hosts the token.php script.

    ReplyDelete
  4. hi..i copy and paste your code above and modify the id's..but the problem is when i click the button nothings happen..i am new in php..please help me...

    ReplyDelete
    Replies
    1. Since you are worried about the php, can you run token.php from the command line? It will fail, because the parameters are not there, but you should get something like this:

      php t.php
      {"error":"invalid_client","error_description":"ACS50012: Authentication failed.\r\nTrace ID: b717e0d4-e797-4a35-b312-ccb64c97ee07\r\nCorrelation ID: f52d8a15-87f8-485d-b08b-80dc6f841c41\r\nTimestamp: 2014-07-07 04:58:05Z"}

      Delete
  5. Hi John,
    thank you for your code and all that. However, when clicking the button i do not get any result. Calling token.php directly gives me no answer either. I modified the last line a bit to:

    print $ch . ":" . $rsp . "--end";

    and in my browser i see:

    Resource id #2:--end

    What I do not understand in your post is the sentence:
    "Note that we use json to access the PHP script above, which must be located on the same server as this html page, and jsonp to allow cross-site access to Bing."
    Isn't json part of php (5.5.x) ?

    I really hope you can help me, thanks in advance,
    Sven

    p.s.: I suspect the your php code is the token.php ...

    ReplyDelete
    Replies
    1. Hello Sven,

      I hope I made it a bit clearer above. You can see what my token.php script produces when it's run directly from a browser. Did you supply your own client ID and client secret?

      John

      Delete
  6. hi
    i am developing a english spanish translator: url is http://goo.gl/kXFV3T

    I'm translating language based on URL.,

    Everything works fine except the loading time.

    here is the javascript used:
    setTimeout(function() {
    translateSourceTarget()
    }, 2000);

    if i dont use setTimeout, the content is not translating., as Bearer variable looks empty.

    Kindly send me your suggestion to fix this.

    Thanks and Regards
    Anandhan

    ReplyDelete
  7. Hi Anandhan, don't do the translation in onload. Onload should just get the token (which takes time) and set the timer to refresh it. The code above assumes there is a delay between the page load and the first click on the translate button by a user. After the initial token is downloaded, the translation itself is fast. Load the javascript when your page loads, then do translation when the user clicks your translate button. Hope that helps, let me know if I'm missing something.

    ReplyDelete
  8. Hi Anandhan, don't do the interpretation in onload. Onload should just get the symbol (which takes time) and set the clock to renew it. The rule above represents there is a wait between the site fill and the first click on the convert key by a customer. After the initial symbol is downloadable, the interpretation itself is fast. Load the javascript when your web page plenty, then do interpretation when the customer mouse clicks your convert key. Wish that helps, let me know if I'm losing something. Fourneau Bruleur de Graisse

    ReplyDelete
  9. Thanks very much for this great article;this is the stuff that keeps me going through out these day.
    happy birthday in Spanish

    ReplyDelete
  10. Is there anyway to update this to allow it to work with the new API?

    I'm not very good at PHP so most of this just goes over my head :-(

    Thanks

    http://pastebin.com/urqXpJ7V

    ReplyDelete
  11. I'm sure it can be done, but I prefer keeping the PHP to a minimum by doing all the real work in the browser with javascript. After registering with the new microsoft azure service, it was fairly easy to upgrade my tiny PHP script. For your much larger script, I still think it will require only a few changes. There is a single key rather than a key and a secret, and the endpoints have changed.

    ReplyDelete
  12. Thanks for providing your information, Keep updating with us Azure Online Course Hyderabad

    ReplyDelete
  13. This comment has been removed by a blog administrator.

    ReplyDelete
  14. This comment has been removed by a blog administrator.

    ReplyDelete
  15. This comment has been removed by a blog administrator.

    ReplyDelete
  16. This comment has been removed by a blog administrator.

    ReplyDelete