JavaFX 2 및 국제화


84

기본 사항을 배운 후 첫 번째 JavaFX 2 응용 프로그램을 작성하기 시작했으며이를 국제화하고 싶습니다.

JavaFX 1.x에서 스크립팅 언어는 문자열의 매우 간단한 국제화를 허용했습니다. JavaFX 2에 유사한 기능이 있습니까?

기본적으로 : JavaFX 2 애플리케이션을 국제화하기위한 모범 사례는 무엇입니까?


[: 언어를 전환하기위한 몇 가지 정보 인 stackoverflow.com/a/26318795/2131257][1] [1] stackoverflow.com/a/26318795/2131257
— Androdos

답변:


166

자바 앱 국제화의 기본 단계는 Locale라이징 및 리소스 번들링입니다. 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);
    }
}

스크린 샷 :

여기에 이미지 설명 입력


훌륭한 대답이며 그대로 받아들이 겠지만 FXML이 아닌 코드로 인터페이스를 구축하고 있다고 언급 했어야했습니다. 코드에서 국제화하는 빠르고 쉬운 방법이 있습니까? ResourceBundle.getBundle + 조회를 수행 할 수 있지만 대신 사용할 수있는 % key 표기법과 같은 것이 있기를 바랐습니다.
— wobblycogs 2012

7
그런 다음 다른 Java 응용 프로그램에서와 같이 일반적인 방법으로 수행 할 수 있습니다. 사용자 / 클라이언트의 로케일을 결정한 다음 그에 따라 앱의 로케일을 변경합니다 (DB 대에서 언어 별 데이터 가져 오기). 으로 적절한 번들을로드합니다 ResourceBundle.getBundle("bundles.MyBundle", locale). 보기 / 페이지에서 사용한 모든 텍스트를 bundle.getString("key").
— Uluk Biy 2012

2
setResources () 메서드를 통해 ResourceBundle을 제공하면 작동하지 않습니다. load () 메서드를 통해 ResourceBundle을 제공 할 때 작동합니다.
— Jurica Krizanic 2013 년

1
@Jurica Krizanic는 : 같은 문제가 있고, 같은 방법을 통해 해결 : 문자열과 방법 적절한 로케일을 반환 자원. FXMLLoader.load(getClass().getResource(sceneId), getResources())sceneIdgetResources()
— TG

훌륭합니다. oracle 튜토리얼과 같은 정적 방식의 변형도 작동합니다 (짧은 길이 임). Pane pane = (BorderPane) FxmlLoader.load (this.getClass (). getResource ( "MyView.fxml"), ResourceBundle.getBundle ( "bundles.MyBundle", 로케일));
— pdem

15

이것은 나를 위해 작동합니다.

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

번들 패키지에는

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"));
    // ...
}

4

내 예를 봐 여기에 이미지 설명 입력

여기 또는 GitHub 에 설명 된 추가 정보

최신 정보:

해결책은 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에 대한 응답으로 링크를 포함했습니다. 당신 때문에 나는 모든 곳에 동일한 코드를 삽입해야합니다. 싫어하는처럼이 아니라 내가 당신을 기대
— 안드레이 Krasutski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.