javafx
Internazionalizzazione in JavaFX
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
:
- il titolo della finestra è associato direttamente utilizzando
StringBinding
. - i pulsanti usano il metodo di supporto con i tasti dei messaggi
- l'etichetta usa il metodo di supporto con un
Callable
. QuestoCallable
utilizza il metodoI18N.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.