How to reload the screen when changing languages​ in JavaFX?


Question

Well, I'm beginner with java and fxml.

I creating a application and need to change language of the screen. I have the file with internationalized keys but i no have idea as reload the screen with the changed language. The application have a menu where have the language available. I want just refresh screen when the user change language.

change is still manual, as you can see on code: (Main.java):

public class Main extends Application {

private Locale locale = new Locale("en", "US");
private Image icon = new Image("picture.jpg");

@Override
public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("Home.fxml"), ResourceBundle.getBundle("label", locale));
    Scene scene = new Scene(root);
    stage.setTitle("GUI");
    stage.getIcons().add(icon);
    stage.setScene(scene);
    stage.show();
}
public static void main(String[] args) {
    launch(args);
}

This code is on the controller, when change language:

    @FXML
private void btnMenuLanguageEnglishAction(ActionEvent event) {
    this.locale = new Locale("en", "US");
}

@FXML
private void btnMenuLanguagePortuguesAction(ActionEvent event) {
    this.locale = new Locale("pt", "BR");
}

how to send this locale to main and refresh the screen? as will be the method that I use? I've tried some that I saw here on the site but no one answered my question.

1
4
1/16/2014 7:38:18 PM

Accepted Answer

Here is my implementation:

import javafx.fxml.FXMLLoader;
import javafx.geometry.NodeOrientation;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

/**
 * Created by Fouad on 3/20/2015.
 */
public abstract class I18NController
{
    private Stage primaryStage;

    public void setPrimaryStage(Stage primaryStage){this.primaryStage = primaryStage;}
    public Stage getPrimaryStage(){return primaryStage;}

    public final void changeLanguage(I18NLanguage language) throws IOException
    {
        StateBundle stateBundle = new StateBundle();
        onSaveState(stateBundle);

        Locale locale = language.getLocale();
        Locale.setDefault(locale);

        ResourceBundle resourceBundle = getResourceBundle(locale);
        URL fxml = getFXMLResource();
        FXMLLoader loader = new FXMLLoader(fxml, resourceBundle);
        Parent root = loader.load();

        NodeOrientation nodeOrientation = language.getNodeOrientation();
        root.setNodeOrientation(nodeOrientation);

        primaryStage.setScene(new Scene(root));
        primaryStage.sizeToScene();

        I18NController newController = loader.getController();
        newController.setPrimaryStage(primaryStage);

        onLoadState(newController, language, resourceBundle, stateBundle);
    }

    protected abstract ResourceBundle getResourceBundle(Locale locale);
    protected abstract URL getFXMLResource();
    protected abstract void onSaveState(StateBundle stateBundle);
    protected abstract void onLoadState(I18NController newController, I18NLanguage newLanguage, ResourceBundle resourceBundle, StateBundle stateBundle);

    public static interface I18NLanguage
    {
        Locale getLocale();
        NodeOrientation getNodeOrientation();
    }

    public static class StateBundle
    {
        private Map<String, Object> sMap = new HashMap<>();

        StateBundle(){}

        public void putData(String key, Object value)
        {
            sMap.put(key, value);
        }

        public <T> T getDate(String key, Class<T> type)
        {
            return type.cast(sMap.get(key));
        }
    }
}

You can use this class as base class to the controller, something like this:

JavaFXController.java:

public class JavaFXController extends I18NController implements Initializable
{
    @FXML private DatePicker dpDate;
    @FXML private RadioButton rdoArabic;
    @FXML private RadioButton rdoEnglish;

    // ...

    @Override
    public void initialize(URL location, ResourceBundle resources)
    {
        // ...

        rdoEnglish.setOnAction(e ->
        {
            try
            {
                changeLanguage(AppSettings.Language.ENGLISH);
            }
            catch(IOException e1)
            {
                e1.printStackTrace();
            }
        });

        rdoArabic.setOnAction(e ->
        {
            try
            {
                changeLanguage(AppSettings.Language.ARABIC);
            }
            catch(IOException e1)
            {
                e1.printStackTrace();
            }
        });
    }

    // ...

    @Override
    protected ResourceBundle getResourceBundle(Locale locale)
    {
        return ResourceBundle.getBundle("com/stackoverflow/gui/resources/JavaFXResourceBundle", locale, new UTF8Control());
    }

    @Override
    protected URL getFXMLResource()
    {
        return getClass().getResource("resources/JavaFXDocument.fxml");
    }

    @Override
    protected void onSaveState(StateBundle stateBundle)
    {
        LocalDate localDate = dpDate.getValue();
        boolean isRdoArabicSelected = rdoArabic.isSelected();
        boolean isRdoEnglishSelected = rdoEnglish.isSelected();

        stateBundle.putData("localDate", localDate);
        stateBundle.putData("isRdoArabicSelected", isRdoArabicSelected);
        stateBundle.putData("isRdoEnglishSelected", isRdoEnglishSelected);
    }

    @Override
    protected void onLoadState(I18NController newController, I18NLanguage newLanguage, ResourceBundle resourceBundle, StateBundle stateBundle)
    {
        JavaFXController controller = (JavaFXController) newController;
        controller.getPrimaryStage().setTitle(resourceBundle.getString("window.title"));
        NodeOrientation nodeOrientation = newLanguage.getNodeOrientation();

        LocalDate localDate = stateBundle.getDate("localDate", LocalDate.class);
        boolean isRdoArabicSelected = stateBundle.getDate("isRdoArabicSelected", Boolean.class);
        boolean isRdoEnglishSelected = stateBundle.getDate("isRdoEnglishSelected", Boolean.class);

        controller.dpDate.setValue(localDate);
        controller.rdoArabic.setSelected(isRdoArabicSelected);
        controller.rdoEnglish.setSelected(isRdoEnglishSelected);
    }
}

AppSettings.java:

import com.parmajeyat.autobooking.gui.I18NController;
import javafx.geometry.NodeOrientation;

import java.util.Locale;

/**
 * Created by Fouad on 2/7/2015.
 */
public final class AppSettings
{
    private static final class Locales
    {
        public static final Locale SAUDI_AR_LOCALE = new Locale.Builder().setLanguageTag("ar-SA-u-nu-arab").build(); // nu is for numbers
        public static final Locale SAUDI_EN_LOCALE = new Locale("en", "SA");
    }

    public static enum Language implements I18NController.I18NLanguage
    {
        ARABIC(Locales.SAUDI_AR_LOCALE, NodeOrientation.RIGHT_TO_LEFT),
        ENGLISH(Locales.SAUDI_EN_LOCALE, NodeOrientation.LEFT_TO_RIGHT);

        private Locale locale;
        private NodeOrientation nodeOrientation;

        Language(Locale locale, NodeOrientation nodeOrientation)
        {
            this.locale = locale;
            this.nodeOrientation = nodeOrientation;
        }

        public Locale getLocale(){return locale;}
        public NodeOrientation getNodeOrientation(){return nodeOrientation;}
    }
}

UTF8Control.java:

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

/**
 * Created by Fouad on 2/1/2015.
 */
public class UTF8Control extends ResourceBundle.Control
{
    public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException
    {
        // The below is a copy of the default implementation.
        String bundleName = toBundleName(baseName, locale);
        String resourceName = toResourceName(bundleName, "properties");
        ResourceBundle bundle = null;
        InputStream stream = null;

        if(reload)
        {
            URL url = loader.getResource(resourceName);
            if(url != null)
            {
                URLConnection connection = url.openConnection();
                if(connection != null)
                {
                    connection.setUseCaches(false);
                    stream = connection.getInputStream();
                }
            }
        }
        else
        {
            stream = loader.getResourceAsStream(resourceName);
        }

        if(stream != null)
        {
            try
            {
                // Only this line is changed to make it to read properties files as UTF-8.
                bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
            }
            finally
            {
                stream.close();
            }
        }

        return bundle;
    }
}
5
3/20/2015 10:33:51 PM

I know, it is quite old question, but I am new in JavaFX and I had just the same problem. Here is my final solution for change language in app. It may not be ideal, but it works for me. In controller I have that method:

@FXML
private BorderPane root;  //root pane 

@FXML
private void changeLocale(ActionEvent event) throws IOException{
    Scene scene = root.getScene();
        if(event.getSource().equals(lang_en)){
            scene.setRoot(FXMLLoader.load(getClass().getResource("Layout.fxml"),ResourceBundle.getBundle("resources/Bundle", Locale.ENGLISH))); // = new Locale("en")
        }else if(event.getSource().equals(lang_cs)){
            scene.setRoot(FXMLLoader.load(getClass().getResource("Layout.fxml"),ResourceBundle.getBundle("resources/Bundle", new Locale("cs", "CZ"))));
        }else{
        }
}

The method loads new loader into my scene (to load into stage also works).

For complete scenario ... I can change current language with two radiomenuitems in menu, so after loading new loader (in the method "public void initialize(URL location, ResourceBundle resources)" in controller) I change selection of radiomenuitems with this switch:

switch(resources.getLocale().getLanguage()){
        case "en":
            lang_en.setSelected(true);
            break;
        case "cs":
            lang_cs.setSelected(true);
            break;
        default:
            break;
        }

It is simple and may be useful for someone else who will have that problem.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon