ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Java Internationalization and Localization

by Jaric Sng
04/12/2001
  Related Reading:
Java Internationalization

Java Internationalization
By Andy Deitsch & David Czarnecki
March 2001
0-596-00019-7, Order Number: 0197
51 pages, $39.95

Internationalization (I18N) and Localization is easier with Java, in contrast with other, previous native applications. Credit goes to IBM's Center for Java Technology, Silicon Valley.

Although it is much easier to use I18N and L10N due to the different approaches by Sun and Microsoft, we are faced with new challenges. On one hand we want to target a wider audience, without maintaining two sets of code (using the Java "write once, run anywhere" vision). On the other, we want to use as many features as possible in the latest Java release. Of course, there is a limit to what we can achieve.

In this article, we will look at employing the Java reflection so that our Java applet utilizes system fonts found in Java SDK 1.3 as well as the MS VM implementation, while not causing backward compatibility problems for Navigator 4.76 with Java 1.1.5. With this technique, we will be able to solve the problem of Chinese, Japanese, and Korean characters displayed as "blocks" in IE with MS VM.

The display of these characters with an English language OS in Navigator 4 requires the necessary changes to the font.properties file on a client machine. As for Navigator 6 and IE, we just need to run the test applet and use the correct font in our applet. That's it.

The issues

In JDK 1.1, font mapping between the Java logical fonts and system physical fonts is achieved by making the necessary changes in font.properties, which lists the font results only by logical font names.

The situaion is much better now with JDK 1.3. We can get a list of the user's system TrueType fonts or create it ourselves. Similarly, Microsoft makes it possible to do this with their own implementation. Due to the different implementations of Sun and Microsoft, how can we provide a uniform access to these different solutions?

The solution:

Java reflection and a helper class NEFontFactory uses the Factory pattern. Before we can do this, we need to

Accessing the system font listing

In JDK 1.1, we cannot access the system font listing except logical fonts names. These logical fonts are mapped to the system physical fonts by editing the font.properties found in the ..\lib directory (See Creating font.properties.en for Netscape 4).

In JDK 1.1, the font listing API returns a list of logical font names:

java.awt.Toolkit.getDefaultToolkit().getFontList() // deprecated since JDK1.2

In JDK 1.3, the listing of system fonts is available using the following:

java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()

In MS VM, the listing of system fonts is available using the following:

com.ms.awt.FontX.getFontList()

Creating Font Object

In JDK 1.1, we cannot create the TrueType font using its name. Instead we create the font using a logical font name, which is mapped to a TrueType font in the font.properties.

Now in JDK 1.3, we can create the TrueType font simply by specifying its name:

new Font(fontName, fontStyle, fontSize)

In MS Java, the system font is obtained via com.ms.awt.FontX:

public static Font getFont(String face, int style, int size, int flags);
public static Font getFont(String face, Vector v, int size);

Development environment

For your reference, the following hardware and software configurations is recommended for use in our development environment.

Hardware

Software

Installing the True Type Font

Getting it
One way is to install it from the MultiLanguage Pack from Office 2000.

For a listing of fonts provided by the MultiLanguage Pack (including the code pages and the languages they support), check out Administering Fonts in an International Office.

Otherwise, let IE do the job for you. To download Simplify Chinese font, simply access Chinese language News paper web site in Singapore using IE. IE will prompt you to download the necessary font in order to view the page. For the following fonts, try the sites below:

We may be able to view the page in Chinese, but what font is IE using to display the page?

Determining the font name

If you have IE (5.0 through 5.5), it is easy to find the font used to display a particular language. For example, I would like to find out the fonts available for displaying Chinese web pages. I can do the following:

Access the IE menu: Tools/Internet Options/General/Fonts (see Figure 1). Then use the following fonts to display Simplified Chinese text.

Screen shot.
Figure 1. Web font listing in IE for Simplify Chinese text.

