32 i18n

Pravin Jain

epgp books

 

What is i18n? 

 

i18n Stands for Internationalization. Here i18n is used as short form for internationalization because there are 18 letters between “i” and “n” in internationalization. There is another related term called l10n which stands for Localization.

 

What is Internationalization about? 

 

Internationalization is about developing software which can change its behavior or it adapts its behavior according to preference of the user. The user preference is in terms of language and country(mainly Languages). We have different people coming with different languages. In user Interface there are so many things which are to be displayed in the language which is convenient to the user i.e. which is being preferred by user and this change should not require change to the software and it should not require many efforts in order to start supporting any new language. Internationalization is mainly about how do we create software which is sensitive to the Locale. The  term Locale stands for the language and country settings. In java.util package we have a class called Locale. The object of Locale encapsulates information related to the language and country preferences. Users preferences about language, country and cultural things can be represented in terms of Locale object. First we will look at Locale class and then we will move onto the class called ResourceBundle. There are different types of resource bundles and they help us iin different ways to create application which is sensitive to the Locale quite easily. The idea here would be to develop software where we can support few different number of locales. It decides that user can give certain preferences and according to preference it can make changes in its behavior. It can use different types of text maybe different types of objects which are sensitive to the Locale and then we will see how simple it would be to add or support new Locale and that’s where Localization comes in. Localization is just building the support for another Locale.

What is a Locale Class? 

 

Locale class is just  representation of Language  and country settings. A  Locale class has a constructor which can take parameters such as the Language, the country and the variant. So in Locale class we have 3 different constructors as follows:

 

Locale(String language) 

Locale(String language, String country)

Locale(String language, String country, String variant)

 

So, in the constructor, we can give only Language or a Language and country both because sometimes if language is English then it may be different for different country. For Example the spelling of color is different in U.K. and U.S. Now over and above language and country we want some more variant or variation we can use variant as a parameter. We get 3 components within our Locale object i.e. Language, Country and Variant. Now to specify Language and country we have some ISO standards. So there are 2 letter ISO codes for different Languages (ISO 639) and Regions (ISO 3166). Those are being understood by the constructor of our Locale class. Objects of Locale class are immutable. Our machines have locale settings, same way JVM also has its own Locale settings. So when we start our JVM, our JVM has a default Locale. So in our Locale class we have a static method to get default Locale i.e.

 

public static Locale getDefault() 

 

Another static method is

 

publicstaticLocalesetDefault(Localel)

 

This method is used to set the default Locale for the JVM Session. There are many other methods in Locale class. For Example: We use ISO code for creating our Locale object. So having a Locale object if we want to know what is the name of the country, eg. if we have,

 

Locale l1 = new Locale(“en”,”US”) 

 

and let us create another Locale object L2 as,

 

Locale l2 = new Locale(“hi”,”IN”) 

 

Here we have interesting methods called getDisplayCountry() and getDisplayLanguage(). So, if we invoke getDisplayCountry() on a Locale Object it will give the full name of the country and if we invoke getDisplayLanguage() on a Locale Object it will give us the name of the Language. So if we write l1.getDisplayLanguage(), the output will depend on the user preference. Here it will depend on the default Locale Settings and most commonly default settings will have English as default Language So the output will be “English” as these Locale was created with Language English . Now if we say l1.getDisplayCountry() and we don’t pass any parameter it would give output “United States of America”. But what if the default Locale was different i.e. when we use getDisplayCountry() or getDisplayLanguage() on a Locale object, here we have an option of saying I want this but my preference is to see this according to different Locale. For Example: we created l2 object and if we pass this as parameter like

l1.getDisplayCountry(l2) 

 

It  will  give  the  output  “                                                   ”  ,  because  our  language preference is Hindi as specified by passing l2. So, for same l1 object, but the output has to change according to Locale. Internationalization exactly is about this only. We can see that our Locale class itself is sensitive to Locale. So, the above example is an example where we don’t have to make any changes to the default Locale, here Preference is specified by a parameter and a change in the preference changes the output. It is adapting to the user’s preference. So, if we say l1.getDisplayLanguage(l2), it won’t say “English” it would give output “                          ”(English in Hindi). In Hindi the Devanagri Script will be used. So, this is an example of a class called Locale.

