Overview
internationalization is the process of designing a software so that it can potentially be adapted to various languages and regions without engineering changes. Localization is the process of adapting internationalized software for a specific region or language by adding locale-specific components and translating text. Due to the length of the terms, both internationalization and localization are frequently abbreviated to i18n and l10n respectively, where 18 and 10 stand for the number of letters between the initial and the final letters of the respective words. is used to store and fetch strings that are locale sensitive. A bundle allows you to easily provide different translations for your application.
Creating Properties Files
MyBundle in the following example. The characters following the base name indicate the language code, country code, and variant of a Locale, for example:
MyBundle.propertiesMyBundle_de.propertiesMyBundle_en_GB.propertiesMyBundle_fr_CA_VAR1.propertiesMyBundle_en_GB, for example, matches the Locale specified by the language code for English (en) and the country code for Great Britain (GB). default properties file, without any language code, country code or variant. In our example the contents ofMyBundle.propertiesare as follows:
additional properties file that contains the translated values. No changes to your source code are required, because your program references the keys, not the values.game=My Super Cool GamenewMission={0}, you have a new mission. Reach level {1}.coveredPath=You covered {0,number}% of the pathhighScoreTime=High score achieved on {0,date} at {0,time}
MyBundle.propertiesand place them in a file namedMyBundle_it.properties. The contents ofMyBundle_it.propertiesare as follows:newMission={0}, hai una nuova missione. Raggiungi il livello {1}.coveredPath=Hai coperto il {0,number}% del percorsohighScoreTime=High score ottenuto il {0,date} alle ore {0,time}
gameis missing from the Italian properties file since we want the game’s name to remain unchanged regardless of the locale. If a key is not found, the key will be looked up in decreasingly specific properties files until found; for instance, if a key is not found inMyBundle_en_GB.properties, it will be looked up inMyBundle_en.properties, and if not found there either, in the defaultMyBundle.properties.Creating a Bundle
I18NBundleclass manages the named strings for a locale after loading into memory the appropriate properties files. Invoke the factory methodcreateBundleto get a new instance of the desired bundle:
MyBundle that are located in the i18n folder, more precisely in “assets/i18n”. You do not need to create the MyBundle subfolder as well. :FileHandle baseFileHandle = Gdx.files.internal("i18n/MyBundle");Locale locale = new Locale("fr", "CA", "VAR1");I18NBundle myBundle = I18NBundle.createBundle(baseFileHandle, locale);
assetManager.load("i18n/MyBundle", I18NBundle.class);// ... after loading ...I18NBundle myBundle = assetManager.get("i18n/MyBundle", I18NBundle.class);
createBundlethen the default locale is used. This is usually what you want..propertiesfile extension in both cases. If a property file for the specified Locale does not exist,createBundletries to find the closest match. For example, if you requested the localefr_CA_VAR1and the default Locale isen_US,createBundlewill look for files in the following order:MyBundle_fr_CA_VAR1.propertiesMyBundle_fr_CA.propertiesMyBundle_fr.propertiesMyBundle_en_US.propertiesMyBundle_en.propertiesMyBundle.propertiescreateBundlelooks for files based on the default Locale before it selects the base fileMyBundle.properties. IfcreateBundlefails to find a match it throws aMissingResourceException. To avoid throwing this exception, you should always provide a base file with no suffixes.Fetching Localized Strings
getmethod as follows:String value = myBundle.get(key);
getmethod corresponds to the specified key. The string is in the proper language, provided that a properties file exists for the specified locale. If no string for the given key can be found by thegetmethod (or its parametric formformat) aMissingResourceExceptionis thrown.formatmethod as follows:
For example, to retrieve the strings from the properties files above your code might look like this:String value = myBundle.format(key, arg1, arg2, ...);
String game = myBundle.format("game");String mission = myBundle.format("newMission", player.getName(), nextLevel.getName());String coveredPath = myBundle.format("coveredPath", path.getPerc());String highScoreTime = myBundle.format("highScoreTime", highScore.getDate());
getandformatare equivalent. This is not entirely true. Thegetmethod returns the string exactly as it was specified in the properties file, while theformatmethod might make replacements for escaping even if no arguments are present. As a result,getis slightly faster, but unless you are certain that none of your translations contain any escaping it’s best to always useformat.Message Format
java.text.MessageFormatAPI. In short, a pattern can contain zero or more formats of the form{index, type, style}where the type and the style are optional. Refer to the official JavaDoc of the to learn all its features. Important: There is one change that libGDX makes toMessageFormat‘s syntax, because the default escaping rules have proved to be somewhat confusing to localizers. If you want to use a literal{in your string, normally you would need to escape it using single quotes ('). In libGDX however, you just double it; for example,{{0}is interpreted as the literal string{0}, without any format elements. As a result, single quotes never need to be escaped!3.14becomes3,14for the Italian locale; notice the comma in place of the decimal point. (If you’re using GWT, there are some limitations to this.)java.util.Properties, the default encoding isUTF-8. If, for whatever reason, you don’t want to use UTF-8 encoding, invoke one of the overloaded forms of thecreateBundlemethod that allows you to specify the desired encoding.Plural Forms
choiceformat provided byMessageFormat. See the official documentation of the class. I’m going to show you just an example. Let’s consider the following property:
You can retrieve the localized string as usual:collectedCoins=You collected {0,choice,0#no coins|1#one coin|1<{0,number,integer} coins|100<hundreds of coins} along the path.
And the output result would be like the following:System.out.println(myBundle.format("collectedCoins", 0));System.out.println(myBundle.format("collectedCoins", 1));System.out.println(myBundle.format("collectedCoins", 32));System.out.println(myBundle.format("collectedCoins", 117));
You collected no coins along the path.You collected one coin along the path.You collected 32 coins along the path.You collected hundreds of coins along the path.
{0,number,integer}inside{0,choice,...}.GWT Limitations and Compatibility
As said before, the I18N system provided by libGDX is cross-platform. However there are some limitations when it comes to the GWT back-end. In particular:- Simple format: The format syntax of
java.text.MessageFormatis not fully supported. You’ll have to stick to a simplified syntax where formats are made only by their index, i.e.{index}. Format’s type and style are not supported and cannot be used; otherwise anIllegalArgumentExceptionis thrown. - Non localizable arguments: Formats are never localized, meaning that the arguments passed to the
formatmethod are converted to a string with thetoStringmethod, so without taking into account the bundle’s locale. I18NBundle.setSimpleFormat(true) when the application starts. This way all subsequent invocations of the factory methodcreateBundlewill create bundles having the same behavior on all back-ends. If you don’t do this, you’ll get the localized string3,14for the Italian locale on a non-GWT back-end (notice the comma and the rounding) and the non-localized string3.1415927for the same locale on the GWT back-end. On the contrary, ifsimpleFormatis set totrueyou’ll get the non-localized string3.1415927for any locale on any back-end.Multiple Bundles
Of course you can use multiple bundles in your application. For example, you might want to use a different bundle for each level of your game. Using multiple bundles offers some advantages: - -
iOS
Info.plist.xmlfile. Add the following for supporting English, Spanish and German:<key>CFBundleLocalizations</key><array> <string>en</string> <string>es</string> <string>de</string></array>