Due to the large character sets of many Asian languages, their font file is larger than the character sets typical of most Western languages. With this knowledge, we can sort the font listing based on file size as shown in Figure 2 from Control Panel/Font.

Screen shot.
Figure 2. Font Listing on my system.

From the figure shown above, one or many of the above fonts "Batang", "Bitstream Cyberbit", "SimSun & NsimSun", "SimHei", and "MS Song" can be used to view Chinese characters. We can use Navigator 4 to test which particular font is suitable.

In Navigator 4, go to Edit/Preferences/Appearance/Fonts, and set the preferences as shown in the following figure.

Screen Shot.
Figure 3. Setting Preferences in Netscape 4.76

With the completion of the above setting, access the site http://www.zaobao.com to view the text correctly when that particular font is used. In this case, the SimSun font can be used to display Chinese characters.

Yet another way of knowing what font can be used to display native text is to use Office 2000 for opening the native text file. It will automatically detect the encoding, then format the text with the necessary font.

Creating font.properties.en for Netscape 4

With the font on your system and the knowledge of what font name to use, we can create font.properties.en for Navigator 4 so that our Java applet will display Chinese characters.

First, create a copy of font.properties.zh file found in Netscape's ..\classes directory. Then rename the file font.properties.en in the same directory.

font.properties.zh for Netscape 4 can be found at

C:\program files\netscape\communicator\program\java\classes

Edit the font.properties.en by replacing all occurrences of \uXXXX unicode strings related to the SimSun font. In our case, replace \u5b8b\u4f53 with SimSun.

With this new font.properties.en your Navigator 4, we will be able to display the Chinese characters when we use any of the logical fonts in our Java applet.

We can make changes to the original font.properties too. However, it is easier to distribute font.properties.en, so that we aren't required to backup files.

The NEFontFactory Helper Class

Now we want to create a helper class, called NEFontFactory, which will have the following features:

The Helper class makes use of a Factory Pattern to create Font objects.

Detecting the Java version and vendor

By detecting the Java version and vendor, we ensure that our code will always work. We will not encounter unnecessary exceptions when we obtain Class objects for either GraphicsEnvironment or FontX class.

We can obtain the Java version and vendor as follows:

String vendor = System.getProperty("java.vendor");
String version = System.getProperty("java.version");

The following table shows the vendor values and versions on different browsers.

  Netscape Navigator 4.76 Netscape Navigator 6 IE
Vendor Netscape Communications Corporation Sun Microsystems Inc. Microsoft Corp.
Version 1.1.5 1.3.0_01 1.1.4

Java Reflection

With Java reflection, we ensure that we are able to compile our NEFontFactory class using any version of the Java compiler, since we do not need to import any Java version specific code. For more information on Java Reflection, check out Sun's Java Tutorial.

Retrieving Class Objects

The first step is to retrieve the Class object. With the knowledge of Java version and vendor, we obtain our FontX and GraphicsEnvironment Class objects with the following method calls for IE VM and JDK 1.3, respectively.

Class msFontX = Class.forName("com.ms.awt.FontX"); // Class object for IE environment
Class graphicEnv = Class.forName("java.awt.GraphicsEnvironment"); // JDK 1.3 environment

Invoking Methods

Next, we will look at invoking methods. In the MS SDK Java doc, we find the following documentation.

FontX Class
The FontX Class of the com.ms.awt package creates a font object that defines the font family, font face, style, and size. The FontX class also supports TrueType fonts.

public class FontX extends FxFont
{
// Fields
public static final int EMBEDDED;
public static final int OUTLINE;
public static final int STRIKEOUT;
public static final int UNDERLINE;

// Constructors
public FontX();
public FontX(String name, int style, int size);
public FontX(String name, int style, int size, boolean bEmbed);
public FontX(String name, int style, int size, int xFlags);

// Methods
public boolean equals(Object obj);
public static String[] getAttributeList();
public static String[] getAttributeList(String face);
public int getFlags();
public static int getFlagsVal(String face, String attribute);
public static Font getFont(String face, int style, int size, int flags);
public static Font getFont(String face, Vector v, int size);
public static String[] getFontList();
public static int getStyleVal(String face, String attribute);
public boolean isTypeable(int language);
public static boolean matchFace(String face);
public String toString();
}

