How LinkedIn does localization in 19 languages ​​in 1 night

Original author: Ursula Huang
  • Transfer
“I want that after the programmer added a new line to the interface, she herself translated into 19 languages ​​and put herself in SVN and was ready for release in the morning” - this is the dream of any developer who has tasted the forbidden fruit of product localization into foreign languages. At Alconost, we help if not to fulfill this dream, then at least get closer to it. Yes, a solution similar to the one described in the article exists not only for LinkedIn developers, but also for mere mortals.

About how the process is built on LinkedIn - in this article (Attention - Java).

Internationalization has a critical impact on the operations and development of LinkedIn, which is now available in 19 different languages. To speed up work with localized texts, an international engineering team has developed a system for dynamically introducing translated content strings into working services.

The new system allows us to quickly modify content: make quick edits and changes without involving developers, rebuilding and restarting the service.


All content is initially written in English by our developers and product managers. Typically, text that needs translation is contained in a properties file:

add_to_network__send_invitation=Send invitation
add_to_network__add_message=Add a personal message
add_to_network__user_message=I'd like to add you to my professional network.\n\n- {0}

Then our full-time localization team translates the content into different languages. Here, for example, is the same properties file translated into Italian:

add_to_network__send_invitation=Invia un invito
add_to_network__add_message=Aggiungi un messaggio personale
add_to_network__user_message=Vorrei aggiungerti alla mia rete professionale.\n\n\n\n-{0}

In order for the text to appear on the page, we use internationalization functions in the templates, which access the properties file for the user locale using the specified key.

The old way

Before implementing dynamic language loading, the system collected all properties-files into one artifact together with the application code (WAR). This led to certain problems:

  1. Adding translations meant rebuilding and restarting the entire service.
  2. If an error occurred in the translation, you could not just go back to the old version of the text: you had to roll back the application code to the previous version.
  3. Translations can be used in many services - and all of them, in which case, had to be reassembled and restarted.

In a new way

The new system collects and uploads properties files separately from the application code. We introduced the concept of a language pack - this is a JAR file containing all translated content for a specific language. Updated versions of such language packs can be uploaded to the web server at any time. They can also be rolled back at any time if errors are detected.

We have added a new resource loading library, which determines the availability of new language packs and starts using updated translations - all this without restarting the service. If the library does not find a translation, it uses the original English language strings.


The introduction of new translations is only part of the big picture: we also needed to find a way to quickly find new and freshly changed lines, deliver them to translators and include the result of the translation in a language pack. This is what our process looks like:

  1. Engineer sends new or updated English language strings to version control system.
  2. The localization server scans the version control system for changes once a day and issues a request to translate all new and changed lines.
  3. Once an hour, the localization server collects ready-made translations. He checks the new content and then publishes the full language pack with all translations for a specific language in the Repository.
  4. The implementation system once an hour sends updated language packs for testing and loads it into a working service twice a day.
  5. In case the localization team needs to change the translation urgently, it is possible to load translations manually at any time in one click.

backward compatibility

Due to the fact that the dynamic language loading system separates localized texts from code, it became possible to lay out new lines of content in a working service earlier than the application code that uses them. Therefore, now all our internationalization resources should be backward compatible with previous versions to ensure that nothing breaks when introducing new lines.

In this context, backward compatibility means that adding new lines is always safe, but if you modify an existing line, you must keep the number and type of variables unchanged. For example, initially we had this line:

accept_invite__hello_connect=Hello {0}, would you like to connect with {1}?

We can easily change some words:

# This change is backwards compatible
accept_invite__hello_connect=Hi {0}, would you like to add {1} to your professional network?

But deleting, adding or changing the type of variables will violate backward compatibility, since the application code will only provide values ​​for the previous variables:

# This change is NOT backwards compatible!
accept_invite__hello_connect=Hello {0}, would you like to connect with {1}, a coworker at {2}?

We achieve backward compatibility by using code that executes before committing, which prevents resource deletion and at the same time checks that updates to existing text resources are compatible with a set of variables. The code snippet below demonstrates part of our validation logic:

 * Verify that translation isn't missing indexes used with the 
 * original as well as does not specify additional indexes not
 * present in the original.
private boolean verifyPlaceholderIndexMatch(String originalMessageTemplate, 
                                            PlaceholderInfo originalPlaceholderInfo,
                                            PlaceholderInfo placeholderInfo)
  if(originalPlaceholderInfo.keySet().equals(placeholderInfo.keySet()) == false)
    // remove all index numbers in the translation from the index 
    // set in the original to determine what's missing in the 
    // translation
    Set missingIndexSet = new HashSet(originalPlaceholderInfo.keySet());
    // remove  all index numbers in the original from the set of
    // indexes in the translation to determine what extra index
    // we have in the translation
    Set extraIndexSet = new HashSet(placeholderInfo.keySet());
    return false;
  return true;

19 languages ​​are introduced, ahead - all the rest!

The new system accelerated the translation process on LinkedIn: getting new lines into a working service became much faster and easier, and we got the opportunity to gradually roll out translations and roll them back if necessary. And most importantly: our internationalization system can now be scaled to meet the growing number of applications, languages ​​and participants.

About the translator

Translation of the article was done in Alconost.

Alconost localizes applications, games and sites in 60 languages. Native translators, linguistic testing, cloud platform with API, continuous localization, project managers 24/7, any format of string resources.

We also make promotional and educational videos.- for sites selling, image-building, advertising, training, teasers, expliners, trailers for Google Play and the App Store.

Read more:

Also popular now: