Part 1: Hot localisations update

Dmitry Marushchenko
Bumble Tech
Published in
10 min readNov 3, 2017

--

I am going to start off my article with a confession: I am a little bit jealous of people whose native language is English. In the modern world English, has become the international lingua franca, the de facto standard. Practically every popular application supports English. It is unlikely that English speakers have ever downloaded a long-awaited game from the App Store, only to discover to their disappointment that it doesn’t support their native tongue.

Badoo language selector

Games and applications are more attractive and easier to use in one’s own language, and for some people, their native tongue is the only language available to them. That’s why companies operating on the international market translate their applications into the most widely spoken languages.

This article is intended for those developers who have already thought about localising their application, have taken the first steps in this direction and have encountered their first problems. In the first part of the article we shall briefly describe approaches to localising applications. We shall then focus in greater detail on localisation from the client-side point-of-view, describe its negative aspects and then give our suggestion as to how to overcome these. In the second part of the article we shall discuss in detail how to implement our approach on mobile OS.

Localisation using built-in tools

Mobile applications may be divided into three groups, depending on where they save strings requiring translation:

● All the strings come from the server

● All the strings are stored in the application source code

● Some of the strings are stored in the application, and some come from the server

Applications developed within our company fall into the third group, but the approaches described in this article can also be applied to the second group, with the proviso that a given application actually has a server component. This is why this article will focus on the second group of applications.

Popular mobile OS have in-built tools for localising applications. For example, in Android you can create a resource file with strings per each supported language, and the OS will automatically try to find the correct translated string first from the file for the current locale, and only if there is no such string there (or the file itself does not exist) — it will fall back to the default file:

res/values-en-rGB/strings.xml → res/values-en/strings.xml → res/values/strings.xml

ℹ️ Here and below we will use examples for Android, although a similar scheme used in IOS.

This is the approach used in most applications due to its simplicity and convenience. But just because something is convenient for developers doesn’t mean it is convenient for copywriters and translators. It is difficult to imagine a translator who would commit changed translations straight into git. For this reason, many companies (including, of course, Badoo) are inventing systems to make life easier for translators.

Advanced localisation

Screenshot from our translation management system

Let’s imagine what we might want from such a system.

It should be easy and convenient for developers to add strings in such a system during development.
Two approaches are possible here: either the developer adds non-translated strings to the resource file and the system automatically pulls this file from the VCS and adds the newly created strings to its database, or, conversely, the developer adds strings to the system database via a convenient interface and the system then generates default (not yet translated) resource files for the application and pushes it to the VCS, so it’s immediately available for the developer to use in the source code.

Translators can see added strings on the system and are then able to translate them into other languages.
It is preferable if translators are able to understand the context in which a given phrase is used. Ideally this might be a screenshot from the application in which the phrase occurs.

The system is able to operate with substitutions, and also with strings which depend on numbers (“You have 1 friend” vs “You have 15 friends”) and gender.

Translated phrases are pushed back to the application during the build process.
Resource files with strings are automatically generated based on completed and ready-to-use translations: one file for each language (res/values-en-rXX/strings.xml), plus the default resource file (res/values/strings.xml — this contains strings which haven’t been translated into any language, in the form in which the developer added them).

By the release date, translations are already available at least in the main languages.
This is achieved by translators and developers working in parallel. Additional translations may be added in subsequent releases.

Life after release

Strings included in the application source code have a major drawback: to update them you have to release a new version of the application. Bearing in mind that sometimes the approval process for a new version can take up to a week (Hello Apple!), this is not always the best way.

So, the product team needs to have a way of altering text in applications that doesn’t require waiting for a release. For example, before St. Valentine’s Day in countries which celebrate that holiday, we changed the line, “Looking for: dates,” to, “Looking for: Valentine’s dates.”

In addition, there are many situations in which a translation needs to be updated urgently. Sometimes a translated phrase gets too long and doesn’t fit into the designated space. As a result, the interface gets displaced or the phrase gets truncated. Sometimes translators misunderstand the context of a phrase and the translated phrase looks inappropriate. Or, for example, in a given country, a new law to regulate payments has just been published, which means that the text on the payments page needs to be amended.

And sometimes you can’t avoid weird things happening. During the redesign of one of our projects, the font size of the title of the notification about a new contact was significantly enlarged, but the copy wasn’t updated. The French word ‘connexion’ (Connection) above the new contact’s picture was truncated to ‘con…’. Those who are curious are invited to look up the relevant translation of this word and imagine the reaction of French users on receiving such a message.

Obviously, this sort of conflict requires immediate action to be taken and waiting a week for a release to come out is not an option.

Applications receiving all strings from the server instead of using local resources do not experience this problem, but it doesn’t always make sense to send kilobytes of text over the network every time the application is used, especially if the text in question rarely changes. There are also applications which need to be able to operate offline (this includes many games, for example).

‘Hot’ updating of translations

Of course, this is when the idea comes to mind, “Why not combine these approaches?” We could use translations saved locally in the actual application, but be able to background update them when there is an internet connection available.

If you already have a translation system similar to the one we have described above, then with a few tweaks it can solve this problem as well. Here are the changes required:

