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:
Create a resource group named
translator-rg
in thewesteurope
region:az group create --location westeurope --name translator-rg
Create a Text Translator service named
mytranslator
within thetranslator-rg
resource group: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
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.
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.
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 usefrom=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.
The result of running this code
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.
Create a Key Vault with the name
translatorappvault
in the resource grouptranslator-rg
using Azure CLI.az keyvault create --location WestEurope \ --name translatorappvault \ --resource-group translator-rg
Set key from Azure AI Translator.
Note: Replace
[api-key]
with the actual key from the Translator resource.az keyvault secret set --vault-name translatorappvault \ --name "TranslatorKey" \ --value "[api-key]"
Add the following packages to the solution:
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.Accessing Key Vault and retrieving the key
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 ofSecretClient
. 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.In the last part, we need to change the header
Ocp-Apim-Subscription-Key
to use the key retrieved from Azure Key Vault.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.
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:
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.
az ad user show --id "[email-address-of-the-user]" --query id --output tsv
Now we are ready to deploy our resources.
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.
az cognitiveservices account keys regenerate --name mytranslator \
-g translator-rg \
--key-name key1
Run the application and it should fail with 401 Unauthorized response.
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.