A tutorial about PropertyResourceBundle object and .properties files; how to create them, load their values, use them in business logic code, or JSF view page.
This tutorial will try to gather all the uses in one place.

Contents:
1. Prelude
2. What is the PropertyResourceBundle ?
3. Loading .properties Values into PropertyResourceBundle
4. Using FacesMessageUtil class
5. Fetching Property Bundle Values in JSF 2
6. Dynamic Parameters and Property Bundle Files
7. Displaying Property Bundle Values as Rich Text in JSF 2
8. Choices in Property Bundle Value

1. Prelude

When developing a software with multiple languages support, it’s a necessity to separate your software labels, texts or anything that is locale-specific from your software code.
With separation you guarantee that your software can be translated into the desired languages, and handle multiple locales at once, also it makes adding a new language an easy task.

2. What is the PropertyResourceBundle ?

PropertyResourceBundle is a concrete subclass of ResourceBundle that manages resources for a locale using a set of static strings from a property file.

from the Java Development Kit (JDK) specification
The property file is a text file with the extension .properties, each line is either a comment, blank line, or a key=value entry.
Your resource bundle file can support multiple language by following a naming convention, first the default property file has the bundle family base name, for example UIResources.properties:

# Comment to tell you this is the english file
label.email=Email
label.password=Password
button.save=Save
button.cancel=Cancel
validEmail=Email Available
emailExists=Email Already Exists

Whenever key label.email is fetched the value ‘Email’ is returned.
When a new language is desired, let’s say German, another property file is created, that has the family base name suffixed with an ‘_’ (UNDERSCORE) and the country code of the desired locale. so our German property file would take the name UIResources_de.properties:

label.email=Email
label.password=Passwort
button.save=Speichern
button.cancel=Abbrechen
validEmail=Email erhältlich
emailExists=Email ist bereits vorhanden

The country codes follow the ISO 3166 standard, for example:
Germany -> de
Spain -> es
You can find all the codes in Wikipedia’s ISO 3166 article.

3. Loading .properties Values into PropertyResourceBundle

The ResourceBundle.getBundle method takes two parameters, the first is a String of the full qualifier name of the your bundle file base name, the second is a java.util.Locale object to define the loaded resource bundle.
The method automatically look for the appropriate properties file and create a PropertyResourceBundle that refers to it.
For example we have three languages support:
English -> UIResources.properties
German -> UIResources_de.properties
French -> UIResources_fr.properties

Now we first load the resource bundle:

String bundleQualifierName = "tdb.view.bundles.ui.UIResources";
ResourceBundle resourceBundle = ResourceBundle.getBundle(bundleQualifierName, new Locale("de"));

UIResources_de.properties will be loaded, to get a value using its key:

String email = resourceBundle.getString("label.email");

4. Using FacesMessageUtil class

I’ve created a utility class FacesMessageUtil that can be pretty helpful in real life JSF software:

- UPDATED -

package project.view.util;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

/**
 * Utility class to help JSF developers create <code>FacesMessage</code> passing normal String, or retrieve values from UIResources.properties file.
 * The created <code>FacesMessage</code> can be added to a desired <code>UIComponent</code>, or the current <code>FacesContext</code>.
 * 
 * @author Qussay Najjar
 * @version 2.1 2013-04-22
 * @link http://qussay.com/2011/08/29/java-localization-with-propertyresourcebundle
 */
public class FacesMessageUtil {

    private static final String UI_BUNDLE = "tdb.view.bundles.ui.UIResources";
	
    private static ResourceBundle uiBundle = null;

    private FacesMessageUtil() {
        // Utility class, hide constructor.
    }

    /**
     * Returns a string value from the <code>UIResources.properties</code> bundle associated with the given key,
     * localized, and formatted with the given parameters, if any.
     * @param key The string key in the loaded resource bundle.
     * @param locale Defines the localization file.
     * @param params Optional parameters to format the string using <code>MessageFormat</code>.
     * @return The string value from the resource bundle associated with the given key, formatted
     * with the given parameters, if any.
     * @see FacesMessageUtil#getString(String, ResourceBundle)
     */
    public static String getString(String key, Locale locale, Object... params) {
    	try {
    		if(uiBundle == null)
                uiBundle = ResourceBundle.getBundle(UI_BUNDLE, locale);
    	} catch(MissingResourceException e) {
    		return "'"+ UI_BUNDLE + "' Missing resource.";
    	}
    	
        return getString(key, uiBundle, params);
    }

    /**
     * Retrieve a defined string from the given bundle file,
     * according to the passed key.
     *
     * @param key The string key in the loaded resource bundle.
     * @param bundle The <code>ResourceBundle</code> file where the key is located.
     * @param params Optional parameters to format the message using <code>MessageFormat</code>.
     * @return The string value from the given bundle file matching the given key.
     */
    public static String getString(String key, ResourceBundle bundle, Object...params) {
    	if (key==null || key.isEmpty())
    		return "";
    	try {
    		return MessageFormat.format(bundle.getString(key), params);
    	} catch(MissingResourceException e) {
    		return "?? "+key+" ?? Key not found.";
    	}
    }
    
    /**
     * Add a <code>FacesMessage</code> from the loaded <code>ResourceBundle</code> file, to the current <code>FacesContext</code>, with a defined severity.
     *
     * @param severity <code>FacesMessage.Severity</code> defines the <code>FacesMessage</code> status of four options,
     * <code>FacesMessage.SEVERITY_INFO</code>, <code>FacesMessage.SEVERITY_WARN</code>, 
     * <code>FacesMessage.SEVERITY_ERROR</code>, <code>FacesMessage.SEVERITY_FATAL</code>.
     * @param summaryKey The summary message key in the loaded resource bundle.
     * @param detailKey The detail message key in the loaded resource bundle.
     * @param params Optional parameters to format the message using <code>MessageFormat</code>.
     * @see FacesMessageUtil#addBundledFacesMessage(UIComponent, FacesMessage.Severity, String, String, Object[])
     */
    public static void addContextBundledFacesMessage(FacesMessage.Severity severity, String summaryKey, String detailKey, Object...params) {
    	addBundledFacesMessage(null, severity, summaryKey, detailKey, params);
    }
    
    /**
     * Add a <code>FacesMessage</code>, to the current <code>FacesContext</code>, with a defined severity.
     *
     * @param severity <code>FacesMessage.Severity</code> defines the <code>FacesMessage</code> status of four options,
     * <code>FacesMessage.SEVERITY_INFO</code>, <code>FacesMessage.SEVERITY_WARN</code>, 
     * <code>FacesMessage.SEVERITY_ERROR</code>, <code>FacesMessage.SEVERITY_FATAL</code>.
     * @param summary The summary message.
     * @param detail The detail message.
     * @see FacesMessageUtil#addFacesMessage(UIComponent, FacesMessage.Severity, String, String)
     */
    public static void addContextFacesMessage(FacesMessage.Severity severity, String summary, String detail) {
    	addFacesMessage(null, severity, summary, detail);
    }
    
    /**
     * Add a <code>FacesMessage</code> from the loaded <code>ResourceBundle</code> file, to the given <code>UIComponent</code>, with a defined severity.
     * 
     * @param component The <code>UIComponent</code> to associate the created <code>FacesMessage</code> with.
     * @param severity <code>FacesMessage.Severity</code> defines the <code>FacesMessage</code> status of four options,
     * <code>FacesMessage.SEVERITY_INFO</code>, <code>FacesMessage.SEVERITY_WARN</code>, 
     * <code>FacesMessage.SEVERITY_ERROR</code>, <code>FacesMessage.SEVERITY_FATAL</code>.
     * @param summaryKey The summary message key in the loaded resource bundle.
     * @param detailKey The detail message key in the loaded resource bundle.
     * @param params Optional parameters to format the message using <code>MessageFormat</code>.
     * @see FacesMessageUtil#newBundledFacesMessage(FacesMessage.Severity, String, String, Object[])
     */
    public static void addBundledFacesMessage(UIComponent component, FacesMessage.Severity severity, String summaryKey, String detailKey, Object...params) {
    	FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(component!=null ? component.getClientId(context) : null, newBundledFacesMessage(severity, summaryKey, detailKey, params));
    }
    
    /**
     * Add a <code>FacesMessage</code>, to the given <code>UIComponent</code>, with a defined severity.
     *
     * @param component The <code>UIComponent</code> to associate the created <code>FacesMessage</code> with.
     * @param severity <code>FacesMessage.Severity</code> defines the <code>FacesMessage</code> status of four options,
     * <code>FacesMessage.SEVERITY_INFO</code>, <code>FacesMessage.SEVERITY_WARN</code>, 
     * <code>FacesMessage.SEVERITY_ERROR</code>, <code>FacesMessage.SEVERITY_FATAL</code>.
     * @param summary The summary message.
     * @param detail The detail message.
     * @see FacesMessageUtil#newFacesMessage(FacesMessage.Severity, String, String)
     */
    public static void addFacesMessage(UIComponent component, FacesMessage.Severity severity, String summary, String detail) {
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(component!=null ? component.getClientId(context) : null, newFacesMessage(severity, summary, detail));
    }
    
    /**
     * Get a new <code>FacesMessage</code> object with the given summary, detail, and the passed severity status.
     *
     * @param severity <code>FacesMessage.Severity</code> defines the <code>FacesMessage</code> status of four options,
     * <code>FacesMessage.SEVERITY_INFO</code>, <code>FacesMessage.SEVERITY_WARN</code>, 
     * <code>FacesMessage.SEVERITY_ERROR</code>, <code>FacesMessage.SEVERITY_FATAL</code>.
     * @param summary The summary message.
     * @param detail The detail message.
     * @return <code>FacesMessage</code> object.
     */
    public static FacesMessage newFacesMessage(Severity severity, String summary, String detail) {
    	return new FacesMessage(severity, summary, detail);
    }
    
    /**
     * Get a new <code>FacesMessage</code> object, from the loaded <code>ResourceBundle</code> file, with the given summary, detail, and the passed severity status.
     *
     * @param severity <code>FacesMessage.Severity</code> defines the <code>FacesMessage</code> status of four options,
     * <code>FacesMessage.SEVERITY_INFO</code>, <code>FacesMessage.SEVERITY_WARN</code>, 
     * <code>FacesMessage.SEVERITY_ERROR</code>, <code>FacesMessage.SEVERITY_FATAL</code>.
     * @param summaryKey The summary message key in the loaded resource bundle.
     * @param detailKey The detail message key in the loaded resource bundle.
     * @param params Optional parameters to format the message using <code>MessageFormat</code>.
     * @return <code>FacesMessage</code> object.
     */
    public static FacesMessage newBundledFacesMessage(Severity severity, String summaryKey, String detailKey, Object...params) {
    	return new FacesMessage(severity, getString(summaryKey, getLocale()), getString(detailKey, getLocale(), params));
    }
    
    /**
     * Retrieve the set <code>Locale</code> in the <code>UIViewRoot</code> of the current <code>FacesContext</code>.
     *
     * @param context The current <code>FacesContext</code> object to reach the JSF pages <code>UIViewRoot</code>.
     * @return Current set <code>Locale</code> in <code>UIViewRoot</code>.
     */
    public static Locale getLocale() {
        Locale locale = null;
        UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
        if (viewRoot != null)
            locale = viewRoot.getLocale();
        if (locale == null)
            locale = Locale.getDefault();
        return locale;
    }
    
    /**
     * Set the Locale in the <code>UIViewRoot</code> of the current <code>FacesContext</code>.
     *
     * @param locale The required <code>Locale</code> value to set in the <code>UIViewRoot</code>.
     */
    public static void setLocale(Locale locale) {
        FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
    }
}

Donwload FacesMessageUtil.java

The class provides five functionalities:
4.1. Get a string from a resource bundle the is used a lot in our software, method getString(String, Locale, Object...) always refer to UIResources.properties bundle (in my case) and match the passed key id.

...
String password = ResourseBundleUtil.getString("label.password", new Locale("fr"));
...

Don’t mind the Object... params we’ll talk about it in a little while.

4.2. Get a string from a desired resource bundle that can be passed as a parameter as in the overloaded method getString(String, ResourceBundle, Object...).

4.3. Create a FacesMessage and add it to a desired UIComponent, this can be helpful for JSF developers.

Either your message texts is in a bundle file, you can use:

...
//bound with <h:inputText> in jsf page
UIInput emailInput;
...
Object[] paramOptions = {"option 1", "option 2"};
FacesMessageUtil.addBundledFacesMessage(emailInput, FacesMessage.SEVERITY_INFO, "msg.email.summary", "msg.email.detail" , paramOptions);
...

Or you want to pass your message texts directly as String values:

FacesMessageUtil.addFacesMessage(emailInput, FacesMessage.SEVERITY_INFO, "My Summary Message", "My Detail Message");

In view, your component should have a <h:message> attached to it, to display the added FacesMessage:

<h:inputText id="email" value="#{myBean.email}" />
<h:message for="email" />

4.4. Create a FacesMessage and add it to the FacesContext.
Following the same concept in the previous usage, you can either use:

FacesMessageUtil.addContextBundledFacesMessage(FacesMessage.Severity severity, String summaryKey, String detailKey, Object...params)

or

FacesMessageUtil.addContextFacesMessage(FacesMessage.Severity severity, String summary, String detail)

The added message will be displayed later on using <h:messages> component in your page.

4.5. Create a FacesMessage and return it for later use maybe as validator message for example.

...
//Inside validator method, and after condition is true
throw new ValidatorException(FacesMessageUtil.newFacesMessage(FacesMessage.SEVERITY_ERROR, "Validation Error", "Not a valid email formation"));

The class is fully documented, just take a look at each method’s documentation.

5. Fetching Property Bundle Values in JSF 2

In JSF you have one of two ways to load bundle files:
5.1. As an application parameter:
When you need to use your bundle in all or many of your pages, you don’t load it every time you need it.
You load it once as an application parameter, in your faces-config.xml:

<application>
    <resource-bundle>
        <base-name>tdb.view.bundles.ui.UIResources</base-name>
	<var>uir</var>
    </resource-bundle>
</application>

5.2. In the needed page only:
In some cases you might have a bundle file for specific page, you can load it in that page only as next:

<f:loadBundle basename="tdb.view.bundles.help.Help" var="help" />

Of course we have a .properties file in package tdb.view.bundles.help named Help.properties

Using either loadings, fetching values is as next:

<h:commandButton value="#{uir['button.save']}" ..... />
<h:outputText value="#{uir['label.email']}" />
<h:outputText value="#{help['some.reference']}" />

6. Dynamic Parameters and Property Bundle Files

Sometimes you want to pass dynamic values to a certain resource bundle value, it’s easily done as next:
First the resource bundle value would look something like this:

loginInfo=Logged in as {0}, at time {1}

What we did is telling value of key ‘loginInfo‘ that it will take two parameters at run time to form a readable sentence.
Second we either call it from our source code, and again using my FacesMessageUtil.java presented earlier, in method getString, notice the Object... params which is an optional parameters declaration, that this method will take zero or more Object param.

String firstName = "John";
Timestamp currentTime = new Timestamp(System.currentTimeMillis());
String loginInfo = FacesMessageUtil.getString("loginInfo", new Locale("en"), firstName, currentTime);

Or we fetch the value from our JSF page using as next:

<h:outputFormat value="#{uir['loginInfo']}">
    <f:param value="#{bean.firstName}" />
    <f:param value="#{bean.currentTime}" />
</h:outputFormat>

You can add as much parameters as you wish, just keep the numbering sequence {2}, {3},…

7. Displaying Property Bundle Values as Rich Text in JSF 2

We can write complete html tags inside our bundle value.
add another value to UIResource.properties:

copyright=All Rights Reserved to <b>Company</b> &copy; <br />\
	For more information please contact us.

In this value you should see two new things, html tags, and our value has occupied two lines, we’ve managed to do that using ‘\’ (SLASH) character at the end of line one, so line two became a continuity to “copyright" value.
All you have to do in your JSF page is the next:

<h:outputFormat value="#{uir['copyright']}" escape="false" />

escape attribute is "true" by default, if set to "false" it allows the passed value html tags to be translated as rich text, not plain text.

8. Choices in Property Bundle Value

Another nifty trick can be done with bundle value, is to add a condition depended on a param value, long talk cut short, see the next value in UIResources.properties:

label.salutation={0,choice,1#Mr|2#Ms|3#Mrs}

In the value you see above, we have one parameter {0}, the word 'choice' is a keyword for its next, 1# is to be translated like this 'value#', so if parameter {0} had the value 1, the text 'Mr' will be returned, so forth if value is 2 the text 'Ms' is returned.

This is all I know so far about PropertyResourceBundle and its uses, if you know more, or didn’t understand a part of this tutorial I’ll be glad to answer your questions just comment out.

Copyrights Notice: You can freely use class FacesMessageUtil.java or any code of this tutorial in your application or modify it to your needs, just keeping my name and the link would be fair.