Towards Bug-Free Code

Towards Bug-Free Code

I18N using Type-Safe Resource Bundles

A way of preventing such mistakes from happening is by using a ListResourceBundle where the locale-specific messages are written as Java classes instead of as property files. The ListResourceBundle can be enhanced to accept keys belonging to an enum type. If the keys are wrong, then the code will fail the compilation step and it can be corrected immediately. The task becomes easier if you use a good Java editor that has an auto-completion feature.



public abstract class

                AbstractTypeSafeResourceBundle

                <K extends Enum<K>>

                extends ListResourceBundle

{

    private final EnumMap<K, Object> catalog;

    private final Object[][] cachedCatalog;



    public AbstractTypeSafeResourceBundle(

                Class<K> clazz)

    {

        catalog = new EnumMap<K, Object>(clazz);



        loadCatalogMap();



        //---------------------------------------



        cachedCatalog =

                new Object[catalog.size()][2];



        int i = 0;

        for (K key : catalog.keySet())

        {

            cachedCatalog[i][0] = key.name();

            cachedCatalog[i][1] = catalog.get(key);

            i++;

        }

    }



    public EnumMap<K, Object> getCatalogCopy()

    {

        return catalog.clone();

    }



    protected final void load(K key, Object obj)

    {

        catalog.put(key, obj);

    }



    protected abstract void loadCatalogMap();



    //--- ListResourceBundle methods ---



    protected final Object[][] getContents()

    {

        return cachedCatalog;

    }

}

        

Each module or project should create an enum, which will serve as the set of keys to the bundle:



public enum ResourceKey

{

    info_Hello,

    info_GoodMorning;

}

        

The locale-specific bundles will look like this:



//English version.

public class ProjectXResBundle extends

AbstractTypeSafeResourceBundle<ResourceKey>

{

    public ProjectXResBundle()

    {

        super(ResourceKey.class);

    }



    protected void loadCatalogMap()

    {

        load(ResourceKey.info_GoodMorning,

                "Good morning");



        load(ResourceKey.info_Hello, "Hello!");

    }

}



//German version.

public class ProjectXResBundle_de_DE extends

AbstractTypeSafeResourceBundle<ResourceKey>

{

    public ProjectXResBundle_de_DE()

    {

        super(ResourceKey.class);

    }



    protected void loadCatalogMap()

    {

        load(ResourceKey.info_GoodMorning,

                "Guten Morgen");



        load(ResourceKey.info_Hello, "Hallo!");

    }

}

        

A direct way of using the bundles would be as shown below:



final ResourceBundle englishResourceBundle =

                ResourceBundle.getBundle(

                ProjectXResBundle.class.getName());



System.out.println(

                englishResourceBundle.getString(

                ResourceKey.info_Hello.name()));





final ResourceBundle germanResourceBundle =

                ResourceBundle.getBundle(

                ProjectXResBundle.class.getName(),

                new Locale("de", "DE"));



System.out.println(germanResourceBundle.getString(

                ResourceKey.info_Hello.name()));

        

Or you can define a helper class that takes care of substituting the parameters to construct the localized message by using the varargs feature in 1.5:



public class Helper

{

    public static String format(ResourceBundle

                resourceBundle,

                Enum key,

                Object... paramsForI18NMsg)

    {

        final Object l10nObj =

                resourceBundle.getObject(key.name());



        final String messagePattern =

                l10nObj.toString();



        if (paramsForI18NMsg.length > 0)

        {

            final MessageFormat formatter =

                new MessageFormat(messagePattern,

                resourceBundle.getLocale());



            return formatter.format(paramsForI18NMsg);

        }



        return messagePattern;

    }

}

        

If the message takes time as a parameter to format according to locale, it will look like this (standard ResourceBundle conventions):



public class ProjectXResBundle_de_DE extends

AbstractTypeSafeResourceBundle<ResourceKey>

{

    public ProjectXResBundle_de_DE()

    {

        super(ResourceKey.class);

    }



    protected void loadCatalogMap()

    {

        load(ResourceKey.info_GoodMorning,

                "Guten Morgen {0,time,short} - {1}");



        load(ResourceKey.info_Hello, "Hallo!");

    }

}

        

With the substitution parameters forming the varargs, the code looks a lot simpler and cleaner:



//No parameters, yet no unsightly "new Object[]{}"

//that had to be used until 1.5.

System.out.println(Helper.format(

                germanResourceBundle,

                ResourceKey.info_Hello));



System.out.println(Helper.format(

                germanResourceBundle,

                ResourceKey.info_GoodMorning,

                new Date(), "Ashwin"));

        

The last message prints "Guten Morgen 17:58 - Ashwin" with the time in the 24-hour format as it is used in Germany.

The source code contains a package called common, which takes the Registry a step further by allowing ComponentFactories to be added to the Registry instead of just Component implementations (if required) and a generic logging framework that allows enums to be used for internationalized messages in conjunction with AbstractTypeSafeResourceBundle. The projectx package has examples that use these.

Resources

  • Sample code for this article
  • EasyMock: Mock objects framework
  • "Design Principles and Design Patterns" (PDF): a must read for object-oriented designers and developers
  • "OO Design Quality Metrics" (PDF): Formulae to assess an OO project's design quality
  • "Design for Testability" (PDF): An essay on writing code that allows for easy testing
  • HiveMind and Spring: Two advanced frameworks for larger projects
  • JavaForU.com: Ashwin Jayaprakash's web site, where the complete source code of ProjectX and related packages can be downloaded

Ashwin Jayaprakash is a software engineer at BEA Systems R & D Centre, Bangalore, using Java/J2EE to develop WebLogic Integration products.


Return to ONJava.com.

Prev  [1] [2] [3] [4] 

Close    To Top
  • Prev Article-Java:
  • Next Article-Java:
  • Now: Tutorial for Web and Software Design > Java > Java Basic > Java Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction