
Introduction
I will begin my article with a confession: I envy people whose native language is English a little. In the modern world, it has become the language of international communication, the unspoken standard. Almost any popular application supports English. English-speaking people hardly ever downloaded the long-awaited game from the App Store and were disappointed that it did not support their native language.
Games and applications are more pleasant and easier to use in your native language, and for some people it is generally the only one available. That is why companies operating in the international market translate their applications into common languages.
')
This article is for those developers who have already thought about localizing their application, made the first steps in this direction, and stepped on their first rake. In today's article, I will briefly describe the approaches to localizing applications, I’ll dwell more on localization on the client side, talk about the shortcomings of this approach, and suggest a way to get around them. And in the next part, my colleagues will talk in more detail about the details of the implementation of our approach in mobile OS.
We already wrote about web application localization
here .
Badoo Language ScreenLocalization methods
Mobile applications can be divided into three groups from the point of view of the approach to storing the lines to be translated:
- all lines come from the server;
- all strings are stored in the application itself;
- part of the lines is stored in the application, part - comes from the server.
Applications that we develop in Badoo belong to the third group, but the approaches discussed in the article can be successfully applied to the second group, provided that the application still has a server part. Therefore, I will devote the article to just such applications.
Popular mobile operating systems have built-in tools for localizing applications. For example, in Android, you can create a resource file with strings for each of the supported languages, and the OS will automatically select values ​​for strings first from the file corresponding to the current locale, and only if there is no such value (or not the file itself) will it roll back to default file:
res/values-en-rGB/strings.xml → res/values-en/strings.xml → res/values/strings.xml
️ Later in the article I will illustrate what was said with examples for Android, although a similar scheme is used in iOS.This method is used in many applications due to its simplicity and convenience. But what is convenient for developers is not always convenient for copywriters and translators. It’s hard to imagine a translator who commits translation changes directly to Git. Therefore, many companies (including, of course, ours) invent their own
bicycles to simplify the lives of translators.
Advanced localization
Screenshot from our translation management systemLet's think about what we would like from such a system.
- Developers can conveniently add new lines to the system during the development of features.
There are two possible approaches: either the developer adds lines to the resource file (that is, directly into the source code), and the system automatically pulls the file to itself and adds the created lines to its database, or vice versa: the developer adds lines to the system database via a user-friendly interface , and the system then generates resource files for the application and adds them to the source code. 
- Translators in the system see the added lines and can translate them into various languages.
It is desirable that translators understand the context in which the phrase is used. The best option is a screenshot of the application screen on which this phrase appears. - The system is able to work with substitutions, as well as with strings that depend on gender and numbers (“You have 1 friend” vs “You have 15 friends”).
- Translated strings enter the application during the build process.
Resource files with strings are automatically generated from the finished translations, one for each language (res/values-en-rXX/strings.xml)
, as well as a default resource file (res/values/strings.xml)
- there are strings in it , not translated into any languages, in the form in which the developer added them. - By the time of release, features are already ready to translate at least into the main languages.
This is achieved by parallelizing the work of translators and developers. Additional translations can be added in future releases.
More details about our translation system can be found here and here .Life after release
The lines included in the application code have a significant disadvantage: in order to update them, you have to release a new version of the application. Given that sometimes the process of approving a new version may take a week (
hello, Apple! ), This is not always the right option.
Therefore, the product team needs a way to change the text in the application, without waiting for the release. For example, before Valentine's Day, we changed the text in countries celebrating this day from “Looking for: dates” to “Looking for: Valentine's dates”.
In addition, there are many other situations when emergency updates of translations may be needed. It happens that the translated phrase turns out to be too long and does not fit in the space allotted to it, because of which the interface is leaving or the phrase is cut off. Sometimes it happens that translators misunderstand the context of the phrase, and the translated phrase looks out of place. Or, say, in some country, a new law regulating payments is issued, and you have to change the text on the payment page.

