Recently, I had an idea for an experiment to see what kind of impact localization had on app downloads from various markets. One of my casual games for Windows 8 and Windows Phone 8.1 (Piano Tap) had decent momentum in English-speaking markets, but not a lot of activity elsewhere. So, I put out a call for assistance to help translate strings into new languages.

Many thanks to those who contributed: David Nielsen (Dansk), Hervé Thouzard (Française), Kenneth Truyers (Nederlandse), Sara Silva (Português), митрий Кульшицкий (русский), and Seth Juarez (Español). Note: I’d love to include even more languages or regional translations, if you have something to contribute (especially non-Latin languages).

How

Piano Tap is a Windows Universal App written in HTML5/JavaScript. Since I had written Piano Tap entirely in English, strings were embedded in the app a couple of different ways and some refactoring was necessary to prepare for localization.

Some strings were embedded directly in the HTML itself, like the text for the “Play Again” button:

< button data-bind ="click: replay" > Play Again </ button >

To provide translations, I needed to extract these strings into a resource file (more on that below), and then use a WinJS data-win-res attribute to indicate the string’s resource ID to be used as the source for the button’s text content:

< button data-bind ="click: replay" data-win-res ="{textContent: 'play_again'}" ></ button >

Note that for the app to crawl the HTML and make the necessary substitutions, you must call WinJS.Resouces.processAll() (I did it in the handler of the Loaded event of WinJS.Application):

app.onloaded = function () { WinJS.Resources.processAll(); }

Other strings were embedded in the JavaScript code, like the object literals that contain configuration for the game and menus:

{ id: 'NeedforSpeed' , name: "Need for Speed" , max: 50, ...

For these, I used the WinJS.Resources.getString(id) function to retrieve the string from the resource file:

{ id: 'NeedforSpeed' , name: WinJS.Resources.getString( 'NeedforSpeed' ).value, max: 50,

Resource Files

For each language being translated, you will need to create a folder and a resources.rejson file in your project. Since my app is a Universal App, I did this in the Shared project:

At runtime, the user’s machine will try to find a resource entry that is as close as possible to the current culture code. For example, someone in Australia will likely have “en-AU” as their current culture code. If I had provided a resources.rejson for en-AU, then those strings would be used. Since I didn’t, it will look for a neutral culture for that language (just “en”). And, it appears that if a neutral culture does not exist, but an alternative locale does for that language (such as “en-US”), then it will choose that resource. Such is the case for Portuguese in my app, where I have a resource defined for the locale pt-PT, but this is used by machines in Brazil (pt-BR) as well instead of reverting to the the app’s default of en-US.

A resources.rejson file contains a strict form of JSON with key/value pairs:

{ // Button label to take the player back to the menu screen "menu" : "Menú" , // Button label to restart the game that the player just finished "play_again" : "Jugar de Nuevo" , // Button label to share a high score with other people "share" : "¡Compartir!" , // Name of a game. Comes from a movie quote in Top Gun "I feel the need for speed!". // Can be translated as "Need to go very fast", etc. In the game, the player must // tap as fast as they can to get to the end. "NeedforSpeed" : "¡Necesidad de Velocidad!" , // Instructions to the player displayed before starting the game "NeedforSpeed.Description" : "Correr hacia 50 sin tocar los azulejos Blancos" ,



...

The key is the Resource ID, which is used by WinJS to locate the strings (see the “How” section above). The value part is the translated string itself. By suggestion, I added comments to the file in order to provide hints to my translators about the meaning of each string, since a lot of times, slang or colloquialisms will not have a direct equivalent in a different culture.

In addition to the strings used within the application, I also discovered that you will need text used in the app store listing to be translated as well (more on this in the next section). I simply added these at the bottom of the resource file as comments so that I could just copy/paste when needed:

// Also need these for the store listing: // "Tap your way through songs as quickly as you can by only touching the black tiles as they scroll past. Sounds easy enough, right?" // "Pulsar a través de varios canciones rapidamente solamente tocando los azulejos negros. Suena fácil, ¿no?" // "Three different games" // "Tres juegos diferentes" // "Piano and simple beep sound effects" // "Piano y bips simples" // "Three different games, and your choice of piano notes, a simple beep, or no sounds." // "Tres juegos diferentes y la opción de notas de piano, un bip simple, o la ausencia de sonidos." // "Click only on the lowest black cells. How fast can you go?" // "Hacer clic en las celdas negras más bajas. ¿Qué tan rápido puedes ir?" // "If you click a white cell, then the game is over." // "Hacer clic en una celda blanca causará el fin" // "Touch screen works best, but mouse and keyboard are equally supported" // "Las pantallas táctiles funcionan mejor pero tambien soporta ratón y teclado"

Store Listing

So far, localizing my app was pretty simple since I just needed to do string replacements. Even if I had images that needed to be localized, I feel that it would be a pretty straight-forward process that would not take a lot of time to retrofit. What I was not prepared for, though, was how tedious the store submission process was once you added multiple languages.

Note: If you’re reading this, MSFT folks in charge of the Store, I would *love love love* to see something like having special key/values in the resouces.rejson file be used to auto-populate the store listing for each language.

Essentially, each culture requires its own store listing. When you upload an appxbundle package, the store will detect all of the languages and require you to provide descriptions, screenshots, etc. for each of those languages (sometimes, twice for a given language – once in the neutral code, and once in a default locale for that language such as “Spanish” and “Spanish (Spain)”).

If you later add an additional language, it will appear as “Not Started” in the Description step, preventing you from submitting your update until the required information is provided.

This is where docking Visual Studio and my web browser side-by-side proved handy, since I could then just copy/paste as needed:

Notice that I said above that screenshots are required for each language, which just makes sense since I would not want to see an app listing with screenshots in Chinese or some other language that I could not read. To capture a screenshot in another language (and for general testing in general), you will need to temporarily configure your machine to use that language or culture code.

First, add new languages to your machine using the Language Control Panel:

Then you can change the order of the languages to place the one you need at the top. Launch your app, and the strings will appear translated. Note that this is also useful to browse the Windows Store in other locales (to see your app’s store placement in the Top lists, etc).

The Impact

So, what has the impact been for these new languages? Is it noticeable?

Since I use MarkedUp to provide some analytics and crash reporting for my apps, it is easy to see the installs by country over time:

It’s very clear on these spark graphs when translations were added for France, Belgium, Russia, Netherlands, and Portugal. Other countries where I would have expected a bump (such as Brazil) produced only a minor enhancement. Note: The Spanish translation was just added today, so I’ll be watching over the next few weeks to see what impact that has for Spanish-speaking countries.

Conclusion

Localizing an application is not a trivial process, even though my experiment was just simple string translations. You need to find translators who understand both your native culture as well as their own culture so that the resulting localized text is appropriate to the context. Even though I had volunteers from the community to assist me, this will not always be the case and professional services will likely be required.

In addition, you need to consider things like date and number formats, as well as left-to-right versus right-to-left languages. If you use iconography, know that certain symbols are not universal (for example, a Stop Sign in the USA is a red octagon with a white border, while a Stop Sign in Japan is red triangle with a white border). All of these things combined are necessary to completely localize an app. However, localization of your app will certainly be appreciated by users in those other locales.

Before taking on the effort, it is wise to consider the return on your investment. My experiment showed a definite bump in France, who all but ignored the app before I provided a French version (but now, it is #1 within its subcategory, and continues to move up in the rankings for Games in general). But, is the audience in France large enough to justify the time required and any necessary expenditure that may have been needed to perform the localization? These are hard things to estimate without actually taking the plunge.

The conclusion for me, personally, based on this experiment is that the bump in downloads due to translation is not worth investing money upfront (especially for a free app that only makes tens of dollars in ad revenue). Perhaps for a different app, maybe a Top 25 in its category with tens of thousands of downloads where every 100 additional users produces a measurable increase in ad revenue, would I consider going through the effort again (and even paying for it this time!).

But, for now, I’ll just continue to dream of having an app that proves to be that popular… and save my money for the next burrito at Chipotle.