With JDK 1.1, fonts are mapped with a font properties file. So, we can obtain different fonts by editing this file. However, the Microsoft implementation does not support this type of font association. Therefore, we can obtain desired fonts with FontX objects, which are created with Win32 font names.

FontX field definitions
EMBEDDED

Indicates the new font should be linked to a privately embedded font. Note This is only significant for fonts that are loaded by non-Java objects. Fonts that are loaded by Java objects are automatically embedded.

OUTLINE

Indicates that the text drawn by using the new font should be drawn by using the outline of the glyph. This is only supported by certain fonts in the system, and only GraphicsX will support this feature. Trying to draw outline fonts using a Win32Graphics object will result in the glyph being drawn normally.

STRIKEOUT

Indicates that text drawn using the new font should be struck through.

UNDERLINE

Indicates that text drawn using the new font should be underlined.

We are interested in getFont(String face, int style, int size, int flags) and getFontList() in FontX.

We get the available font listing and Font object from the FontX class using reflection.

Getting a font listing:

Method getFontListApi = msFontX.getMethod("getFontList", null);
// need to perform sorting of the listing .... (see Exercise)
String fontListing[] = (String[])getFontListApi.invoke(null, null);

Creating a Font:

String fname = new String(fontName);

Class[] param = new Class[4];
param[0] = fname.getClass();
param[1] = Integer.TYPE;
param[2] = Integer.TYPE;
param[3] = Integer.TYPE;

Method getFontApi = msFontX.getMethod("getFont", param);

Field field = msFontX.getField("EMBEDDED");

Integer fstyle = new Integer( style );
Integer fsize = new Integer( size );
Integer fflags = (Integer)field.get( null ); // null because it is a static field.

Object[] args = { fname, fstyle, fsize, fflags};
Font f = ( Font ) getFontApi.invoke(null, args ); // null because it is a static method.

For Sun Java VM (1.1 and above), the Font object is created normally,

new Font(fontName, style, size);

while the font listing is obtained via Java Reflection for Java 1.3:

Method getLocalGraphicsEnvironmentApi =
graphicEnv.getMethod("getLocalGraphicsEnvironment", null);
// invoke the API to obtain an instance of GraphicsEnvironment
Object GEInstance =
getLocalGraphicsEnvironmentApi.invoke(null, null); // static method with no parameters
Method getAvailableFontFamilyNamesApi =
graphicEnv.getMethod("getAvailableFontFamilyNames",null);
String fontListing [] =
(String[])getAvailableFontFamilyNamesApi.invoke(GEInstance, null);

Font caching

We know that object creation takes up a fair bit of time, and so font caching is added to the NEFontFactory class to improve the performance of Font object creation.

Caching is implemented using java.util.Hashtable, where the key is formed using fontName+style+size.

We place a Font object into Hashtable by the following (where f is the Font object):

String name = f.getName();
int style = f.getStyle();
int size = f.getSize();
cache.put( name+style+size, f);

Putting in all together

Now we can obtain a Font object by calling

public static Font NEFontFactory.getFont(Sting fontName, int style, int size)

And for a listing of font names available on your system, we can call

public static String[] NEFontFactory.getFontList()

Demo Applet

An applet was created as shown in Figure 4, with the following applet parameters.

The applet reads the file with the specified encoding, then displays the text using the preferred font name and size. The country and lang parameters are used to create the java.util.Locale object. The text is displayed using java.awt.Canvas. Line wrapping is performed using java.text.BreakIterator.

Some Observations

