Home » AppInventor » A multilingual & multidevice template for AppInventor

A multilingual & multidevice template for AppInventor

Introduction

NOTE:

This post was initially published for AppInventor classic, but currently sources are available also for AI2. However, I have not updated the comments and images because the code is roughly the same.

English is not the only language in the world. If we want our apps to be widely spread we’d rather let them speak to each user in the language he likes to hear. In my case the first time I required multilingual capabilities was for a small app addressed to fans of Barça football club. I thought it was of local interest so I developed it just in Catalan… and was surprised to realize one the firsts downloads came from Hungary! So I hurried to prepare a multilingual version for the app and since I was in it I prepared a template to avoid redoing the work in future apps. This tutorial explains this template and provides the sources for those who consider it can also save time for them.

We can say something similar about multi-device. The more devices we support the wider acceptance for our app, and the main point here is resizing components height and width according to the screen size. The requirement is similar to multilingual and, as it may look logical, has a similar solution.

So I’ve decided to write a unique tutorial covering both functionalities. To be honest, multidevice was not a great concern for me but I added it into the template since once the structure is set for multilingual it takes very little effort to support also multidevice.

The first idea we could consider for our app to speak several languages and appear smartly in any device is developing separate applications, but it’s pretty obvious that maintenance would become a nightmare. Imagine we intend to support two languages and two devices. This means each modification must be implemented in four projects, so we must package and test each combination and finally publish four versions. It’s true that Google’s Multiple APK allows uploading several apps to Google Play under a single product as explained in this post, but the same site discourages us from following this practice and recommends distributing a single APK as the easiest way to manage and maintain any app.

So it seems pretty obvious that the right approach consists in having a unique app and dynamically adapting its texts and size to the current language and device. A trivial way to do that is to inform the right values in the Initialize event using the SetText, SetHeight and SetWidth properties of components. But for large amounts of components, languages and devices this is really tedious, error prone and difficult to maintain.

A first improvement would consist in placing the texts and components in a list and program a loop in the Initialize event to go through this list and make the assignments (see this example from Puravida). This way the adaptations are encapsulated into known places in the code and it’s relatively easy to add languages, modify translations, etc. This is the approach explained in the first part of this tutorial.

However, most of us are used to a smarter approach from other environments where texts are stored in a separate resource file and the app dynamically consults this file in order to show the texts in the right language. Therefore, the second part of this tutorial shows a mechanism to separate text from code, making our program cleaner and easier to maintain since modifications that affect only texts can be implemented by simply modifying the resource file and without need to redeploy the app. In a professional environment this means we can delegate text styling to external people who can correct or translate texts simply modifying this resource file.

What are we going to build

We’ll develop an app with a couple of screens containing several graphical components including a listpicker to choose the language. Then we’ll program the code blocks necessary to show the component texts in the selected language and resize them according to the device. We’ll also have a button that shows a message in the current language:

multilang_03

About this tutorial

The purpose of this tutorial is that you understand the idea and main implementation aspects of the template, so that you’re able to adapt it to your needs. Even though the sources provided should work directly, if you intend to use the template it’s absolutely necessary that you understand how it works for two main reasons:

  • You need to know where to put your own texts
  • You must be able to solve any errors in case the resource file is not correct (typically a message like “attempt to get item 4 from a list of length 3”). The code has some protection against these errors but they still may occur

Since the code is quite extensive, the tutorial does not cover every detail. For example we are not going to see the graphical interface (which is quite trivial), and we’ll only remark the most relevant aspects of the code. If you have any doubt about the omitted details you can inspect the sources or leave a comment that I’ll be pleased to answer.

Also notice that this tutorial does not explain much about the resizing algorithm. You can get some more details in the How to adapt to screen orientation post.

To sum up, if you’re interested in using this template I’d suggest you to proceed as follows:

  1. Download the sources
  2. Play with the sample app to understand how it works
  3. Follow this tutorial
  4. Adapt the template according to your needs
  5. Develop and publish fantastic apps for several languages and devices

Installation

You can install the template by downloading these sources and uploading them into AppInventor. The app should work straight forward.

To use the template in further apps you must create a new project as a copy of these sources and adapt the Procedure InitConstants in order to indicate the real components and their texts and size proportion. Next chapter explains how to do it.

How it works

To start off here is the big picture of the code:

multilang_31

