javafx
Internationalisering in JavaFX
Zoeken…
Bronnenbundel laden
JavaFX biedt een eenvoudige manier om uw gebruikersinterfaces te internationaliseren. Tijdens het maken van een weergave van een FXML-bestand kunt u de FXMLLoader
voorzien van een FXMLLoader
:
Locale locale = new Locale("en", "UK");
ResourceBundle bundle = ResourceBundle.getBundle("strings", locale);
Parent root = FXMLLoader.load(getClass().getClassLoader()
.getResource("ui/main.fxml"), bundle);
Deze meegeleverde bundel wordt automatisch gebruikt om alle teksten in uw FXML-bestand te vertalen die beginnen met een %
. Laten we zeggen dat uw eigenschappenbestand strings_en_UK.properties
de volgende regel bevat:
ui.button.text=I'm a Button
Als u een knopdefinitie in uw FXML als volgt heeft:
<Button text="%ui.button.text"/>
Het ontvangt automatisch de vertaling voor de sleutel ui.button.text
.
controleur
A Bronbundels bevatten locale-specifieke objecten. U kunt de bundel doorgeven aan de FXMLLoader
tijdens het maken. De controller moet de Initializable
interface implementeren en de methode initialize(URL location, ResourceBundle resources)
overschrijven. De tweede parameter voor deze methode is ResourceBundle
die wordt doorgegeven van de FXMLLoader naar de controller en door de controller kan worden gebruikt om teksten verder te vertalen of andere locale-afhankelijke informatie te wijzigen.
public class MyController implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
label.setText(resources.getString("country"));
}
}
Schakel dynamisch van taal wanneer de toepassing wordt uitgevoerd
Dit voorbeeld laat zien hoe een JavaFX-toepassing wordt gebouwd, waarbij de taal dynamisch kan worden gewijzigd terwijl de toepassing wordt uitgevoerd.
Dit zijn de berichtenbundelbestanden die in het voorbeeld zijn gebruikt:
messages_nl.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}
Het basisidee is om een gebruiksklasse I18N te hebben (als alternatief kan dit een singleton zijn).
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;
}
}
Deze klasse heeft een statisch veld locale
dat is een Java Locale
voorwerp gewikkeld in een JavaFX ObjectProperty
, zodat bindingen kunnen worden gemaakt voor deze eigenschap. De eerste methoden zijn de standaardmethoden om een JavaFX-eigenschap op te halen en in te stellen.
De get(final String key, final Object... args)
is de kernmethode die wordt gebruikt voor de echte extractie van een bericht uit een ResourceBundle
.
De twee methoden met de naam createStringBinding
maken een StringBinding
die gebonden is aan het locale
veld en dus veranderen de bindingen wanneer de eigenschap locale
verandert. De eerste maakt gebruik van deze argumenten op te halen en opmaken van een bericht met behulp van de get
methode hierboven genoemde, de tweede wordt doorgegeven in een Callable
, waarin de nieuwe reeks waarde moet produceren.
De laatste twee methoden zijn methoden om JavaFX-componenten te maken. De eerste methode wordt gebruikt om een Label
te maken en gebruikt Callable
voor de interne stringbinding. De tweede maakt een Button
en gebruikt een sleutelwaarde voor het ophalen van de stringbinding.
Natuurlijk kunnen er veel meer verschillende objecten worden gemaakt, zoals MenuItem
of ToolTip
maar deze twee zouden genoeg moeten zijn voor een voorbeeld.
Deze code laat zien hoe deze klasse wordt gebruikt in de toepassing:
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);
}
}
De applicatie toont drie verschillende manieren om StringBinding
gecreëerd door de I18N
klasse:
- de venstertitel wordt gebonden door rechtstreeks een
StringBinding
. - de knoppen gebruiken de helper-methode met de berichttoetsen
- het label gebruikt de helper-methode met een
Callable
. DezeCallable
gebruikt de methodeI18N.get()
om een opgemaakte vertaalde tekenreeks te krijgen die het werkelijke aantal schakelaars bevat.
Als u op een knop klikt, wordt de teller verhoogd en wordt de I18N
de I18N
ingesteld, waardoor de I18N
gewijzigd en de string van de gebruikersinterface dus op nieuwe waarden wordt ingesteld.