Javafx国际化与自定义语言 [英] Javafx Internationalization with custom language

查看:1381
本文介绍了Javafx国际化与自定义语言的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个支持多种语言的JavaFX应用程序。我的应用程序有时会显示一个警告框,例如:

 包应用程序; 

import java.util.Locale;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.BorderPane;


公共类主扩展应用程序{
@Override
public void start(Stage primaryStage){
try {
Button btn = new按钮(显示警报);
btn.setOnAction(this :: handleButton);

BorderPane root = new BorderPane();
root.setCenter(btn);
场景场景=新场景(root,200,200);
primaryStage.setScene(scene);
primaryStage.show();
} catch(例外e){
e.printStackTrace();
}
}

void handleButton(ActionEvent e){
警报警报=新警报(AlertType.CONFIRMATION);
alert.showAndWait();
}

static Locale getLocaleSettingFromConfigurationFile(){
返回Locale.FRENCH;
//返回新的Locale(vi);
}

public static void main(String [] args){
Locale appLocale = getLocaleSettingFromConfigurationFile();
Locale.setDefault(appLocale);

launch(args);
}
}

语言设置是通过 getLocaleSettingFromConfigurationFile()方法

在上面的代码中,我使用 Locale.FRENCH 作为应用程序语言,一切正常:
$


两个确认按钮已翻译为法语。


现在我希望我的应用程序也支持越南语(取消注释从上面的代码中返回新的Locale(vi))。在深入细节之后,我发现:



- >两个确认按钮Ok,Cancel构造自:

  package javafx.scene.control; 

import com.sun.javafx.scene.control.skin.resources.ControlResources;

import javafx.beans.NamedArg;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;

/ **
* ButtonType类用作JavaFX {@link Dialog} API(更多
*具体来说,{@link DialogPane} API)的一部分指定对话框中的用户显示哪些按钮
*。有关如何使用此类的更多信息,请参阅{@link DialogPane}类javadoc
*。
*
* @see提醒
* @see对话
* @see DialogPane
* @since JavaFX 8u40
* /
公共决赛class ButtonType {

/ **
*一个预定义的{@link ButtonType},它显示Apply并且有一个
* {@link ButtonData} {@link ButtonData#APPLY}。
* /
public static final ButtonType APPLY = new ButtonType(
Dialog.apply.button,null,ButtonData.APPLY);

/ **
*一个预定义的{@link ButtonType},显示OK并且有一个
* {@link ButtonData} {@link ButtonData#OK_DONE }。
* /
public static final ButtonType OK = new ButtonType(
Dialog.ok.button,null,ButtonData.OK_DONE);

/ **
*一个预定义的{@link ButtonType},显示取消并且有一个
* {@link ButtonData} {@link ButtonData#CANCEL_CLOSE }。
* /
public static final ButtonType CANCEL = new ButtonType(
Dialog.cancel.button,null,ButtonData.CANCEL_CLOSE);

/ **
*一个预定义的{@link ButtonType},显示关闭并且有一个
* {@link ButtonData} {@link ButtonData#CANCEL_CLOSE }。
* /
public static final ButtonType CLOSE = new ButtonType(
Dialog.close.button,null,ButtonData.CANCEL_CLOSE);

/ **
*一个预定义的{@link ButtonType},显示是并且有一个
* {@link ButtonData} {@link ButtonData#YES }。
* /
public static final ButtonType YES = new ButtonType(
Dialog.yes.button,null,ButtonData.YES);

/ **
*一个预定义的{@link ButtonType},显示No并且有一个
* {@link ButtonData} {@link ButtonData #NO }。
* /
public static final ButtonType NO = new ButtonType(
Dialog.no.button,null,ButtonData.NO);

/ **
*一个预定义的{@link ButtonType},显示Finish并且有一个
* {@link ButtonData} {@link ButtonData #FINISH }。
* /
public static final ButtonType FINISH = new ButtonType(
Dialog.finish.button,null,ButtonData.FINISH);

/ **
*一个预定义的{@link ButtonType},显示Next并且有一个
* {@link ButtonData} {@link ButtonData#NEXT_FORWARD }。
* /
public static final ButtonType NEXT = new ButtonType(
Dialog.next.button,null,ButtonData.NEXT_FORWARD);

/ **
*一个预定义的{@link ButtonType},显示Previous并且有一个
* {@link ButtonData} {@link ButtonData#BACK_PREVIOUS }。
* /
public static final ButtonType PREVIOUS = new ButtonType(
Dialog.previous.button,null,ButtonData.BACK_PREVIOUS);

private final String key;
private final String text;
private final ButtonData buttonData;


/ **
*使用给定文本创建ButtonType实例,ButtonData将
*设置为{@link ButtonData#OTHER}。
*
* @param text在控件的text属性中显示的字符串,如
* as {@link Button#t​​extProperty()Button}。
* /
public ButtonType(@NamedArg(text)String text){
this(text,ButtonData.OTHER);
}

/ **
*使用给定文本创建ButtonType实例,并按指定设置ButtonData
*。
*
* @param text在控件的text属性中显示的字符串,如
* as {@link Button#t​​extProperty()Button}。
* @param buttonData应该从此ButtonType创建的按钮类型。
* /
public ButtonType(@NamedArg(text)字符串文本,
@NamedArg(buttonData)ButtonData buttonData){
this(null,text,buttonData) ;
}

/ **
*提供密钥或文字。另一个应为null。
* /
私有ButtonType(字符串键,字符串文本,ButtonData buttonData){
this.key = key;
this.text = text;
this.buttonData = buttonData;
}

/ **
*返回构造函数中为此ButtonType指定的ButtonData。
* /
public final ButtonData getButtonData(){return this.buttonData; }

/ **
*返回构造函数中为此ButtonType指定的文本;
* /
public final String getText(){
if(text == null&& key!= null){
return ControlResources.getString(key);
} else {
返回文字;
}
}

/ ** {@inheritDoc} * /
@Override public String toString(){
returnButtonType [text = + getText()+,buttonData =+ getButtonData()+];
}
}

- >显示文本的按钮来自 ControlResources.getString(key),其源代码:

  package com.sun .javafx.scene.control.skin.resources; 

import java.util.ResourceBundle;

公共最终类ControlResources {

//可翻译属性
私有静态最终字符串BASE_NAME =com / sun / javafx / scene / control / skin / resources /控制;

//不可翻译的属性
private static final String NT_BASE_NAME =com / sun / javafx / scene / control / skin / resources / controls-nt;

//不要在此处缓存捆绑包。它由ResourceBundle
//类缓存,如果默认语言环境发生更改,则可能会更新。

private ControlResources(){
// no-op
}

/ *
*在属性文件中查找字符串对应于
*默认语言环境(即应用程序的语言环境)。如果没有找到,那么
*搜索将回退到基本的controls.properties文件,
*包含默认字符串(通常是英文)。
* /
public static String getString(String key){
return ResourceBundle.getBundle(BASE_NAME).getString(key);
}

/ *
*在属性文件
*中查找与默认语言环境对应的不可翻译字符串(即应用程序的
*环境)。如果未找到,则搜索将回退到包含默认字符串的基本
* controls-nt.properties文件。
*
*请注意,属性值可以在特定于语言环境的文件中设置,
*例如为某个国家/地区定义属性值而不是
*语言时。但是,
* JavaFX 8中不包含此类文件,但可能会被开发人员或
*用户添加到类路径中。
* /
public static String getNonTranslatableString(String key){
return ResourceBundle.getBundle(NT_BASE_NAME).getString(key);
}
}

现在,我尝试了以下解决方案:

第1步:创建越南语资源文件 com / sun / javafx / scene / control / skin / resources / controls_vi.properties 在项目中$

  ###对话框### 

Dialog.apply.button =Ápd\\\ụng
Dialog.ok.button = OK
Dialog.close.button = \\\Đóng
Dialog.cancel.button = H\\\ủy b\\\ỏ
Dialog.yes.button =Có
Dialog.no.button =Không
Dialog.finish.button =Hoànthành
Dialog.next.button = Ti\\\ếp
Dialog.previous.button = Tr\\\ư\\\ớc

在推出应用程序后,按钮语言仍然是英语。


第2步:我发现加载JavaFx资源文件的类加载器与我的app类加载器不同(请参阅 ResourceBundle .getBundle(BASE_NAME) API)。这是 jfxrt.jar 内的资源:



我尝试使用应用程序类加载器加载 ControlResources 类但仍无结果:

  public static void main(String [] args)throws Exception {
List< Locale> fxSupported = Arrays.asList(Locale.ENGLISH,Locale.FRENCH); //稍后添加....
区域设置appLocale = getLocaleSettingFromConfigurationFile();
Locale.setDefault(appLocale);

//从当前类加载器加载类
if(!fxSupported.contains(appLocale)){
ClassLoader loader = Main.class.getClassLoader();
Class<?> loadedCls = Class.forName(com.sun.javafx.scene.control.skin.resources.ControlResources,true,loader);

System.out.printf(Loader 1:%s\\\
loader 2:%s \ n,loader,loadedCls.getClassLoader());
// Loader 1:sun.misc.Launcher$AppClassLoader@73d16e93
// loader 2:sun.misc.Launcher$ExtClassLoader@6d06d69c

}

launch(args);
}

后备解决方案

我可以创建自己的 ButtonType 确定,取消并加载我自己的资源字符串,将创建的按钮列表设置为警告 object,但我想使用系统提供的资源。

  ResourceBundle res = ResourceBundle.getBundle(application.myownres); 
ButtonType OK = new ButtonType(res.getString(btn.ok),ButtonData.OK_DONE);
ButtonType CANCEL = new ButtonType(res.getString(btn.cancel),ButtonData.CANCEL_CLOSE);

警报提醒=新警报(AlertType.CONFIRMATION,你确定,OK,CANCEL);
alert.showAndWait();

所以,任何人都有解决方案,不需要创建新的 ButtonType 对象。

谢谢

解决方案

我很不高兴里面的JRE非常很少有异国语言。这是一个大问题。我一直在寻找解决方案,我创建了一个开源项目,演示了如何在这个项目中添加新的语言资源。





我的项目结构:

  java 
| ------ com\krasutski\language\Messages.java
| ------ com\krasutski\util\PropertyLoader.java
| ------ com\krasutski\util\ReflectionUtils.java
| ------ com\krasutski\view \ MainController.java
| --- --- com.\\krasutski\MainApp.java
资源
| ------ com\sun\javafx\scene\control\skin\resources\controls_be_BY .properties
| ------ com\sun\javafx \\ \\ scene\control\skin\resources\controls_ru.properties
| ------ fxml\main.fxml
| ------ icons\app-128x128x32 .png
| ------ messages \messages.properties
| ------ messages \messages_be_BY.properties
| ------ messages \ messages_ru.properties
| ------ styles\styles.css

该问题的解决方案是 Messages.java

  / ** 
*包含此应用程序所有消息的类。
* /
公共抽象类消息{

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扩展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)
抛出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();
}
}

返回包;
}
}

示例切片文件 controls_be_BY .properties

  #coding = utf-8 
#ProgressIndicator,显示的字符串100%
ProgressIndicator.doneString =Гатова

#ListView
ListView.noContent =Нямазмесціва

#TableView
TableView.noContent = $Нямазмесціваўтабліцы
TableView.noColumns =Нямакалонакўтабліцы

这里你没有需要使用特殊字符 \u 你只需要写入任何支持Unicode的文本编辑器。



你可以添加此项目的外来语言文件夹 resources / com / sun / javafx / scene / control / skin / resources 。发给我你的 controls _ *。properties 然后我会将它们添加到这个项目中。



你可以准备好组装的例子下载发布部分


I'm developing a JavaFX application with multiple language support. My app sometimes shows an alert box, for example:

package application;

import java.util.Locale;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.BorderPane;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Button btn = new Button("Show alert");
            btn.setOnAction(this::handleButton);

            BorderPane root = new BorderPane();
            root.setCenter(btn);
            Scene scene = new Scene(root,200, 200);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    void handleButton(ActionEvent e){
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.showAndWait();
    }

    static Locale getLocaleSettingFromConfigurationFile(){
        return Locale.FRENCH;
        //return new Locale("vi");
    }

    public static void main(String[] args) {
        Locale appLocale = getLocaleSettingFromConfigurationFile();
        Locale.setDefault(appLocale);

        launch(args);
    }
}

The language setting is obtained via getLocaleSettingFromConfigurationFile() method
In the code above, I used Locale.FRENCH as app language and everything works file:

Two confirm buttons have been translated to French.

Now I want my app to support Vietnamese as well (uncomment return new Locale("vi") from the code above). After digging into details, I found that:

->Two confirm button "Ok", "Cancel" are constructed from:

package javafx.scene.control;

import com.sun.javafx.scene.control.skin.resources.ControlResources;

import javafx.beans.NamedArg;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;

/**
 * The ButtonType class is used as part of the JavaFX {@link Dialog} API (more
 * specifically, the {@link DialogPane} API) to specify which buttons should be
 * shown to users in the dialogs. Refer to the {@link DialogPane} class javadoc
 * for more information on how to use this class.
 *
 * @see Alert
 * @see Dialog
 * @see DialogPane
 * @since JavaFX 8u40
 */
public final class ButtonType {

    /**
     * A pre-defined {@link ButtonType} that displays "Apply" and has a
     * {@link ButtonData} of {@link ButtonData#APPLY}.
     */
    public static final ButtonType APPLY = new ButtonType(
            "Dialog.apply.button", null, ButtonData.APPLY);

    /**
     * A pre-defined {@link ButtonType} that displays "OK" and has a
     * {@link ButtonData} of {@link ButtonData#OK_DONE}.
     */
    public static final ButtonType OK = new ButtonType(
            "Dialog.ok.button", null, ButtonData.OK_DONE);

    /**
     * A pre-defined {@link ButtonType} that displays "Cancel" and has a
     * {@link ButtonData} of {@link ButtonData#CANCEL_CLOSE}.
     */
    public static final ButtonType CANCEL = new ButtonType(
            "Dialog.cancel.button", null, ButtonData.CANCEL_CLOSE);

    /**
     * A pre-defined {@link ButtonType} that displays "Close" and has a
     * {@link ButtonData} of {@link ButtonData#CANCEL_CLOSE}.
     */
    public static final ButtonType CLOSE = new ButtonType(
            "Dialog.close.button", null, ButtonData.CANCEL_CLOSE);

    /**
     * A pre-defined {@link ButtonType} that displays "Yes" and has a
     * {@link ButtonData} of {@link ButtonData#YES}.
     */
    public static final ButtonType YES = new ButtonType(
            "Dialog.yes.button", null, ButtonData.YES);

    /**
     * A pre-defined {@link ButtonType} that displays "No" and has a
     * {@link ButtonData} of {@link ButtonData#NO}.
     */
    public static final ButtonType NO = new ButtonType(
            "Dialog.no.button", null, ButtonData.NO);

    /**
     * A pre-defined {@link ButtonType} that displays "Finish" and has a
     * {@link ButtonData} of {@link ButtonData#FINISH}.
     */
    public static final ButtonType FINISH = new ButtonType(
            "Dialog.finish.button", null, ButtonData.FINISH);

    /**
     * A pre-defined {@link ButtonType} that displays "Next" and has a
     * {@link ButtonData} of {@link ButtonData#NEXT_FORWARD}.
     */
    public static final ButtonType NEXT = new ButtonType(
            "Dialog.next.button", null, ButtonData.NEXT_FORWARD);

    /**
     * A pre-defined {@link ButtonType} that displays "Previous" and has a
     * {@link ButtonData} of {@link ButtonData#BACK_PREVIOUS}.
     */
    public static final ButtonType PREVIOUS = new ButtonType(
            "Dialog.previous.button", null, ButtonData.BACK_PREVIOUS);

    private final String key;
    private final String text;
    private final ButtonData buttonData;


    /**
     * Creates a ButtonType instance with the given text, and the ButtonData set
     * as {@link ButtonData#OTHER}.
     *
     * @param text The string to display in the text property of controls such
     *      as {@link Button#textProperty() Button}.
     */
    public ButtonType(@NamedArg("text") String text) {
        this(text, ButtonData.OTHER);
    }

    /**
     * Creates a ButtonType instance with the given text, and the ButtonData set
     * as specified.
     *
     * @param text The string to display in the text property of controls such
     *      as {@link Button#textProperty() Button}.
     * @param buttonData The type of button that should be created from this ButtonType.
     */
    public ButtonType(@NamedArg("text") String text,
                        @NamedArg("buttonData") ButtonData buttonData) {
        this(null, text, buttonData);
    }

    /**
     * Provide key or text. The other one should be null.
     */
    private ButtonType(String key, String text, ButtonData buttonData) {
        this.key = key;
        this.text = text;
        this.buttonData = buttonData;
    }

    /**
     * Returns the ButtonData specified for this ButtonType in the constructor.
     */
    public final ButtonData getButtonData() { return this.buttonData; }

    /**
     * Returns the text specified for this ButtonType in the constructor;
     */
    public final String getText() {
        if (text == null && key != null) {
            return ControlResources.getString(key);
        } else {
            return text;
        }
    }

    /** {@inheritDoc} */
    @Override public String toString() {
        return "ButtonType [text=" + getText() + ", buttonData=" + getButtonData() + "]";
    }
}

->The button displaying text is rendered from ControlResources.getString(key), its source code:

package com.sun.javafx.scene.control.skin.resources;

import java.util.ResourceBundle;

public final class ControlResources {

    // Translatable properties
    private static final String BASE_NAME = "com/sun/javafx/scene/control/skin/resources/controls";

    // Non-translateable properties
    private static final String NT_BASE_NAME = "com/sun/javafx/scene/control/skin/resources/controls-nt";

    // Do not cache the bundle here. It is cached by the ResourceBundle
    // class and may be updated if the default locale changes.

    private ControlResources() {
        // no-op
    }

    /*
     * Look up a string in the properties file corresponding to the
     * default locale (i.e. the application's locale). If not found, the
     * search then falls back to the base controls.properties file,
     * containing the default string (usually English).
     */
    public static String getString(String key) {
        return ResourceBundle.getBundle(BASE_NAME).getString(key);
    }

    /*
     * Look up a non-translatable string in the properties file
     * corresponding to the default locale (i.e. the application's
     * locale). If not found, the search then falls back to the base
     * controls-nt.properties file, containing the default string.
     *
     * Note that property values may be set in locale-specific files,
     * e.g. when a property value is defined for a country rather than
     * a language. However, there are no such files included with
     * JavaFX 8, but may be added to the classpath by developers or
     * users.
     */
    public static String getNonTranslatableString(String key) {
        return ResourceBundle.getBundle(NT_BASE_NAME).getString(key);
    }
}

Now, I tried my solution as follow:
Step 1: create Vietnamese resource file com/sun/javafx/scene/control/skin/resources/controls_vi.properties in the project

### Dialogs ###

Dialog.apply.button = Áp d\u1EE5ng
Dialog.ok.button = OK
Dialog.close.button = \u0110óng
Dialog.cancel.button = H\u1EE7y b\u1ECF
Dialog.yes.button = Có
Dialog.no.button = Không
Dialog.finish.button = Hoàn thành 
Dialog.next.button = Ti\u1EBFp 
Dialog.previous.button = Tr\u01B0\u1EDBc 

After lauching the app, the button language still English.
Step 2: I figured out that the class loader to load JavaFx resource file is differ from my app class loader (see ResourceBundle.getBundle(BASE_NAME) API). This is resource inside jfxrt.jar:

I tried to load the ControlResources class with application class loader but still no result:

public static void main(String[] args) throws Exception {
    List<Locale> fxSupported = Arrays.asList(Locale.ENGLISH, Locale.FRENCH); // Add later ....
    Locale appLocale = getLocaleSettingFromConfigurationFile();
    Locale.setDefault(appLocale);

    // Load class from current class loader
    if (!fxSupported.contains(appLocale)){
        ClassLoader loader = Main.class.getClassLoader();
        Class<?> loadedCls = Class.forName("com.sun.javafx.scene.control.skin.resources.ControlResources", true, loader);

        System.out.printf("Loader 1: %s\nloader 2: %s\n", loader, loadedCls.getClassLoader());
//          Loader 1: sun.misc.Launcher$AppClassLoader@73d16e93
//          loader 2: sun.misc.Launcher$ExtClassLoader@6d06d69c

    }

    launch(args);
}

Fallback solution
I can create my own ButtonType "OK", "Cancel" and load my own resource string, the set created button list to the Alert object, but I want to use the system provided resource instead.

ResourceBundle res = ResourceBundle.getBundle("application.myownres");
        ButtonType OK = new ButtonType(res.getString("btn.ok"), ButtonData.OK_DONE);
        ButtonType CANCEL = new ButtonType(res.getString("btn.cancel"), ButtonData.CANCEL_CLOSE);

        Alert alert = new Alert(AlertType.CONFIRMATION, "Are you sure", OK, CANCEL);
        alert.showAndWait();

So, anyone has solution that does not need to create new ButtonType object.
Thanks

解决方案

I am very upset that the JRE inside very few exotic languages. And this is a big problem. I've been looking for a solution, I created an open source project which demonstrates how to add new languages resources in this project.

Project on GitHub

I translated the system controls JavaFX into a new language (be-BY, ru-RU):

Structure of my project:

java
    |------ com\krasutski\language\Messages.java
    |------ com\krasutski\util\PropertyLoader.java
    |------ com\krasutski\util\ReflectionUtils.java
    |------ com\krasutski\view\MainController.java
    |------ com\krasutski\MainApp.java
resources
    |------ com\sun\javafx\scene\control\skin\resources\controls_be_BY.properties
    |------ com\sun\javafx\scene\control\skin\resources\controls_ru.properties
    |------ fxml\main.fxml
    |------ icons\app-128x128x32.png
    |------ messages\messages.properties
    |------ messages\messages_be_BY.properties
    |------ messages\messages_ru.properties
    |------ styles\styles.css

the solution to the problem is in 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;
    }
}

and in 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;
    }
}

An example slice file controls_be_BY.properties

# encoding=utf-8
# ProgressIndicator, the string that's displayed at 100%
ProgressIndicator.doneString=Гатова

# ListView
ListView.noContent=Няма змесціва

# TableView
TableView.noContent=Няма змесціва ў табліцы
TableView.noColumns=Няма калонак ў табліцы

Here you don't need to use a special character \u you just write to any text editor which supports Unicode.

You can add your exotic languages folder resources/com/sun/javafx/scene/control/skin/resources of this project. Send me your controls_*.properties and I'll add them to this project.

Ready assembled example you can download in the releases section

这篇关于Javafx国际化与自定义语言的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