
Handling languages in Ultimate with the Messaging Widget
This article contains two approaches of collecting language context from customers contacting your AI Agent, powered by Ultimate via the Zendesk Messaging Widget.
Zendesk's native bot supports multiple languages and even offers an auto-translation feature for generative replies and custom flows.
The AI Agent powered by Ultimate uses a slightly different approach in which you can define duplicates of your replies for each language. So where generative replies automatically adapt the language of the conversation, you have full control of the steps and translations used in your flows per language.
However, in order for this to work correctly you need to make sure you can detect your conversation languages correctly. Zendesk's support help center has extensive documentation on this topic, but in this article I'd like to take a more structured and practical approach to this.
We'll focus on one specific use case, one that is – I think – the most common in use. We have a native Zendesk Messaging widget embedded on a website which is linked to the Ultimate Bot.
Retrieving a user's language
There's three ways to pass a locale to the Ultimate Bot:
- System. If nothing is set, it just inherits and uses the locale of the browser or website language if defined. This is the least stable option and doesn't always work since every website and user's setup is unique.
- Widget Locale. This uses the locale of the Zendesk Widget. You can use code to manually overwrite the locale client side so you're in control on what gets passed to the bot.
- Ticket Fields. You can also pass conversation metadata to the Zendesk Widget. This is stored in ticket fields and can be retrieved by the bot to set the language.
Setting the language
The active language in a conversation managed by Ultimate is stored in a system variable called active_language
.
You can use actions to overwrite the current value with your own and you can do this at the start of a conversation, or midway a conversation.
If the active_language
is set, Ultimate will use this value to select the right system language, and use active replies for that language.
If no languages match the system will fall back to the default language set.


To make sure you're matching the right language, you can add the expected values for that language as Locale Info to the language.
For example, in English you might expect en
, en-us
and en-uk
.
Using the Widget Language
You can set a Messaging Widget's language in Zendesk via the following javascript call:
<script>
zE('messenger:set', 'locale', 'en-us');
</script>
This will translate the widgets' UI and will also store the locale on the user level.
This means that all conversation started by that user from then on will be considered to be in that language. This also means that if the user opens the widget and starts a conversationon a domain.com/en-us
and then navigates to domain.com/fr
the system will still assume this user is speaking English.
Retrieving the language
If you use the widget locale to set the language you can use it in Ultimate via Bot Settings > Events and Actions.
Create a new action that fires when a Chat is started
- Give it a name: Set locale based on widget language
- Target: Sunshine Conversations
- Task: Get user
- Field to retrieve: locale
- Save as parameter:
active_language

Next Steps
Nothing else needs to be done. If your active_language
matches a language setup for your bot, it'll jump right into the Welcome Reply for that locale, and if not, it'll use the default language's Welcome Reply
Using a custom field language
You can set a Conversation language in Zendesk via the following Javascript call.
Note that the ID is that of an end-user editable text-field (or dropdown) in Zendesk.
<script>
zE("messenger:set", "conversationFields", [
{id: 1234567890, value: "en-us"}
]);
</script>
This will NOT translate the widgets' UI but will store the locale on the conversation level.
This means that only this conversation started by that user will be in this language.
This also means that if the user opens the widget and starts a conversation on a domain.com/en-us
and then navigates to domain.com/fr
and starts a new conversation, system, will start the new conversation in french if set so by the code.
Retrieving the language
If you use the widget locale to set the language you can use it in Ultimate via Bot Settings > Events and Actions.
Create a new action that fires when a Chat is started
- Give it a name: Set locale based on metadata
- Target: Sunshine Conversations
- Task: Get conversation metadata
- Field to retrieve: <Metadata>
- Key:
zen:ticket_fields:1234567890
- Save as parameter:
active_language

Next Steps
Nothing else needs to be done. If your active_language
matches a language setup for your bot, it'll jump right into the Welcome Reply for that locale, and if not, it'll use the default language's Welcome Reply
Verifying the data is correctly set
If you're just setting this up you might want to check if everything is setup correctly client side in order to make troubleshooting easier.
If you start a new conversation in the Web Widget, you can open your browser Inspect Tools, and go into the Networks tab. Look for a network call to AppUsers. This contains a payload that, within the conversations[]
array has a metadata:{}
object that will contain the ConversationsField you set earlier:
{
"settings": {
/.../
},
"conversations": [
{
"_id": "67865f8099658d0434f350bf",
"type": "personal",
"brandId": "21612166319762",
"isDefault": true,
"lastUpdatedAt": 1736859520.552,
"createdAt": 1736859520.552,
"metadata": {
"zen:ticket_field:1234567890": "en-us"
},
"unreadCount": 0,
"status": "active",
/.../
}
]
}
Similarly there's an appUser{}
object that contains the locale
you set via the widget locale setting.
{
"settings": {
/.../
},
"appUser": {
"locale": "fr",
"localeOrigin": "apiRequest",
"signedUpAt": "2025-01-14T12:58:40.525Z",
"identities": [],
"conversationStarted": true,
"clients": [
/.../
]
}
By using this network inspection you can easily troubleshoot if a wrong language setting is caused by the locale not being passed to the widget initially, or a bad configuration you made in Ultimate.
What happens if I set both values?
You can set both the widget language and the ticket field language. As long as you don't retrieve and set both values in Ultimate you won't go into any conflict.
For example, in this scenario I set the widget to English, while setting the language field French
<script>
zE('messenger:set', 'locale', 'en-us');
zE("messenger:set", "conversationFields", [
{id: 1234567890, value: "fr"}
]);
</script>
I can then choose to retrieve either one of these languages as the conversation language by either retrieving the user- or conversation metadata via an action.
If I do retrieve both, the last one set will win (or in other words the action lowest in the list)
Advanced Uses
Handling unsupported languages
Let's say my Bot only supports English and French. But I have a customer contacting me in Spanish.
Since Spanish is not a supported language the bot will respond with my default, English, Welcome reply.
I can however use conditional steps in my Welcome Reply to let the customer know that yes, I see he's speaking Spanish, and that he can choose between English and French to talk to the Bot.
- After the welcome message, add a Conditional Flow
- Set the Conditional to
active_language
- If the language is not
en
we want to tell the customer we detected an unsupported language- Add a bot message that "I detected your language as {{active_language}}. Please note I only support French and English for now. You can switch to French, or ask your question."
- Add a visitor question that offers French as an alternative
- Associate a Collect Parameter step to this option that sets the parameter
active_language
tofr
- Link to the French Welcome Reply
- In the other Condition (which is English) we ask the customer "How can I help you?" mimicking the original Welcome Reply



Use other variables to detect a language
We can use the conversationFields option to build more complex flows.
Let's say we pass a city to the widget:
<script>
zE("messenger:set", "conversationFields", [
{id: 1234567890, value: "rome"}
]);
</script>
We could create an action in Ultimate that stores this zen:ticket_field:134567890
as current_city
in our conversation.
We can then use a conditional flow that looks at the City and, for example, sets the active_language
to Italian for Rome, French for Paris and German for Berlin. Each of those conditions can then link to their respective Welcome Replies, thus linking location and cities to languages.
Create a language use-case
We can create an use-case that react to "Wrong language" replies. The reply can then offer a list of supported languages as a carousel or buttons.
The customer can then choose a language (which updates the active_language
via an action or parameter) and links to the now correct welcome reply.