Well, it doesn’t do without curiosities: in one of our partner projects in the process of redesign, the font size for the notification window for a new contact was greatly increased, but they did not have time to update the translation. The French word Connexion (Connection) above the image of the new contact was clipped to Con. Those interested can search for the translation and present what the reaction of the French users was.
Of course, this kind of lining requires immediate intervention, and you cannot wait a week before the release of the release.
Applications that receive all the lines from the server instead of using local resources do not have such a problem, but it does not always make sense to drive kilobytes of text through the Network every time the application is used, especially if this text rarely changes. In addition, there are applications that need to be able to work without connecting to the Web (for example, many applications include such games).
"Hot" update translations
Of course, the idea immediately came up with the idea of ​​combining approaches: use local translations stored in the application itself, but be able to update them in the background when there is an Internet connection.
If you already have a translation system similar to the one I described above, then with minor modifications it can also solve this problem. Here are the changes you need:
- The status of transfers at any time should be described by a certain version.
Each translation change should increase the version (added a new line to version 25 - we believe that we now have version 26; we translated the line into Portuguese - we increase the version to 27; and so on). This is similar to the version control system behavior: translation changes are commits. In practice, you can build a translation system on top of the version control system and get the described functionality out of the box. - Ability to mark versions as “ready for release in production”.
This is not a prerequisite, but very useful. For example, imagine that we decided to rename the heroine in the game: make Anna from Anna. Obviously, you need to change all the lines where the name occurs, and only after that give the new version of the translation to the release, otherwise it may happen that some application will receive a non-consistent version of the translation, where Anna meets in half of the plot, and Elena in the other half . This can be avoided by making changes to the common database not atomically, but in large chunks, but then the problem of change conflicts arises if there are several translators (and this usually happens). On the other hand, the version control system can be useful here. - The ability of the system to generate diff between two versions of translations.
Diffs can be large, and their generation can take a lot of time, so it would be nice to make a line of generating diffs and store ready diffs somewhere instead of counting them on the fly every time.
The mobile application must know the version of the translations it currently possesses, and be able to transfer this version to the server at the moment when it has an active connection.
AppStartup { translation_version: <client_version>, // ... // , }
Having received the translation version from the mobile application, the server checks if the new version is available. If a new version is available, the server checks if there is a ready diff between the two versions. If not, then a task is created in the diff generation queue (if there is no such task in the queue, of course).
At the moment when the diff is ready, the server notifies the application that a new version of the translations is available. Now the application can request diff and save it locally.

Below is an example of how a server response might look. It includes all the lines (we historically call them “tokens”, although this is not quite the correct use of the term), modified from the version that the application currently has.
Lexemes { server_version: <server_version>, // lexemes: [ Lexeme { // key: "badoocredits.profile.button.topup", mode: LEXEME_MODE_SIMPLE, value: LexemeValue { text: " ", } }, Lexeme { // , key: "cmd.deleteselected", mode: LEXEME_MODE_NUM_DEPENDANT, value: LexemeValue { plural_forms: [ PluralForm { category: PLURAL_CATEGORY_ZERO, text: " ", }, PluralForm { category: PLURAL_CATEGORY_ONE, text: " %d ", }, PluralForm { category: PLURAL_CATEGORY_TWO, text: " %d ", }, PluralForm { category: PLURAL_CATEGORY_MANY, text: " %d ", }, ] } } ] }
As you can see, phrases can be simple, and may depend on the number. In phrases depending on the number, substitutions can be used. For number-dependent strings, we use six forms: zero, one, two, few, many, other.
Here's how, for example, the phrase “N dogs, N cats” in Welsh looks like (Wales, United Kingdom):

As you can see, all six forms are used. Read more about it
here .
A / B testing
The translation update system on the fly has another distinct advantage. In addition to correcting translation errors and solving other problems with localization, we get the opportunity to conduct A / B testing of various translation options.
Lexemes { server_version: <server_version>, // lexemes: [ Lexeme { // / key: "popularity.share.title", mode: LEXEME_MODE_SIMPLE, value: LexemeValue { // - text: " !", }, test_id: "popularity_test_android", // id variations: [ LexemeVariation { variation_id: "control", value: LexemeValue { text: " !", } }, LexemeVariation { variation_id: "rock", value: LexemeValue { text: " !", } }, LexemeVariation { variation_id: "cool", value: LexemeValue { text: " !", } }, ] } ] }
In the example above, there is the phrase “You are popular!”, Which can be used, for example, as the text in a notification. In order to increase the click rate of this notification, our product team decided to conduct an A / B test and see how the changed text will affect the percentage of clicks on the notification. Without an update system on the fly, we would have to set the task for the developers of the application, then release a new version (another week of waiting) and then test it. With the new system, analysts can conduct a test on their own, check the result and in all applications replace the text with the winning option.
Conclusion
Once again in brief: if you store translations in the application itself, you lose the ability to quickly change them. We offer the idea of ​​updating translations in the background, which allows you to quickly correct translation errors and test various phrases.
If you are interested in our approach and you are already thinking about how to implement it in your application on iOS or Android, then the second part of the article, which we will publish in the near future, is for you. In it, we will describe how to change your mobile application so that it can use the received diff, but at the same time avoid laborious and long rewriting of all places in the code where translations are used.