Chapters
ResourceBundle enables us to pack and load locale-specific data in a persistent file. By using resource bundles, we can assign a name to our application's elements, such as GUI elements, with multiple-locale.
Also, updating locale-specific data in a resource bundle is much more easier than hardcoding it in our code. Thus, it's recommended to use resource bundles for storing names such as names of GUI elements. resource bundle has a hierarchy.
We define a base name to our base resource. Then, we can create a family of resources. For naming convention, Each bundle name in a resource bundle family, except for the base resource, should copy the name of the base resource with an abbreviation of language code. We may append country code if there's a language code in the name. We may append platform code if the country and language code are present in the name.
User underscore to separate base name and codes. For example:
Codes in the file name are case-insensitive
e.g. MyResources_en_uS
However, by convention, lowercase language code; uppercase country and platform codes are preferrable. In a list resource file name, country and platform codes should in uppercase.
Resource bundles store data in the form of case-sensitive key-value pairs. Resource bundle is categorized into two types:
PropertyResourceBundle is a concrete subclass of ResourceBundle that manages resources for a locale using a set of static strings from a property file. The file extension of a property file is
These are the elements that we can write to our properties file.
API Note:
PropertyResourceBundle can be constructed either from an InputStream or a Reader, which represents a property file. Constructing a PropertyResourceBundle instance from an InputStream requires that the input stream be encoded in UTF-8.
By default, if a MalformedInputException or an UnmappableCharacterException occurs on reading the input stream, then the PropertyResourceBundle instance resets to the state before the exception, re-reads the input stream in ISO-8859-1, and continues reading.
If the system property java.util.PropertyResourceBundle.encoding is set to either "ISO-8859-1" or "UTF-8", the input stream is solely read in that encoding, and throws the exception if it encounters an invalid sequence.
If "ISO-8859-1" is specified, characters that cannot be represented in ISO-8859-1 encoding must be represented by Unicode Escapes as defined in section 3.3 of The Java Language Specification whereas the other constructor which takes a Reader does not have that limitation.
Other encoding values are ignored for this system property. The system property is read and evaluated when initializing this class. Changing or removing the property has no effect after the initialization.
ListResourceBundle is an abstract subclass of ResourceBundle that manages resources for a locale in a convenient and easy to use list. File extension of this resource bundle is
This is how we create key-value pairs in this resource bundle.
Now we know how to create a resource bundle. Let's try using a resource bundle in our program. First off, let's create a properties files. Create a file and name it "PropertyResources.properties" and write this in the file:
One of the advantages of list resource to a property resource is that list resource contains values of any type whereas property resource only contain values of String type. We can use
When java looks up for a resource bundle, it looks for a specific bundle with specified locale. If there's no match, java will look for a bundle in the family with a locale that is equivalent to our platform's default locale which is returned by invoking Locale.getDefault.
If there's still no match, java will look for default resource bundle or base resource bundle. If there's still no match, an exception will be thrown. If a property resource and list resource have the same name and in the same package or directory, java will prioritize the list resource.
Resource bundle hierarchy implements inheritance. A bundle with more specific name in the family inherits key-value pairs from a bundle with less specific name. For example, Assume we have three resource bundles in the same family and the base name of the family is "MyResources":
However, if the bundle with more specific name has equivalent key-value pairs to its less specific counterparts, the key-value pairs in the bundle with more specific name override the equivalent key-value pairs in the bundle with less specific name.
bundles with equivalent specificity don't inherit key-value pairs. For example:
ResourceBundle.Control defines a set of callback methods that are invoked by the ResourceBundle.getBundle factory methods during the bundle loading process.
In other words, a ResourceBundle.Control collaborates with the factory methods for loading resource bundles. The default implementation of the callback methods provides the information necessary for the factory methods to perform the default behavior.
We can override some methods of
Introduction
ResourceBundle enables us to pack and load locale-specific data in a persistent file. By using resource bundles, we can assign a name to our application's elements, such as GUI elements, with multiple-locale.
Also, updating locale-specific data in a resource bundle is much more easier than hardcoding it in our code. Thus, it's recommended to use resource bundles for storing names such as names of GUI elements. resource bundle has a hierarchy.
We define a base name to our base resource. Then, we can create a family of resources. For naming convention, Each bundle name in a resource bundle family, except for the base resource, should copy the name of the base resource with an abbreviation of language code. We may append country code if there's a language code in the name. We may append platform code if the country and language code are present in the name.
User underscore to separate base name and codes. For example:
//base name MyResources //sibling name with language code MyResources_en //sibling name with language code //and country code MyResources_en_US //sibling name with language code, //country code and platform code MyResources_en_US_UNIXFor language code reference, you may look at this article. For country code reference, you may look at this article.
Codes in the file name are case-insensitive
e.g. MyResources_en_uS
However, by convention, lowercase language code; uppercase country and platform codes are preferrable. In a list resource file name, country and platform codes should in uppercase.
Resource bundles store data in the form of case-sensitive key-value pairs. Resource bundle is categorized into two types:
PropertyResourceBundle
and ListResourceBundle
.
PropertyResourceBundle
PropertyResourceBundle is a concrete subclass of ResourceBundle that manages resources for a locale using a set of static strings from a property file. The file extension of a property file is
.properties
These are the elements that we can write to our properties file.
# Labels HiLabel = Hi ! Buttons abortButton Abort aboutButton: About"#" and "!" denotes a comment. Characters after these symbols are ignored. Next, key-value pairs can be separated by whitespace ( ), colon (:) or equal (=) symbols.
API Note:
PropertyResourceBundle can be constructed either from an InputStream or a Reader, which represents a property file. Constructing a PropertyResourceBundle instance from an InputStream requires that the input stream be encoded in UTF-8.
By default, if a MalformedInputException or an UnmappableCharacterException occurs on reading the input stream, then the PropertyResourceBundle instance resets to the state before the exception, re-reads the input stream in ISO-8859-1, and continues reading.
If the system property java.util.PropertyResourceBundle.encoding is set to either "ISO-8859-1" or "UTF-8", the input stream is solely read in that encoding, and throws the exception if it encounters an invalid sequence.
If "ISO-8859-1" is specified, characters that cannot be represented in ISO-8859-1 encoding must be represented by Unicode Escapes as defined in section 3.3 of The Java Language Specification whereas the other constructor which takes a Reader does not have that limitation.
Other encoding values are ignored for this system property. The system property is read and evaluated when initializing this class. Changing or removing the property has no effect after the initialization.
ListResourceBundle
ListResourceBundle is an abstract subclass of ResourceBundle that manages resources for a locale in a convenient and easy to use list. File extension of this resource bundle is
.java
This is how we create key-value pairs in this resource bundle.
import java.util.ListResourceBundle; import java.time.LocalDate; public class ListResource extends ListResourceBundle{ @Override protected Object[][] getContents(){ return new Object[][]{ {"item1", "Toy Car"}, {"release-date", LocalDate.of(2021, 10, 20)}, {"tags", new String[]{"Automotive", "Toys"}} }; } }Remember that keys are String type and values can be of any type.
Using ResourceBundle
Now we know how to create a resource bundle. Let's try using a resource bundle in our program. First off, let's create a properties files. Create a file and name it "PropertyResources.properties" and write this in the file:
# Labels HiLabel: Hi # Buttons abortButton: Abort aboutButton: AboutWe're going to create a family bundle so let's create another bundle and name it "PropertyResource_en_GB.properties" and write this in the file:
# Labels HiLabel = Hello # Buttons okButton Okay aboutButton: AboutNext, let's create a code that will access our bundle.
import java.util.Locale; import java.util.ResourceBundle; public class SampleClass{ public static void main(String[] args){ ResourceBundle rb = ResourceBundle.getBundle ("MyResources", Locale.UK); //If your resource is in a package //("Package-Name.MyResources", Locale.UK); for(String key : rb.keySet()){ System.out.println(key + " | " + rb.getString(key)); } } } Result HiLabel | Hello okButton | Okay aboutButton | AboutThis code above also works with list resources which have
.java
file extension. Remember to compile your list resources first because java look for .class file of your list resources. In the getBundle
method, we just need to put the base name of our resource bundle. We don't need to add file extensions or codes like country code.
One of the advantages of list resource to a property resource is that list resource contains values of any type whereas property resource only contain values of String type. We can use
handleGetObject(String key)
if we want to convert values to Object type that can be cast to other object types. handleGetObject(String key)
gets an object for the given key.
handleGetObject(String key)
has protected access in ResourceBundle
. We need to use ListResourceBundle
or PropertiesResourceBundle
reference in order to access this method. For example:
ListResourceBundle prb = (ListResourceBundle)ResourceBundle.getBundle ("MyResources", Locale.US); ... prb.handleGetObject(key);
ListResourceBundle
has getContents()
method. This method returns an array in which each item is a pair of objects in an Object array. Resource bundles can be deployed in different ways such as deploying them together with an application. You can read them in the documentation.
When java looks up for a resource bundle, it looks for a specific bundle with specified locale. If there's no match, java will look for a bundle in the family with a locale that is equivalent to our platform's default locale which is returned by invoking Locale.getDefault.
If there's still no match, java will look for default resource bundle or base resource bundle. If there's still no match, an exception will be thrown. If a property resource and list resource have the same name and in the same package or directory, java will prioritize the list resource.
Inheritance
Resource bundle hierarchy implements inheritance. A bundle with more specific name in the family inherits key-value pairs from a bundle with less specific name. For example, Assume we have three resource bundles in the same family and the base name of the family is "MyResources":
//base MyResources.properties okButton = Ok //specific MyResources_en.properties acceptButton = accept //more specific MyResources_en_GB.properties confirmButton = confirmWhen we use
MyResources_en_GB.properties
in our program, this resource bundle will inherit the values in MyResources.properties
and MyResources_en.properties
.
However, if the bundle with more specific name has equivalent key-value pairs to its less specific counterparts, the key-value pairs in the bundle with more specific name override the equivalent key-value pairs in the bundle with less specific name.
bundles with equivalent specificity don't inherit key-value pairs. For example:
... MyResources_en_US.properties confirmButton = confirm MyResources_en_GB.properties commitButton = commitWhen we use
MyResources_en_GB.properties
, it won't inherit the key-value pair in MyResources_en_US.properties
. Also, list resource and property resource have different hierarchies. Thus, a list resource can't inherit any key-value pairs from property resource and vice-versa.
ResourceBundle.Control
ResourceBundle.Control defines a set of callback methods that are invoked by the ResourceBundle.getBundle factory methods during the bundle loading process.
In other words, a ResourceBundle.Control collaborates with the factory methods for loading resource bundles. The default implementation of the callback methods provides the information necessary for the factory methods to perform the default behavior.
We can override some methods of
ResourceBundle.Control
to change some behaviors during bundle loading process. For example, we can override getCandidateLocales method to filter candidate locales.
import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; import java.util.ResourceBundle.Control; public class SampleClass{ public static void main(String[] args){ ResourceBundle rb = ResourceBundle.getBundle ("MyResources", Locale.FRANCE, new CustomResourceControl()); for(String key : rb.keySet()){ System.out.println(key + " | " + rb.getString(key)); } } } class CustomResourceControl extends ResourceBundle.Control{ @Override public List<Locale> getCandidateLocales(String s, Locale locale) { if(locale.getCountry().equals("US") || locale.getCountry().equals("UK")){ return super.getCandidateLocales(s, locale); } else{ System.err.println ("Warning: Locale should be US or UK."); System.err.println("Locale has been " +"automatically set to Locale.ROOT"); return Arrays.asList(Locale.ROOT); System.out.println(); } } } Result Warning: Locale should be US or UK. Locale has been automatically set to Locale.ROOT
No comments:
Post a Comment