
Whenever you need to write tests that check for a text in several languages, you don’t need to write one test for each language that you check for. Instead, you can use property files to store translations and just write one test that will check the text across all supported languages. Read below to see how and checkout my GitHub project for the examples presented in this post.
A property file in Java is nothing more than a file that contains key/value pairs. In the case of translations, you can think of the key as the identifier of the text that you need to check, and of the value as the corresponding text in the language for which you built that property file.
Creating the property file
The property file for each language that is in scope of your testing needs to have a name that reflects that language. For example, you could choose “en” as the name for the English language, and “de” for the German file.
Basically you need to follow some sort of localization convention, and you can check out the Java locale support documentation for an idea of what to name these files. This documentation suggests going further than just naming the English file “en”, by specifying whether you are referring to British English or American English. In this case you might have a “en_GB” file and a “en_US” file. But if you only need to generically test English texts, you can simply name the property file “en”.
All of the translation files need to have a .properties extension, therefore you will create the “en.properties” file. All these file need to be placed inside your Java project, under the “src/main/resources” folder. Here, you can further create other folders, to reflect what you are doing.
This approach is particularly useful because it allows to store all translations related to a feature in the same location, thus easily finding them in case you need to look at or modify them.
Adding the key/value pairs
Now that you have the files, you need to add the translated text in all of these. First you will need to define the key that will represent the text. The recommendation for key names is to be all lower case, and if you need a separator inside the key name, you should use a dot character (“.”). The translated text will be the value. A key/value pair will look like key=value, each on a new line.
You can use just one word as the key, if that is enough. However if you need to define several keys for related items, you should have a structure like “feature.firstkey”, “feature.secondkey” and so on.
As an example, if the key needs to represent just one color, the key can be the name of the color in English. For example blue. An entry for blue in the English property file will be blue=blue. The same key needs to be present in all the translation property files you will use, together with the translated value. So, for German you would have blue=blau, for Spanish blue=azul, and so on.
Now, if you want to define more colors, you can group them like: color.blue, color.green, color.red. Therefore the English file will contain, each on a new line and without separators at the end of the line: color.blue=blue, color.green=green, color.red=red. The corresponding Spanish file would contain: color.blue=azul, color.green=verde and color.red=rojo. You can use this same convention when you have several words as a key and you need a separator for them. For example, if your key represents the “grocery store timetable”, you can name it grocery.store.timetable.
Reading from the property file
Now that you have the translations in place, you need to have a way for your tests to read them. For this purpose you can create a method as the one below:
public String getTranslation(String key, String language) throws IOException {
Properties prop = new Properties();
FileInputStream input = new FileInputStream("src/main/resources/languages/" + language + ".properties");
prop.load(new InputStreamReader(input, Charset.forName("UTF-8")));
input.close();
return prop.getProperty(key);
}
The method i wrote here opens the file and reads the value from the file, corresponding to the key it receives, which is the first parameter of this method. The file is determined from the second parameter, which represents the name of the file you defined earlier (without the .properties extension). You can see that i wrote the path to the language files inside the method, that is why you only need to send the file name to this method. In my case, the translation file is inside the src/main/resources/languages folder. If you store your translations elsewhere, this needs to be reflected in this method.
A usage example for this method would be:
getTranslation(key, language);
Let’s say, for key color, and language German, this call would be:
getTranslation("color", "de");
Writing the test
Ok, so now you have the property files, and a way to read them. How do you write just one test to check for the translated text? Well, looking at the getTranslation() method, and thinking that in a test you need to check for one property in each language where it is available, that means that when calling the getTranslation() method, the key will always be the same (when you test for that particular key). But you will need to run through all the available languages. For this purpose you can use a data provider which will pass the language to the test. Therefore a test would look something like:
@Test(dataProvider = "languages")
public void translations(String language) throws IOException {
doSomethingWith(getTranslation("keyNameHere", language));
}
Read below for a full example.
Example
Let’s say that your translation testing is related to English, German, Spanish and Norwegian. Therefore, corresponding files need to be created. In my GitHub project, i created the following files: en.properties, de.properties, es.properties and no.properties in the following location: https://github.com/iamalittletester/learning-project/tree/master/src/main/resources/languages.
The texts that need to be checked based on these files are: in one test an “i love testing” text, translated into all languages mentioned above, and a superheros’ first name and last name. For this task, the following three keys were defined: testing, superhero.firstname and superhero.lastname.
The tests themselves are located under https://github.com/iamalittletester/learning-project/blob/master/src/test/java/translations/TranslationsTest.java. They are not actually testing anything, but are doing some simple outputs to the console of the translated texts for the specified languages. Just to give you an idea of how to write such a test.
To start with, the data provider contains the four languages for which i created property files:
@DataProvider(name = "languages")
public Object[][] languages() {
return new Object[][]{
{"en"},
{"de"},
{"es"},
{"no"}
};}
Then, i wrote a test for checking the translations in all these languages for the key “testing”:
@Test(dataProvider = "languages")
public void readTesting(String language) throws IOException {
System.out.println("Translation for key 'testing' in language '" + language + "' is : " + getTranslation("testing", language));
}
So in this case, the call to the getTranslation() method receives the testing key, and for each run, a different language. Based on it, the test will read the translation corresponding to this key from all available languages. My test here will just print these results to the console, as follows:
Translation for key 'testing' in language 'en' is : i love testing
Translation for key 'testing' in language 'de' is : ich liebe testing
Translation for key 'testing' in language 'es' is : yo amo testing
Translation for key 'testing' in language 'no' is : jeg elsker testing
The other test in this class will output the first name and last name of superheroes, as they can be found inside the translation files. It is written similar to the first one:
@Test(dataProvider = "languages")
public void readSuperheroes(String language) throws IOException {
System.out.println("For language '" + language + "' - superhero firstname and lastname: " +
getTranslation("superhero.firstname", language) + " " +getTranslation("superhero.lastname", language));
}
The console output in this case is:
For language 'en' - superhero firstname and lastname: steve rogers
For language 'de' - superhero firstname and lastname: kurt wagner
For language 'es' - superhero firstname and lastname: manuel de la rocha
For language 'no' - superhero firstname and lastname: thor odinson
You could this approach for Selenium tests for example, to check that the text on a WebElement equals an expected translated tests. So something like:
driver.get(thePageInTheDesiredLanguage);
.....
assertEquals(element.getText(), getTranslation(key, language));
Why you shouldn’t use your developers’ property files
Chances are that for the same task for which you need to check translations, the developers already created property files structured as i describe in this post, for implementing the feature you are testing.
You shouldn’t use their files to define your expected translations, since that is the text they are using for the implementation. It would mean comparing the actual text with itself. If there is anything wrong in those files, your tests would not pick that up, since their “expected” content would be the “actual” one.
That is why i recommend to keep separate property files for the testing activities.