英文:
What is the best practice for locales and multiple language support?
问题
我想在我的程序中使用多种语言。Java的Locale
类有一些预设的语言环境,比如Locale.GERMAN
、Locale.ENGLISH
等等。但我还需要其他语言环境,比如西班牙语等等。
如何妥善地存储并正确地使用它们呢?有什么最佳实践吗?
我是否应该创建一种称为LanguageHandler的机制,将所有需要的语言环境存储在一个数组中?在这个主题上是否有一些经验和做法?
编辑:这个应用程序是为桌面平台编写的,使用Java语言。
英文:
I want to have multiple languages in my program. The java Locale
class has some locales for example Locale.GERMAN
, Locale.ENGLISH
and so on. But I need additional locales for example spanish etc.
How can I store them and use them properly? What is the best practice?
Should I create some kind of LanguageHandler that stores all required locales in an array? Any experiences and practices on that topic?
Edit: The application is for Desktop and written in Java.
答案1
得分: 2
获取所有支持的区域设置列表:
您可以使用 Locale spanish = new Locale("es", "ES");
来使用西班牙的区域设置。
如何存储它们并正确使用?有什么最佳实践吗?
根据我的理解,您不必存储一个支持的区域设置的主列表。您可以按需使用它们。通过如上所述定义区域设置将帮助您通过传递区域设置对象来加载ResourceBundles,但您需要为您在应用程序中想要使用的每个区域设置创建ResourceBundles。
例如,对于不同语言的某些资源,您可能想要考虑翻译。
进一步阅读如何使用区域设置初始化ResourceBundle。
http://tutorials.jenkov.com/java-internationalization/resourcebundle.html
https://mkyong.com/java/java-resourcebundle-example/
英文:
Get a list of all the supported locales:
You can use Locale spanish = new Locale("es", "ES");
to use Spanish in Spain locale.
> How can I store them and use them properly? What is the best practice?
As per my understanding you don't have to store a master list of supported locales. You can use them as you go. By defining locale as mentioned above will help you load the ResourceBundles by passing locale objects but then you would need to create ResourceBundles for each locale you want to use in your application.
For example for some resources in different language you might want to consider translations.
Further reading on how to use locale to init resourceBundle.
http://tutorials.jenkov.com/java-internationalization/resourcebundle.html
答案2
得分: 1
1中的回答是正确的,应该被接受。他的评论促使了这个回答。
枚举
关于如何跟踪你的应用当前支持的区域设置(locales),可以定义一个枚举。枚举是处理在编译时已知并且在执行过程中不变的项目列表的好方法。请参见教程(来自 Oracle)。
public enum L10n
{
fr_CA, es_ES; // 引用变量的名称会在加载该类时自动设置为指向对象的名称,这些对象在自动实例化时被实例化。
}
使用构造函数传递每个枚举元素所代表的Locale
对象。我们将L10n
作为枚举的类名,其中“L10N”是“本地化”一词的常见缩写。
public enum L10n
{
// 枚举元素。
fr_CA(Locale.CANADA_FRENCH),
es_ES(new Locale("es", "ES"));
final private Locale locale;
// 构造函数
L10n(Locale localeArg)
{
this.locale = Objects.requireNonNull(localeArg);
}
}
枚举元素是常量。因此在 Java 命名约定中,每个元素的名称应全部大写,如FR_CA
和ES_ES
。我认为在这种情况下,我们可以将其视为该约定的一个例外,因为大小写对于程序员识别每个名称的含义(语言和文化代码)非常重要。
你可以使用此枚举中包含的Locale
对象来访问你的资源束(Resource Bundle)。我们添加一个getResourceBundle
方法来提供这个功能。
public enum L10n
{
// 枚举元素。
fr_CA(Locale.CANADA_FRENCH),
es_ES(new Locale("es", "ES"));
final private Locale locale; // 记住每个枚举元素后面的`Locale`对象。
// 构造函数
L10n(Locale localeArg)
{
this.locale = Objects.requireNonNull(localeArg);
}
// 访问特定区域设置的资源束(Resource Bundle)。
public ResourceBundle getResourceBundle()
{
…
}
}
注意:考虑线程安全性。每个枚举对象在整个应用程序中只有一个实例。因此,如果可能在多个线程之间调用getResourceBundle
方法,它必须是线程安全的。如果只从单个与 UI 相关的线程调用,则线程安全性不是问题。但请记住考虑可能从除主 UI 线程之外的线程调用的测试套件、日志记录或其他位置。
使用getResourceBundle
方法的示例。
L10n preferredLocalization = L10n.es_ES; // 可能是从与用户的对话交互中获取的。
ResourceBundle resourceBundle = preferredLocalization.getResourceBundle();
现在,此枚举在处理资源束(Resource Bundle)时充当外观(facade)。您可以重新设计该处理代码,而不影响在应用程序中调用L10n::getResourceBundle
的各个位置。
为了展示应用程序支持的本地化列表,添加一个获取显示名称的方法。此方法委托给包含的Locale
对象的Locale::getDisplayName
方法。您可能希望将该显示名称本地化为每个区域设置的语言和文化规范。
public enum L10n
{
// 枚举元素。
fr_CA(Locale.CANADA_FRENCH),
es_ES(new Locale("es", "ES"));
final private Locale locale; // 记住每个枚举元素后面的`Locale`对象。
// 构造函数
L10n(Locale localeArg)
{
this.locale = Objects.requireNonNull(localeArg);
}
// 访问特定区域设置的资源束(Resource Bundle)。
public ResourceBundle getResourceBundle()
{
…
}
// 用于向用户展示。
public String getDisplayName()
{
String name = this.locale.getDisplayName(this.locale); // 将一个`Locale`传递给`Locale::getDisplayName`,以自动本地化该区域设置的名称。
return name;
}
}
获取显示名称列表。
您可以获得枚举元素的集合。Enum.values
方法是Java 规范中定义的一个特殊的“隐式”方法,返回在该枚举上定义的所有对象的数组。不幸的是,该方法几乎在Enum
的 Javadoc 中被省略,只在valueOf
方法的描述中提到。
for (L10n localization : L10n.values())
{
System.out.println(localization.getDisplayName());
}
您甚至可以通过为枚举类添加另一个成员字段来实现更加复杂的效果,用于表示语言和文化的国家旗帜图标。然后,该旗帜可以出现在用户界面中,用于标识当前的本地化。在构造函数的第二个参数中为每个枚举元素传递一个图标图像,或者在枚举类上编写代码来查找图标图像。当然,还需要在我们的枚举类中添加一个getFlag
方法来返回该旗帜图像。
[7
英文:
The Answer by Rupesh is correct and should be accepted. His comment prompted this Answer.
Enum
Regarding how to track the locales currently supported by your app, define an enum. An enum is a good way to handle a list of items known at compile-time and unchanging during execution. See tutorial by Oracle.
public enum L10n
{
fr_CA , es_ES ; // Names of reference variables automatically set to point to objects automatically instantiated when this class loads.
}
Use a constructor to pass the Locale
object represented by each enum element. We use L10n
as the class name for our enum, with “L10N” being a common abbreviation of the word “localization”.
public enum L10n
{
// Enum elements.
fr_CA( Locale.CANADA_FRENCH ) ,
es_ES( new Locale("es", "ES") ) ;
final private Locale locale ;
// Constructor
L10n( Locale localeArg )
{
this.locale = Objects.requireNonNull( localeArg ) ;
}
}
Enum elements are constants. So in Java naming conventions, each element name should be in all uppercase, such as FR_CA
& ES_ES
. I consider our case here to be an exception to that convention, as the upper/lowercase is important for the programmer to recognize the meaning of each name (language & culture codes).
You can use the Locale
objects contained in this enum to access your resource bundle. We add a getResourceBundle
method to provide this facility.
public enum L10n
{
// Enum elements.
fr_CA( Locale.CANADA_FRENCH ) ,
es_ES( new Locale("es", "ES") ) ;
final private Locale locale ; // Remember the `Locale` object behind each element in this enum.
// Constructor
L10n( Locale localeArg )
{
this.locale = Objects.requireNonNull( localeArg ) ;
}
// Access the ResourceBundle for a particular locale.
public ResourceBundle getResourceBundle()
{
…
}
}
Caution: Think about thread-safety. There is only one instance per enum object across your app. So that getResourceBundle
method must be thread-safe if you might invoke across threads. If only called from within the single UI-related thread, then thread-safety is not an issue. But remember to consider test suites, logging, or other places that might call from threads other than the main UI thread.
Example using that getResourceBundle
method.
L10n preferredLocalization = L10n.es_ES ; // Perhaps taken from a dialog interaction with user.
ResourceBundle resourceBundle = preferredLocalization.getResourceBundle() ;
This enum now acts as a façade in front of your resource bundle handling. You can refactor that handling code without affecting the places throughout your app where you call L10n::getResourceBundle
.
To present a list of the localizations supported by your app, add a method to get a display name. This method delegates to the contained Locale
object’s Locale::getDisplayName
method. You might want to localize that display name to the language and cultural norms of each locale itself.
public enum L10n
{
// Enum elements.
fr_CA( Locale.CANADA_FRENCH ) ,
es_ES( new Locale("es", "ES") ) ;
final private Locale locale ; // Remember the `Locale` object behind each element in this enum.
// Constructor
L10n( Locale localeArg )
{
this.locale = Objects.requireNonNull( localeArg ) ;
}
// Access the ResourceBundle for a particular locale.
public ResourceBundle getResourceBundle()
{
…
}
// For presentation to the user.
public String getDisplayName()
{
String name = this.locale.getDisplayName( this.locale ) ; // Pass a `Locale` to `Locale::getDisplayName` to have that locale's name automatically localized.
return name ;
}
}
Get a list of display names.
You can get a collection of the enum’s elements. The Enum.values
method is a special “implicit” method defined in the Java specs returning an array of all the objects defined as elements on that enum. Unfortunately this method is nearly omitted from the Enum
Javadoc, with a mention buried in the valueOf
method’s description.
for ( L10n localization : L10n.values() )
{
System.out.println( localization.getDisplayName() );
}
>français (Canada)
>español (España)
You could get even fancier with this by adding another member field to the enum class for an icon of the country flag representing that language & culture. The flag could then appear in the user-interface to identify the current localization. Pass an icon image for each enum element as a second argument on the constructor. Or write code on the enum class to do a lookup for the icon image. And of course add a getFlag
method to our enum class to return said flag image.
答案3
得分: 0
你可以始终使用与 ISO 3166 标准相对应的国家代码。对于西班牙语来说,那将是
Locale spanish = new Locale("es", "ES");
英文:
You could always use the country code corresponding to the ISO 3166 standard. For Spanish that would be
Locale spanish = new Locale("es", "ES");
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论