javafx
Internationalisation en JavaFX
Recherche…
Chargement d'un ensemble de ressources
JavaFX fournit un moyen facile d'internationaliser vos interfaces utilisateur. Lors de la création d'une vue à partir d'un fichier FXML, vous pouvez fournir à FXMLLoader
un regroupement de ressources:
Locale locale = new Locale("en", "UK");
ResourceBundle bundle = ResourceBundle.getBundle("strings", locale);
Parent root = FXMLLoader.load(getClass().getClassLoader()
.getResource("ui/main.fxml"), bundle);
Ce bundle fourni est automatiquement utilisé pour traduire tous les textes de votre fichier FXML qui commencent par un %
. Disons que votre fichier de propriétés strings_en_UK.properties
contient la ligne suivante:
ui.button.text=I'm a Button
Si vous avez une définition de bouton dans votre fichier FXML comme ceci:
<Button text="%ui.button.text"/>
Il recevra automatiquement la traduction de la clé ui.button.text
.
Manette
Un regroupement de ressources contient des objets spécifiques aux paramètres régionaux. Vous pouvez transmettre le bundle à FXMLLoader
lors de sa création. Le contrôleur doit implémenter l'interface Initializable
et remplacer la méthode d' initialize(URL location, ResourceBundle resources)
. Le second paramètre de cette méthode est ResourceBundle
qui est transmis du FXMLLoader au contrôleur et peut être utilisé par le contrôleur pour traduire davantage de texte ou modifier d’autres informations dépendantes de la localisation.
public class MyController implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
label.setText(resources.getString("country"));
}
}
Changer de langue de manière dynamique lorsque l'application est en cours d'exécution
Cet exemple montre comment créer une application JavaFX, dans laquelle le langage peut être changé de manière dynamique pendant l'exécution de l'application.
Voici les fichiers de regroupement de messages utilisés dans l'exemple:
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}
L'idée de base est d'avoir une classe d'utilitaire I18N (en alternative, cela pourrait être un 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;
}
}
Cette classe a un locale
champ statique qui est un objet Java Locale
enveloppé dans JavaFX ObjectProperty
, afin que des liaisons puissent être créées pour cette propriété. Les premières méthodes sont les méthodes standard pour obtenir et définir une propriété JavaFX.
La méthode get(final String key, final Object... args)
est la méthode de base utilisée pour l'extraction réelle d'un message à partir d'un ResourceBundle
.
Les deux méthodes nommées createStringBinding
créent un objet StringBinding
lié au champ des locale
. Par conséquent, les liaisons changent chaque fois que la propriété locale
change. Le premier utilise ses arguments pour récupérer et formater un message en utilisant la méthode get
mentionnée ci-dessus, le second est passé dans un Callable
, qui doit produire la nouvelle valeur de chaîne.
Les deux dernières méthodes sont des méthodes pour créer des composants JavaFX. La première méthode est utilisée pour créer une Label
et utilise un Callable
pour sa liaison de chaîne interne. Le second crée un Button
et utilise une valeur de clé pour la récupération de la liaison String.
Bien sûr, de nombreux objets différents pourraient être créés comme MenuItem
ou ToolTip
mais ces deux éléments devraient suffire pour donner un exemple.
Ce code montre comment cette classe est utilisée dans l'application:
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);
}
}
L'application montre trois manières différentes d'utiliser le StringBinding
créé par la classe I18N
:
- le titre de la fenêtre est lié en utilisant directement un
StringBinding
. - les boutons utilisent la méthode d'assistance avec les clés de message
- l'étiquette utilise la méthode d'assistance avec un
Callable
. CeCallable
utilise la méthodeI18N.get()
pour obtenir une chaîne traduite formatée contenant le nombre réel de commutateurs.
En cliquant sur un bouton, le compteur est augmenté et la propriété locale de I18N
est définie, ce qui déclenche la modification des liaisons de chaînes et définit ainsi la chaîne de l'interface utilisateur sur de nouvelles valeurs.