Building Multilingual Teams Bots: Adding Adaptive Cards with Dynamic Language Support

Sammy-Jo Wymer
REWRITE TECH by diconium
6 min readSep 13, 2024

When building bots for Microsoft Teams, using Adaptive Cards provides a visually engaging way to present content.

For example, instead of a simple bot message:

Teams bot message

You can impress your user with a more entertaining adaptive card:

Simple adaptive card in Teams

Even better… what if you could detect the user’s locale and dynamically change the greeting language based on this?

In this article, I’ll walk you through how to create dynamic adaptive cards in a Teams bot using TypeScript.

What are Adaptive Cards?

An adaptive card is basically a configurable card that you can add to your Microsoft Teams application. As highlighted above, these cards are designed to make messages look better and more interactive. Instead of being limited to sending plain text chat messages, you can add features such as images, buttons, and formatted text.

Pre-requisites

  • A working Microsoft Teams bot — read my last article to see how you can easily set yours up.

Table of Contents

  1. Setting up Localisation in Teams Bot
  2. Creating an Adaptive Card with Translations
  3. Displaying the Adaptive Card in Teams
  4. Complete Code Example
  5. Testing in Microsoft Teams
  6. Additional configuration (extra)

1. Setting up Localisation in Teams Bot

To handle text translations, we will use i18next.

So, let’s jump right in! Open up your bot code in VS code.

Install i18next

If you don’t have i18next installed already, you can do so by running the following command in your terminal:

npm install i18next

Create your translations files for different languages. Here is a sample structure for English and Spanish translations:

Translations folder structure

Each translation.json file contains key-value pairs for translated text.
For example, in src/locales/en/translation.json:

{
"chat": {
"greeting_text": "Welcome to the bot!",
"greeting_text_help": "How can I assist you today?",
}
}

In src/locales/es/translation.json:

{
"chat": {
"greeting_text": "¡Bienvenido al bot!",
"greeting_text_help": "¿Cómo puedo ayudarte hoy?",
}
}

Create a file in the src folder called i18n.ts and add the following code:

// src/i18n.ts

import i18n from "i18next";
import en from "./locales/en/translation.json";
import es from "./locales/es/translation.json";

i18n.init({
resources: {
en,
es,
},
lng: "en",
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
});
export default i18n;

This code initialises the i18n library with the English (en) and Spanish (es) translation files that we just created. It sets English as the default language (lng) and fallback language if no other locale is detected.

2. Creating an Adaptive Card with Translations

Now, let’s get to the fun stuff and create the adaptive card. Add the following function to your main bot file (mine is app.ts) and we’ll dive into what it means.

// src/app.ts

// ...previous bot code

function createGreetingCard(locale: string) {
return {
$schema: "https://adaptivecards.io/schemas/adaptive-card.json",
type: "AdaptiveCard",
version: "1.0",
body: [
{
type: "Image",
url: "https://github.com/wymersam/dicogptimage/blob/main/dicoGPT.png?raw=true",
height: "150px",
},
{
type: "TextBlock",
text: i18n.t("chat.greeting_text", { lng: locale }),
wrap: true,
color: "accent",
},
{
type: "TextBlock",
text: i18n.t("chat.greeting_text_help", { lng: locale }),
color: "warning",
},
],
};
}

// ...end of file

Code Explanation:

This is the code that creates an adaptive card with dynamic translation possibilities.

  • Structure: Adaptive cards are created via a JSON object, which makes them really easy to configure. In our super simple example, we include an image (note that this has to be a URL) and two blocks of text which come from the translation files we created in the previous step. We’ll add some more styling in the next steps.
  • i18n.t: This method retrieves the translated text based on the locale (e.g. en-US) passed. As you can see, we pass the locale as an argument to this function to determine which language will be shown. But where do we get this locale value from?
Simple adaptive card in Teams

3. Displaying the Adaptive Card in Teams

Once you have the function to create the adaptive card, you then need to create another function to display the card with the correct translation in the Teams chat:

// src/app.ts

