Ricerca…


Caricamento del pacchetto di risorse

JavaFX offre un modo semplice per internazionalizzare le interfacce utente. Durante la creazione di una vista da un file FXML puoi fornire a FXMLLoader un pacchetto di risorse:

Locale locale = new Locale("en", "UK");
ResourceBundle bundle = ResourceBundle.getBundle("strings", locale);

Parent root = FXMLLoader.load(getClass().getClassLoader()
                                  .getResource("ui/main.fxml"), bundle);

Questo pacchetto fornito viene automaticamente utilizzato per tradurre tutti i testi nel file FXML che iniziano con % . Diciamo che il file delle proprietà strings_en_UK.properties contiene la seguente riga:

ui.button.text=I'm a Button

Se hai una definizione di pulsante nel tuo FXML in questo modo:

<Button text="%ui.button.text"/>

Riceverà automaticamente la traduzione per la chiave ui.button.text .

controllore

Un pacchetto di risorse contiene oggetti specifici delle impostazioni internazionali. È possibile passare il pacchetto a FXMLLoader durante la sua creazione. Il controller deve implementare il metodo Initializable interface e override initialize(URL location, ResourceBundle resources) . Il secondo parametro di questo metodo è ResourceBundle che viene passato da FXMLLoader al controller e può essere utilizzato dal controller per tradurre ulteriormente i testi o modificare altre informazioni dipendenti dalla localizzazione.

public class MyController implements Initializable {

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        label.setText(resources.getString("country"));
    }
}

Cambio della lingua in modo dinamico quando l'applicazione è in esecuzione

Questo esempio mostra come creare un'applicazione JavaFX, in cui la lingua può essere cambiata dinamicamente mentre l'applicazione è in esecuzione.

Questi sono i file di bundle dei messaggi utilizzati nell'esempio:

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'idea di base è di avere una classe di utilità I18N (in alternativa potrebbe essere implementata una 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;
    }
}

Questa classe ha un'impostazione locale campo statica che è un oggetto Locale Java racchiuso in una ObjectProperty JavaFX, in modo da poter creare associazioni per questa proprietà. I primi metodi sono i metodi standard per ottenere e impostare una proprietà JavaFX.

Il get(final String key, final Object... args) è il metodo di base utilizzato per l'estrazione reale di un messaggio da un ResourceBundle .

I due metodi denominati createStringBinding creano un StringBinding associato al campo locale e quindi i binding cambieranno ogni volta che la proprietà locale cambia. Il primo usa i suoi argomenti per recuperare e formattare un messaggio usando il metodo get menzionato sopra, il secondo è passato in un Callable , che deve produrre il nuovo valore di stringa.

Gli ultimi due metodi sono metodi per creare componenti JavaFX. Il primo metodo viene utilizzato per creare Label e utilizza un Callable per il suo binding di stringhe interne. Il secondo crea un Button e utilizza un valore chiave per il recupero del collegamento stringa.

Naturalmente molti altri oggetti possono essere creati come MenuItem o ToolTip ma questi due dovrebbero essere sufficienti per un esempio.

Questo codice mostra come questa classe viene utilizzata all'interno dell'applicazione:

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'applicazione mostra tre diversi modi di utilizzare StringBinding creato dalla classe I18N :

  1. il titolo della finestra è associato direttamente utilizzando StringBinding .
  2. i pulsanti usano il metodo di supporto con i tasti dei messaggi
  3. l'etichetta usa il metodo di supporto con un Callable . Questo Callable utilizza il metodo I18N.get() per ottenere una stringa tradotta formattata contenente il conteggio effettivo delle opzioni.

Facendo clic su un pulsante, il contatore viene aumentato e viene impostata la proprietà locale I18N , che a sua volta attiva la modifica delle stringhe e quindi imposta la stringa dell'interfaccia utente su nuovi valori.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow