# How to use Azure AI Translator service for text translation

## Introduction

Azure AI Translator is a potent cloud-based tool provided by Microsoft that automates language translation and localization in applications, websites, and services. If you require translation capabilities in an application, Azure AI Translator can provide the solution.

## Provisioning resource

To begin, we'll utilize the Azure CLI to deploy the necessary resources. Follow these steps:

1. Create a resource group named `translator-rg` in the `westeurope` region:
    
    ```bash
    az group create --location westeurope --name translator-rg
    ```
    
2. Create a Text Translator service named `mytranslator` within the `translator-rg` resource group:
    
    ```bash
    az cognitiveservices account create \
        --kind TextTranslation \ # We need TextTranslator
        --location WestEurope \
        --name mytranslator \
        --resource-group translator-rg \
        --sku S1 \
        --subscription 00000000-0000-0000-0000-000000000000 # Your subscription
        --yes
    ```
    

With these steps, you've established a resource group and created a Text Translator service.

To communicate with Translator API we need to have the endpoint of the service and keys. For text translation, we need to use the global endpoint `https://api.cognitive.microsofttranslator.com/`.

To retrieve the keys we need to execute the following command

```bash
az cognitiveservices account keys list --name mytranslator -g translator-rg
```

Copy and paste one key to the notepad.

> For the production environment use Azure Key Vault to store keys. See "Use Azure Key Vault to store key" below in the article.

Now we have everything to make some translations using Azure AI Translator.

## Working with API

### Basic approach

We need to have some objects for request and response. Let's use records to create them.

```csharp
internal record TranslationRequest(string Text) { }
internal record TranslationResponse(Translation[] Translations) { }
internal record Translation(string Text, string To) { }
```

Now we are ready to call the API.

```csharp
using System.Net.Http.Json;

const string TranslatorKey = "[api-key]";
const string TranslatorEndpoint = "https://api.cognitive.microsofttranslator.com";
const string Location = "[location-of-resource]";

var text = "Let's translate some text";

using var request = new HttpRequestMessage(HttpMethod.Post, 
    $"{TranslatorEndpoint}/translate?api-version=3.0&from=en&to=es&to=cs&to=nl");
request.Headers.Add("Ocp-Apim-Subscription-Key", TranslatorKey);
request.Headers.Add("Ocp-Apim-Subscription-Region", Location);
request.Content = JsonContent.Create(new [] { new TranslationRequest(text) });

var response = await new HttpClient().SendAsync(request);
response.EnsureSuccessStatusCode();

var responseContent = await response.Content.ReadFromJsonAsync<TranslationResponse[]>();
var translations = responseContent?.First()?.Translations;

foreach (var translation in translations)
    Console.WriteLine($"{translation.To}: {translation.Text}");
```

Make sure to replace `[api-key]` with the key obtained from the translator account and `[location-of-resource]` with the location where your translation resource was created.

Pay attention to the requested URL `$"{TranslatorEndpoint}/translate?api-version=3.0&from=en&to=es&to=fr&to=de"` . Let's have a look into it more closely.

* `version=3.0` is the API version we want to use
    
* `from=en` indicating that we would like to have a translation from English. This parameter is optional so if it is missing translator will try to detect the language.
    
* `&to=es&to=cs&to=nl` the list of languages to translate text to. This is a required parameter. To get the list of all supported languages follow this [link](https://api.cognitive.microsofttranslator.com/languages?api-version=3.0).
    

The result of running this code

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1692976189932/9463aa55-7a8d-4e4b-be0d-786fcc5961d7.png align="center")

### Use Azure Key Vault to store key

The problem with the code is that we need to copy-paste and store the key with the application. From a security perspective, it is a breach because everyone can access it, and if for the testing environment, it is not a big problem it is better not to leak production keys.

To solve this problem we can use Azure Key Vault to store our key. Our application will know only about the key name and how to access it. Let's have a look at how to implement such an approach.

1. Create a Key Vault with the name `translatorappvault` in the resource group `translator-rg` using Azure CLI.
    
    ```bash
    az keyvault create --location WestEurope \
       --name translatorappvault \
       --resource-group translator-rg
    ```
    
2. Set key from Azure AI Translator.
    
    Note: Replace `[api-key]` with the actual key from the Translator resource.
    
    ```bash
    az keyvault secret set --vault-name translatorappvault \
       --name "TranslatorKey" \
       --value "[api-key]"
    ```
    
3. Add the following packages to the solution:
    
    ```bash
    dotnet add package Azure.Security.KeyVault.Secrets
    dotnet add package Azure.Identity
    ```
    
    `Azure.Security.KeyVault.Secrets` provides the implementation of the API necessary to access Key Vault. `Azure.Identity` provides the authentication possibility.
    
