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 :

  1. le titre de la fenêtre est lié en utilisant directement un StringBinding .
  2. les boutons utilisent la méthode d'assistance avec les clés de message
  3. l'étiquette utilise la méthode d'assistance avec un Callable . Ce Callable utilise la méthode I18N.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.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow