javafx
Internacjonalizacja w JavaFX
Szukaj…
Ładowanie pakietu zasobów
JavaFX zapewnia łatwy sposób na internacjonalizację interfejsów użytkownika. Podczas tworzenia widoku z pliku FXML możesz dostarczyć FXMLLoader
z pakietem zasobów:
Locale locale = new Locale("en", "UK");
ResourceBundle bundle = ResourceBundle.getBundle("strings", locale);
Parent root = FXMLLoader.load(getClass().getClassLoader()
.getResource("ui/main.fxml"), bundle);
Ten dostarczony pakiet jest automatycznie używany do tłumaczenia wszystkich tekstów w pliku FXML, które zaczynają się od %
. Powiedzmy, że twój plik właściwości strings_en_UK.properties
zawiera następujący wiersz:
ui.button.text=I'm a Button
Jeśli masz definicję przycisku w swoim FXML, tak jak to:
<Button text="%ui.button.text"/>
Automatycznie otrzyma tłumaczenie dla klucza ui.button.text
.
Kontroler
Pakiety zasobów zawierają obiekty specyficzne dla ustawień regionalnych. Możesz przekazać pakiet do FXMLLoader
podczas jego tworzenia. Kontroler musi implementować interfejs Initializable
i przesłonić metodę initialize(URL location, ResourceBundle resources)
. Drugi parametr tej metody to ResourceBundle
który jest przekazywany z FXMLLoader do kontrolera i może być wykorzystywany przez kontroler do dalszego tłumaczenia tekstów lub modyfikowania innych informacji zależnych od ustawień regionalnych.
public class MyController implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
label.setText(resources.getString("country"));
}
}
Dynamiczne przełączanie języka podczas działania aplikacji
Ten przykład pokazuje, jak zbudować aplikację JavaFX, w której język można dynamicznie przełączać podczas działania aplikacji.
Są to pliki pakietów wiadomości użyte w przykładzie:
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}
Podstawową ideą jest posiadanie klasy użyteczności I18N (alternatywnie może to być implementowane jako singleton).
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;
}
}
Ta klasa ma statyczne locale
które są obiektami locale
Locale
Java opakowanymi w obiekt JavaFX ObjectProperty
, dzięki czemu można utworzyć powiązania dla tej właściwości. Pierwsze metody to standardowe metody uzyskiwania i ustawiania właściwości JavaFX.
get(final String key, final Object... args)
to podstawowa metoda używana do rzeczywistego wyodrębnienia wiadomości z zestawu ResourceBundle
.
Dwie metody o nazwie createStringBinding
tworzą StringBinding
który jest powiązany z polem locale
StringBinding
, więc powiązania będą się zmieniać za każdym razem, gdy zmieni się właściwość locale
StringBinding
. Pierwszy z nich wykorzystuje To argumenty, aby pobrać i formatowanie wiadomości za pomocą get
metoda wspomniano powyżej, drugi jest przekazywany w Callable
, które muszą produkować nową wartość ciągu.
Dwie ostatnie metody to metody tworzenia komponentów JavaFX. Pierwsza metoda służy do tworzenia Label
i wykorzystuje Callable
do wewnętrznego wiązania łańcucha. Drugi tworzy Button
i używa wartości klucza do pobrania powiązania String.
Oczywiście można stworzyć wiele innych obiektów, takich jak MenuItem
lub ToolTip
ale te dwa powinny wystarczyć na przykład.
Ten kod pokazuje, jak ta klasa jest używana w aplikacji:
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);
}
}
Aplikacja pokazuje trzy różne sposoby korzystania z StringBinding
utworzonego przez klasę I18N
:
- tytuł okna jest związany bezpośrednio za pomocą
StringBinding
. - przyciski używają metody pomocnika z klawiszami wiadomości
- etykieta używa metody pomocniczej z
Callable
. TenCallable
używaI18N.get()
metodę, aby uzyskać przetłumaczone sformatowany ciąg zawierający rzeczywistą liczbę przełączników.
Po kliknięciu przycisku licznik jest zwiększany i ustawiana jest właściwość locale I18N
, co z kolei powoduje zmianę powiązań ciągów, a tym samym ustawienie ciągu interfejsu użytkownika na nowe wartości.