JavaFX 2 và quốc tế hóa


83

Tôi vừa mới bắt đầu viết ứng dụng JavaFX 2 đầu tiên của mình sau khi tìm hiểu các kiến ​​thức cơ bản và muốn quốc tế hóa nó.

Tôi nhận thấy rằng trong JavaFX 1.x, ngôn ngữ kịch bản cho phép quốc tế hóa các chuỗi rất đơn giản. Có tính năng nào tương tự trong JavaFX 2 không?

Về cơ bản: thực tiễn tốt nhất để quốc tế hóa một ứng dụng JavaFX 2 là gì?


Để chuyển đổi giữa các ngôn ngữ là một số thông tin ở đây: [ stackoverflow.com/a/26318795/2131257][1] [1]: stackoverflow.com/a/26318795/2131257
Androdos

Câu trả lời:


163

Các bước cơ bản (trong số các bước khác) của một ứng dụng java quốc tế hóa, là Localelizing và gói tài nguyên. Trong JavaFX, bạn có thể sử dụng FXMLLoader#setResources()cho các mục đích đó. Đây là một bản demo SSCCE để chứng minh điều đó. Các mã này là tự mô tả.
Cấu trúc gói demo:

bundledemo
    |------ BundleDemo.java
    |------ MyController.java
    |------ MyView.fxml  
bundles
    |------ MyBundle_en.properties
    |------ MyBundle_kg.properties

MyBundle_en.properties

key1=Name Surname
key2=How are you?

MyBundle_kg.properties

key1=Aты Жөнү
key2=Кандайсың?

MyView.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.*?>

<BorderPane fx:controller="bundledemo.MyController" xmlns:fx="http://javafx.com/fxml">
    <top>
        <!-- This label's text will be set by the controller -->
        <Label fx:id="lblTextByController"/> 
    </top>
    <center>
        <!-- This label's text will be taken from the bundle automatically -->
        <Label text="%key2"/>
    </center>
</BorderPane>

MyController.java

package bundledemo;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class MyController implements Initializable {

    @FXML private Label lblTextByController;
    private ResourceBundle bundle;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        bundle = resources;
        lblTextByController.setText(bundle.getString("key1"));
    }
}

BundleDemo.java

package bundledemo;
// imports are ignored.

public class BundleDemo extends Application {

    private Stage stage;

    @Override
    public void start(Stage primaryStage) {
        stage = primaryStage;
        Button btnEN = new Button();
        btnEN.setText("English");
        btnEN.setOnAction(new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent event) {
                loadView(new Locale("en", "EN"));
            }
        });

        Button btnKG = new Button();
        btnKG.setText("Kyrgyz");
        btnKG.setOnAction(new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent event) {
                loadView(new Locale("kg", "KG"));
            }
        });

        VBox root = new VBox(20);
        root.getChildren().add(HBoxBuilder.create().spacing(10).style("-fx-background-color: gray").padding(new Insets(5)).children(btnEN, btnKG).build());
        root.getChildren().add(new StackPane());
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }

    private void loadView(Locale locale) {
        try {
            FXMLLoader fxmlLoader = new FXMLLoader();
            fxmlLoader.setResources(ResourceBundle.getBundle("bundles.MyBundle", locale));
            Pane pane = (BorderPane) fxmlLoader.load(this.getClass().getResource("MyView.fxml").openStream());
            // replace the content
            StackPane content = (StackPane) ((VBox) stage.getScene().getRoot()).getChildren().get(1);
            content.getChildren().clear();
            content.getChildren().add(pane);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Ảnh chụp màn hình:

nhập mô tả hình ảnh ở đây


Câu trả lời tuyệt vời và tôi sẽ chấp nhận nó như vậy nhưng tôi nên đề cập rằng tôi đang xây dựng giao diện bằng mã chứ không phải FXML. Có cách nào nhanh chóng và dễ dàng để quốc tế hóa mã không, tôi nhận ra mình có thể thực hiện tra cứu ResourceBundle.getBundle + nhưng tôi đã hy vọng có một cái gì đó giống như ký hiệu phím% mà tôi có thể sử dụng thay thế.
wobblycogs 13/12/12

7
Sau đó, bạn có thể làm điều đó theo cách bình thường như trong bất kỳ ứng dụng Java nào khác. Xác định ngôn ngữ của người dùng / khách hàng sau đó thay đổi ngôn ngữ của ứng dụng cho phù hợp (lấy dữ liệu ngôn ngữ cụ thể từ DB vs.). Tải gói thích hợp bằng ResourceBundle.getBundle("bundles.MyBundle", locale). Thay đổi mọi văn bản bạn đã sử dụng trong chế độ xem / trang của mình bundle.getString("key").
Uluk Biy

2
Nó không hoạt động đối với tôi nếu tôi cung cấp ResourceBundle qua phương thức setResources (). Nó đang hoạt động khi tôi cung cấp ResourceBundle thông qua phương thức load ().
Jurica Krizanic

1
@Jurica Krizanic: gặp vấn đề tương tự và đã giải quyết nó theo cách tương tự: FXMLLoader.load(getClass().getResource(sceneId), getResources())đâu sceneIdlà chuỗi và phương thức getResources()trả về tài nguyên với ngôn ngữ phù hợp.
TG

Tuyệt vời, một biến thể với cách tĩnh như trong hướng dẫn oracle cũng hoạt động (và nó ngắn hơn): Pane pane = (BorderPane) FxmlLoader.load (this.getClass (). GetResource ("MyView.fxml"), ResourceBundle.getBundle ("Bundles.MyBundle", ngôn ngữ));
pdem

14

Điều này phù hợp với tôi:

└───src
    ├───app
    ├───bundles // <- here the "bundles"
    ├───dicts
    ├───images
    ├───libs
    └───resources

Trong gói gói là

LangBundle_en.properties
LangBundle_de.properties

Nội dung mẫu:

enter_pwd=Enter your password:

Để tải chúng, tôi sử dụng mã sau:

@Override
public void initialize(URL location, ResourceBundle resources) {
    ResourceBundle lngBndl = ResourceBundle
            .getBundle("bundles.LangBundle", new Locale("en", "EN"));

    tvSetupPwd.setText(lngBndl.getString("enter_pwd"));
    // ...
}

4

Nhìn vào ví dụ của tôi nhập mô tả hình ảnh ở đây

Tôi đã mô tả thêm ở đây hoặc trên GitHub

Cập nhật:

giải pháp nằm trong Messages.java

/**
 * The class with all messages of this application.
 */
public abstract class Messages {

    private static ResourceBundle BUNDLE;

    private static final String FIELD_NAME = "lookup";
    private static final String BUNDLE_NAME = "messages/messages";
    private static final String CONTROLS_BUNDLE_NAME = "com/sun/javafx/scene/control/skin/resources/controls";

    public static final String MAIN_APP_TITLE;

    public static final String DIALOG_HEADER;
    public static final String MAIN_CONTROLLER_CONTENT_TEXT;
    public static final String MAIN_CONTROLLER_HELLO_TEXT;
    public static final String MAIN_CONTROLLER_GOODBYE_TEXT;

    static {
        final Locale locale = Locale.getDefault();
        final ClassLoader classLoader = ControlResources.class.getClassLoader();

        final ResourceBundle controlBundle = getBundle(CONTROLS_BUNDLE_NAME,
                locale, classLoader, PropertyLoader.getInstance());

        final ResourceBundle overrideBundle = getBundle(CONTROLS_BUNDLE_NAME,
                PropertyLoader.getInstance());

        final Map override = getUnsafeFieldValue(overrideBundle, FIELD_NAME);
        final Map original = getUnsafeFieldValue(controlBundle, FIELD_NAME);

        //noinspection ConstantConditions,ConstantConditions,unchecked
        original.putAll(override);

        BUNDLE = getBundle(BUNDLE_NAME, PropertyLoader.getInstance());

        MAIN_APP_TITLE = BUNDLE.getString("MainApp.title");

        DIALOG_HEADER = BUNDLE.getString("Dialog.information.header");
        MAIN_CONTROLLER_CONTENT_TEXT = BUNDLE.getString("MainController.contentText");
        MAIN_CONTROLLER_HELLO_TEXT = BUNDLE.getString("MainController.helloText");
        MAIN_CONTROLLER_GOODBYE_TEXT = BUNDLE.getString("MainController.goodbyeText");
    }

    public static ResourceBundle GetBundle() {
        return BUNDLE;
    }
}

và trong PropertyLoader.java

public class PropertyLoader extends ResourceBundle.Control {

    private static final String PROPERTIES_RESOURCE_NAME = "properties";

    private static final PropertyLoader INSTANCE = new PropertyLoader();

    public static PropertyLoader getInstance() {
        return INSTANCE;
    }

    @Override
    public ResourceBundle newBundle(final String baseName, final Locale locale, final String format,
                                    final ClassLoader loader, final boolean reload)
            throws IllegalAccessException, InstantiationException, IOException {

        final String bundleName = toBundleName(baseName, locale);
        final String resourceName = toResourceName(bundleName, PROPERTIES_RESOURCE_NAME);

        ResourceBundle bundle = null;
        InputStream stream = null;

        if (reload) {

            final URL url = loader.getResource(resourceName);

            if (url != null) {
                final URLConnection connection = url.openConnection();
                if (connection != null) {
                    connection.setUseCaches(false);
                    stream = connection.getInputStream();
                }
            }

        } else {
            stream = loader.getResourceAsStream(resourceName);
        }

        if (stream != null) {
            try {
                bundle = new PropertyResourceBundle(new InputStreamReader(stream, StandardCharsets.UTF_8));
            } finally {
                stream.close();
            }
        }

        return bundle;
    }
}

@Moritz, bạn sẽ nhấp vào liên kết và xem phản hồi chi tiết và các tệp nguồn đầy đủ, tôi đã bao gồm một liên kết để phản hồi lại stackoverflow.com. Vì Bạn, tôi phải chèn vào mọi nơi cùng một mã. Tôi hy vọng bạn thích và không thích
Andrei Krasutski
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.