The Source for Java Technology Collaboration
User: Password:



John O'Conner's Blog

April 2004 Archives


Groovy? Are you serious?

Posted by joconner on April 27, 2004 at 12:02 AM | Permalink | Comments (12)

You've seen the titles in the bookstore: Rock Gardening for Dummies, Needlepoint for Dummies, etc. If ever I'm tempted to pick one of them up, I quickly put it back on the shelf...and of course I could never let myself actually buy one regardless of the quality. OK, I know that I shouldn't judge a book by its cover (or title), but I just can't get past the name. Something about dummies just offends me, or maybe targets me just a bit too accurately on some subjects. Whatever the reason, I'm affected by the name, and yes, I'm a little ashamed to admit it. I wonder how many others are affected in the same way.

Now for another secret...deep breath...I'm affected similarly by Groovy. You know...it's the latest Java-like scripting language that targets the Java platform. Yes, I think the platform could use a quick, easy scripting language. I've no problem with that idea. I don't even have an issue with Groovy, the actual tool/language/environment. But couldn't it have a different name? Groovy? I wonder how many people have just overlooked it so far because of its silly name? Is it irresponsible to overlook it for that reason? Yes, most definitely. But will some of you? Come on, be honest. Doesn't the name put you off just a little? Can serious professionals use something that sounds so silly?



I18n How-to: ResourceBundle naming

Posted by joconner on April 23, 2004 at 04:46 PM | Permalink | Comments (4)

Two types of ResourceBundles are provided in the Java platform:

  • PropertyResourceBundle
  • ListResourceBundle
Either one can store localizable resources for your application. Let's assume we use a PropertyResourceBundle.

Starting were we left off, let's assume we have text strings in a bundle named GreetingResources.properties. Our application loads that bundle with the following:
ResourceBundle res = ResourceBundle.getBundle("com.joconner.demo.GreetingResources");

Once you've loaded the bundle, you can retrieve the localizable text with res.getString("SOME_KEY"). However, how do you create a localized file, and how do you retrieve that bundle?

Create a localized bundle by translating all resource text and placing it in a separate resource bundle. If your original bundle is named GreetingResource.properties, your new bundle should be named GreetingResource_.properties, where represents the target locale for the localization. For example, if we create a Japanese localization, the bundle would be GreetingResource_ja.properties. A Canadian English version would be GreetingResource_en_CA.properties.

The first two lowercase letters of the represent the language, and the next two uppercase letters (if any) represent the region or country for the localization.

When you load bundles, the JRE will search for appropriate bundle for your locale even if you do not specify a locale in the method. Be careful that you do not include the locale designation within the bundle name argument. For example,
res = ResourceBundle.getBundle("com.joconner.demo.GreetingResources");
is correct, but
res = ResourceBundle.getBundle("com.joconner.demo.GreetingResources_ja");
is not.

If you want to specify a locale in the method, you must provide the locale as a second argument:
res = ResourceBundle.getBundle("com.joconner.demo.GreetingResources", new Locale("ja"));

In brief,

  1. your application should have a default bundle without any locale extensions
  2. each additional localization will have its own bundle with the same bundle base name but with a extension added just before the filename extension
  3. the JRE will find and load the appropriate bundle based on the default host locale of your system unless you specify a specific locale in the bundle load method.



I18n How-to: Just get started!

Posted by joconner on April 19, 2004 at 11:31 PM | Permalink | Comments (6)

Developers and project managers make lots of excuses for not internationalizing an application. But it's easier to get started than you might imagine. Don't worry too much about all your difficult questions...you'll never begin. Just get started!

Although there are several steps to creating a fully internationalized application, you can start by separating localizable text from your core business logic. You should place that text in a separate file, either a PropertyResourceBundle or a ListResourceBundle. Since a PropertyResourceBundle is so much more simple, I'll start with that. Let's pretend you're creating a simple application with a single button and a text field. When the user presses the button, the application will display a simple greeting. Your application might start like this:

package com.joconner.demo;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Greeting extends JFrame {
    JButton btnGreeting = new JButton();
    JTextField tfGreeting = new JTextField();
    ResourceBundle res =  GridBagLayout layout = new GridBagLayout();

    public Greeting() {
        try {
            btnGreeting.setText("Show Greeting");
            this.setTitle("I18n Demo");
            btnGreeting.addActionListener(new ActionListener() {
    
                public void actionPerformed(ActionEvent event) {
                    tfGreeting.setText("Hello, world!");
                }
    
            });
            this.getContentPane().setLayout(layout);
            this.getContentPane().add(btnGreeting,    new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
                ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
            this.getContentPane().add(tfGreeting,     new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0
                ,GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Greeting app = new Greeting();
        app.pack();
        app.show();
    }
}

To get started with internationalization, you first need to pull all the localizable text from the source code. For this app, it's simple; you just have 3 strings:

  • Show Greeting!
  • Hello, world!
  • I18n Demo

Put these strings in a PropertyResourceBundle. A PropertyResourceBundle is a simple text file with key/value pairs. Let's name our bundle "GreetingResources.properties" and we'll place it in the same package as our application's .java file. Proving 3 keys for each string, the property bundle will look like this:

BTN_TEXT=Show Greeting!
GREETING=Hello, world!
TITLE=I18n Demo

Now we have to touch up the original source code to use our new ResourceBundle. The modified code is below:

package com.joconner.demo;

import javax.swing.*;
import java.awt.*;
import java.util.ResourceBundle;
import java.awt.event.*;

public class Greeting extends JFrame {
    JButton btnGreeting = new JButton();
    JTextField tfGreeting = new JTextField();
    ResourceBundle res = ResourceBundle.getBundle("com.joconner.demo.GreetingResources");
    GridBagLayout layout = new GridBagLayout();

    public Greeting() {
        try {
            String btnGreetingText = res.getString("BTN_TEXT");
            btnGreeting.setText(btnGreetingText);
            String title = res.getString("TITLE");
            this.setTitle(title);
            btnGreeting.addActionListener(new ActionListener() {
    
                public void actionPerformed(ActionEvent event) {
                    String tfGreetingText = res.getString("GREETING");
                    tfGreeting.setText(tfGreetingText);
                }
    
            });
            this.getContentPane().setLayout(layout);
            this.getContentPane().add(btnGreeting,    new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
                ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
            this.getContentPane().add(tfGreeting,     new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0
                ,GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Greeting app = new Greeting();
        app.pack();
        app.show();
    }
}

You'll notice that we've done 3 new things:

  1. load a PropertyResourceBundle containing key/value pairs for each piece of localizable text
  2. retrieve each piece of text by its key using the getString(String key) method of the bundle
  3. use the retrieved String to set the text of the appropriate GUI component.

Now believe it or not, you've started to internationalize your application with these easy steps! You can now localize the PropertyResourceBundle file into multiple languages, and the Java platform will find the localized bundle for you. In my next blog tip, I'll discuss how to name your bundles for multiple localizations of the same resources.

Want more information about resource bundles? Check this out:
Java Internationalization: Localization with ResourceBundles



Unicode 4.0 support in J2SE 1.5

Posted by joconner on April 16, 2004 at 01:08 AM | Permalink | Comments (1)

The JavaTM platform has always supported Unicode, but the newest changes for Unicode 4.0 deserve special comment. Unicode itself has evolved to support over a million different code points or basic characters. The code point range is now 0x0000 through 0x10FFFF.

Some major changes were required for J2SE 1.5 to provide support for all Unicode 4.0 code points. Since changes could potentially affect the Java language itself, the Java Community Process was used to determine how the platform should change. JSR 204 was created for that purpose.

In summary, you'll find the following changes for Unicode 4.0 in Java 1.5:

  • char is a UTF-16 code unit, not a code point
  • new low-level APIs use an int to represent a Unicode code point
  • high level APIs have been updated to understand surrogate pairs
  • a preference towards char sequence APIs instead of char based methods

Hey, that's a code unit, not a character! Java created the char type as a 16-bit entity. At one time a char represented a complete Unicode code point or character. Now, however, a single char clearly cannot represent the entire range of valid Unicode characters. char is now a UTf-16 code unit. Characters in the code point range 0000 through FFFF are still represented by a single char code unit, but supplementary characters (those above FFFF) require two char values. Although I hesitate to say it, you probably won't notice anything until you localize your product for use in Japan, Korea, China, or Taiwan.

New low-level APIs in Character and elsewhere use the int type to represent Unicode code points. Since 16 bits can't represent all possible character values, new APIs were added that utilize 32 bit int values instead. In reality only 21 of the 32 bits are needed. Now you have overloaded methods like Character.isLetter(int ch) and Character.isJavaIdentifierStart(int ch). Of course, new APIs like Character.toChars(int cp) and Character.toCodePoint(char high, char low) make conversions between char arrays and code points easier.

Under the hood, high level APIs now work with surrogate pairs! And you'll not have to learn any new API to get the benefit either. For example, String.toUpperCase() will now work with surrogate pairs that represent supplementary characters. Regular expressions, collation APIs, and all the text rendering in the 2D APIs can now property process, sort, and display supplementary characters without any changes to your application. Of course, you'll need an appropriate font to see those new blocks of characters.

No char is an island. The Java platform will begin to show a clear bias towards "char sequence" APIs instead of single char API. A char sequence is a char[], String, StringBuffer, or other structure that can hold 2 or more char values. Why the bias? Well, a char just isn't everything it used to be. Characters typically aren't standalone entities...they usually come in a group. Characters can be composed of multiple char values include combing marks(accents, tone marks, etc) or surrogate pairs that represent the new supplementary characters. Operations like uppercasing have always had the potential to produce multiple char values. For example, uppercasing the German 'ß' doesn't generate just one char; it creates "SS", which is a string, not a single char at all. So char sequences are definitely the right way to process characters.

What's this mean to you? Well...it depends. If your applications have primarily used char based APIs, you may have considerable work to update to char sequenced based methods. However, if your application mostly uses Strings, StringBuffers, or char[]s either as method arguments or as return values, you may not have much to do at all. Most developers will be somewhere in the middle, so it will probably require a litte work from everyone to properly support all of the Unicode characters in your application. Regardless of where you are, you can be assured that the underlying support is there when you decide you need it.

For more information, please see the following:





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds