How to mess up UI strings

Marta Bartnicka, inspired by Bogna Czyżewska

I have put together a few examples of placing user interface strings in code. After working several years in the localization industry, all professional translators have a feeling that there must be some reason why programmers usually manage the strings wrong: in such a way that makes it hard not only to translate UI, but – in the first place – to extract the strings and to put them back.
The reason exists, and can be found in pretty every programming course, be it academic, bootcamp, or self-study. And while print ("Hello, World!") is a cool way to get started, you can really do better with UI strings management – not only for the future translators, but also for your future self maintaining the code (and resources; we’ll get to that).

NOTE: Programming examples in this article are written in Cython++.

0. Level disaster: just add them in code

Because this is what you learn in your first programming lesson!

English should be OK for everyone

Code

if situation == welcome:
    print "Welcome!";
else:
    print "Goodbye!"

Resources

What???

Need translation

Code

if situation == welcome:
    if language == en_US:
        print "Welcome!";
    if language == es_ES:
        print "¡Hola!";
    if language == pl_PL:
        print "Witaj!";
else:
    if language == en_US:
        print "Goodbye!";
    if language == es_ES:
        print "¡Adiós!";
    if language == pl_PL:
        print "Do widzenia!"

Resources

Busy typing the conditions, no time for that!!!!!1111

1. Level in the know: use resource files

Whatever made you separate UI strings from code, you are going in the right direction!

English should be OK for everyone

Code

// from_resources picks string from a resource file by key
if situation == welcome:
    print (from_resources (UI.resources, "welcome_msg"))
else:
    print (from_resources (UI.resources, "goodbye_msg"))

Resources

UI.resources

welcome_msg = Welcome!
goodbye_msg = Goodbye!

Need translation

Code

// from_resources picks string from a resource file by key and language 
// other than that, no changes to code!
if situation == welcome:
    print (from_resources (UI.resources, "welcome_msg"))
else:
    print (from_resources (UI.resources, "goodbye_msg"))

Resources

UI.resources for English

language_code = en_US
welcome_msg = Welcome!
goodbye_msg = Goodbye!

NOTE: Only the resource file goes to translation! This applies to all further examples.

UI.resources for Spanish

language_code = es_ES
welcome_msg = ¡Hola!
goodbye_msg = ¡Adiós!

UI.resources for Polish

language_code = pl_PL
welcome_msg = Cześć!
goodbye_msg = Do widzenia!

2. Level I got the power: concatenate

This was in your second programming lesson. Or in the third.

English should be OK for everyone

Code

