Skip to content

Implementation of multilingual Power Apps

A few months ago we had a hackathon where our team was making a concept for multilingual Power Apps. We had the following requirements for the implementation concept

  • In addition to the user interface, the content is also language-versioned
  • Adding a new language does not cause changes to the data model
  • The same solution works as widely as possible in different applications (canvas Power Apps, model-driven apps, Power Pages and Power BI)

Implementation concept from Power Pages

We went through various implementation concepts and eventually borrowed the idea from Power Pages. In Power Pages, the supported languages ​​are defined at the site level.

All language versionable elements have separate fields per language based on the supported languages.

How is this implemented? In practice, the different language versions of the column are stored as JSON in one column. In the user interface, each language is presented in a separate column. When we add a new supported language, a new column automatically appears on the form.

Now let’s use this same method for language versioning of Canvas Power Apps. Both the user interface elements and the actual content. The beauty of the solution lies in the fact that now language versions are maintained in the same way for model-driven apps, Canvas apps, and Power Pages.

Canvas Power Apps – Language versioning of the user interface elements

Let’s start with the easiest, i.e. the language version of the user interface elements. Let’s create a separate table for them (UI Strings). The table has two fields

  • The internal name (code) with which the text is searched
  • Translations, where language versions are stored as language code (LCID) and value pairs

The maintenance of user interface texts looks like the following in the model-drive app. The Translations field containing the JSON is shown for illustrative purposes only. It would be hidden.

The JSON of the Translations field is extracted into separate fields with the help of a web resource.

Implementing translations with a function

First, we add a language selection to the user interface.

A constant (constUIStrings) is created for the language versions, where the values ​​in the UI Strings table are read. This way, every request for a translation does not cause a query to the Dataverse.

Then to the actual work. Let’s create a new component for translations (cmpLanguages​). Add a function-type property UIStrings to it. It has parameters UIString and LanguageCode. The value of the function is then the correct language version of the searched UIString with the given language code.

The return value, i.e. the value of the UIStrings property, is formed as follows.

With(
    {
        MyJSON: ForAll(
            Table(
                ParseJSON(
                    LookUp(
                        constUIStrings,
                        Code = UIString
                    ).Translations
                )
            ),
            {
                LCID: Text(ThisRecord.Value.LCID),
                Value: Text(ThisRecord.Value.Value)
            }
        )
    },
    Coalesce(
        LookUp(
            MyJSON,
            LCID = LanguageCode
        ).Value,
        LookUp(
            MyJSON,
            LCID = "1033"
        ).Value
    )
)

In practice:

  • Search for the correct term from the constUIStrings collection (e.g. Save)
  • Create a typed JSON from the untyped JSON stored in its translations field
  • Retrieve the correct language text from the generated JSON. If it is not found, the English version is returned (language code 1033).

The function is used wherever text is presented in user interface elements. Below is a title for the button.

This is how the text changes when changing the language.

This was the easy part.

Canvas Power Apps – Language versioning of content

Now all content is produced in several languages. Below is an example of what the theme and its description look like in model-drie Apps in three languages.

This content should also be presented smartly in Canvas Power Apps. Some of the content should also be able to be updated there.

Implementing translations with a function

Let’s start by making a function (Translate), which receives as a parameter

  • the language versions of the content text (for example, the product name) in JSON format
  • the language code of the desired language

The value of the function is almost the same as the previous one. This time we get directly as a parameter the translations of the desired text in JSON format (UITitleAsJSON). The code is thus slightly shorter.

With(
    {
        MyJSON: ForAll(
            Table(ParseJSON(UITitlesAsJSON)),
            {
                LCID: Text(ThisRecord.Value.LCID),
                Value: Text(ThisRecord.Value.Value)
            }
        )
    },
    Coalesce(
        LookUp(
            MyJSON,
            LCID = LanguageCode
        ).Value,
        LookUp(
            MyJSON,
            LCID = "1033"
        ).Value & " en"
    )
)

Now we can translate on the fly, for example, the content displayed in the gallery.

For the selection list, we add a new column (UIDisplayName) having translated text as its value. We show this value in the user interface.

Editing multilingual content

Finally, the most difficult part, i.e. editing multilingual content.

Let’s make another new function (UpdateTranslation). It gets as a parameter

  • A text in JSON format that contains the current translations
  • Updated value
  • The language code of the language version to be updated

The function returns the updated JSON.

With(
    {
        varMyJSON: ForAll(
            Table(ParseJSON(CurrentUIStringJSON)),
            If(
                ThisRecord.Value.LCID = LanguageCode,
                {
                    LCID: Text(ThisRecord.Value.LCID),
                    Value: Text(UpdatedValue)
                },
                {
                    LCID: Text(ThisRecord.Value.LCID),
                    Value: Text(ThisRecord.Value.Value)
                }
            )
        )
    },
    JSON(varMyJSON)
)

Finally, we make a function (TranslationExists) that checks if a language version already exists with the given language code. The function receives as parameters

  • A text in JSON format that contains the current translations
  • The language code of the language version

The return value of the function is true or false.

With(
    {
        varMyJSON: ForAll(
            Table(ParseJSON(UIStringJSON)),
            {
                LCID: Text(ThisRecord.Value.LCID),
                Value: Text(ThisRecord.Value.Value)
            }
        )
    },
    !IsBlank(
        LookUp(
            varMyJSON,
            LCID = LanguageCode
        )
    )

How to use these functions?

Our application has a form to edit the title value. The language versions of the title are stored in the Name Translations field, which is now visible only to illustrate what is happening. The form does not update the Name field, only the Name Translations field.

When the user selects a language, the correct language version is searched for in the name field with the translate function.

When saving the form, the value of the Name Translations field is always rebuilt. If the language version is found, its value is updated with the UpdateTranslation function. If not, the missing language and values ​​are added after the JSON.

In code, this looks like the following.

If(
    cmpLanguages_1.TranslationExists(
        ThisItem.'Name Translations',
        rdSelectedLanguage.Selected.Value
    ),
    
    cmpLanguages_1.UpdateTranslation(
        ThisItem.'Name Translations',
        inpLabel.Text,
        rdSelectedLanguage.Selected.Value
    ),
        Left(
            ThisItem.'Name Translations',
            Len(ThisItem.'Name Translations') - 1
        ) & ",{""LCID"":"""& rdSelectedLanguage.Selected.Value &""",""Value"":"""& inpLabel.Text&"""}" & "]"
)

You are right. It would be neater to package the whole logic inside the UpdateTranslation function. However, there were problems that did not make sense to solve within the framework of the hackathon. Regrets.

Summary

Now we have an application whose language can be changed so that both the user interface and the contents change. The languages ​​to be used can be added without changes to the code or data model. The same concept can be used in Canvas Power Apps, model-driven Apps (content), Power Pages and, where applicable, Power BI (content).

But there is still work to be done here. For example:

  • The name field in Model-driven Apps is used in views and selection lists. Can it be easily translated on the fly, is an English translation always used, for example, or how does it work?
  • How to make the language versioning of Rich text fields work?
  • If the Canvas Apps recording messes up the JSON of the translations field, the translations of that field in the line will stop working. So be careful with certain brands.
  • Performance. If language versions of untyped JSON are searched on the canvas screen in, say, a hundred different places, does it show up in the performance?

Despite everything, this was a really fun topic to work on in a nice group!

Canvas AppmultilingualPower Appstranslations

Leave a Reply

Your email address will not be published. Required fields are marked *