During the last several months I was discovering no-code tools. n8n was not the first one  I tried, but I immediately liked it because of its high flexibility, Function node and helpful community.

Even though I consider myself experienced in certain areas of programming, there are still plenty of technologies where my knowledge is very limited. I found out that n8n helps fill many gaps, allows me to build products very fast, and saves a tremendous amount of time.

One of the pet projects I created is a Telegram bot that extracts text from images. This bot is also able to communicate to users in several languages. In this tutorial, I'll share my approach to creating multilingual Telegram bots in n8n.

Table of contents

The use case
Prerequisites
Building the workflow
What's next?

The use case

It's possible to create a simple multilingual Telegram bot in n8n with only a few lines of JavaScript and some core nodes. But when the project gets bigger, it becomes challenging to find a balance between the complexity of the workflow and the possibility to communicate with users in their preferred language.

One solution is to ask the user about their language preference, i.e. create a special button in a bot and add many workflow branches in n8n for each language.

But what if I tell you it doesn’t matter how many languages are supported, the bot workflow can stay the same? I made it possible in this workflow, and below I'll walk you through the setup process. Feel free to copy-paste the workflow into your Editor UI and follow along this tutorial.

Workflow for a multilingual Telegram bot

Prerequisites

  • n8n: this is a fair-code licensed automation tool. What I personally like in n8n as a programmer is its flexibility. n8n can be installed in Docker on my PVS server.
  • NocoDB: this is an open source no-code database management tool. It can be installed on your VPS via Docker-compose along with an n8n instance.
  • Telegram bot. If you have never created Telegram bots before, here is a tutorial for that.
  • Basic knowledge of JavaScript: Please note that this article contains a bit of JavaScript code, so it would be helpful to understand what the provided code snippets do or if you want to tweak the code.

Building the workflow

First of all, let’s have a general overview of a simplified workflow. I have created a skeleton of a multilingual bot that can reply to simple commands in several languages.

There are two sections of the bot:

  1. Initialization.
  2. Processing of bot commands (like /help or /start).

Before looking into the initialization process we need to create a bot dictionary.

Bot dictionary

I created a dictionary that contains all possible phrases using NocoDB. Later on, specific messages are selected dynamically from the dictionary. This approach allows me to add many languages with virtually zero changes in a bot workflow. Here’s how the dictionary for several languages looks like:

Dictionary for the Telegram bot

Bot initialization

Each time the Telegram Trigger node is activated, an initialization process begins.

First, the user's language is detected and if it is not supported yet, then the default language is assigned:

// Telegram uses the following language codes: https://en.wikipedia.org/wiki/IETF_language_tag

// Check user and assign the default language if the translation is not yet ready

var data = $node["Telegram Trigger"].json; const botlang = ["ru", "en"];
// Update this after adding new language in the dictionary

var curlang = botlang.includes(data.message.from.language_code) ? data.message.from.language_code : "en";
return [{json:
    {chatID : data.message.chat.id,
    lang : curlang}
    }];

Don’t forget to update the botlang variable after adding a new language to the bot dictionary!

Second, the dictionary is loaded via the NocoDB node. Some additional processing of the dictionary is needed via a Function node.

let data = {};
for (item of items) {  data[item.json.botmessage]=item.json;}
return data;

Each bot message can be reached now by its name, languages are nested inside each message. The resulting dictionary structure looks like this:

Function node configuration for bot messages

Finally, the bot has very simple user management: each new user is greeted, known users are welcomed back, and the original user language is stored in a NocoDB database.

After the initialization process is completed, I placed a Merge node. It is configured in a Pass-through mode and waits for the initialization to finish before actually processing the Telegram message.

Commands processor

Next section of the bot is a command processor.

Since the bot supports only a few commands, one Switch node is enough.

Switch node configuration

The bot supports only two commands right now: /start and /help. Every other text goes to the third output and is treated as an incorrect command.

There are several Telegram nodes at the end of the workflow, all configured in a similar way.

Telegram node configuration

The Parse Mode is set to Markdown, because the help message contains a link to the n8n website.

Let’s take a closer look at the Text field, which is based on the dictionary and a user's language.

{{$evaluateExpression($node["botmessages"].json["help"][$node["chatID"].json["lang"]])}}

The bot looks for a message called “help” that is stored in a Function node “botmessages”. The user language is stored in another Function node called “ChatID”. Both of them were created at the initialization step.

When a new language is added into a dictionary, it will be automatically loaded and correctly applied in the Telegram node. No big changes in the workflow are required!

Of course, if you would like to extend the bot and support new commands, then an additional “botmessage” record should be added into a dictionary and some n8n workflow changes are also needed.

Last but not least, $evaluateExpression() is important, because some text messages can have dynamic parts, like this:

Nice to meet you, {{$node["Telegram Trigger"].json["message"]["from"]["first_name"]}}. I am n8n-powered test bot, I can reply in several languages. Your current language seems to be: {{$node["Telegram Trigger"].json["message"]["from"]["language_code"]}}.

Adding $evaluateExpression() allows n8n to correctly parse the user name and language code.

What’s next?

Though the Telegram bot is functional in this form, there are several things that could be improved or optimized:

  • Loading the whole dictionary from the NocoDB database each time a user sends the Telegram message is not optimal. It increases both the server load and the response time. The bot performance could be improved by storing the bot dictionary on the server in a JSON format. For my own bots I use a separate workflow which is activated every time the dictionary is updated:
Workflow part for storing bot dictionary
  • The dictionary is stored on the server as a JSON file and is loaded during the bot initialization phase. Two different nodes are used for that: Read Binary File and Move Binary Data, so the initialization phase should be slightly changed as follows:
Workflow part for loading bot dictionary
  • Finally, it is also possible to update the botlang variable from the ChatID Function node and detect all bot languages fully automatically. But this task requires some extra knowledge of JavaScript or an additional table in NocoDB database, which stores all known translations in a single variable.

Feel free to take on some of these tweaks and build your own multilingual Telegram bot! If you have any questions or automation ideas, you can contact me on LinkedIn or write in the n8n forum.