最佳实践是什么,用于本地化和多语言支持?

huangapple go评论112阅读模式
英文:

What is the best practice for locales and multiple language support?

问题

我想在我的程序中使用多种语言。Java的Locale类有一些预设的语言环境,比如Locale.GERMANLocale.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/

https://stackoverflow.com/questions/1172424/how-to-load-a-resource-bundle-from-a-file-resource-in-java

英文:

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

https://mkyong.com/java/java-resourcebundle-example/

https://stackoverflow.com/questions/1172424/how-to-load-a-resource-bundle-from-a-file-resource-in-java

答案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_CAES_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");

huangapple
  • 本文由 发表于 2020年4月4日 23:18:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/61030257.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定