What follows are some observations when the demo applet is executed on IE, Navigator 4.76, and 6 for Chinese, Japanese, and Korean text.

Encoding in IE
IE does not support every encoding supported in the JDK. For example, if you try reading a native Korean character in our test applet you will encounter the following:

UnsupportedEncodingException

In order to display Korean characters correctly in all browsers, we need to do some preprocessing. We need to perform transcoding, where the Korean text file is transformed from one encoding to another; in this case, EUC-KR to UTF8. Transcoding can be implemented easily in Java. We read in the data in one encoding and write out the data in another encoding.

After transcoding the Korean text files to UTF8, we specify the encoding parameter in the applet as UTF8. Then use the preferred font name "Batang" to display Korean characters in the applet.

Screen shot.
Figure 4. Korean Text Demo Applet on IE Browser.

IE Java VM
If the Font object is not created with FontX class but, rather, with

new Font("system font name") 

then our Korean characters will be displayed as follows on IE Java VM, using the "Batang" and "Dialog" font names.

Screen shot.
Figure 5. Display of Korean text for a 'Batang' font name if we did not make use of FontX class.

Screen shot.
Figure 6. Display of Korean text when we use 'Dialog' logical font name

Netscape Navigator 6 JDK 1.3
Navigator 6 with Sun JDK 1.3 VM works well, except for updated fonts with only a change of font style.

For example, if the current Font setting in the applet is

Font name = 'Batang'
Font size = 14
Italic = No
Bold = No

and we want to have the same font name, size, but with italic rather than bold, then if we just check the Italic checkbox, and depress the 'Update' button, it will not work.

Instead, we need to set Font Size to any value other than 14 (the current value), and set Italic to "Yes". Then depress the "Update" button. Then set Font Size back to 14. Depress the "Update" button again. You can then get what you wanted as follows:

Font name = 'Batang'
Size = 14
Italic = Yes
Bold = No

On IE and Navigator 4.76 (assuming the necessary changes to font.properties.en), we do not have this problem.

When we run the Korean character demo in Navigator 6, the loading of the applet took much longer to load than with IE. It may be due to the size of this TrueType Font (15157KB) (see Figure 2). Another reason is likely due to IE being integrated with the OS.

Exercise

You may now want to consider the following suggestions to NEFontFactory class.

Since the font listing returns by getFontList() of IE VM are not sorted alphabetically, as compared to getAvailableFontFamilyNames() in Java 1.3. Therefore, we may want to sort the result on IE VM. If we do want the code to be internationalized, we will need to make use of the java.text.Collator and java.util.Locale information.

Another exercise is to use Java Reflection with the following API found in Java 1.3, where Font object is replicated with just a change of size or style or both.

public Font deriveFont(float size)
public Font deriveFont(int style, float size)
public Font deriveFont(AffineTransform trans)
public Font deriveFont(int style, AffineTransform trans)
public Font deriveFont(int style)
public Font deriveFont(Map attributes)

Resources

The latest MS VM can be obtained from Microsoft's Java site.

MS Java SDK

References on I18N and Java

Summary

In this article, we illustrated how to achieve the display of Asian characters in applets on IE by using Java Reflection. Otherwise, these texts would have appeared in "block" characters. We also illustrated how to obtain a listing of user system fonts depending on which version of VM you are using.

This technique has been implemented in our client-side Java-based product, Facado, which in addition to being internationalized, offers interactive and collaborative computing capabilities in the browser.

I welcome your feedback and exchange of ideas. Contact me via email.

Acknowledgement

I would like to thank Steve Anglin, Managing Editor of ONJava.com, for giving me this opportunity to share this information with you and NexusEdge Technologies Pte Ltd for providing the environment to develop leading-edge Java solutions.

I would like to thank my colleague, Thierry Bedos, for his valuable discussion on Java reflection and feedback on this article.

Jaric Sng is a Java GUI designer and developer with interest and expertise with Java Internationalization.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.