Internationalization for Mobile Applications

A screen shot shows the word desafortunadamente truncated inside a button Image by Sepideh Miller

Up until last year, I had never worked with an application that supported internationalization. I had assumed that we just needed someone to translate strings, and everything would be fine. Internationalization, the process of making an application adaptable to multiple languages, can be challenging for both designers and developers.

I had not realized that there were design implications in supporting internationalization until I was learning some Spanish words in Duolingo, and my son pointed out that “desafortunadamente” was a much longer word than “unfortunately.”

In mobile designs, buttons frequently have specified sizes. These sizes can be specified in pixels or based on the size of the device. The text on the buttons can also have a specified size. When the text is too long, it either gets wrapped, ellipsized, or simply cut off. This type of truncation can create a poor experience in languages that were not considered in the original design. Designers have to consider whether they can shrink their text without losing accessibility, or whether their button should be on a separate line. Fortunately, Figma has some plugins to help designers with internationalization and localization considerations. Localization is the process of updating an application for a specific language.

The snippet of Jetpack Compose code below shows how the unfortunate layout at the top of this entry was created. It can be replicated by creating an Empty Activity in Android Studio and replacing the content inside the Surface.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Row(
   verticalAlignment = Alignment.CenterVertically
) {
    Image(
        modifier = Modifier
            .weight(1f)
            .padding(start = 16.dp),
        painter = 
            painterResource(
                id = R.drawable.disappointed_face_svgrepo_com
            ),
        contentDescription = "Disappointed Face"
    )
    Spacer(Modifier.width(8.dp))
    Column(
        modifier = Modifier
            .weight(2f)
            .padding(end = 16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        DisappointingButton(text = "Unfortunately")
        Spacer(Modifier.height(8.dp))
        DisappointingButton(text = "Desafortunadamente")
      }
   }

@Composable
fun DisappointingButton(text: String) {
    Button(
        modifier = Modifier.height(64.dp),
        content = { ButtonText(text = text) },
        onClick = {}
    )
}

@Composable
fun ButtonText(text: String) {
    Text(
        text = text,
        fontSize = 32.sp,
        overflow = TextOverflow.Ellipsis
    )
}

Another aspect of internationalization that I had never thought deeply about was date formatting. I did not realize until recently that date patterns do not support languages by default in Java. Android has a special function called getBestDateTimePattern that takes a specified DateFormat and reformats it for a given locale. A locale consists of a two-letter language code as specified in ISO 639 and a two-letter country code as specified in ISO 3166. American English likes to have the month before the date while many other countries like to have the day, followed by the month, and the year.

Screen shot showing internationalized dates

The composable to internationalize the dates in the image above is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 
@Composable
fun DateText(locale: Locale){
    val localDate = LocalDate.now()
    val skeleton = 
        DateFormat.getBestDateTimePattern(locale,"MMM dd, yyyy")
    val formatter = DateTimeFormatter.ofPattern(skeleton, locale)
    val date = localDate.format(formatter).toString()
    Text(
        text = date,
        fontSize = 24.sp,
        fontWeight = FontWeight.Bold
    )
}

The getBestDateTimePattern converts the original pattern to one appropriate for the given locale. Then, the locale on the DateTimeFormatter.ofPattern(skeleton, locale) makes sure that the name of the month is also properly translated. In the example above, I used the Locale.US to get the formatting required by American English and Locale("es", "ES") to get the formatting for Spanish in Spain.

It has been interesting to learn how to better support mobile application internationalization. This entry has only touched up on how to localize for Spanish. It gets more complicated as we deal with languages that require taller line heights and right-to-left text orientation.