async function showGreetingCard(context: TurnContext, locale: string) {

// Show the adaptive card in the chat
await context.sendActivity({
attachments: [CardFactory.adaptiveCard(createGreetingCard(locale))],
});
}

Code Explanation:

  • context.locale: This is where we find out the locale of the user.
  • defaultLanguage: In case no locale is detected, a fallback locale is used (e.g. en-US for English).
  • CardFactory.adaptiveCard(): This method allows us to use the JSON object we created in the createGreetingCard function to style our adaptive card.

4. Complete Code Example

Here’s the complete code block for reference, including the event handler (onInstallationUpdate()) for when the bot is installed:

// src/app.ts

import { CardFactory, TurnContext } from 'botbuilder';
import i18n from 'i18next';

// ...other code and imports

// Set a default locale as a fallback value
const defaultLanguage = "en-US";

// Show the adaptive card when the bot is installed/updated
// This event handler comes from the bot-builder package
this.onInstallationUpdate(async (context: TurnContext, next) => {

const { locale } = context.activity;

await showGreetingCard(context, locale ? locale : defaultLanguage);

await next();
});

// ...main bot code


function createGreetingCard(locale: string) {
return {
$schema: "https://adaptivecards.io/schemas/adaptive-card.json",
type: "AdaptiveCard",
version: "1.0",
body: [
{
type: "Image",
url: "https://github.com/wymersam/dicogptimage/blob/main/dicoGPT.png?raw=true",
height: "150px",
},

{
type: "TextBlock",
text: "Welcome to the bot!",
wrap: true,
color: "accent",
},
{
type: "TextBlock",
text: "How can I assist you today?",
wrap: true,
color: "warning",
},
],
};
}


async function showGreetingCard(context: TurnContext, locale: string) {

await context.sendActivity({
attachments: [CardFactory.adaptiveCard(createGreetingCard(locale))],
});
}

// end of code

5. Testing in Microsoft Teams

Once you’ve added this functionality to your bot, redeploy it to Azure and test it in your Microsoft Teams environment (if needed, you can refer to my previous article on how to do this).

  1. Install the Bot in a Team: After installing the bot on Teams, it should send the greeting card in the language based on your Teams locale settings.
    Note: The text will only be translated if you have the appropriate translation.json file for the user’s locale/language. For example, if your locale is de-DE but you don’t have a translation file for German, then the language will fallback to English.
  2. Change Teams Locale: Go to Teams settings and change the language (e.g. from English to Spanish). Reinstall or update the bot, and you should see the greeting card now appears in Spanish!
Adaptive card translation

6. Additional Configuration

You can configure your adaptive cards even more with features such as columns, buttons, colours, spacing, and more.

Adaptive card with additional configuration

In this example, I updated the adaptive card JSON object to have columns, gave the columns a colour, and also added a button which would take the user to the URL provided:

// src/app.ts

function createGreetingCard() {
return {
$schema: "https://adaptivecards.io/schemas/adaptive-card.json",
type: "AdaptiveCard",
version: "1.0",
body: [
{
type: "ColumnSet",
style: "accent",
bleed: true,
columns: [
{
type: "Column",
items: [
{
type: "Image",
url: "https://github.com/wymersam/dicogptimage/blob/main/dicoGPT.png?raw=true",
height: "150px",
},
],
width: "auto",
},
{
type: "Column",
style: "warning",
bleed: true,
items: [
{
type: "TextBlock",
text: "Welcome to the bot!",
wrap: true,
},
{
type: "TextBlock",
text: "How can I assist you today?",
},
{
type: "ActionSet",
actions: [
{
type: "Action.Submit",
title: "Click Me",
url: "www.example.com",
},
],
},
],
width: "stretch",
},
],
},
],
};
}

Conclusion

In this guide, we covered how to create an adaptive card in Microsoft Teams that updates the text language dynamically based on the user’s locale. By using i18next and the Microsoft Bot Framework, you can create dynamic, user-friendly bots that support multiple languages.

Thanks for reading! Why not jump right in and start designing an adaptive card that fits your bot’s requirement?

--

--