javafx
JavaFXでの国際化
サーチ…
リソースバンドルのロード
JavaFXは、ユーザーインターフェイスを国際化する簡単な方法を提供します。 FXMLファイルからビューを作成する際に、 FXMLLoader
にリソースバンドルを提供することができます。
Locale locale = new Locale("en", "UK");
ResourceBundle bundle = ResourceBundle.getBundle("strings", locale);
Parent root = FXMLLoader.load(getClass().getClassLoader()
.getResource("ui/main.fxml"), bundle);
この提供されたバンドルは、 %
で始まるFXMLファイルのすべてのテキストを自動的に翻訳するために使用されます。プロパティファイルstrings_en_UK.properties
に次の行が含まれているとします。
ui.button.text=I'm a Button
FXMLに以下のようなボタン定義がある場合:
<Button text="%ui.button.text"/>
ui.button.text
キーの翻訳が自動的に受信されます。
コントローラ
リソースバンドルには、ロケール固有のオブジェクトが含まれています。バンドルはFXMLLoader
作成時に渡すことができます。コントローラはInitializable
インターフェイスを実装し、 initialize(URL location, ResourceBundle resources)
メソッドをオーバーライドする必要があります。このメソッドの2番目のパラメータはResourceBundle
これはFXMLLoaderからコントローラに渡され、コントローラがテキストをさらに翻訳したり、他のロケール依存の情報を変更するために使用できます。
public class MyController implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
label.setText(resources.getString("country"));
}
}
アプリケーションの実行時に言語を動的に切り替える
この例では、アプリケーションの実行中に言語を動的に切り替えることができるJavaFXアプリケーションを構築する方法を示します。
この例で使用されているメッセージバンドルファイルは次のとおりです。
messages_en.properties :
window.title=Dynamic language change
button.english=English
button.german=German
label.numSwitches=Number of language switches: {0}
messages_de.properties :
window.title=Dynamischer Sprachwechsel
button.english=Englisch
button.german=Deutsch
label.numSwitches=Anzahl Sprachwechsel: {0}
基本的なアイデアは、ユーティリティクラスI18Nを持つことです(代替として、これはシングルトンに実装されるかもしれません)。
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
/**
* I18N utility class..
*/
public final class I18N {
/** the current selected Locale. */
private static final ObjectProperty<Locale> locale;
static {
locale = new SimpleObjectProperty<>(getDefaultLocale());
locale.addListener((observable, oldValue, newValue) -> Locale.setDefault(newValue));
}
/**
* get the supported Locales.
*
* @return List of Locale objects.
*/
public static List<Locale> getSupportedLocales() {
return new ArrayList<>(Arrays.asList(Locale.ENGLISH, Locale.GERMAN));
}
/**
* get the default locale. This is the systems default if contained in the supported locales, english otherwise.
*
* @return
*/
public static Locale getDefaultLocale() {
Locale sysDefault = Locale.getDefault();
return getSupportedLocales().contains(sysDefault) ? sysDefault : Locale.ENGLISH;
}
public static Locale getLocale() {
return locale.get();
}
public static void setLocale(Locale locale) {
localeProperty().set(locale);
Locale.setDefault(locale);
}
public static ObjectProperty<Locale> localeProperty() {
return locale;
}
/**
* gets the string with the given key from the resource bundle for the current locale and uses it as first argument
* to MessageFormat.format, passing in the optional args and returning the result.
*
* @param key
* message key
* @param args
* optional arguments for the message
* @return localized formatted string
*/
public static String get(final String key, final Object... args) {
ResourceBundle bundle = ResourceBundle.getBundle("messages", getLocale());
return MessageFormat.format(bundle.getString(key), args);
}
/**
* creates a String binding to a localized String for the given message bundle key
*
* @param key
* key
* @return String binding
*/
public static StringBinding createStringBinding(final String key, Object... args) {
return Bindings.createStringBinding(() -> get(key, args), locale);
}
/**
* creates a String Binding to a localized String that is computed by calling the given func
*
* @param func
* function called on every change
* @return StringBinding
*/
public static StringBinding createStringBinding(Callable<String> func) {
return Bindings.createStringBinding(func, locale);
}
/**
* creates a bound Label whose value is computed on language change.
*
* @param func
* the function to compute the value
* @return Label
*/
public static Label labelForValue(Callable<String> func) {
Label label = new Label();
label.textProperty().bind(createStringBinding(func));
return label;
}
/**
* creates a bound Button for the given resourcebundle key
*
* @param key
* ResourceBundle key
* @param args
* optional arguments for the message
* @return Button
*/
public static Button buttonForKey(final String key, final Object... args) {
Button button = new Button();
button.textProperty().bind(createStringBinding(key, args));
return button;
}
}
このクラスには、JavaFX ObjectProperty
ラップされたJava Locale
オブジェクトである静的フィールドlocale
があり、このプロパティに対してバインディングを作成できます。最初のメソッドは、JavaFXプロパティを取得および設定するための標準的なメソッドです。
get(final String key, final Object... args)
は、 ResourceBundle
からの実際のメッセージ抽出に使用されるコアメソッドです。
createStringBinding
という2つのメソッドは、 locale
フィールドにバインドされたStringBinding
を作成し、 locale
プロパティが変更されるたびにバインディングが変更されるようにしlocale
。最初の引数は、上記のget
メソッドを使用してメッセージを取得および書式設定する引数を使用し、2番目の引数はCallable
に渡され、新しい文字列値を生成する必要があります。
最後の2つのメソッドは、JavaFXコンポーネントを作成するメソッドです。最初のメソッドは、 Label
を作成するために使用され、内部文字列バインディングのためにCallable
を使用します。 2番目はButton
を作成し、キー値を使用してStringバインディングを取得します。
もちろん、 MenuItem
やToolTip
ように多くの異なるオブジェクトを作成することができToolTip
が、これらの2つの例で十分です。
このコードは、このクラスがアプリケーション内でどのように使用されるかを示しています。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.util.Locale;
/**
* Sample application showing dynamic language switching,
*/
public class I18nApplication extends Application {
/** number of language switches. */
private Integer numSwitches = 0;
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.titleProperty().bind(I18N.createStringBinding("window.title"));
// create content
BorderPane content = new BorderPane();
// at the top two buttons
HBox hbox = new HBox();
hbox.setPadding(new Insets(5, 5, 5, 5));
hbox.setSpacing(5);
Button buttonEnglish = I18N.buttonForKey("button.english");
buttonEnglish.setOnAction((evt) -> switchLanguage(Locale.ENGLISH));
hbox.getChildren().add(buttonEnglish);
Button buttonGerman = I18N.buttonForKey("button.german");
buttonGerman.setOnAction((evt) -> switchLanguage(Locale.GERMAN));
hbox.getChildren().add(buttonGerman);
content.setTop(hbox);
// a label to display the number of changes, recalculating the text on every change
final Label label = I18N.labelForValue(() -> I18N.get("label.numSwitches", numSwitches));
content.setBottom(label);
primaryStage.setScene(new Scene(content, 400, 200));
primaryStage.show();
}
/**
* sets the given Locale in the I18N class and keeps count of the number of switches.
*
* @param locale
* the new local to set
*/
private void switchLanguage(Locale locale) {
numSwitches++;
I18N.setLocale(locale);
}
}
このアプリケーションは、 I18N
クラスによって作成されたStringBinding
を使用する3つの異なる方法を示しています。
- ウィンドウタイトルは
StringBinding
直接使用してバインドされます。 - ボタンはヘルパーメソッドをメッセージキーと共に使用します
- ラベルは
Callable
ヘルパーメソッドを使用します。このCallable
はI18N.get()
メソッドを使用して、スイッチの実際の数を含む書式付きの変換された文字列を取得します。
ボタンをクリックすると、カウンタが増加し、 I18N
のロケールプロパティが設定され、文字列バインディングの変更がトリガされ、UIの文字列が新しい値に設定されます。