The state of translations at any given moment in time must be described by a version number.
Each change to the translations must increase the version number (if you add new string to version 25, then you are onto version 26; translate this string into Portuguese and you progress to version 27 and so on). This is similar to how the version control system behaves: any change to translations is basically a commit. In practice, a translation system may be constructed on top of the version control system and the functionality we have described may be obtained ‘out of the box’.

We would like to be able to mark some versions as “ready for release to production.”
This is not a necessary condition, but it is very helpful. For example, imagine you have decided to rename the main character in a game, changing her name from Anna to Helen. Obviously, you need to change all the strings in which her name occurs, and only then you can release the new translation version, otherwise it could end up that a given application has an inconsistent translation, in which she is Anna half of the time and Helen the other half. This situation may be avoided if changes to the translation are introduced to the database not on a one-by-one basis but in large chunks, however the problem then arises of conflicts between various different changes, if there are several translators involved (which there usually are). On the other hand, the version control system may also come in useful here.

The system is able to generate diffs between two translation versions.
Diffs can be large and generating them may take a long time, so it wouldn’t be a bad idea to create a queue for generating diffs, and for diffs which have already been generated to be saved somewhere, instead of calculating them each time ‘on the go’.

A mobile app should store the current translation version and be able to provide this version to the server when an active internet connection is available:

AppStartup {
translation_version: <client_version>, // current client version of
the lexemes
… // other startup parameters
}

Having received a given translation version from the mobile app, the server checks whether a new version is available. If a new version is available, the server checks whether a diff between these two versions is ready. If it isn’t, then a task is created in the diff generation queue (if, of course, there isn’t already a task of this nature in the queue).

If the diff is available, the server notifies the application that a new translation version is available. The app can now request a diff and save it locally. Otherwise the same process will repeat on the next startup, and by that time the diff hopefully will have been generated already.

An example is given below of what a server response with a diff might look like. It includes all the strings (historically we call these ‘lexemes’, although this is not an entirely correct use of the term) which have been altered since the current translation version in the application.

Lexemes {
server_version: <server_version>, // current version of the lexemes
lexemes: [
Lexeme { // simple lexeme
key: "badoocredits.profile.button.topup",
mode: LEXEME_MODE_SIMPLE,
value: LexemeValue {
text: "Refill",
}
},
Lexeme { // number-dependant lexeme
key: "cmd.deleteselected",
mode: LEXEME_MODE_NUM_DEPENDANT,
value: LexemeValue {
plural_forms: [
PluralForm {
category: PLURAL_CATEGORY_ONE,
text: "Delete just this one",
},
PluralForm {
category: PLURAL_CATEGORY_ZERO,
text: "What to delete?",
},
PluralForm {
category: PLURAL_CATEGORY_MANY,
text: "Delete all %d",
},
PluralForm {
category: PLURAL_CATEGORY_OTHER,
text: "Delete just these %d",
}
]
}
}
]
}

As you can see, the phrases may be simple, or may depend on a number. In the case of phrases which depend on a number, substitutions may be used (although this is not obligatory). In the case of strings which depend on a number, we use 6 forms: zero, one, two, few, many and other. In English, some of these forms are the same (“You have two books” vs “You have many books”), but there are languages where all 6 are used:

Welsh language, the phrase is “N dogs, N cats”

You can read up about this in more detail here: http://cldr.unicode.org/index/cldr-spec/plural-rules

A/B testing

The system for updating translations “on the fly” has another unquestionable advantage. Besides correcting mistakes in translation and other localisation problems, it gives us a chance to A/B test various translation options.

Lexemes {
server_version: <server_version>, // current version of the lexemes
lexemes: [
Lexeme { // Lexeme with A/B variants
key: "popularity.share.title",
mode: LEXEME_MODE_SIMPLE,
value: LexemeValue { // default variant
text: "You're popular!",
},
test_id: "popularity_test_android",
variations: [
LexemeVariation {
variation_id: "control",
value: LexemeValue {
text: "You're popular!",
}
},
LexemeVariation {
variation_id: "rock",
value: LexemeValue {
text: "You rock!",
}
},
LexemeVariation {
variation_id: "cool",
value: LexemeValue {
text: "You're cool!",
}
},
]
}
]
}

The example above contains the phrase, “You’re popular!”, which can be used, for example, as the title of a profile sharing screen. Let’s say, in order to increase the click rate for this notification, our product team decided to carry out an A/B test and to see how changes to the text affected the percentage rate of clicks on the notification. Without a system for updates “on the fly”, they would have had to create a task for the app developers, then release a new version (another week of waiting) and only then test it. With the new system, they can perform the test themselves, check the result and then change the text in all applications to the winning variant.

Conclusion

Once again in brief: keeping translations in the application source code means you lose the opportunity of changing them quickly. We are proposing the idea of requesting updated translations from the server in the background, which gives you the opportunity to correct translation mistakes or to test various wordings.

If you are interested in our approach, and you are already thinking about implementing it in your application on IOS or Android, then the second part of this article (coming soon) is for you. In it we will tell you how to amend your mobile application code so that it can use the received diff while avoiding the labour intensive and drawn-out task of re-writing all the places in the code which use translations.

--

--