Grio was recently asked by Soundwater Technologies to add Spanish and Portuguese translations to their iOS and Android mobile applications. The app pairs with Soundwater’s hardware to use ultrasonics to measure water flow. This project was a large undertaking, but it could have been avoided: if internationalization and localization patterns had been used during the initial app development, the process would have been nearly instantaneous.
In this post, I’ll go through the difference between internationalization and localization, and discuss the important benefits of building both into your applications right from the start.
What is Internationalization?
W3 describes internationalization as “enabling code to support local, regional, language, or culturally related preferences. Typically this involves incorporating predefined localization data and features derived from existing libraries or user preferences. Examples include date and time formats, local calendars, number formats and numerical systems, etc…”
More simply, internationalization refers to the ways you approach app development to establish the potential for use of different content and languages down the road.
What is Localization?
Localization is “the adaptation of a product, application, or document content to meet the language, cultural, or other requirements of a specific target market.”
Localization and internationalization are dependent variables that must be completed together for an app to be successful. While internationalization is making an app globally scalable, localization is then utilizing that foundation to localize the app to a specific region. Localization typically deals with string changes or asset changes, and it is more content driven than internationalization.
To put it another way, internationalization is primarily the focus of the engineers, while localization is driven by product and design.
iOS Text Assignment
In the Soundwater iOS app, we had a lot of code that declared text in a string format like this:
let title = “Internationalize Your Apps”
If you want to create an internationalized app, you instead have to use NSLocalized String, which gives you a look-up table for the string you wish to use. It also allows you to attach a comment (as seen in the second example below) that gives you a Strings File that has both the original text you’re looking for and the direct translation:
let title = NSLocalizedString(“Internationalization Your Apps”,”Recommendation”)
“Internationalization Your Apps” = “Internacionalizatión de sus Aplicaciones”;
Doing this NS Localized String essentially internationalizes your application, and at any time allows you to easily add new content.
One of the challenges with internationalizing iOS occurs in the Storyboards. If you use Interface builder, it is driven by a WYSIWYG editor that requires you to add text in moveable boxes. For things like this, internationalization requires a couple extra steps.
For WYSIWYG, you select localize on the menu, and it creates an object to string mapping. Since this is generated by the WYSIWYG editor and interface builder, you don’t have a lot of control over what things are called. To internationalize, it creates an ObjectID and the .text for it.
While there are a few alternative methods, such as creating a custom class that allows you to set it in code, all of them still require extra steps to complete.
Android Text Assignment
Android tends to be a bit simpler than iOS, because there is an auto-extraction tool that automatically extracts strings for you. Android’s guidelines also tell you to complete an internationalization setup from the beginning and will display warnings if you input strings manually instead of putting them into string files.
For internationalization, it’s important to make sure that your font choice supports Unicode. If you choose a Unicode font to begin with, then this is something you won’t have to worry about later.
Images can be problematic for internationalization. Images that appear to have text typically come in two different formats:
Images “With” Text: For any image that has text on it, you must create a new asset with the text overlaid in it for every language that is supported by your app. The good thing about this is:
- You have full control over the exact layout for each language
- Your image looks the same on all screen sizes
- You can control the languages at app installation or change languages at any time
However, the downsides include:
- It is cumbersome to maintain the images
- It creates larger app download sizes
- You have to build resource management for these images
Images “Without” Text: This is the option I prefer. In this situation, the images have no text; rather, a flexible UILabel or TextView is overlaid and constraints are defined to make it scalable within a certain space. The benefits of this method include:
- You only have one image asset and text overlay layout to update
- There are no additional downloads required
- There is no additional resource management required
However, the downsides of this method include:
- You have dynamic layouts for text on images, so the image and text may look different depending on the language.
- The device screen determines how the text gets drawn, so it may look different on different screens.
If numbers are not localized, a number formatter can be used to update numbers into whatever format is defaulted on the device. Numbers are represented differently in different countries, so if you use the device’s default locale, the commas and periods will be in different locations based on the device language.
It is important to use local number styles, so your users feel like you are meeting their needs, rather than confusing them with US-specific formats. For example, the codes below show how the number (195325.75) will be updated for both Italy and Japan.
Balance = 195325.75
Non_loc_balance = NumberFormat.getInstance().format(balance)
Locale = getDefaultLocale()
Loc_balance = NumberFormat.getInstance(locale).format(balance)
Within your app, you can also convert numbers into specific currencies. Typically, the operating system will represent the currency in the format defaulted on the device. For example, in the code below you can see how the balance has been shifted to two separate currencies:
Balance = 23500
NumberFormat.getCurrencyInstance(Locale(“en”,”US”)).format(balance) // $23,500.00
NumberFormat.getCurrencyInstance(Locale(“sk”,”SK”)).format(balance) // 23,500,00 €
You can also apply these converters to user inputs. If you have an app that requires user input, you can run that input through a formatter on the backend to universalize user answers into one standard number or currency format.
Dates and times tend to be one of the largest challenges for internationalization during app development. Unless there is a specific format that a client wants their app to use for dates and times, it is best to put in the work at the start of the project to internationalize; otherwise, localization in the future takes a lot of work to complete.
iOS Date/Time Localization
If you are internationalizing your date and time, you want to use ICUs, which are pre-defined, short representations of how you want the date and time represented. The ICU can then be represented for each region or locale. In the example below, the ICU defines the date set up for the United States and the United Kingdom:
let template = “yMMMMd”
let dateString =
fromTemplate: template, options: 0, locale: Local.current)
//English (United States) = “MMMM d, y”
//English (United Kingdom) = “d MMMM y”
Android Date/Time Localization
Localizing the date and time in Android apps also uses ICUs. To localize to different regions, you would use the following code:
dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
dob = LocalDate.of(1991, Month.OCTOBER, 13)
formattedDob = dob.format(dateFormatter)
//Canadian French: 91-10-13
As you can see, the year-month ICU converts the date into the typical format for various regions.
When creating the layout for your app, it’s important to consider how various languages will fit with the layout. Both iOS and Android have evolved to constraint-based layouts, which allows you to build your interface to dynamically adapt to different screen sizes. This evolution has also helped account for the different string lengths and the read directions of various languages.
For languages that have different read directions, Android now uses “start” and “end” for alignments, rather than “right” and “left” alignments, to ensure that the user interface wraps correctly depending on the language used.
To account for changes in language lengths, it’s important to make sure any text boxes readjust font size, or can scale or word wrap accordingly. If using word wrapping, it’s important to understand the difference between line breaks and word wrapping, as line breaks will give hard breakpoints that don’t account for changes in language length.
Once your layout is complete, it is always important to test it on multiple devices with various screen sizes.
Pseudolocalization is a way to change text live within an app to see how different languages will fit and flow. You can also use pseudolocalization to double the text length or flip the text on your screen to test how the app works with different language lengths and read directions.
How to Enable Pseudolocalization on Android
To enable Pseudolocalization on Android, you enable the pseudolocalization and then use your settings to change your app pseudolanguage.
As long as you have developer mode enabled on your device, any app that has debugging will allow you to use pseudolanguages.
How to Enable Pseudolocalization on iOS
For iOS, when you actually build the app to push it on your device, you can explicitly choose the language you want it to render in. iOS supports double length, right-to-left, accented, and other combinations of language examples for pseudolocalization.
There are also tools available that allow you to create a dummy language for the same purposes. The main difference between iOS and Android is that with iOS, you have to specifically re-install the app if you want to test a different language.
As you develop and debug your app, I recommend that you always have a pseudo locale active in this manner. It is the best way to find bugs and issues as you develop your app to prevent issues down the road.
If you get into the habit of building your apps with internationalization in mind, it will save you countless hours in the future. For the Soundwater project, what could have been a one-day task, but instead spanned multiple days because internationalization had not been built into the app originally.