TheResourceBundleclass 

 

To have our application to be Locale-sensitive we require a class called ResourceBundle. First step is to identify all those kinds of objects which need to be changing according to the Locale in our Application. There will be many things which are not dependent on locale but there are certain things which will change according to Locale. So, identify all such objects. We will be developing it for one particular Locale as default, It means you will identify some Locale of yours which your software understands as a default. Application would not understand the default Locale of the system where it is going to run. There is some default Locale which would be decided by the developer. Developer would decide the default support for certain Locale. Now, Let’s explore the class ResourceBundle. An object of ResourceBundle is having all the various objects for a particular Locale only. So, one single Locale and all such Locale sensitive objects are identified and managed in a ResourceBundle. So, in ResourceBundle each kind of object is given certain identity. So, there would be some kind of unique identification name given to all such objects. So, we have a kind of name given to that and against them we have an object. So, let us say, in one ResourceBundle we have identified 5 different elements, Each of the 5 different elements have some kind of name and against these names we have got our objects. This ResourceBundle is used to manage things in this manner.  ResourceBundle class is an abstract class. We don’t have one ResourceBundle we have a family of ResourceBundles. If we have 5 elements then these set of objects is common to all the ResourceBundle. But each Bundle is created for different Locale and the name of all the bundles would have common base name, then it would have a suffix to indicate what kind of Locale that Bundle is for. So, for example we have identified various Labels and for various Labels we want to create various ResourceBundles, so, we can call it as LabelBundle this can be a base name for all Label now if we want to use this LabelBundle for English we can have names like LabelBundle_en or to be more specific LabelBundle_en_US. So LabelBundle here is base name.

 

As a Developer you will be deciding as to what would be the default Locale for your Software. Therefore, you should have one ResourceBundle with a base name and no suffix, this is must. All these classes should be sub classes of ResourceBundle class. ResourceBundle is an abstract class and this abstract class has two abstract methods that developer needs to override.

 

publicabstractEnumeration<String>getKeys()

protectedabstractObjecthandleGetObject(Stringkey)

 

So, first abstract method is getKeys() which returns enumeration of strings which indicates what are all different id’s we are having and the other method is, for a given particular key, I want to get an object i.e. we have an protected abstract method which is handleGetObject(key). This method requires a parameter i.e. a id(key). Given a key it should be returning an object. We have a public method which is implemented and a non-abstract method called getObject(key) and would return an object for a particular key. On similar notes we have a method getString(key) which would return a String i.e. it would do casting to a String. If we want string[] we have a method called getStringArray(key) which would return a string[] i.e. it would do casting to a String[].

 

public Object getObject(String key) public String getString(String key)

public String[] getStringArray(String key)

 

So, these are the methods to extract object from given Id(or key) from the Bundle. We have to override the two abstract methods to get key and according to that key an object can be obtained. What is the difference between handleGetObject() and getObject(). So, to understand the difference these ResourceBundle do have a parent child relationships. The parent-child relationships exists within a family of ResourceBundle. For example: LabelBundle being a parent for LabelBundle_en and LabelBundle_en is a parent for LabelBundle_en_US. Now, when someone says handleGetObject()  the handleGetObject needs to implement what is different from the parent. So, if the default has something which is exactly what you want then you don’t need to handle such keys, the keys are to be handled which have different value from the parent. The user of the ResourceBundle i.e. main Application will be using getObject() method. And the developer of ResourceBundle would have to override the handleGetObject(). So, this is the difference between getObject and handleGetObject().

 

The ListResourceBundle class 

 

We have a subclass of ResourceBundle i.e. ListResourceBundle which has only one abstract method getContents() where the return type is a two-dimensional array of object. Each element of two-dimensional object Array has inner array which has string i.e. Key and second Element would be object required against that key. So you can have ResourceBundle objects created per Locale. Now we will see how it is going to be used, The Application which uses ResourceBundle i.e. a user has preference of a particular Locale, we need to pick up a particular ResourceBundle for user and that task is made simpler by static method available in a ResourceBundle class.

 