// from_resources picks string from a resource file by key
// user_name picks user name
if situation == welcome:
    print (from_resources (UI.resources, "welcome_msg") + ", " + user_name + !")
else:
    print (from_resources (UI.resources, "goodbye_msg") + ", " + user_name + !")

Resources

UI.resources

welcome_msg = Welcome 
goodbye_msg = Goodbye 

Results:
Welcome, Tina Turner!
Goodbye, Tina Turner!

Need translation

Code

// from_resources picks string from a resource file by key and language 
// other than that, no changes to code!
// user_name picks user name
if situation == welcome:
    print (from_resources (UI.resources, "welcome_msg") + " " + user_name + !")
else:
    print (from_resources (UI.resources, "goodbye_msg") + " " + user_name + !")

Resources

UI.resources for English

language_code = en_US
welcome_msg = Welcome
goodbye_msg = Goodbye

Results:
Welcome, Tina Turner!
Goodbye, Tina Turner!

UI.resources for Spanish

language_code = es_ES
welcome_msg = Hola
goodbye_msg = Adiós

Results:
Hola, Concha Buika!
Adiós, Concha Buika!

This has wrong punctuation for Spanish

UI.resources for Polish

language_code = pl_PL
welcome_msg = Cześć
goodbye_msg = Do widzenia

Results:
Cześć, Maryla Rodowicz!
Do widzenia, Maryla Rodowicz!

This sounds in Polish as creepy as a talking robot

3. Level enlightened: use variables

Do you want to define an UI string as a whole? Yes, please!

English should be OK for everyone

Code

// from_resources picks string from a resource file by key and inserts variable into placeholder
// user_name picks user name
if situation == welcome:
    print (from_resources (UI.resources, "welcome_msg", user_name))
else:
    print (from_resources (UI.resources, "goodbye_msg", user_name))

Resources

UI.resources

welcome_msg = Welcome, {0}! 
goodbye_msg = Goodbye, {0}! 

Results:
Welcome, Tina Turner!
Goodbye, Tina Turner!

Need translation

Code

// from_resources picks string from a resource file by key and language, and inserts variable into placeholder
// other than that, no changes to code!
// user_name picks user name
if situation == welcome:
    print (from_resources (UI.resources, "welcome_msg", user_name))
else:
    print (from_resources (UI.resources, "goodbye_msg", user_name))

Resources

UI.resources for English

language_code = en_US
welcome_msg = Welcome, {0}! 
goodbye_msg = Goodbye, {0}! 

Results:
Welcome, Tina Turner!
Goodbye, Tina Turner!

UI.resources for Spanish

language_code = es_ES
welcome_msg = ¡Hola {0}! 
goodbye_msg = ¡Adiós {0}! 

Results:
¡Hola Concha Buika!
¡Adiós Concha Buika!

Punctuation is adjusted to Spanish standards

UI.resources for Polish

language_code = pl_PL
welcome_msg = {0}: witamy!
goodbye_msg = {0}: do widzenia!

Results:
Maryla Rodowicz: witamy!
Maryla Rodowicz: do widzenia!

Phrase is reverted to create a message that is acceptable in Polish

4. Level God of I18n: handle risks

Because the world is complicated.

NOTE: For clarity, examples in this chapter only show resources. Code to pick strings from resources is the same as in the last previous example.

Concatenate strings only when necessary

You already know that putting two sub-sentence fragments together, or adding variable text into a phrase, can cause trouble in translation. Please don’t concatenate to save a few bytes only.

Let’s imagine menu options: New file, New table, New tree (whatever they are in a given use case).

It’s technically possible to organize resources like this:

option_1_new = New
option_2_file = file
option_2_table = table
option_2_tree = tree

And then, you could build options from resources like this:

option_1_new + option_2_file
option_1_new + option_2_table
option_1_new + option_2_tree

(Yes, you also need to insert the space somewhere between the strings.)

It’s not a problem to translate these strings into Polish for example:

option_1_new = Nowe
option_2_file = plik
option_2_table = tabela
option_2_tree = drzewo

So what’s wrong with this solution in translations?
Gender.
The nouns „file”, „table” and „tree” may have different genders in various languages, and in many of these languages the gender of a noun also implies gender of an adjective.

In Polish, the only correct translation of the entire options would be:

Nowy plik
Nowa tabela
Nowe drzewo

The translation I randomly chose for „New” – „Nowe” – only matches the last option, „tree” („drzewo”), but is incorrect for the other two.
There is no single translation of „New” that would fit in all cases, and there is no way to translate the nouns in a gender-neutral way. The only reasonable translation you get when translating phrases „New file”, „New table” and „New tree” as whole.

Now, was it really worth it? Knowing that translation may be impacted, does it pay off to extract „New” into a separate string to reuse it 2 (two) more times? I say no.

If the list of objects attached to „New” were longer, some 10 for example, then it would actually make sense to cut the string in two. And there is a work-around that takes into account any gender-aware languages and allows to translate „New” in a neutral way that fits in all cases; it’s a bit stiff an unnatural, but works and is both understandable and acceptable for such cases:

option_1_new = New item:
option_2_file = file
option_2_table = table
option_2_tree = tree
option_2_collection = collection
option_2_list = list
...

Polish translation:

option_1_new = Nowy element:
option_2_file = plik
option_2_table = tabela
option_2_tree = drzewo
option_2_collection = kolekcja
option_2_list = lista
...

This solution makes sense for a list of 10 or so objects, but would look rather awkward for a list of 3.

NOTE: Beside gender, there are other factor that disables building sentences and phrases from fragments: numbers, flexion and more. And Polish just one of good examples because it is a morphologically rich language.

Be prepared that something may still not work in some language

In the earlier code and resources sample, the phrase „Welcome, X” does not translate well to any form of welcome that is actually used in Poland; to get something merely acceptable, translators had to revert the order of string and variable and change punctuation.
A question that experienced localization professionals would ask, is: Do we really need to mention the user name in this message?

  • If yes, then we communicate to translators that variable is mandatory, and they use their magic to rephrase the message so that it sounds, at least, acceptable in their language.
  • If not (for example: the name of an user is mentioned earlier/later on the same screen, page or message), then we should allow the variable to be deleted in translation – in the languages where the entire message is hard to translate. In other words, we make variable not mandatory:

UI.resources for English

language_code = en_US
welcome_msg = Welcome, {0}! 
goodbye_msg = Goodbye, {0}! 

Results:
Welcome, Tina Turner!
Goodbye, Tina Turner!

UI.resources for Spanish

language_code = es_ES
welcome_msg = ¡Hola {0}! 
goodbye_msg = ¡Adiós {0}! 

Results:
¡Hola Concha Buika!
¡Adiós Concha Buika!

UI.resources for Polish

language_code = pl_PL
welcome_msg = Witamy!
goodbye_msg = Do widzenia!

Results:
Witamy!
Do widzenia!

Ask for advice

What if none of the above solutions fit your problem? Please put together the code, resources and your use case, list the issues that you encountered in localization, and mail kontakt@localization.pl. We will respond as soon as we can to propose a solution, or at least we will point you in the right direction.