The Microsoft Power Platform allows us to easily build Apps for our customers and our colleagues. In multi-national organizations this means that your app probably needs to support multiple languages. So how do we do that in Canvas Apps?

This is not the first article to tackle this topic. There are different solutions that are described on the internet. But I have added a bit of software engineering on top of that to create, in my opinion, a better solution. Not so much easier, but it puts the difficult parts in one place, to make the other parts easier to use and understand.

What’s wrong with existing multilingual Canvas App solutions?

Microsoft Docs for Power Apps: Language function in Power AppsThis article builds on the existing examples. As input, the following articles and blogs are used:

The core issue I have with these solutions is that they are all based on using ‘complex’ expressions. Take the following example, where we get the translation for a single label:

Canvas1

LookUp('Resource Strings', Key = "AppTitle" And Language.'ISO Code' = Language(), Value)

After this article you will be able to write just this:

varStrings.AppTitle

Power Apps Translations in practice

This is not a very complex example. The original expression works fine for these simple texts. In practice, most Power Apps need more than simple texts to display their data. Imagine the following scenario. The App has a text that shows if a user has a reservation:

On April 21 Joe has a reservation.

Let’s build that text using an expression:

LookUp('Resource Strings', Key = "On" And Language.'ISO Code' = Language(), Value) & " " & Text(varDate, "mmmm d") 
& " " & varUserName & " " & LookUp('Resource Strings', Key = "Reservation" And Language.'ISO Code' = Language(),
 Value)

That already starts to look more complicated. You need to look a few times to see what is going on there. It’s hard to see the logic between the lookups. Also, we are making a few assumptions in this expression. These assumptions will become a problem if you want to translate this app correctly.

So, let’s try to fix this by using a ‘template’; a text that contains placeholders for specific values. The template can be defined as:

Using the Substitute()-function in the expression we can then replace the {0} and {1} with the correct values. We can also define an extra “DateFormat” that holds how the date needs to be formatted in a specific language. Combine that and we get the following Expression:

Substitute(Substitute(LookUp('Resource Strings', Key = "HasReservation" And 
Language.'ISO Code' = Language(), Value), "{0}", Text(varDate, LookUp('Resource Strings', 
Key = "DateFormat" And Language.'ISO Code' = Language(), Value))), "{1}", varUserName)

There is a lot to unpack here, so the expression is no longer easy to understand. It is hard to read and hard to maintain. A citizen developer could be dissuaded by this. This is a perfect example of why a good multilingual solution is needed.

Explaining the multilingual app solution – how var strings helps

In this solution we will be separating the translation logic and the business logic. The goal is to have the following expression, based on the previous example. It focusses just on the business logic. In this case, the substitution of the correct values.

All the parts pertaining to getting the correct translated texts are no longer in this expression but are tucked away in the magic of varStrings.

The underlying solution is to prepare a Record (in this case varStrings) which contains all the translations string. When creating this record all these Lookup queries can be done. So, there will be one place where all the Lookup code is done. This will be a big (and a bit ugly looking) piece of code. The big upside is that there is only one place where this code resides! Everywhere else you can use the varStrings-variable.

So how does this work?

Canvas3

Set(varStrings, {

AppTitle: LookUp('Resource Strings', Key = "AppTitle" And Language.'ISO Code' = Language(), Value),

HasReservation: LookUp('Resource Strings', Key = "HasReservation" And Language.'ISO Code' = Language(), Value),

DateFormat: LookUp('Resource Strings', Key = "DateFormat" And Language.'ISO Code' = Language(), Value) });

We use Set() to create a variable that contains a Record. You can create any record using the { } syntax. In this case our record has three keys: AppTitle, HasReservation and DateFormat. These are now properties of varStrings. So everywhere in your app where you use varStrings it will have these properties. As you can see, all the lookups are now bundled into one place in the code too.

Structuring your translations

Using Records is a very useful feature. Especially since you can nest records inside records! This makes it possible to add structure to your records. An example is to group your translations based on the screen it is used in.

Canvas4

You can now access the strings based on the groups Global, Simple Example and About. For example: Using Records is a very useful feature. Especially since you can nest records inside records! This makes it possible to add structure to your records. An example is to group your translations based on the screen it is used in.

Canvas5

By using records in Power Apps you can easily create structured data. These structures can then be easily used in the Power App. The App Studio will automatically recognize your data structure so that you can autocomplete your code.

Canvas

If the logic of retrieving the Resource string ever changes there is one place to maintain. Maybe you start out using an Excel sheet to hold the Translations and later switch to a Dataverse or SharePoint. As long as the structure of your Records remains the same, the app will keep working without errors.

Further thoughts on developing a multilingual power app

The resource strings are now retrieved one-by-one. This can be rather slow for many resources. See if you can grab just all resources in one call and then build the records based on the retrieved collection.In the examples above, things have been simplified for brevity or ease of explanation. When building an app based on this design you should take care of the following:

Business functionality over code complexity in developer a Power App

It is easy to use the Record type in Microsoft Power Apps to structure your data. By using this feature, you can create custom structures that fit your need. In this case we use it to structure our resources strings.There are many things to take into account when creating a full featured multilingual app. Some of these things can be made easy by rearranging the logic in your app.

The structuring of the data happens in a single place. At that moment, the code might be complex in order to retrieve the correct string for the current resource key. But all this complexity is contained in a single place.

Using resource string in the screens is now focused on functionality instead of the complexities of retrieving the correct resources. This makes it much easier to focus on the business functionality your app is delivering. And it makes it easier to support and maintain your app going forward.

If you are looking at developing a multilingual Power App for your organization, and have found my hints and tips useful but want more support, then please reach out to me at Prodware.