Creating a Multilingual Copilot Chatbot with automated Translations using PAC and PowerShell as a Pro Developer
Davy De Roeck
Senior Technical Specialist Business Applications/Low-Code at Microsoft
In my previous article, I explained how we could create a chatbot using PowerShell and PAC command line.
Now, let's take it one step further. In this new article, we'll extend our existing chatbot with new languages and automatically translate them using Azure Translate. By doing so, you can easily add additional languages and provide automatic translations to CoPilot.
Why Multilingual Support Matters
As developers, we understand the importance of reaching a global audience. Offering multilingual support enhances user experience and accessibility. Whether you're building a chatbot, an app, or any other software, providing content in multiple languages is a valuable feature.
Leveraging Azure AI Translation Service
For our translation needs, we'll use Azure AI Translation Service. While other options like GPT (Generative Pre-trained Transformer) or AI builder translation exist, Azure AI provides a robust solution. As a pro developer, it's essential to explore alternatives beyond language models. Azure AI Translation Service offers accuracy and scalability.
Getting Started with Azure AI Translation Service
The for more information about the AI translator service - see https://azure.microsoft.com/en-us/products/ai-services/ai-translator.
This service comes for free for the first 2M translations / month - I assume this is already a good start and normally this would be enough to start exploring the options.
Let's start building the logic.
After we provisioned the Azure service (see above), we need to store the API key and the Service Uri.
Replace those with your own keys and Uri of course.
# Set your API key
$apikey = 'replace_with_your_own_azure_api_key'
# Set the service URL
$serviceUrl = 'https://__yourownservice__.cognitiveservices.azure.com'
Translate Function
Let’s start by creating a Translate function.
Since we’ll be using this function many times later when working with our XML file, it makes sense to define it as a function. This function translates text from one language to another using the Azure Cognitive Services Translator API.
Description
The `Translate` function sends a text string to the Azure Translator API and returns the translated text.
Parameters
Example
powershell Translate -TextToTranslate "Hello, world!" -fromLanguage 'en' -toLanguage 'fr' -ApiKey 'your_api_key_here'
function Translate {
param (
[string]$TextToTranslate,
[string]$fromLanguage,
[string]$toLanguage,
[string]$ApiKey
)
# Constructing the URI with dynamic language information
$convertURI = "$serviceUrl/translator/text/v3.0/translate?api-version=3.0&from=$fromLanguage&to=$toLanguage"
$headers = @{
'Ocp-Apim-Subscription-Key' = $apikey
'Content-Type' = 'application/json'
}
# Build Conversion Body
$text = ConvertTo-Json @( @{ "Text" = $textToTranslate })
# Convert
$conversionResult = Invoke-RestMethod -Method POST -Uri $convertURI -Headers $headers -Body $text
$translatedText = $conversionResult[0].translations[0].text
return $translatedText
}
Next, we will test our `Translate` function for the first time. If the function works correctly, it should provide accurate translations.
If not, please double-check the parameters and ensure they are correctly set before proceeding.
#Let's test the function
echo (Translate `
"Multiple translations allowed, use | as a separator for multiple translations." `
"en" `
"nl" `
$apikey)
When everything went well, you would see simular output:
Meerdere vertalingen toegestaan, gebruik | als scheidingsteken voor meerdere vertalingen.
Now that we have met the prerequisites outside of the Power Platform or CoPilot, we can proceed with the actual work.
In the previous steps, we have set up the Azure AI Translation Service and obtained the necessary API key and service URL. We have also created a `Translate` function that utilizes the Azure Cognitive Services Translator API to perform translations.
In the upcoming steps, we will focus on getting the translations from the bot and translating them. We will download the CoPilot bot and extract the primary language information. Then, we will detect the primary language and set it as the source language for translation. We will also specify the destination language and create a new translated file. Finally, we will load the XML content, translate the values and comments, and save the modified XML back to the file. Let's continue with the next steps to retrieve and translate the bot's translations.
Create/Manage Multilanguage Bot
Before we can proceed, we need to have the bot GUID stored. In my previous article, I explained how you could dynamically retrieve or define this when starting from scratch.
领英推荐
However, in this case, I will simply hardcode the GUID for simplicity.
Please make sure to replace this hardcoded value with the appropriate GUID for your bot.
$botid = "00000000-1111-2222-3333-999999999999"
Download CoPilot to manage translations
To manage translations in CoPilot, we need to download the language files using the pac copilot extract-translation command. After retrieving the language files, we can use the pac copilot merge-translation command to update them back to the server.
To detect filename information later on dynamically from the output, we need to store the output now from the server. We will save this into a output variable for now.
$output = pac copilot extract-translation `
--bot $botid `
--all `
--overwrite
The next code is used to extract the filename from the output and store it in the variable $sourcefileName. It uses regular expressions to match the desired value.
The extracted filename is then used to retrieve the file using the Get-ChildItem cmdlet and store it in the variable $sourcefile. The file information will been used later on the copy this file over into the destination language file.
#Get the filename from the output
# Use regex to extract the desired value
$sourcefileName = [regex]::Match($output, '(\w+\.resx)').Groups[1].Value
# get the actual file from the filename
$sourcefile = Get-ChildItem $sourcefileName
echo $sourcefile.baseName
We would see the base filename of the original language file showed on the screen for now.
Lets now take the language from the box.xml information. We also got the language information from the output above, but we actually need LCID information to proper cast into right culture information.
I didn't found an easy way to do this from displayname from the output we just received in PowerShell, so I decided to take the LCID value out of the bot.xml and use this information to load proper culture information into our logic.
Replace solution folder in the script below with your actual solution folder containing the bot.xml file, or refer to my previous article to see how to retrieve this dynamically
$botxml = Get-ChildItem -path "./solutionfolder/src/bots" -Include *bot.xml* -Recurse | Select-Object -First 1
Let's define the actual destination language (INT) based on the LCID value.
See for complete list of all LCID values for each country/language. Be aware that not yet all languages are supported by CoPilot. See this link for the actual list of languages supported by CoPilot on today.
Replace the value 1036 with the required destination language.
$DestinationLCID = [int]1036
Now that we have the destination language let's cast all this information into the proper Culture Information and show the Source and Destination Language Name and Code. For the code we will need later on as well the twoletterisoLanguage name to send to the translation service (Azure AI) as LanguageName (fr-FR, nl-BE,..) to create the corresponding resource files for each language. All this information we will retrieve from the culture information.
[xml]$xmlContent = Get-Content -Path $botxml.PSPath
$SoureLanguage = [Globalization.CultureInfo] [int]$xmlContent.bot.language
Write-Host "Source Language Name: " $SoureLanguage.NativeName
Write-Host "Source Language Code: " $SoureLanguage.TwoLetterISOLanguageName
## Set your own destination language
$DestinationLanguage = [Globalization.CultureInfo]$DestinationLCID
Write-Host "Destination Language Name: " $DestinationLanguage.NativeName
Write-Host "Destination Language Code: " $DestinationLanguage.TwoLetterISOLanguageName
You would normally see the similar information, showing the proper source and destination language information like:
Source Language Name: English (United States)
Source Language Code: en Destination
Language Name: fran?ais (France)
Destination Language Code: fr
Now we can copy the extracted (base)language file into the destination file. Therefor we need to append the Language Name into the filename.
$destinationXml = "$($sourcefile.BaseName).$($destinationLanguage.Name).resx"
Copy-Item `
-Path $sourcefile `
-Destination $destinationXml `
-Force
Now that we have our destination filename, let's load it into memory as XML and iterate over each data node. Inside each data node, we will translate both the Value node and the Comment nodes. In less than a minute the whole file is translated and saved. Only 1 step is left
# Load the XML content (replace 'path/to/your/sample.xml' with the actual file path)
[xml]$xmlContent = Get-Content -Path $destinationXml
# Select all <data> elements and extract their values and comments
$xmlContent.SelectNodes('//data') | ForEach-Object {
$valueNode = $_.SelectSingleNode('value')
$commentNode = $_.SelectSingleNode('comment')
# Translate the value and comment
$translatedValue = Translate $valueNode.'#text' $SoureLanguage.TwoLetterISOLanguageName $DestinationLanguage.TwoLetterISOLanguageName $apikey
$translatedComment = Translate $commentNode.'#text' $SoureLanguage.TwoLetterISOLanguageName $DestinationLanguage.TwoLetterISOLanguageName $apikey
# Update the XML with translated values and comments
$valueNode.'#text' = $translatedValue
$commentNode.'#text' = $translatedComment
}
# Save the modified XML back to the file
$xmlContent.Save($destinationXml)
Final step is to upload the new translated file back to the system. It will also enable this new language automaticly in the system.
Replace foldername with your actual root solution folder name.
pac copilot merge-translation `
--file $destinationXml `
--solution foldername
If everything went well you would see simular output:
Connected as [email protected]
Loading language French into bot 'your_botname_here' (Friendly name of your bot) from file 'fullpath_to_your_file\schemaname_of_your_nobot.fr-FR.resx'.
0 key(s) were missing, 0 value(s) were not used. Use the --verbose switch to get more details.
Updated 13 out of 13 components.
And hereby your chatbot is translated automatically from code behind!
By uploading new languages this way, the languages are enabled by default and active to been used.
Happy coding
Solution Architect | Microsoft Copilot Studio | Power CAT
4 个月Super interesting article Davy De Roeck ! Thanks for sharing all the pro-code ways of creating a Copilot!
Power Platform Technical Specialist at Microsoft
4 个月Amazing work Davy! Definitely gonna try this out in my spare time!?? Two quick questions: - Is there a maximum word count for the Azure Translation service? - Does the power shell code create the localization file as well or did you have to download that manually from the bot first?
Senior Technical Specialist Business Applications/Low-Code at Microsoft
4 个月cc: Henry Jammes, Carsten Groth; Jonathan D'haene, Shriya Malhotra, Sofie Borck Janeheim, Marc Schweigert, Dieter De Cock, Daniel Laskewitz, Harald Wentein