my tech blog

To iterate is human, to recurse divine

Microsoft Translator Api

I have been playing around with the Microsoft Translator API at Windows Azure Marketplace lately with the help of F#.

There are two main resources that has influenced my solution greatly, PerfectShuffle.OAuth at github and this MSDN article.

The Microsoft Translator API has a couple of resources for machine translation, translate a text from a source language to a target language or detect the language that a text is written in. I will use the translate resource in my code example below.

Before we start with the code we will need to obtain some credentials for using the service. First step is to create a basic subscription for the api via Azure Marketplace, it’s free for 2 000 000 charcters/month. Finally we need to register an application with Azure Datamarket to obtain our credentials (client id and client secret). These steps are also explained in the MSDN article.

We will use this wonderful library, FSharp.Data, which have a great type for making simpel HTTP requests, see more info here.

You can easily install it with the help of the Package Manager Console:

1
PM> Install-Package FSharp.Data

We will be using the following namespaces in our code:

Namespaces - MsTranslator.fs
1
2
3
open FSharp.Net
open FSharp.Data.Json
open FSharp.Data.Json.Extensions

The Microsoft Translator API is requiring an access token, so we need to call the token service to get a hold of one. Our container for this information will be a record in F#:

AccessToken - MsTranslator.fs
1
2
3
4
5
6
7
8
9
10
11
type AccessToken =
    {
        /// The access token that you can use to authenticate you access to the Microsoft Translator API.
        AccessToken : string
        /// The format of the access token. Currently, Azure DataMarket returns http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0, which indicates that a Simple Web Token (SWT) token will be returned.
        TokenType : string
        /// The number of seconds for which the access token is valid.
        ExpiresIn : string
        /// The domain for which this token is valid. For the Microsoft Translator API, the domain is http://api.microsofttranslator.com.
        Scope : string
    }

To make the HTTP POST request we will use the following code snippet:

Http.AsyncRequest - MsTranslator.fs
1
2
3
4
5
let request =
    /// By default, the Content-Type header is set to application/x-www-form-urlencoded 
    Http.AsyncRequest
        (datamarketAccessUri,
        bodyValues = data)

I really like how neat the request is thanks to FSharp.Data library and how easy it is to use. The uri and the data in the code above is defined like this:

POST data - MsTranslator.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let datamarketAccessUri = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"

/// The parameters for the token request
let data =
    [
        /// Required. The client ID that you specified when you registered your application with Azure DataMarket.
        ("client_id", clientId)
        /// Required. The client secret value that you obtained when you registered your application with Azure DataMarket.
        ("client_secret", clientSecret)
        /// Required. Use the URL http://api.microsofttranslator.com as the scope value for the Microsoft Translator API.
        ("scope", "http://api.microsofttranslator.com")
        /// Required. Use "client_credentials" as the grant_type value for the Microsoft Translator API.
        ("grant_type", "client_credentials")
    ]

Finally we have to handle the response form the token service:

Handle the response - MsTranslator.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async {
    let! response = request

    let jsonToken = FSharp.Data.Json.JsonValue.Parse(response)

    let access_token = jsonToken?access_token
    let token_type = jsonToken?token_type
    let expires_in = jsonToken?expires_in
    let scope = jsonToken?scope

    let token =
        {
            AccessToken = access_token.AsString()
            TokenType = token_type.AsString()
            ExpiresIn = expires_in.AsString()
            Scope = scope.AsString()
        }

    return token
}

Here are we making use of the FSharp.Data library again to parse the json result. If we now combine all these code snippets to one function we will get something like this:

requestTokenFromTokenService - MsTranslator.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
let requestTokenFromTokenService clientId clientSecret =

    let datamarketAccessUri = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"

    /// The parameters for the token request
    let data =
        [
            /// Required. The client ID that you specified when you registered your application with Azure DataMarket.
            ("client_id", clientId)
            /// Required. The client secret value that you obtained when you registered your application with Azure DataMarket.
            ("client_secret", clientSecret)
            /// Required. Use the URL http://api.microsofttranslator.com as the scope value for the Microsoft Translator API.
            ("scope", "http://api.microsofttranslator.com")
            /// Required. Use "client_credentials" as the grant_type value for the Microsoft Translator API.
            ("grant_type", "client_credentials")
        ]

    let request =
        /// By default, the Content-Type header is set to application/x-www-form-urlencoded 
        Http.AsyncRequest
            (datamarketAccessUri,
            bodyValues = data)

    async {
        let! response = request

        let jsonToken = FSharp.Data.Json.JsonValue.Parse(response)

        let access_token = jsonToken?access_token
        let token_type = jsonToken?token_type
        let expires_in = jsonToken?expires_in
        let scope = jsonToken?scope

        let token =
            {
                AccessToken = access_token.AsString()
                TokenType = token_type.AsString()
                ExpiresIn = expires_in.AsString()
                Scope = scope.AsString()
            }

        return token
    }

The following step is to create another function where we actually send something to the Microsoft Translator API. To do this we need to add a HTTP header name/value pair with the newly fetched token.

1
Authorization : Bearer the-token

The access token will be valid for 10 minutes before it expires. However we won’t consider this restriction in this code sample.

The address to the translate resource is: http://api.microsofttranslator.com/v2/Http.svc/Translate

and it will take three query parameters, text (the text to translate), from (the source language) and to (the target language). The api will return a serialized string with the translated text. With all this information we can now build our translation function:

Translate message - MsTranslate.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let translateMessage message source target accessToken =
    let authorizationToken = sprintf "Bearer %s" accessToken.AccessToken
    let translatorApiUri = "http://api.microsofttranslator.com/v2/Http.svc/Translate"

    let request =
        Http.AsyncRequest
            (translatorApiUri,
            query = ["text", message; "from", source; "to", target],
            headers = [ "Authorization", authorizationToken ])

    async {
        let! response = request
        return response
    }

To test drive our new functions we can use the following code snippet:

Test run - MsTranslator.fs
1
2
3
4
requestTokenFromTokenService "your client id" "your client secret"
|> Async.RunSynchronously
|> translateMessage "Translate this text" "en" "sv"
|> Async.RunSynchronously

Done, give it a try :-)

Comments