- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在开发一个支持多语言的 JavaFX 应用程序。我的应用有时会显示一个警告框,例如:
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);
}
}
语言设置通过getLocaleSettingFromConfigurationFile()
方法获取
在上面的代码中,我使用 Locale.FRENCH
作为应用程序语言,一切正常文件:
两个确认按钮已翻译成法语。
现在我希望我的应用程序也支持越南语(取消上面代码中的 return new 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;
/**
* 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() + "]";
}
}
->显示文字的按钮由ControlResources.getString(key)
渲染,其源码:
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);
}
}
现在,我尝试了以下解决方案:
第一步:在项目中创建越南语资源文件com/sun/javafx/scene/control/skin/resources/controls_vi.properties
### 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
启动应用程序后,按钮语言仍然是英文。
第 2 步:我发现加载 JavaFx 资源文件的类加载器与我的应用程序类加载器不同(参见 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); // 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);
}
回退解决方案
我可以创建我自己的 ButtonType
“OK”、“Cancel” 并加载我自己的资源字符串、设置创建的按钮列表到 Alert
对象,但我想使用系统提供资源。
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();
因此,任何人都有不需要创建新的 ButtonType
对象的解决方案。
谢谢
最佳答案
我很苦恼的是JRE里面很少有外来语言。这是一个大问题。我一直在寻找解决方案,我创建了一个开源项目来演示如何在该项目中添加新的语言资源。
项目 GitHub
我将系统控件 JavaFX 翻译成一种新语言(be-BY,ru-RU):
我的项目结构:
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
问题的解决方案在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;
}
}
示例切片文件 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=Няма калонак ў табліцы
在这里你不需要使用特殊字符\u
你只需要写到任何支持Unicode的文本编辑器。
您可以添加您的异国语言文件夹 resources/com/sun/javafx/scene/control/skin/resources
这个项目。将您的 controls_*.properties
发送给我,我会将它们添加到该项目中。
您可以在 releases 下载准备好的示例。节
关于使用自定义语言的 Javafx 国际化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40838804/
至少在某些 ML 系列语言中,您可以定义可以执行模式匹配的记录,例如http://learnyouahaskell.com/making-our-own-types-and-typeclasses -
这可能是其他人已经看到的一个问题,但我正在尝试寻找一种专为(或支持)并发编程而设计的语言,该语言可以在 .net 平台上运行。 我一直在 erlang 中进行辅助开发,以了解该语言,并且喜欢建立一个稳
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be
我正在寻找一种进程间通信工具,可以在相同或不同系统上运行的语言和/或环境之间使用。例如,它应该允许在 Java、C# 和/或 C++ 组件之间发送信号,并且还应该支持某种排队机制。唯一明显与环境和语言
我有一些以不同语言返回的文本。现在,客户端返回的文本格式为(en-us,又名美国英语): Stuff here to keep. -- Delete Here -- all of this below
问题:我希望在 R 中找到类似 findInterval 的函数,它为输入提供一个标量和一个表示区间起点的向量,并返回标量落入的区间的索引。例如在 R 中: findInterval(x = 2.6,
我是安卓新手。我正在尝试进行简单的登录 Activity ,但当我单击“登录”按钮时出现运行时错误。我认为我没有正确获取数据。我已经检查过,SQLite 中有一个与该 PK 相对应的数据。 日志猫。
大家好,感谢您帮助我。 我用 C# 制作了这个计算器,但遇到了一个问题。 当我添加像 5+5+5 这样的东西时,它给了我正确的结果,但是当我想减去两个以上的数字并且还想除或乘以两个以上的数字时,我没有
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 4 年前。 Improve th
这就是我所拥有的 #include #include void print(int a[], int size); void sort (int a[], int size); v
你好,我正在寻找我哪里做错了? #include #include int main(int argc, char *argv[]) { int account_on_the_ban
嘿,当我开始向数组输入数据时,我的代码崩溃了。该程序应该将数字读入数组,然后将新数字插入数组中,最后按升序排列所有内容。我不确定它出了什么问题。有人有建议吗? 这是我的代码 #include #in
我已经盯着这个问题好几个星期了,但我一无所获!它不起作用,我知道那么多,但我不知道为什么或出了什么问题。我确实知道开发人员针对我突出显示的行吐出了“错误:预期表达式”,但这实际上只是冰山一角。如果有人
我正在编写一个点对点聊天程序。在此程序中,客户端和服务器功能写入一个唯一的文件中。首先我想问一下我程序中的机制是否正确? I fork() two processes, one for client
基本上我需要找到一种方法来发现段落是否以句点 (.) 结束。 此时我已经可以计算给定文本的段落数,但我没有想出任何东西来检查它是否在句点内结束。 任何帮助都会帮助我,谢谢 char ch; FI
我的函数 save_words 接收 Armazena 和大小。 Armazena 是一个包含段落的动态数组,size 是数组的大小。在这个函数中,我想将单词放入其他称为单词的动态数组中。当我运行它时
我有一个结构 struct Human { char *name; struct location *location; int
我正在尝试缩进以下代码的字符串输出,但由于某种原因,我的变量不断从文件中提取,并且具有不同长度的噪声或空间(我不确定)。 这是我的代码: #include #include int main (v
我想让用户选择一个选项。所以我声明了一个名为 Choice 的变量,我希望它输入一个只能是 'M' 的 char 、'C'、'O' 或 'P'。 这是我的代码: char Choice; printf
我正在寻找一种解决方案,将定义和变量的值连接到数组中。我已经尝试过像这样使用 memcpy 但它不起作用: #define ADDRESS {0x00, 0x00, 0x00, 0x00, 0x0
我是一名优秀的程序员,十分优秀!