The union of the previous recipes of this book provides you with a complete set of tools to develop what could be a successful game except for its narrow audience.
Libgdx comes with an out of the box internationalization and localization (i18N) system, which means that you will be able to localize your application according to the user's needs without the hassle of implementing your own or hardcoding. In addition, this will allow you to have one or more language files that will contain all the strings of your application, identified by a name that will abstract you from dealing with each language separately.
This data-driven approach fits perfectly for nonprogrammers, translators, or community projects where everybody can contribute with new translations just by editing a friendly text file.
Firstly, check whether you have imported the sample projects into your Eclipse workspace as described in Chapter 1, Diving into Libgdx.
Language files are located in the i18n
folder within the assets
directory of the Android project:
strings_en_GB.properties
strings_es_ES.properties
You can guess that Spanish (es) and English (en) are the chosen languages, specifying their variation from Great Britain (GB) and Spain (ES), respectively, in capital letters. Other language variations of the same countries are en_UK (United Kingdom) and es_MX (Mexico).
To keep things tidy, we will have a LanguageManager.java
class within the com.cookbook.localization
package, in charge of abstracting the programmer from dealing with each language.
Finally, the nub of the issue can be found in LocalizationSample.java
, where a practical example of usage is provided.
The process to localize your application is divided into three sections.
One of the main targets of this recipe is to keep the addition of new languages as simple as possible, so we will have a plain text file with the .properties
extension containing a set of strings together with their identifier. In the following examples, bookTitle
and introduction
are keys and the right parts of the equalities are values associated with them:
bookTitle = Libgdx for Cross Platform Game Development Cookbook introduction = {0} pragmatic recipes to master cross platform 2D game development using the powerful Libgdx framework.
There is an intruder {0}
whose value is replaced at runtime, so it works as a parameter. However, this magic is made by java.text.MessageFormat
, which considers them as patterns that are processed and placed at the appropriate places. Double a left curly bracket in order to escape it so you can write it within your text strings.
The following patterns can be built by specifying at least the first of the following properties:
ArgumentIndex
: This sets which argument will replace the pattern.FormatType
: This can be used to format the pattern to number, date, time, or choice.FormatStyle
: This will give extra precision to the format so a number can be an integer, a currency, or a percent. A date or a time can be formatted to short, medium, long, or full. You can add your custom styles too through DecimalFormat
or SimpleDateFormat
. A choice will take the style to specify which message must be shown under certain conditions.Some practical code examples will shed light on its usage:
Introduction = It's {0, time} on {0,date}. The world is swarming with zombies. Options = Please, select at least {0, choice, 1# option|1<{0, number,integer} options}
GWT only supports ArgumentIndex
, throwing an IllegalArgumentException
if you try to apply FormatType
and FormatStyle
. In addition, it will convert every argument into a string without taking into account its locale. In case you want to keep uniform behavior in all your platforms, you should make use of setSimpleFormat(true)
from the I18NBundle
class that we will cover later on.
We can have a set of structured files with all the translations data, but we need to make our application able to understand and process them. The next approach is intended for applications that want to support constant language switching. Perform the following steps:
I18NBundle
comes into action, loading a locale-specific language property file:I18NBundle.createBundle(Gdx.input.Internal("i18n/strings_en_GB.properties", Locale.UK));
I18NBundle
class field for each language is not elegant neither maintainable. Instead, we can create a LanguageManager
class to gather all loaded languages and keep track of the selected one:public class LanguageManager { private ObjectMap<String, I18NBundle> languages; private String currentLanguage; ...
public LanguageManager() { languages = new ObjectMap<String, I18NBundle>(); currentLanguage = null; }
I18NBundle
or creating a new one:public void loadLanguage(String name, I18NBundle bundle) { if(name!=null && !name.isEmpty() && bundle != null) languages.put(name.toLowerCase(), bundle); } public void loadLanguage(String name, FileHandle fileHandle, Locale locale) { if(name!=null && !name.isEmpty() && fileHandle != null && locale != null) languages.put(name.toLowerCase(), I18NBundle.createBundle(fileHandle, locale)); }
currentLanguage
field must be included too, so the user can consult it or modify it at runtime:public String getCurrentLanguage() { return currentLanguage; } public void setCurrentLanguage(String name) { if(languages.containsKey(name.toLowerCase())) currentLanguage = name; }
I18NBundle
:public I18NBundle getCurrentBundle() {
return languages.get(currentLanguage);
}
Implementing the preceding approach makes your localization code simpler and cleaner than handling each I18NBundle
separately. Perform the following steps:
LanguageManager
object and populate with some bundles through the loadLanguage(…)
method:LanguageManager langManager = new LanguageManager(); FileHandle englishFileHandle = Gdx.files.internal("i18n/strings_en_GB"); FileHandle spanishFileHandle = Gdx.files.internal("i18n/strings_es_ES"); lm.loadLanguage("English", englishFileHandle, Locale.UK); lm.loadLanguage("Spanish", spanishFileHandle, new Locale("es", "ES"));
lm.setCurrentLanguage("English");
I18NBundle bundle = lm.getCurrentBundle(); String title = bundle.get("bookTitle"); String introduction = bundle.format("introduction", 81); String body = bundle.get("body");
setCurrentLanguage("Spanish")
and update the UI text containers as written in the previous code snippet.You can still improve the LanguageManager
class by automatically detecting, within its constructor, the system default language and using it as the current one for the application:
currentLanguage = Locale.getDefault().toString();
3.146.176.88