NOTE: This code must be included in every screen. Unluckily AppInventor only allows to Copy&Paste blocks within the same screen but the copier facility provided by the App Inventor Repository can ease the duplication of these blocks into each new screen.
Adapting an existing app will be somewhat more complicated since you’ll have to merge this code with your own blocks. Hopefully this merge will not be too hard since the names of variables and procedures are not likely to collide. Just take into account to add a call to InitMultilang in the Screen.Initialize even.
If you require adapting your app to multilingual and find it too hard or have no time, I can do it for you for 5$ per screen. Send me an email for the details.

Let’s see the main aspects of these functions. The association of attributes to controls is based on four “lists of lists”:

multilang_01

  • COMPONENTS (lstItems): Contains one list for each type of multilingual control: label, button, textbox, screen, listpicker, checkbox, password, message (the last one is not exactly a control but we profit the same structure). Each list contains the components of that type for which we intend to change any attribute (text, width or height)
  • TEXTS, HEIGHT, WIDTH (lstItemsTexts, lstItemsHeight, lstItemsWidth): Contain the same exact structure as the COMPONENTS list, but in this case the contents of the inner lists is the value of the related attribute (text, height or width) for the component. Notice we needn’t inform all the attributes (e.g. we don’t inform a height or width for the screen)

The algorithm to associate texts consists simply in a loop through the first list to obtain the components and inform the corresponding attributes obtained from the other lists:

multilang_13

The tricky part here is the way to associate the right attribute depending on each kind of control. This is performed with an IFELSE series in the setComponentAttributes function:

multilang_14

Notice this piece of code shows only how to set the Text attribute. The same must be done for Height and Width, but I’m not showing it here for a matter of space. The real pattern would be as follows:

multilang_15

NOTE: This piece of code shows the algorithm for working out the height and width to be assigned to each component. It’s based on multiplying the proportion indicated in the InitConstants function by the size of each cell (calculated as a division of the screen size by the constants NUM_ROWS and NUM_COLUMNS). You’ll naturally have to make some tries to get the exact appearance you’re looking for. A couple of tricks:
A fast way to increase or reduce all the components together is modifying the constants NUM_ROWS and NUM_COLUMNS.
The proportion indicated in the initConstants function needn’t be integer numbers. For example, if a component has a proportion of “1” and is somewhat too large we can change it to “0.9”.

Now let’s see how to fill in these four lists (lstItems, lstItemsHeight, lstItemsWidth and lstItemsText). We’ll define their values in some constants at initialization time. We could think of assigning their values when defining the variables, but AppInventor does not allow assigning components in the variables definition, so we need to do it in the InitConstants function. The first three lists are not dependent on the language and therefore can be initialized directly:

multilang_32

Instead the texts list is somewhat more complicated because we need to inform the texts for all languages. We’ll implement it with a new level of lists with an item for each language. Next image shows the beginning of the block (not complete for a matter of space):

multilang_33

Once these constants are set, the variables are filled each time the user selects a language by means of the InitTextsList function:

multilang_34

And that’s it. We’ve got an app that adapts automatically to any size and to all supported languages. Encapsulating texts into a unique function greatly eases the app evolution. For example:

  • We can add a new language by simply including a new list at the second level of the LST_DEFAULT_TEXTS constant
  • If we add new components to our app, we just need to add them also in the four constants in the InitConstants function

Further ideas

There are obviously many enhancements to be considered for the template. Here ara a couple of them, but these are just my own thinkings which may not be the most useful or even viable so I’d be grateful to receive any suggestions about how to evolve the template.

  • A more elaborate way to select the current language would be obtaining automatically the device language as explained in this  snippet fromPuravida.
  • The current resizing mechanism does not take into account text size, and this produces some undesirable effects when the device size changes considerably regarding the original one. The solution would be to have an additional list (say LST_TEXTROWS) where we would be able to change dynamically text size according to the current height, i.e. we could set a text size corresponding to 90% of height for those components informed in the list, and the value would indicate us how many rows are required (e.g. if HEIGHT = 20 and LST_TEXTROWS = 2 the text would be adjusted to 9)
  • A limitation of the current solution is that this kind of changes requires deploying again our app. Instead, the mechanism implemented in the second part of this tutorial allows to make some of these changes without redeploying the app (we just need to modify a resource file stored in the web.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: