【问题标题】:JavaFX 2 and InternationalizationJavaFX 2 和国际化
【发布时间】:2012-04-25 23:15:07
【问题描述】:

在学习了基础知识后,我刚刚开始编写我的第一个 JavaFX 2 应用程序,并希望将其国际化。

我注意到在 JavaFX 1.x 中,脚本语言允许非常简单的字符串国际化。 JavaFX 2 中是否有类似的功能?

基本上:国际化 JavaFX 2 应用程序的最佳实践是什么?

【问题讨论】:

标签: internationalization javafx-2


【解决方案1】:

Java 应用程序国际化的基本步骤(除其他外)是Localelizing 和资源捆绑。在 JavaFX 中,您可以为此目的使用 FXMLLoader#setResources()。这里有一个 SSCCE 演示来演示它。这些代码是自我描述的。
演示包结构:

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);
    }
}

截图:


如果您的国际化文本需要以用户目标系统上可能存在的字体呈现,那么您可以:

  1. 在您的应用程序中嵌入字体:

  1. Use web(Google) fonts in JavaFX

如果所需的字体不可用,则国际化文本可能会显示为难以理解的乱码,即使有关设置的其他一切都很好。

【讨论】:

  • 优秀的答案,我会按原样接受它,但我应该提到我正在用代码而不是 FXML 构建接口。是否有一种快速简便的代码国际化方法,我意识到我可以进行 ResourceBundle.getBundle + 查找,但我希望可以使用类似 %key 表示法的东西。
  • 然后您可以像在任何其他 Java 应用程序中一样以普通方式执行此操作。确定用户/客户的语言环境,然后相应地更改应用程序的语言环境(从数据库获取特定于语言的数据)。通过ResourceBundle.getBundle("bundles.MyBundle", locale) 加载适当的捆绑包。通过bundle.getString("key") 更改您在视图/页面中使用的每个文本。
  • 如果我通过 setResources() 方法提供 ResourceBundle,这对我不起作用。当我通过 load() 方法提供 ResourceBundle 时它正在工作。
  • @Jurica Krizanic:遇到了同样的问题,并通过相同的方式解决了它:FXMLLoader.load(getClass().getResource(sceneId), getResources()) 其中sceneId 是字符串,方法getResources() 返回具有适当语言环境的资源。
  • 您好@UlukBiy 我们可以在&lt;Label text="%key2"/&gt; 中多次添加文本,例如“%key2%key1”。因为我已经尝试过了,但它给了我例外......但我不知道它是否可能......!
【解决方案2】:

这对我有用:

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

在bundles包中有

LangBundle_en.properties
LangBundle_de.properties

示例内容:

enter_pwd=Enter your password:

要加载它们,我使用以下代码:

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

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

【讨论】:

    【解决方案3】:

    看看我的例子

    更多我描述了hereGitHub

    更新:

    解决方案在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;
        }
    }
    

    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 你会点击链接并看到详细的响应和完整的源文件,我已经包含了一个链接来响应 stackoverflow.com。因为你,我必须在任何地方插入相同的代码。我希望你喜欢而不是不喜欢
    猜你喜欢
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 2017-09-16
    • 2023-01-10
    • 2017-04-11
    • 1970-01-01
    • 2017-06-09
    相关资源
    最近更新 更多