Java Localization - Create ResourceBundles from text files

dimitrilc 2 Tallied Votes 220 Views Share

Introduction

When localizing your application, ResourceBundle can be a great way to load Strings from different locales.

The only concrete implementation of ResourceBundle is PropertyResourceBundle, which allows us to load static Strings from a text file. Because .properties is a common file extension used for ResourceBundles, creating ResourceBundle objects using this file format would be the focus of this tutorial.

Property files are commonly used to load application properties for Spring Boot projects, so if you have been working with Spring Boot, then this file format should be familiar to you; the only difference to what we create today is that we will be using property files to load localization data for the ResourceBundle object, so there are some rules that we must follow.

Goals

At the end of the tutorial, you would have learned:

  1. How to create ResourceBundle objects from property files.

Prerequisite Knowledge

  1. Basic Java.
  2. Basic understanding of the java.util.Locale class.

Tools Required

  1. A Java IDE such as IntelliJ Community Edition.

Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Java project.
  2. Create a package com.example.
  3. Create a class called Entry.
  4. Create the main() method inside Entry.java.

Concept: Family

When working with ResourceBundle, the most basic concept that developers need to understand is “family”. A family is technically just a group of property files with a common base (file) name. For example, all of the property files in the list below belong to the same family

  • Menu.properties
  • Menu_en.properties
  • Menu_en_US.properties
  • Menu_es.properties
  • Menu_es_MX.properties

because they all share the common base name of “Menu”.

Creating the property files

To continue with our tutorial, we would need to create some resource bundle files. The scenario that we will use for this tutorial is a restaurant menu that can display multiple languages.

The instructions below are specific to IntelliJ, but other IDEs might have similar options. You only need to make sure that the directory containing the property files is on the class-path/module-path when building your project.

  1. Right-click on the project root -> New -> Directory.
  2. Name it “res”, which will be used to store our property files.
  3. Right-click on the “res” directory -> Mark directory as -> Resources Root

Property files are only text files, but my IDE(IntelliJ) treats them as special files. I can simply

  1. right-click on res ->
  2. New ->
  3. Resource Bundle

resourcebundle.png

Then a “Create Resource Bundle” menu will pop up, at which point I can conveniently add multiple resource files for different locales at once.

create_resource_bundle.png

The created files will also be nicely grouped together by the common base name as well.

resource_bundle_group.png

If your IDE does not support this feature, you can just create 3 empty property files using the same names above.

Language and country codes

If you have been wondering what the language and country codes after the base name do(Menu_en_US), then I will explain them now.

To create a ResourceBundle object, we can use static builder methods getBundle(). It mandates that the language and the country code must be specified in a specific order, separated by underscores(_). The formula is

baseName + _ + language + _ + script + _ + country + _ + variant

Except for the baseName, everything else is optional. If any operand is omitted, the getBundle() method will just use the default Locale. We are not concerned about the script and the variant in this tutorial.

The property file with the “vi_VN” Locale stands for “Vietnamese_VIETNAM”. I chose Vietnamese as a translation target because I am fluent in it so the translations would be as natural as possible. I did not want to pick a language that I was not familiar with and risk translating English to something weird/offensive with Google Translate.

Property file format

In its simplest form, the content inside the property file follows a simple format

propertyName=propertyValue

The property name follows the camelCase convention, whereas the property value is implementation-specific. In our scenario, we can just use plain English/Vietnamese. We can also skip the double quotes entirely.

In the Menu_en.properties file, add the properties below.

pumpkinSoup=Pumpkin Soup
cheeseCake=Cheese Cake

In the Menu_vi_VN.properties file, add the properties below,

pumpkinSoup=Canh Bí Đỏ
cheeseCake=Bánh Phô Mai

Create the ResourceBundle

Now, we can create a method to show the menu to our English-speaking patrons from the code below(inside Entry class).

private static void printEnglishMenu(){
   ResourceBundle rb = ResourceBundle.getBundle("Menu", Locale.ENGLISH); //1
   System.out.println("~~~English Menu~~~");
   System.out.println(rb.getString("pumpkinSoup")); //2
   System.out.println(rb.getString("cheeseCake")); //3
}
  1. As explained previously, we can use the getBundle() method to get an instance of the ResourceBundle object, which explains line 1.
  2. We can access the property value by giving the property name to the getString() instance method on line 2 and line 3.

When we call this method in main(), we get:

~~~English Menu~~~
Pumpkin Soup
Cheese Cake

If you have already set up your property files correctly(added them to the classpath), then ResourceBundle should not have any problem finding your resource bundles. If your program throws MissingResourceException, however, then you should double check your build/run configurations to see if the property files are included.

Now that we have an English menu ready, the next step is to add another method to show the Vietnamese menu.

Because the Locale class did not provide a convenient constant for the Locale “vi, VN”, create a constant in our program to make our code cleaner whenever we wanted to use the “vi_VN” Locale. In the Entry class, create a constant VIETNAM like the code below:

private static final Locale VIETNAM = new Locale("vi", "VN");

Finally, add the code for the printVietnameseMenu() method:

private static void printVietnameseMenu(){
   ResourceBundle rb = ResourceBundle.getBundle("Menu", VIETNAM); //using constant
   System.out.println("~~~Vietnamese Menu~~~");
   System.out.println(rb.getString("pumpkinSoup"));
   System.out.println(rb.getString("cheeseCake"));
}

The printVietnameseMenu() method is almost identical to the printEnglishMenu() method, except for the VIETNAM constant. When called in main(), this method prints:

~~~Vietnamese Menu~~~
Canh Bí
Bánh Phô Mai

Solution Code

Entry.java

package com.example;

import java.util.Locale;
import java.util.ResourceBundle;

public class Entry {
   private static final Locale VIETNAM = new Locale("vi", "VN");

   public static void main(String[] args){
       //printEnglishMenu();
       printVietnameseMenu();
   }

   private static void printEnglishMenu(){
       ResourceBundle rb = ResourceBundle.getBundle("Menu", Locale.ENGLISH); //1
       System.out.println("~~~English Menu~~~");
       System.out.println(rb.getString("pumpkinSoup")); //2
       System.out.println(rb.getString("cheeseCake")); //3
   }

   private static void printVietnameseMenu(){
       ResourceBundle rb = ResourceBundle.getBundle("Menu", VIETNAM); //using constant
       System.out.println("~~~Vietnamese Menu~~~");
       System.out.println(rb.getString("pumpkinSoup"));
       System.out.println(rb.getString("cheeseCake"));
   }
}

Menu_en.properties

pumpkinSoup=Pumpkin Soup
cheeseCake=Cheese Cake

Menu_vi_VN.properties

pumpkinSoup=Canh Bí
cheeseCake=Bánh Phô Mai

Summary

Besides text files, there are also other ways to create ResourceBundles such as ListResourceBundle or implementing your own ResourceBundle.

Also, another benefit of property files for translations is that you can have the translation team responsible for their own files without access to your code base.

The full project code can be found here https://github.com/dmitrilc/DaniwebPropertiesFile