pubilc ResourceBundle getBundle(String baseName, Locale locale) 

 

In ResourceBundle class there is a static method getBundle(basename) which will take a parameter basename and as a second parameter we can pass a Locale Object. So from this, it will return a ResourceBundle for particular basename and for particular Locale. It may happen that for a particular Locale you don’t have ResourceBundle. So, it has a mechanism to try to give the best possible ResourceBundle. i.e. if Locale object says, I want a ResourceBundle for this LabelBundle and I am looking for English in Canada and we don’t have a ResourceBundle for English in Canada then the getBundle() method will automatically try to fall back by just discarding only the last element and it will say do I have a LabelBundle in English and if it is there, it will pick this up and use it. It won’t go to default straight away, so that’s one idea here. getBundle() automatically takes care by locating best bundle for you. There is a logic about how it tries to find the best one and if it is unable to find the one it will fall back to default from the Application. i.e. the default ResourceBundle.

 

public Locale getLocale() 

 

In the ResourceBundle, we also have a method called getLocale() so if we get a ResourceBundle Object we can verify whether this ResourceBundle is the one, which you are looking for. So, that way we can actually use ResourceBundle and create different ResourceBundle. This is extensible as if we try to give support for newer Locales we just have to create a new ResourceBundle. eg. in case of LabelBundle, if we don’t have support for French we have to just add LabelBundle_fr. In this way we can add more and more ResourceBundles, that is one aspect. Now to start using ResourceBundle in Application the application will typically do.

 

ResourceBundle var_name=ResourceBundle.getBundle

 

and pass family name and Locale in getBundle() and get the best one and where the Locale Sensitive thing is to be used we don’t hardcode them rather we always get them from the ResourceBundle i.e. first get the ResourceBundle and then fetch your thing from the ResourceBundle by using getBundle().

 

The PropertyResourceBundle class(Using the properties file)

 

Now we have seen two options. One option was using ResourceBundle by implementing two method’s directly from ResourceBundle class and other option was to override getContents() method of ListResourceBundle and there is another class called PropertyResourceBundle which is commonly used for text where the types of the Resources which are needed are Texts. Even without writing any class we can manage this, we are developing the classes for the basename. Now we will create file as basename.properties that is we created properties file and while deploying them, deploy them at the place you have classes to be loaded from, keep them along with the class files and when we say ResourceBundle.getBundle() first of all it will try to locate a class file and when it does not see any class file it will try to locate properties files and it will create a PropertyResourceBundle object from the properties file. The properties file is a plain text file in ASCII, made up several lines, one line per key(id). A line would be like

 

key=value

 

This is how we can create properties files. So, inspite of organizing ResourceBundle as classes you can organize them as properties. If there is a class File and a Property File then first Priority goes to the Class File. It will load from the class and if it is not available then it will load from the properties file. This thing can also be used in our web application. All these Properties file can be kept in a classes folder. We have Locale class which has it own Sensitivity, same way we have lot many things. We have java.text package and there we have lot of classes which are been helpful for Internationalization. For Example: DateFormat class, NumberFormat , MessageFormat all these classes are useful for Internationalization. Even printf method can take Locale as a parameter. For Example:

 

System.out.printf(new Locale(“hi”,”IN”), “%d”, 75 );

 

Here if Locale object has Hindi as parameter the output would be in Hindi. There is Limited support for Indian Languages, in case of printf and format methods. The support for Hindi can only be used when we pass “hi” and “IN” both. There options of developing support for other languages, if you like and you can add that.

 

So, this how internationalization can be done in Java.

you can view video on i18n

Suggested Reading:

  1. Core Java Volume 2 by Cay Horstmann & Gary Cornell, Ninth Edition, Pearson Education.
  2. Java Internationalization by Andrew Deitsch, David Czarnecki O’Reilly Media
  3. https://docs.oracle.com/javase/tutorial/i18n/