2

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
F_Schmidt
  • 902
  • 1
  • 11
  • 32
  • 1
    The methods and practices depend on the environment where your program runs. Is it a desktop app? Android app? Web app? For example, for desktop and mobile it's reasonable to expect the system default locale match what the user expects, but not for web app – Joni Apr 04 '20 at 16:15
  • Related: [*How to get Locale from its String representation in Java?*](https://stackoverflow.com/q/2522248/642706) – Basil Bourque Apr 04 '20 at 23:02

3 Answers3

2

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/

How to load a resource bundle from a file resource in Java?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Rupesh
  • 2,627
  • 1
  • 28
  • 42
  • Yes, I know how to create rescource bundles and I will create one for each language. But is it a smart idea to somehow store all languages that my app supports within an array / arraylist? Then I don't have to create a new locale-object whenever I want to get a custom resource or something else? – F_Schmidt Apr 04 '20 at 15:35
  • Yes that way it makes sense. You can define enum and provide factory methods to load resource bundles which can then force user to select values defined in enum. Whenever you add one more resource you can add an entry in enum so that client can use it. – Rupesh Apr 04 '20 at 15:38
  • Okay, that sounds good. Can you explain how I could create that factory method? Not quite sure how your idea is... – F_Schmidt Apr 04 '20 at 15:45
1

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

You could always use the country code corresponding to the ISO 3166 standard. For Spanish that would be

Locale spanish = new Locale("es", "ES");
Hakan Dilek
  • 2,178
  • 2
  • 23
  • 35