4. Accessing Key Vault and retrieving the key
    
    ```csharp
    const string TranslatorKey = "TranslatorKey";
    const string KeyVaultEndpoint = "https://translatorappvault.vault.azure.net/";
    
    var client = new SecretClient(new Uri(KeyVaultEndpoint), new DefaultAzureCredential());
    var secretResponse = await client.GetSecretAsync(TranslatorKey);
    ```
    
    Notice that we use `DefaultAzureCredential` during the instantiation of `SecretClient`. This type of credential allows us to use passwordless authentication and the most important part is that this code will work similarly in every environment either development, testing, or production.
    
5. In the last part, we need to change the header `Ocp-Apim-Subscription-Key` to use the key retrieved from Azure Key Vault.
    
    ```csharp
    request.Headers.Add("Ocp-Apim-Subscription-Key", secretResponse.Value.Value);
    ```
    

Running this code will give the same translations as before with the only difference that we don't provide any key.

We still have room for improvement. As you remember we set the key to Key Vault manually and if for some reason we need to change it (leaks or key rotation) we need to perform the same steps. Not only is it error-prone but also a potential place for leaking credentials.

### Small deployment improvement

To improve our solution we can use Bicep. The domain-specific language developed by Microsoft to describe and deploy Azure resources.

The following script describes our resources.

```bash
param principalId string
param location string = resourceGroup().location

var tenant = subscription().tenantId
var secretsUserRoleId = '4633458b-17de-408a-b874-0445c86b69e6'

resource translator 'Microsoft.CognitiveServices/accounts@2023-05-01' = {
  name: 'mytranslator'
  location: location
  sku: {
    name: 'S1'
    tier: 'Standard'
  }
  kind: 'TextTranslation'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {  
  }
}

resource keyvault 'Microsoft.KeyVault/vaults@2023-02-01' = {
  name: 'translatorappvault'
  location: location
  properties: {
	tenantId: tenant
    sku: {
      family: 'A'
      name: 'standard'
    }
    enableRbacAuthorization: true
    accessPolicies: []
  }
}

resource translatorKeySecret 'Microsoft.KeyVault/vaults/secrets@2023-02-01' = {
  name: 'translatorKey'
  parent: keyvault
  properties: {
    value: translator.listKeys().key1
  }
}

resource secretsUserAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(keyvault.id, secretsUserRoleId, principalId)
  scope: keyvault
  properties: {
    roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', secretsUserRoleId)  
    principalId: principalId
    principalType: 'User'
  }
}
```

We have four different resources here `translator`, `keyvault`, `translatorKeySecret` and `secretsUserAssignment` let's have a look at them.

`translator` and `keyvault` are the resources we previously created with Azure CLI, but why do we need two others?

Previously we created `translatorKeySecret` as well as with the command `az keyvault secret set`. This resource holds the key to the translator API. The interesting part of this code is the following:

```bash
value: translator.listKeys().key1
```

In such a declarative way we say that the value for `TranslatorKey` is the `key1` of `translator`. So that every time we deploy our resources this value would be set, thus the key would be up-to-date.

The latest resource is `secretsUserAssignment` with the type `Microsoft.Authorization/roleAssignments` permits the user to read secrets. Because right now we run our app on behalf of the user account we give such permission to the user. If our application were running in the cloud we would permit it instead of a user.

To execute the deployment script we need to obtain the principal ID of the user who runs the application. Execute the following command in the Azure CLI replacing `[email-address-of-the-user]` with your email address.

```bash
az ad user show --id "[email-address-of-the-user]" --query id --output tsv
```

Now we are ready to deploy our resources.

```bash
az deployment group create --resource-group translator-rg \
    --template-file resources.bicep \
    --parameters principalId="[guid-from-previous-step]"
```

`resources.bicep` is the file name of the deployment script. If you name it differently don't forget to change it in the command.

Running the application after the script has been finished should give identical translations.

Let's check that new keys are applied automatically. For that, we manually regenerate the key1 using this command.

```bash
az cognitiveservices account keys regenerate --name mytranslator \
    -g translator-rg \
    --key-name key1
```

Run the application and it should fail with 401 Unauthorized response.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1693241727290/842aa9b2-7648-4429-859d-aacdb90c6f7a.jpeg align="center")

It is expected because we changed the keys but did not redeploy our resources thus Key Vault still has the old key. Run the deployment command. When it is finished run the application to check that everything works as expected.

## Summary

Azure AI Translator offers robust text translation capabilities that are fast and provide high-quality translations. At the same time, the use of keys instead of managed identity might be considered as a downside. We can use Azure Key Vault to eliminate this problem.

For more detailed information on available methods and features, refer to [the official documentation](https://learn.microsoft.com/en-us/azure/ai-services/translator/reference/rest-api-guide).

Image credits: "[Books](https://www.pexels.com/photo/books-768125/)" by [Emily](https://www.pexels.com/@emily-252615/)
