Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java currency display name internationalization

Is there any reason why certain display names are displayed in English despite the locales not being in English (ie: not translated).

For example: Locale: "ru" is not translated

Locale locale = new Locale("ru");
Currency curr = Currency.getInstance("USD");
System.out.println(curr.getDisplayName(locale));
// US Dollar

Locale: "es" is translated

Locale locale = new Locale("es");
Currency curr = Currency.getInstance("USD");
System.out.println(curr.getDisplayName(locale));
// dólar estadounidense

Is this intentional? Or has Java not gotten around to translating it? Or am I doing something incorrectly?

I tried to locate the files where these translations are stored but couldn't find them. If someone could point me to that resource as well that would be helpful.

Thanks.

like image 744
theyuv Avatar asked Oct 31 '17 10:10

theyuv


1 Answers

Locale Service Providers

Java uses an extensible mechanism to provide data (such as strings, formatters, etc) that is localized.

Classes can implement LocaleServiceProvider to be a factory of local-sensitive data. A lot of classes in java.util and java.text rely on these providers to work properly with different Locale by delegating the creating of objects to them.

You can find examples of Local Service Providers in the java.util.spi package, that are usually used to display text or numbers in a way that is dependent on the Locale. It includes CurrencyNameProvider which is used by Currency when calling Currency#getDisplayName.

Finding implementations

Classes wanting to use a specific LocaleServiceProvider (such as CurrencyNamePovider) use LocaleServiceProviderPool to find the instances of the provider that support a specific Locale.

LocaleServiceProviderPool tries first to use a default implementation included in the JRE. If it's not found, it relies on the simple Service Provider Interface (SPI) mechanism in Java and uses a ServiceLoader1 to try to find implementations provided by third party libraries.

This is what is written in the tutorial about Locale:

These methods first check whether the Java runtime environment supports the requested locale; if so, they use that support. Otherwise, the methods call the getAvailableLocales() methods of installed providers for the appropriate interface to find a provider that supports the requested locale.

The default implementations of the providers that come with the JRE can be found in the package sun.util.locale.provider. It's complex, but it essentially gets the data from the jar localedata.jar. In Oracle JDK, it is located in java_home/jre/lib/ext/localedata.jar. If you list the files that are in this jar, and check the files in sun.util.resources.es and in sun.util.resources.ru, you will see that there is many more currency names defined for Spanish than for Russian.

Here are the files for OpenJDK: Russian vs Spanish.

What if it's not defined at all

Locales are organized in a hierarchy. For example, a specific region of a country can have a Locale which reflects some local differences from the Locale of the country. If data is not found for a Locale, LocaleServiceProviderPool will try to use the Locale's parent.

The root of the tree of Locales is basically a "fallback" fictional Locale which provides default values for all localized data.

That's what probably happening when you ask for the display name of USD in russian.

Extensibility

Any program can provide additional Locale information. They need to define a service provider1 by creating the metadata file and implement a CurrencyNameProvider. You can fill in the missing localized data in your own jar.

Conclusion

Or has Java not gotten around to translating it?

That's pretty much the case.

Or am I doing something incorrectly?

No, you can either rely on the defaults or provide the localized data yourself.


1 The ServiceLoader will find them by asking the classloader to load the resource META-INF/services/java.text.spi.DateFormatProvider. If such file is found, it should have in it the specific class name of the implementation. It then tries to create an instance of it through the classloader.

like image 54
Duarte Meneses Avatar answered Nov 11 '22 14:11

Duarte Meneses