Breaking Down Language Barriers in JavaFX Apps
With JavaFX technology, you can easily internationalize your application for worldwide users.
Understanding the Code
Internationalizing a JavaFX application is very similar to internationalizing a Java application with the .properties file, except that in the JavaFX language you use the .fxproperties file. The .fxproperties file differs from the .properties file in the following ways:
- All the message keys and corresponding strings are quoted with " and combined with =.
- You no longer need native2ascii for .fxproperties. You can specify encoding at the top of the .fxproperties file. For example, @charset="shift_jis". The default encoding is UTF-8.
- The .fxproperties file can use a string as a message ID, like a .po file. Also, the file can use a message key like the .properties file.
- To call a message from .fxproperties in JavaFX, use ##, for example:
- var s = ##"This needs to be localized"
- To call a message from .fxproperties by using message ID, specify the ID between [ and ]. For example:
- var s = ##[msgID]"This needs to be localized"
// Common Strings, DO NOT Localize "English" = "English" "Japanese" = "?????????" "Chinese" = "????????????" "Korean" = "?????????" "Brazilian Portuguese" = "Portugu??s do Brasil" // Strings for localization. "Start breaking the barriers!" = "???????????????????????????" "NB_Title" = "JavaFX ??? NetBeans IDE" "NB_Desc" = "JavaFX ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????" "PS_Title" = "JavaFX Production Suite" "PS_Desc" = "??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? JavaFX ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????" "SDK_Title" = "JavaFX SDK" "SDK_Desc" = "?????????????????????????????????????????????????????? JavaFX ???????????????????????????????????????????????????????????? "
Figure 1: Main_ja.fxproperties
The code in Figure 2 defines each language button.
class langButton extends CustomNode {
// Button coordinates
var button_x: Number; var button_y: Number;
// Text in button coordinates
var text_x: Number; var text_y: Number;
// This is used to identify which button is clicked.
// Also used to get the localized button label
var text: String;
// Button width
var button_width: Number;
// Localized button label
var ButtonText: String;
// This function is used when a button is clicked
// to calculate coordinates for each city,
// and get the time there.
// English: Los_Angeles
// Japanese: Tokyo
// Simplified Chinese: Shanghai
// Korean: Seoul
// Brazilian Portuguese: Sao Paulo
public function CalcLoc():Void {
StartX = movedStartX;
movedStartX = StartX + MoveX;
JPX = JPX + MoveX;
USX = USX + MoveX;
CNX = CNX + MoveX;
KRX = KRX + MoveX;
BRX = BRX + MoveX;
}
override public function create():Node {
// Initial locale is English (Los Angeles).
Locale.setDefault(new Locale("en", "US"));
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
// Get localized button label from Main_en.properties
if (text.equals("English")) {
ButtonText = ##"English";
} else if (text.equals("Japanese")) {
ButtonText = ##"Japanese";
} else if (text.equals("Chinese")) {
ButtonText = ##"Chinese";
}else if (text.equals("Korean")) {
ButtonText = ##"Korean";
}else if (text.equals("Brazilian Portuguese")) {
ButtonText = ##"Brazilian Portuguese";
}
// Button and label group
Group {
content: [
// Button
Rectangle {
x: button_x, y: button_y
width: button_width, height: 40
arcWidth: 20, arcHeight: 55
stroke: Color.BLACK
// Draw Gradient for Button
fill: LinearGradient {
startX: 0.0, startY: 0.0
endX: 0.0, endY: 1.0
proportional: true
stops: [
Stop { offset: 0.0, color: Color.WHITE },
Stop { offset: 1.0, color: Color.BLACK }
]
}
},
// Label
Text {
font: Font { size: 18 }
x: text_x, y: text_y
fill: Color.WHITE
content: ButtonText
}
]
// Add Reflection effect for Button
effect: Reflection {
fraction: 0.9
topOpacity: 0.5
topOffset: 2.5
}
// Action for Button is clicked
onMouseClicked: function( e: MouseEvent ):Void {
// Change Flag gif image
Flag = "{__DIR__}{text}.gif";
// Actions for each Language Button.
// 1. Set locale
// 2. Set timezone
// 3. Play animation
if (text.equals("Japanese")) {
Locale.setDefault(new Locale("ja", "JP"));
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
MoveX = CenterX - JPX;
CalcLoc();
t.playFromStart();
} else if (text.equals("Chinese")) {
Locale.setDefault(new Locale("zh", "CN"));
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
MoveX = CenterX - CNX;
CalcLoc();
t.playFromStart();
} else if (text.equals("Korean")) {
Locale.setDefault(new Locale("ko", "KR"));
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
MoveX = CenterX - KRX;
CalcLoc();
t.playFromStart();
} else if (text.equals("Brazilian Portuguese")) {
Locale.setDefault(new Locale("pt", "BR"));
TimeZone.setDefault(TimeZone.getTimeZone("America/Sao_Paulo"));
MoveX = CenterX - BRX;
CalcLoc();
t.playFromStart();
} else {
Locale.setDefault(new Locale("en", "US"));
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
MoveX = CenterX - USX;
CalcLoc();
t.playFromStart();
}
// Change Display Text.
// Those Strings are bound to the Text in Stage.
viewtext = ##"Start breaking the barriers!";
NBTitle = ##[NB_Title]"";
NBDesc = ##[NB_Desc]"";
PSTitle = ##[PS_Title]"";
PSDesc = ##[PS_Desc]"";
SDKTitle = ##[SDK_Title]"";
SDKDesc = ##[SDK_Desc]"";
}
}
}
}
Figure 2: langButton Class
The onMouseClicked function defines the action of the button clicked. For example, if the '?????????' (which means Japanese) button is clicked, the following actions are performed.
- The application locale is changed to Japan by using the java.util.Locale class:
- Locale.setDefault(new Locale("ja", "JP"));
- The application time zone is changed to Tokyo by using the java.util.Timezone class. This class is for the clock displayed in the bottom of the window.
- TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
- The location of Tokyo is calculated and the animation of the world map starts.
-
MoveX = CenterX - JPX;
CalcLoc();
t.playFromStart();
-
MoveX = CenterX - JPX;
This sample uses the java.util.Locale class for runtime localization. But it can also be implemented by using the javafx.util.StringLocalizer API in JavaFX technology. The StringLocalizer API can define which .fxproperties file will be loaded, so you can load different .fxproperties files based on the clicked language button. But in this case, you need to prepare differently named .fxproperties files, like "Japanese_ja.fxproperties" and "Korean_ko.fxproperties". The reason is if you start a JavaFX application in a Japanese locale, the _ja_JP.fxproperties or _ja.fxproperties file is called from the JavaFX application. So, to use the same file name prefix in runtime localization, you need to change the application locale.
After setting the application locale or time zone from the button action, localized strings are called from the fxproperties. If you clicked the "????????????" (Simplified Chinese) button, Simplified Chinese strings are taken from Main_zh_CN.fxproperties by the code in Figure 3, because the application locale is zh_CN.
// Change Display Text. // Those Strings are bound to the Text in Stage. viewtext = ##"Start breaking the barriers!"; NBTitle = ##[NB_Title]""; NBDesc = ##[NB_Desc]""; PSTitle = ##[PS_Title]""; PSDesc = ##[PS_Desc]""; SDKTitle = ##[SDK_Title]""; SDKDesc = ##[SDK_Desc]"";
Figure 3: Loading Localized String
Those values are bound to the text and they are changed dynamically after the user clicks the language button.
One more useful feature for internationalization in JavaFX technology is extended format for date and time. JavaFX technology can use POSIX strftime style format to display the date and time. Figure 4 is an example of extended format.
class Clock {
public function nextTime() {
var now = new Date();
LocalizedDate = "{%tEc now}";
}
Figure 4: Extended Format
{%tEc now} is the extended format used for the java.util.Date object ("now"). It displays the localized date and time format automatically. The following are examples of extended format.- %tx - Localized date
- %tX - Localized time
- %tu - A day of the week (1-7, 1 is for Monday)
- %tEx - Locale's alternate date representation
- %tEX - Locale's alternate time representation
For details, refer to the POSIX strftime specification.
Kenji Tachibana